使用C++结合Opencv库实现简易汉字识别。


文章目录

  • Opencv实现汉字识别
  • 程序实现思路
  • 图像预处理
  • 思路
  • 实现实例
  • 对比方法
  • 模型训练
  • 识别过程
  • 特殊参数
  • 使用说明
  • 模型训练
  • 模型导入
  • 汉字识别
  • 程序使用结果
  • 不足以及思考


Opencv实现汉字识别

程序实现思路

图像预处理

导入图像进行一系列预处理,使其便于用来识别和其他计算。

思路

  • 灰度化
  • 使用opencv的库函数来实现。
    cv::cvtColor(image, image_gray, CV_BGR2GRAY);
  • 二值化
  • 同样选用库函数来实现,本例使用的是自适应二值化函数。
    cv::adaptiveThreshold(image_gray, image_value, 255, 0, 1, 5, 10);
  • 降噪(去除干扰线)
  • 实现的思路并非去除干扰线,而是尽量不把干扰线选中到识别区域。
  • 先通过腐蚀,使较细的干扰线消去。
    erode(image_value, temp, kernel2);
  • 再通过膨胀,使汉字主体明显。
    dilate(temp, dst, kernel1);
  • 轮廓识别
  • 使用库函数找出外轮廓点集。
    findContours(dst, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point());
  • 最小矩阵
  • 使用库函数找出包含轮廓点集的最小矩形。将其画出。
  • 汉字分割
  • 将矩阵框出的部分,复制到新的Mat数组中。

实现实例

准备一张用于当作模型的图片。

ios opencv文字的相似度 opencv文字识别算法_c++

灰度化

ios opencv文字的相似度 opencv文字识别算法_c++_02

本身就只有大部分为黑白色,故灰度化结果不明显。

二值化

ios opencv文字的相似度 opencv文字识别算法_ios opencv文字的相似度_03

腐蚀

ios opencv文字的相似度 opencv文字识别算法_ios opencv文字的相似度_04

可以发现干扰线已经基本看不见了,但是汉字好似被断裂开。不利于后面的寻找轮廓,故进行膨胀操作。

膨胀

ios opencv文字的相似度 opencv文字识别算法_opencv_05

膨胀有两个目的:一是使腐蚀后断裂的文字合起来。二是使某些上下结构或左右结构明显的字合起来。否则在识别(吴)的区域时,容易将(口)(天)分别框出,不利于识别整体的汉字。

最小矩阵

在膨胀后的基础上,寻找轮廓,并找出轮廓的最小矩阵。

ios opencv文字的相似度 opencv文字识别算法_c++_06

切割后保存

框出文字的矩阵只在测试时画出,实际裁剪保存的时候,并不绘制出矩阵,是为了排除矩阵的存在对识别精确度的影响。

ios opencv文字的相似度 opencv文字识别算法_opencv_07

分割后保存的模板

对比方法

本例使用直方图匹配的方式,此方式的精确度实际并不高。其他如卷积神经网络难度较大,暂时没有足够的时间研究。故采用此种方法。

主要是将c。三者关系一一对应。找出与识别对象的直方图数据匹配图最高的模板图像,就可以确定其文字。

模型训练

模板图像——直方图数据——汉字

采用结构体。

struct pimage {
	char c_name[3];
	Mat img_zft;
};

从模板数据库中,读取每张文字对应的图片。将其灰度化后,计算直方图数据。

本例的数据都没有采用写入到文件的方式保存,故每次运行前都要导入一次模型,用于计算这些数据。

数据关联

for (i = 0; i < num; i++) {
    //直方图计算
	calcHist(&imgNo_gray[i], 1,&channels ,Mat(), hist[i], 1, &histSize,&phranges_arr);
	strcpy_s(p_imgNo[i].c_name, img_word[i]);//手写字关联 
	p_imgNo[i].img_zft = hist[i];//直方图关联
			}

识别过程

完成上面的模型制作,以及数据关联后。就可以进行识别过程了。

先进行预处理过程,再同样计算直方图。和模型数据进行对比。

double Compare,compare;
int word = 0;
for (int j = 0; j < contours.size()-1; j++) {
		if (contours[j].size() < 10) { word++; continue; }
		compare = 0;
		for (int k = 0; k < num; k++) {
			//使用CV_COMP_CORREL方法进行对比    
			Compare = compareHist(hist1[j], p_imgNo[k].img_zft, 0);  	
            		//精准度达90%
					if (Compare >= 0.9) {
						if (compare > Compare) { continue; }
						else {
							compare = Compare;
							strcpy_s(p_img[j].c_name, p_imgNo[k].c_name);
						}
					}
				}
			}
			//识别完成,输出结果
			printf_s("检测到%d个文字,分别是\n",contours.size()-word);
			for (int i = 0; i < contours.size(); ++i) {
				cout << p_img[i].c_name;
			}

特殊参数

constexpr auto img_data = "E:/img";			//模板图像存储路径
#define num 77								//模板汉字个数

使用说明

模型训练

若已有模板图的文件则无需进行训练。

操作:根据提示输入2和训练模型所用的图片。

ios opencv文字的相似度 opencv文字识别算法_c++_08

结果:会在默认的模型保存路径存有模型图片下的每个汉字的切割图。

ios opencv文字的相似度 opencv文字识别算法_ios opencv文字的相似度_09

需要自己手动操作。
img_word[]数组内,按下标顺序依次输入所对应的汉字。

模型导入

就是结构体内数据依次计算,存入的过程。

汉字识别

识别的图像最好是模板图或者其截图的一部分。这是由于使用直方图匹配,重复的概率太大所导致的。

根据提示操作即可。

程序使用结果

ios opencv文字的相似度 opencv文字识别算法_opencv_10

ios opencv文字的相似度 opencv文字识别算法_直方图_11

ios opencv文字的相似度 opencv文字识别算法_c++_12

可以发现识别的准确率还是很高的。

不足以及思考

  1. 由于没有将数据保存在外部,故每次使用前都必须进行依次导入模板的操作。
  2. 只有一个main函数,可以将预处理封装起来。减少代码量。
  3. 使用的识别算法准确率还不够高,只能识别模板图罢了。还不能应用到各种手写体。
  4. 对于部分可能出现的错误操作,也还没有编写足够的错误提示。

思路就是这样