OpenCV 入门(四)—— 车牌号识别

news2024/11/21 2:33:12

OpenCV 入门系列:

OpenCV 入门(一)—— OpenCV 基础
OpenCV 入门(二)—— 车牌定位
OpenCV 入门(三)—— 车牌筛选
OpenCV 入门(四)—— 车牌号识别
OpenCV 入门(五)—— 人脸识别模型训练与 Windows 下的人脸识别
OpenCV 入门(六)—— Android 下的人脸识别
OpenCV 入门(七)—— 身份证识别

本节是车牌识别的最后一部分 —— 车牌字符识别,从一个完整的车牌图片到识别出车牌上的字符大致需要如下几步:

  • 预处理:将车牌图片灰度化、二值化,并去除识别时的干扰因素,比如车牌铆钉
  • 字符分割:将整个车牌图片按照每个字符分割成 7 个单独的字符图片保存到集合中
  • 字符识别:使用经过训练的数字、英文字符、中文字符的特征集合对字符图片进行识别,得到最终的车牌号

下面详解以上步骤。

1、预处理

图像识别的预处理工作一般都是灰度化、二值化这些图像“降噪”处理,当然我们这里还有一个特殊的处理,就是要祛除车牌图像上的铆钉,它是对识别准确度影响较大的一个因素。

1.1 AnnPredictor 预处理

新建一个类 AnnPredictor 用于进行字符识别:

#ifndef ANNPREDICTOR_H
#define ANNPREDICTOR_H

#define ANNPREDICTOR_DEBUG

#include <opencv2/opencv.hpp>
#include <string>
#include <opencv2/ml.hpp>

using namespace std;
using namespace cv;
using namespace ml;

class AnnPredictor {
public:
	AnnPredictor(const char* ann_model, const char* ann_zh_model);
	~AnnPredictor();
	// 字符识别
	string predict(Mat plate);

private:
	// 用于数字和英文字符识别
	Ptr<ANN_MLP> ann;
	// 用于中文字符识别
	Ptr<ANN_MLP> ann_zh;
	// ANN 的 HOG 特征
	HOGDescriptor* annHog = nullptr;
	// 与 SvmPredictor 中的函数相同
	void getHOGFeatures(HOGDescriptor* svmHog, Mat src, Mat& dst);
	// 去除铆钉
	bool clearRivet(Mat &plate);
	// 验证单个字符尺寸
	bool verifyCharSize(Mat src);
	// 获取城市汉字字符在排序后的矩形集合中的索引
	int getCityIndex(vector<Rect> rects);
	// 获取中文字符所在的矩形
	void getChineseRect(Rect cityRect, Rect& chineseRect);
	// 识别车牌字符保存到 str_plate 中
	void predict(vector<Mat> plateCharMats, string& str_plate);
	// 汉字字符集合
	static string ZHCHARS[];
	// 数字与英文字符集合
	static char CHARS[];
};

#endif // !ANNPREDICTOR_H

这样在 LicensePlateRecognizer 中调用 predict() 即可获取到车牌字符串:

LicensePlateRecognizer::LicensePlateRecognizer(const char* svm_model, const char* ann_model, const char* ann_zh_model)
{
	...
	annPredictor = new AnnPredictor(ann_model, ann_zh_model);
}

LicensePlateRecognizer::~LicensePlateRecognizer()
{
	...
	if (annPredictor)
	{
		delete annPredictor;
		annPredictor = nullptr;
	}
}

string LicensePlateRecognizer::recognize(Mat src)
{
	// 1.车牌定位,使用 Sobel 算法定位
	// 2.精选车牌定位得到的候选车牌图,找到最有可能是车牌的图
	...

	// 3.对车牌图进行字符识别
	string str_plate = annPredictor->predict(plate);
	plate.release();

	return str_plate;
}

predict() 内,先进行预处理,像灰度化和二值化这些操作前面已出现过多次就不再赘述了:

string AnnPredictor::predict(Mat plate)
{
	// 1.预处理 
	// 1.1 灰度化
	Mat gray;
	cvtColor(plate, gray, COLOR_BGR2GRAY);

	// 1.2 二值化(非黑即白,对比更强烈)
	Mat shold;
	threshold(gray, shold, 0, 255, THRESH_OTSU + THRESH_BINARY);

	// 1.3 去铆钉
	if (!clearRivet(shold))
	{
		return string("未识别到车牌");
	}
    ...
}

主要说一下如何去掉车牌图片上的铆钉。

1.2 去掉车牌上的铆钉

二值化后的图片仍能看到车牌上的铆钉,这是影响识别准确程度的一个干扰因素:

去柳钉前

因此我们要去掉它,去掉后的效果:

去柳钉后

去除铆钉的思路是,对车牌图像进行逐行扫描,如果这一行是铆钉,那么颜色跳变的次数应该为 4 次,远远少于正常字符的颜色跳变次数:

2024-4-4.去柳钉前颜色跳变示意图1

上面红线是扫描到铆钉的行,先是黑色,扫描到铆钉变为白色,离开铆钉再变为黑色,第二颗铆钉重复上述过程,因此有 4 次黑白之间的颜色跳变。而第二条红线扫描到正常字符,跳变次数远远大于 4,我们就用这个思路去除铆钉:

/**
* 通过一行的颜色跳变次数判断是否扫描到了铆钉,
* 一行最小跳变次数为 12,最大为 12 + 8 * 6 = 60。
* 如果该行是铆钉行,则将该行所有像素都涂成黑色(像素值为 0)
*/
bool AnnPredictor::clearRivet(Mat &plate)
{
	// 1.逐行扫描统计颜色跳变次数保存到集合中
	int minChangeCount = 12;
	vector<int> changeCounts;
	int changeCount;
	for (int i = 0; i < plate.rows; i++)
	{
		for (int j = 0; j < plate.cols - 1; j++)
		{
			int pixel_front = plate.at<char>(i, j);
			int pixel_back = plate.at<char>(i, j + 1);
			if (pixel_front != pixel_back)
			{
				changeCount++;
			}
		}
		changeCounts.push_back(changeCount);
		changeCount = 0;
	}

	// 2.计算字符高度,即满足像素跳变次数的行数
	int charHeight = 0;
	for (int i = 0; i < plate.rows; i++)
	{
		if (changeCounts[i] >= 12 && changeCounts[i] <= 60)
		{
			charHeight++;
		}
	}

	// 3.判断字符高度 & 面积占整个车牌的高度 & 面积的百分比,排除不符合条件的情况
	// 3.1 高度占比小于 0.4 则认为无法识别
	float heightPercent = float(charHeight) / plate.rows;
	if (heightPercent <= 0.4)
	{
		return false;
	}
	// 3.2 面积占比小于 0.15 或大于 0.5 则认为无法识别
	float plate_area = plate.rows * plate.cols;
	// countNonZero 返回非 0 像素点(即白色)个数,或者自己遍历找像素点为 255 的个数也可
	float areaPercent = countNonZero(plate) * 1.0 / plate_area;
	// 小于 0.15 就是蓝背景白字车牌确实达不到识别标准,大于 0.5 是因为
	// 黄背景黑子二值化会把背景转化为白色,由于前面的处理逻辑只能处理
	// 蓝背景车牌,所以黄色车牌的情况也直接认为不可识别
	if (areaPercent <= 0.15 || areaPercent >= 0.5)
	{
		return false;
	}

	// 4.将小于最小颜色跳变次数的行全部涂成黑色
	for (int i = 0; i < plate.rows; i++)
	{
		if (changeCounts[i] < minChangeCount)
		{
			for (int j = 0; j < plate.cols; j++)
			{
				plate.at<char>(i, j) = 0;
			}
		}
	}

	return true;
}

2、字符分割

接下来开始分割字符,主要可以分为三部分:

  1. 找到各个字符的轮廓,并生成轮廓对应的图片
  2. 对汉字字符的轮廓图片进行特殊处理
  3. 将所有 7 个字符的轮廓图片保存到集合中为字符识别做准备

下面详解。

2.1 生成字符轮廓图片

先通过寻找轮廓的函数 findContours() 找到轮廓,生成轮廓矩形集合 vec_ann_rects:

string AnnPredictor::predict(Mat plate)
{
	// 1.预处理 
	...

	// 2.字符分割
	// 2.1 找轮廓
	// vector<Point>是点的集合,可以连成线,线的集合就是轮廓了
	vector<vector<Point>> contours;
	findContours(shold, // 输入的图像
		contours, // 轮廓,接收结果
		RETR_EXTERNAL, // 轮廓检索模式:外轮廓
		CHAIN_APPROX_NONE // 轮廓近似算法模式:不进行轮廓近似,保留所有的轮廓点
	);

	vector<Rect> vec_ann_rects;
	// 在原图上克隆一个用来画矩形
	Mat src_clone = plate.clone();
	for each (vector<Point> points in contours) {
		Rect rect = boundingRect(points);
		Mat rectMat = shold(rect);
		// rectangle(src_clone, rect, Scalar(0, 0, 255));
		// 尺寸判断,符合规格的放入 vec_sobel_rects 集合中
		if (verifyCharSize(rectMat)) {
			vec_ann_rects.push_back(rect);
		}
	}
	...
}

遍历 vec_ann_rects 生成轮廓矩形并生成与矩形对应的图片,这就是分割的字符图片。当然,在将它们存入集合前你需要过滤一下,因为不是所有轮廓都刚好是一个完整的字符。比如“渝”字,由于汉字相比于英文字符和数字,结构复杂,因此无法识别为整个字,而是识别出“渝”字中点或者某个局部部分:

2024-4-8.字符找轮廓效果图2

因为我们导出的车牌宽度只有 136 像素,所以放大后并不清晰,但是能看出来,英文字母和数字的轮廓只有一个,而“渝”的轮廓有多个。面对这种情况,我们先用 verifyCharSize() 进行尺寸校验,将不符合规格的字符图片过滤掉:

bool AnnPredictor::verifyCharSize(Mat src)
{
	// 最理想情况 车牌字符的标准宽高比
	float aspect = 45.0f / 90.0f;
	// 当前获得矩形的真实宽高比
	float realAspect = (float)src.cols / (float)src.rows;
	// 最小的字符高
	float minHeight = 10.0f;
	// 最大的字符高
	float maxHeight = 35.0f;
	// 1、判断高符合范围  2、宽、高比符合范围
	// 最大宽、高比 最小宽高比
	float error = 0.7f;
	float maxAspect = aspect + aspect * error;//0.85
	float minAspect = 0.05f;

	int plate_area = src.cols * src.rows;
	float areaPercent = countNonZero(src) * 1.0 / plate_area;

	if (areaPercent <= 0.8 && realAspect >= minAspect && realAspect <= maxAspect
		&& src.rows >= minHeight &&
		src.rows <= maxHeight) {
		return true;
	}
	return false;
}

汉字被识别为多个部分,无法通过 verifyCharSize() 的校验,会被过滤掉。也就是说,这一步中,我们只获取了英文和数字图片,汉字图片暂时还未获取。

2.2 获取汉字轮廓

思路是,先定位到汉字后面表示城市的那一位英文字符,再向左侧推导获取汉字轮廓。

首先,根据图片的横坐标对 vec_ann_rects 集合内的字符图片进行从左至右的排序:

string AnnPredictor::predict(Mat plate)
{
    ...
    // 2.2 对矩形轮廓从左至右排序
	sort(vec_ann_rects.begin(), vec_ann_rects.end(), [](const Rect& rect1, const Rect& rect2) {
		return rect1.x < rect2.x;
		});
    ...
}

然后获取到表示城市字符的轮廓索引:

string AnnPredictor::predict(Mat plate)
{
    ...
    // 2.3 获取城市字符轮廓的索引
	int cityIndex = getCityIndex(vec_ann_rects);
    ...
}

getCityIndex() 获取城市字符索引的思路是:车牌上共有 7 个字符,那么城市字符位于第 2 位,该字符中间横坐标一定在车牌水平方向的 1/7 ~ 2/7 之间:

/**
* 寻找城市字符(7 位字符中的第 2 位)轮廓索引
*/
int AnnPredictor::getCityIndex(vector<Rect> rects)
{
	int cityIndex = 0;
	for (int i = 0; i < rects.size(); i++)
	{
		Rect rect = rects[i];
		int midX = rect.x + rect.width / 2;
		// 如果字符水平方向中点坐标在整个车牌水平坐标的
		// 1/7 ~ 2/7 之间,就认为是目标索引。136 是我们
		// 训练车牌使用的素材的车牌宽度
		if (midX < 136 / 7 * 2 && midX > 136 / 7)
		{
			cityIndex = i;
			break;
		}
	}
	return cityIndex;
}

获取城市字符索引后,可以根据其横坐标推断出汉字字符的轮廓:

string AnnPredictor::predict(Mat plate)
{
    ...
    // 2.4 推导汉字字符的轮廓
	Rect chineseRect;
	getChineseRect(vec_ann_rects[cityIndex], chineseRect);
    ...
}

getChineseRect() 会将推断出的矩形保存到 chineseRect 中:

/**
* 通过城市字符的矩形,确定汉字字符的矩形
*/
void AnnPredictor::getChineseRect(Rect cityRect, Rect& chineseRect)
{
	// 把宽度稍微扩大一点以包含完整的汉字字符
	// 还有一层理解,就是汉字与城市字符之间的空隙也要计算进去
	float width = cityRect.width * 1.15;

	// 城市轮廓矩形的横坐标
	int x = cityRect.x;

	// 用城市矩形的横坐标减去汉字宽度得到汉字矩形的横坐标
	int newX = x - width;
	chineseRect.x = newX > 0 ? newX : 0;
	chineseRect.y = cityRect.y;
	chineseRect.width = width;
	chineseRect.height = cityRect.height;
}

2.3 保存所有字符图片

最后将 7 个字符的图片保存到 plateCharMats 集合中等待字符识别:

string AnnPredictor::predict(Mat plate)
{
    ...
	// 2.5 将字符图像保存到集合中
	// 先保存汉字字符图像
	vector<Mat> plateCharMats;
	plateCharMats.push_back(shold(chineseRect));

	// 再获取汉字之后的 6 个字符并保存
	int count = 6;
	if (vec_ann_rects.size() < 6)
	{
		return string("未识别到车牌");
	}

	for (int i = cityIndex; i < vec_ann_rects.size() && count; i++, count--)
	{
		plateCharMats.push_back(shold(vec_ann_rects[i]));
	}
    ...
}

3、字符识别

3.1 识别过程

调用 predict() 传入字符图片集合 plateCharMats,识别的字符结果保存在 str_plate 中:

string AnnPredictor::predict(Mat plate)
{
    ...
    // 3.字符识别
	string str_plate;
	predict(plateCharMats, str_plate);
	for (Mat m : plateCharMats) {
		m.release();
	}

	// 4.释放 Mat
	gray.release();
	shold.release();
	src_clone.release();
	
	return str_plate;
}

predict() 内遍历 plateCharMats,提取 HOG 特征后对汉字和数字英文分开识别,注意 ZHCHARS 与 CHARS 内的字符顺序要和训练样本存放顺序相同:

string AnnPredictor::ZHCHARS[] = { "川", "鄂", "赣", "甘", "贵", "桂", "黑", "沪", "冀", "津", "京", "吉", "辽", "鲁", "蒙", "闽", "宁", "青", "琼", "陕", "苏", "晋", "皖", "湘", "新", "豫", "渝", "粤", "云", "藏", "浙" };
char AnnPredictor::CHARS[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };

void AnnPredictor::predict(vector<Mat> plateCharMats, string& result)
{
	for (int i = 0; i < plateCharMats.size(); i++)
	{
		Mat mat_plate_char = plateCharMats[i];

		// 提取 HOG 特征
		Mat features;
		getHOGFeatures(annHog, mat_plate_char, features);

		Mat sample = features.reshape(1, 1);
		Mat response;
		Point maxLoc;
		Point minLoc;

		if (i)
		{
			// 字母和数字
			ann->predict(sample, response);
			minMaxLoc(response, 0, 0, &minLoc, &maxLoc);
			int index = maxLoc.x;
			result += CHARS[index];
		}
		else
		{
			// 汉字
			ann_zh->predict(sample, response);
			minMaxLoc(response, 0, 0, &minLoc, &maxLoc);
			int index = maxLoc.x;
			result += ZHCHARS[index];
		}
	}
}

至此代码结束,先来看一下效果:

2024-4-8.最终识别结果1

对于【渝G 83666】的识别结果为【渝G 80666】,错了一位,这与识别的算法,还有训练样本的数量都有关系。总的来说,Demo 提供了一种车牌识别的思路,但是准确度还是有限的。

3.2 样本制作

最后来说说样本是如何制作的。

识别车牌字符,需要所有字符训练的特征集合,即首位的汉字共 31 个字符、第二位英文字符 24 个(刨除 I 和 O 两个容易被误识别为 1 和 0)、以及后续位数中需要用到的 10 个数字。我们将训练样本分为两类:英文字符和数字的训练样本放入 ann 文件夹,汉字字符放入 ann_zh 文件夹。两个文件夹内都需要对字符进行编号:

  • ann 中 0 号文件夹是数字 0 的训练素材,1 号文件夹是数字 1 的训练素材,以此类推,总共是 10 + 24 = 24 个文件夹
  • ann_zh 中 1 号文件夹是“川”字的训练素材,2 号文件夹是“鄂”字的训练素材,共计 31 个文件夹

这些字符的编号顺序需要记录在一个额外的文档中,内容如下:

"川", "鄂", "赣", "甘", "贵", "桂", "黑", "沪", "冀", "津", "京", "吉", "辽", "鲁", "蒙", "闽", "宁", "青", "琼", "陕", "苏", "晋", "皖", "湘", "新", "豫", "渝", "粤", "云", "藏", "浙"
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'

训练后可以得到两个特征集合文件 ann.xml(数字和英文字符)和 ann_zh.xml(汉字字符),正是初始化 AnnPredictor 的 ann 和 ann_zh 所加载的文件。目录结构如下:

2024-4-8.车牌字符样本训练目录

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1661434.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DiskCatalogMaker for Mac:高效管理磁盘文件助手

DiskCatalogMaker for Mac&#xff0c;助您高效管理磁盘文件&#xff0c;让文件整理变得轻而易举&#xff01;这款软件以其出色的性能和人性化的设计&#xff0c;赢得了广大Mac用户的喜爱。 DiskCatalogMaker支持多种磁盘格式&#xff0c;让您轻松管理硬盘、U盘、光盘等存储设备…

LaTeX公式学习笔记

\sqrt[3]{100} \frac{2}{3} \sum_{i0}^{n} x^{3} \log_{a}{b} \vec{a} \bar{a} \lim_{x \to \infty} \Delta A B C

基于Springboot的微乐校园管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的微乐校园管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

Python尝试安装 pyaudio 时遇到的错误信息表示安装过程失败,原因是找不到 Python.h 头文件

环境&#xff1a; Python 3.8.10 WSL2 问题描述&#xff1a; 尝试安装 pyaudio 时遇到的错误信息表示安装过程失败&#xff0c;原因是找不到 Python.h 头文件 error: subprocess-exited-with-error Building wheel for pyaudio (pyproject.toml) did not run successfully…

组合模式(结构型)

目录 一、前言 二、透明组合模式 三、安全组合模式 四、总结 一、前言 组合模式(Composite Pattern)是一种结构型设计模式&#xff0c;将对象组合成树形结构以表示“部分-整体”得层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 组合模式由以下角色组成…

数据库调优-SQL语句优化

2. SQL语句优化 sql 复制代码 # 请问这两条SQL语句有什么区别呢&#xff1f;你来猜一猜那条SQL语句执行查询效果更好&#xff01; select id from sys_goods where goods_name华为 HUAWEI 麦芒7 魅海蓝 6G64G 全网通; ​ select id from sys_goods where goods_id14967325985…

(九)JSP教程——pageContext对象

pageContext对象是由JSP容器创建并初始化的&#xff0c;相当于当前页面的容器&#xff0c;它可以访问当前页面中的所有对象。它的主要作用是为JSP页面包装上下文&#xff0c;并用于管理属于JSP的特殊可见部分中已命名对象的访问。 一般情况下&#xff0c;使用该对象的应用并不多…

netty配置SSL、netty配置https(开发)

netty配置SSL、netty配置https&#xff08;开发&#xff09; 我们在开发下使用ssl&#xff0c;所用的证书将不被客户端信任。 转自&#xff1a;https://lingkang.top/archives/netty-pei-zhi-ssl 方案一 快速。使用netty提供的临时签发证书 private static SslContext sslC…

富士Apeos 2350 NDA复印机报062 360代码故障

故障描述&#xff1a; 富士Apeos 2350 NDA复印机新机器刚拆箱安装&#xff0c;开机正常&#xff0c;自检扫描头一卡一卡的往前动几下就不动了、扫描灯也不亮扫描头也不能正常复位&#xff1b;按机器的复印键直接报062 360代码&#xff1b; 解答&#xff1a; 此代码为扫描故障&a…

unreal engine4 创建动画蒙太奇

UE4系列文章目录 文章目录 UE4系列文章目录前言一、创建动画蒙太奇 前言 动画蒙太奇的官方解释&#xff1a;Animation Montages are animation assets that enable you to combine animations in a single asset and control playback using Blueprints.You can use Animation…

粤嵌—2024/4/26—跳跃游戏 ||

代码实现&#xff1a; 方法一&#xff1a;回溯 历史答案剪枝优化——超时 int *dis;void dfs(int k, int startindex, int *nums, int numsSize) {if (dis[startindex] < k) {return;}dis[startindex] k;for (int i 0; i < nums[startindex]; i) {if (startindex i &…

mac内存不足怎么清理?有哪些免费的软件工具?

当你的mac电脑使用一段时间之后&#xff0c;你可能就会发现&#xff0c;原本非常流畅的运行开始出现卡顿的现象&#xff0c;此时正是mac内存不足的外在表现。可mac内存不足怎么清理呢&#xff0c;别急&#xff0c;清理内存的方式方法有很多&#xff0c;小编将结合实际情况给大家…

【Web】2023浙江大学生省赛初赛 secObj 题解

目录 step 0 step 1 step 2 step 3 题目本身是不难&#xff0c;简单复健一下 step 0 pom依赖就是spring 反序列化入口在./admin/user/readObj 输入流做了黑名单的过滤&#xff0c;TemplatesImpl不能直接打 可以jackson打SignedObject二次反序列化绕过 具体原理看下面这…

选择定制温度快速温变试验箱,为您开启高效试验新时代

在现代工业生产中&#xff0c;温度快速温变试验是评估产品性能和可靠性的重要手段之一。然而&#xff0c;市面上有大多数企业在选择试验设备时常常面临着一些困惑&#xff0c;那就是温度快速温变试验箱定制的性能与需求不匹配、定制服务不足、售后服务不到位等等。针对这些问题…

98、技巧-颜色分类

思路 这道题的思路是什么&#xff0c;首先典型荷兰国旗问题&#xff1a; 该问题的关键在于我们要将所有的0放到数组的前部&#xff0c;所有的1放在中间&#xff0c;所有的2放在后部。这可以通过使用两个指针&#xff0c;一个指向数组开头的“0”的最后一个位置&#xff0c;另…

第十一篇:操作系统新纪元:智能融合、量子跃迁与虚拟现实的交响曲

操作系统新纪元&#xff1a;智能融合、量子跃迁与虚拟现实的交响曲 1 引言 在数字化的浪潮中&#xff0c;操作系统如同一位智慧的舵手&#xff0c;引领着信息技术的航船穿越波涛汹涌的海洋。随着人工智能、物联网、量子计算等前沿技术的蓬勃发展&#xff0c;操作系统正站在一个…

【智能优化算法】金豺狼优化算法(Golden jackal optimization,GJO)

金豺狼优化(Golden jackal optimization,GJO)是期刊“Expert Systems with Applications”&#xff08;中科院一区IF 8.3&#xff09;的2022年智能优化算法 01.引言 金豺狼优化(Golden jackal optimization,GJO)旨在为解决实际工程问题提供一种替代的优化方法。GJO的灵感来自金…

鸿蒙开发接口Ability框架:【@ohos.application.Want (Want)】

Want Want模块提供系统的基本通信组件的能力。 说明&#xff1a; 本模块首批接口从API version 8 开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import Want from ohos.application.Want; 开发前请熟悉鸿蒙开发指导文档&#xff1…

dev c++调试录入数字后回车直接关闭

1、我的dev c版本是5.11 2、输入7后&#xff0c;回车就没有了&#xff0c;原因是1013,1.cpp未包含在项目中 3、新建项目&#xff0c;并将test_debug.cpp包含在项目内&#xff0c;就可以下断点调试了

C语言—深入理解指针(3)

1.字符指针变量 一般使用&#xff1a; 另一种使用方法&#xff1a; “hello world”是一个常量字符串&#xff0c;不能被修改。 上述代码是将字符串中的首字符‘h’赋值给指针pstr&#xff0c;用%s打印字符串的时候&#xff0c;只需要提供首字符的地址就行。&#xff08;如果…