刚刚接触车牌识别,希望能通过一个完整的程序理解车牌识别的具体实现流程。
借助于java基础,只能选择EasyPR的java版本作为研究对象。希望通过此篇记录下我的问题与学习经验,也希望与所有学习的小伙伴们共同讨论其中的问题。
EasyPR的学习博客:https://blog.csdn.net/liuuze5/article/details/46290455
EasyPR-java的博客及源码:https://blog.csdn.net/lgcjava/article/details/52319146
配置环境:jdk1.8 openCV2.4.11 maven工程 JUnit 4(不知道是否必须,还不太懂JUnit的应用)
附上原图:
(1)第一次尝试调用EasyPrTest.java,但不知道具体如何调用那么多方法。失败。
(2)第二次尝试调用GeneralTest.java,还好,这个只需要调用一个方法,成功运行。
但是会报如下错误:
网上查询,应该是内存溢出的问题。有时候检测到第11张照片后出错,现在第5张照片就会出错。解决方法还没找到,有没有知道解决方法的小伙伴,欢迎留言告知,多谢。
二、学习源码(分析GeneralTest测试的debug)
1、Mat src = imread(file.getPath());
// Mat (openCV提供的类,用于存储图像)
附地址:http://lib.csdn.net/article/opencv/42000
2、 PlateDetect plateDetect = new PlateDetect();
plateDetect.setPDLifemode(true);
//调用PlatDetect(车牌检测)中的setPDLifemode()方法
2.1 setPDLifemode()方法
看到注释:生活模式\工业模式。当前设置为生活模式。(此处的生活模式应该是日常人为照相采集图片,车牌照射角度较小。个人猜测)
2.1.1 setLifemode()方法
设置各项参数。详细参数还不懂,暂时搁置。
3 Vector
4 if (0 == plateDetect.plateDetect(src, matVector)) { } //如果车牌成功检测到
4.1 车牌检测 //plateDetect.plateDetect(src, matVector)
(将图片src检测出车牌图片,存入matVector中)
有些参数对于车牌定位的效果有非常明显的影响,例如高斯模糊半径、Sobel算子的水平与垂直方向权值、闭操作的矩形宽度。
车牌检测整体流程:
4.1.1 高斯模糊
(1)高斯模糊/高斯平滑——用高斯滤波器来模糊一张图片,通常用它来减少图像噪声以及降低细节层次
GaussianBlur (src, src_blur, new Size(gaussianBlurSize, gaussianBlurSize), 0, 0, BORDER_DEFAULT);
//参数介绍(源图像,结果图像,高斯内核的大小(宽度、高度可以不同,但都要是正数和奇数,都是由sigma计算而来),高斯函数在X方向的标准偏差,高斯函数在Y方向的标准偏差,用于推断图像外部像素的某种边界模式)
(2)高斯模糊成功后,将处理后的图片写入到tmp/debug_GaussianBlur.jpg
4.1.2 灰度化
(3)将高斯模糊后的彩色图像灰度化 //cvtColor(src_blur, src_gray, CV_RGB2GRAY);
(4)灰度化成功后,将灰度化图像写入tmp/debug_gray.jpg
4.1.3 Sobel算子
(5)将图像进行Sobel(Sobel算子是一种常用的边缘检测算子,是一阶的梯度算法,Gx及Gy分别代表经横向及纵向边缘检测的图像)运算,包括X方向与Y方向,得到图像的一阶水平方向导数。
(6)将16进制图像转换为8进制图像格式 //convertScaleAbs(grad_x, abs_grad_x);
在Sobel函数的第二个参数这里使用了cv2.CV_16S。因为OpenCV文档中对Sobel算子的介绍中有这么一句:“in the case of 8-bit input images it will result in truncated derivatives”。即Sobel函数求完导数后会有负值,还有会大于255的值。而原图像是uint8,即8位无符号数,所以Sobel建立的图像位数不够,会有截断。因此要使用16位有符号的数据类型,即cv2.CV_16S。
在经过处理后,别忘了用convertScaleAbs()函数将其转回原来的uint8形式。否则将无法显示图像,而只是一副灰色的窗口。
附上查询地址:https://blog.csdn.net/sunny2038/article/details/9170013
(7)将Sobel处理后的X方向的图像与Y方向的图像叠加。
// addWeighted(abs_grad_x, SOBEL_X_WEIGHT, abs_grad_y, SOBEL_Y_WEIGHT, 0, grad);
(8)将叠加后的图片添加到tmp/debug_Sobel.jpg中
4.1.4 二值化(将灰度图像转换为二值图像)
(9)threshold(grad, img_threshold, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
(10)将二值化后的图像存入tmp/debug_threshold.jpg中。
4.1.5 闭操作(先膨胀后腐蚀,排除小型黑洞,突出了比原图轮廓区域更暗的区域)
(11) 首先创建一个固定尺寸的矩形的矩阵 //Mat element = getStructuringElement(MORPH_RECT, new Size(morphSizeWidth, morphSizeHeight));
(12)进行闭操作运算 //morphologyEx(img_threshold, img_threshold, MORPH_CLOSE, element);
(13)将处理后的图片存到tmp/debug_morphology.jpg中
4.1.6 求轮廓
(14)使用findContours()方法,提取二进制图像(上一步处理的图像)的外部轮廓,并将所有轮廓保存在contours中。
(15)在原图中画出轮廓 //drawContours(result, contours, -1, new Scalar(0, 0, 255, 255));
4.1.7 筛选轮廓
(16)对轮廓求最小外接矩形 //RotatedRect mr = minAreaRect(contours.get(i));
(17)将合适大小的轮廓提取出来,添加到rects中
4.1.8 矩形旋转
(18)。。。。。
(19)
4.1.1 车牌定位 //Vector
4.1.2 SVM train
4.1.3 Plate judge
5 CharsRecognise cr = new CharsRecognise(); //进行字符识别
5.1 String result = cr.charsRecognise(matVector.get(i)); //对每一个图片进行字符识别
5.1.2 charsSegment()方法
(1)如果数据不空,将彩色图像转换为灰度图像 cvtColor(input, input_grey, CV_RGB2GRAY);
(2)???Mat tmpMat = new Mat(input, new Rect((int) (w * 0.1), (int) (h * 0.1), (int) (w * 0.8), (int) (h * 0.8)));
(3)判断车牌图像的颜色 switch (getPlateType(tmpMat, true)) { }
(4)按照不同的车牌颜色,进行不同参数的图像二值化
/*
case BLUE:
threshold(input_grey, img_threshold, 10, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);break;
*/
(5)去除处理后的二值图像车牌上方的柳钉以及下方的横线等干扰 clearLiuDing(img_threshold);
柳丁的size=7;
创建一个Map(1行,与图像的行数相同的列数,CV_32F类型).asMat()
未完待续。