基于OPENCV的OCR身份证号识别技术

基于OPENCV的OCR身份证号识别技术

 
前言:
  本篇博客主要介绍OCR数字识别,代码基于C++,会有详细的实现流程以及源码,还会提到部分OPENCV 2和4的一些区别。(本篇文章仅为初学者的一些心得笔记,会有较多不完善和错误的地方,欢迎大家指出!PS:最后的杂谈务必看看)
 
 

一、身份证号码识别流程图
流程图

 
 

二、灰度图获取
  读取图片之后,对图片进行处理,获得图片的灰度图。方法有二:
 
  1)使用imread读取图片的时候,第二个参数可以直接设置为0,即直接读取一张灰度图
  2)使用cvtColor函数,设置参数将彩色图转换为灰度图。(具体方法请百度)但若是前面imread读入的图片就已是灰度图,就不能使用cvtColor函数再进行转换,因为该函数不能转换单通道图片,会报错。
 
  获取了灰度图之后,对图片进行大小调整,调整为(450,600),方便后续获取身份证号码区域。

 
 
三、找到身份证号码区域
  获得图片之后,对图片进行二值化,然后进行闭运算。(定义结构元素后,可用morphologyEx函数进行闭运算)再用findContours函数检测外轮廓,找到每个轮廓的最小界矩形区域。由于身份证号码区域长宽比为一个常数(约为18),设置一定的误差值之后,便可找到符合这一长宽比的矩形,就是身份证号码区域。(图中画出黑框)基于OPENCV的OCR身份证号识别技术_第1张图片
 
 

四、裁剪身份证区域
  前一步找到身份证的区域,并且可以通过RotatedRect类(该类表示平面上的旋转矩形,有质心、边长、旋转角度三个属性)存储矩形区域。使用getRectSubPix函数从图片中裁剪出指定的矩形区域,然后再用resize函数调整图片的尺寸,变为(20,300),便于后面的数字切割。
 
切割出的号码
 
 
五、号码切割
  对号码图片进行二值化后(若之前已进行二值化则不需要再进行),将每一个号码单独切割开来,进行识别。
 
二值化图片
  切割的原理:先创建一个等同于图片列数的flag向量。遍历每一列,对于每一列中,再遍历每一行。若该列中存在一行有黑色像素点,则将该列对应的flag标注为true,否则为false,标注为true的列表示属于数字的一部分。
  然后遍历flag向量,找到标注为true,并且后两个标注为false的那一列,可以认为是到了数字的边缘。同时在遇到true时,应该设置一个变量,记录数字的宽度为多少列,目的是通过找到后边缘与列数,算出前边缘的x坐标,便于后续切割。
  举个例子:如下图,flag为T的表示该列有像素,count统计有像素的列数,由于我们能得到count=14处的x坐标,x-14就为count=0处的坐标,之后便可以使用Rect函数进行切割。并保存在图片数组中。
 
基于OPENCV的OCR身份证号识别技术_第2张图片
 
 
六、提取号码特征
  号码的特征为梯度分布特征、灰度分布特征、水平/垂直方向直方图,每个字符最后都能得到一个1*72的特征向量。计算机主要就是依靠号码的特征,与已经训练好的神经网络进行匹配,以此达到对数字进行辨别。

 
 
七、找到训练样本及提取样本特征
  使用与上一步相同的函数对样本进行特征提取,包括0~9和X,每个数字有50个样本。提取后将其存到trainData中,将trainData和样本的类的标识classes存储到文件ann_xml.xml中,之后便可以直接调用而不需要再次运行该函数。
 
 
八、神经网络训练
  读入ann_xml.xml文件。创建一个3层的神经网络,输入层结点数设置为72,隐藏层结点数为24,输出层结点数为10。(因最后一位为矫正位,并且最后一位识别难度较大,因此只识别前17位,最后一位可通过公式计算出)设置训练方法、激活函数、迭代次数和误差最小值等,使用ann->train对神经网络进行训练。并将训练好的神经网络保存为文件,之后便可直接调用已经训练好的网络,大大节省程序运行的时间。
 
 
九、比对分类
  使用训练好的神经网络对数字进行分类,输入的数字为第五步切割得到的数字图片。使用ann->predict对输入的图片进行预测,找出与输入图片的特征最相似的结果,就是结果。(识别结果如下)
 

基于OPENCV的OCR身份证号识别技术_第3张图片
 
 
十、杂谈
(1)对于初学OPENCV的朋友,首先应该注意的是OPENCV的版本问题,网上很多资源使用的是OPENCV2,而现在来说大多人使用的是OPENCV4,2和4在神经网络的训练部分有较大的出入,因此在看代码的时候,应该明确自己的OPENCV的版本。(本篇博客中的代码均为OPENCV4版本)
  举个例子:

Mat layerSizes(1,3,CV_32SC1);     //3层神经网络
    layerSizes.at<int>( 0 ) = trainData.cols;   //输入层的神经元结点数,设置为72
    layerSizes.at<int>( 1 ) = nlayers; //1个隐藏层的神经元结点数,设置为24
    layerSizes.at<int>( 2 ) = numCharacters; //输出层的神经元结点数为:10
    ann.create(layerSizes , CvANN_MLP::SIGMOID_SYM ,1,1);  //初始化ann  SIGMOID_SYM即Sigmoid函数
    CvANN_MLP_TrainParams param;
    param.term_crit=cvTermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS,5000,0.01);

上面的代码段为OPENCV2环境下的初始化等步骤

	Mat layerSizes(1, 3, CV_32SC1);     //3层神经网络
	layerSizes.at<int>(0) = trainData.cols;   //输入层的神经元结点数,设置为72
	layerSizes.at<int>(1) = nlayers; //1个隐藏层的神经元结点数,设置为24
	layerSizes.at<int>(2) = numCharacters; //输出层的神经元结点数为:10

	ann->setLayerSizes(layerSizes);
	ann->setTrainMethod(ANN_MLP::BACKPROP, 0.1, 0.1);//后两个参数: 权梯度项的强度(一般设置为0.1) 动量项的强度(一般设置为0.1)
	ann->setActivationFunction(ANN_MLP::SIGMOID_SYM);
	ann->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 5000, 0.01));//后两项参数为迭代次数和误差最小值
	

而这部分实现同样功能的代码,使用的方法可以看到是有较大的区别。当环境配置为OPOENCV4而使用2的函数,自然也就会报错。
 
(2)本篇文章的源代码会分享给大家,希望大家在观看代码的时候,能够指出错误与不足,大家一起讨论与进步。代码链接在下方:
 

https://download.csdn.net/download/qq_42884797/12931807
 
(3)最后祝大家0error,0warning!

你可能感兴趣的:(opencv,神经网络,c++)