OpenCV+TesseractOCRiOS实现身份证号/银行卡
2017-08-11 本文已影响0人
一蓑烟雨满眼风波
- iOS 文字识别准备
- 图片灰度化
- 图片二值化
- 根据特征点提取某一区域
声明:思路来源于 iOS身份证号码识别 ,在此我对作者表示感谢,一下内容,是我结合http://www.opencv.org.cn写的一个简单教程,对该工程所用到的方法进行解读,在本文的最后,会附上完整的代码.
正文
iOS 文字识别准备
文字识别重要点在对图片的预处理,预处理做的比较好,识别率就会比较高,本次所介绍的身份证号识别是基于OpenCV、TesseractOCRiOS来实现的
首先使用CocoaPods导入三方库
pod 'OpenCV', '~> 3.0.0'
pod 'TesseractOCRiOS', '~> 4.0.0'
灰度化
灰度化,在RGB模型中,如果R=G=B时,则彩色表示一种灰度颜色,其中R=G=B的值叫灰度值,因此,灰度图像每个像素只需一个字节存放灰度值(又称强度值、亮度值),灰度范围为0-255。
下面说一下如何使用OpenCV进行灰度化处理,使用的OpenCV是一个C++ 的代码,在使用的类中要将.m文件改为.mm
//将UIImage转换成矩阵
cv::Mat resultImage;//定义矩阵
UIImageToMat(image, resultImage);
//转为灰度图
cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
原图.png
灰度化.png
二值化
图像的二值化,就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的只有黑和白的视觉效果。
固定阈值门限分割
- 方法
double threshold( InputArray src, OutputArray dst,
double thresh, double maxval, int type );
- 参数说明
src 输入矩阵
dst 输出矩阵
thresh 阀值
maxval 最大值(这里通常设置为255)
thresholdType 分割类型
- 分割类型说明
//value 矩阵中一个单位的颜色的值
//threshold 阀值
CV_THRESH_BINARY =0, /**value > threshold ? max_value : 0 */
CV_THRESH_BINARY_INV =1, /**value > threshold ? 0 : max_value */
CV_THRESH_TRUNC =2, /** value > threshold ? threshold : value */
CV_THRESH_TOZERO =3, /**value > threshold ? value : 0 */
CV_THRESH_TOZERO_INV =4, /** value > threshold ? 0 : value */
- 使用
cv::threshold(resultImage, resultImage, 100, 255, CV_THRESH_BINARY);
阀值为100二值化.png
这里又有一个很重要的点,阀值的确定,我是在某个博客找到个计算阀值的算法,不过不是很理想,现在也贴出来,但是不知道作者是谁了,很抱歉...
int cvThresholdOtsu(IplImage* src)
{
int height=src->height;
int width=src->width;
//histogram
float histogram[256]={0};
for(int i=0;i<height;i++) {
unsigned char* p=(unsigned char*)src->imageData+src->widthStep*i;
for(int j=0;j<width;j++) {
histogram[*p++]++;
}
}
//normalize histogram
int size=height*width;
for(int i=0;i<256;i++) {
histogram[i]=histogram[i]/size;
}
//average pixel value
float avgValue=0;
for(int i=0;i<256;i++) {
avgValue+=i*histogram[i];
}
int threshold = 0;
float maxVariance=0;
float w=0,u=0;
for(int i=0;i<256;i++) {
w+=histogram[i];
u+=i*histogram[i];
float t=avgValue*w-u;
float variance=t*t/(w*(1-w));
if(variance>maxVariance) {
maxVariance=variance;
threshold=i;
}
}
return threshold;
}
- 截取卡号区域
思路,现将黑色的点放大,这样点会连接到一起,变成一块儿黑色,然后我们通过图片的轮廓,宽高比例来寻找复合的一个区域,将其截取下来
腐蚀&膨胀
- 官方解释:
腐蚀:进行腐蚀操作时,将内核B划过图像,将内核B覆盖区域的最小相素值提取,并代替锚点位置的相素。
膨胀:进行膨胀操作时,将内核B划过图像,将内核B覆盖区域的最大相素值提取,并代替锚点位置的相素。显然,这一最大化操作将会导致图像中的亮区开始”扩展” (因此有了术语膨胀 dilation )。对上图采用膨胀操作我们得到: - 个人理解:
腐蚀:定义一个size,用这个size替换图片每个点(加粗)
膨胀:与腐蚀相反(变细)
个人的认识比较low,在这里呢使用腐蚀目的是将相近的黑色点连接到一块,已便后面获取所有数字连接到一块儿的轮廓,对比每个轮廓的宽高比,截取到卡号区域。
cv::Mat erodeElement = getStructuringElement(cv::MORPH_RECT, cv::Size(25,1));
cv::erode(resultImage, resultImage, erodeElement);
腐蚀之后的照片
在图像中寻找轮廓
- 方法
//获取图片轮廓
findContours( InputOutputArray image, OutputArrayOfArrays contours,
int mode, int method, Point offset = Point());
//将存放Point的数组转为Rect
Rect boundingRect( InputArray points );
- findContours参数说明
InputOutputArray:输入输出的图片
OutputArrayOfArrays:输出的轮廓点,这是一个三维数
std::vector<std::vector<cv::Point>> contours;
mode:轮廓检索模式,具体参考cv::RetrievalModes
CV_RETR_EXTERNAL:只检索最外面的轮廓;
CV_RETR_LIST:检索所有的轮廓,并将其放入list中;
CV_RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;
CV_RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次。
method:轮廓近似法,具体参考cv::ContourApproximationModes
CV_CHAIN_CODE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。
CV_CHAIN_APPROX_NONE:将所有的连码点,转换成点。
CV_CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。
CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS:使用the flavors of Teh-Chin chain近似算法
的一种。
CV_LINK_RUNS:通过连接水平段的1,使用完全不同的边缘提取算法。使用CV_RETR_LIST检索模式能使用此方法。
offset:偏移量,用于移动所有轮廓点。
- 使用
cv::findContours(resultImage, contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));
根据轮廓选取目标区域
(未完)