在进行字符识别(tesseract)之前,使用OpenCV进行图像预处理

14

我正在尝试开发一个简单的PC应用程序,用于车牌识别(Java + OpenCV + Tess4j)。图像质量不太好(以后会改善),我想对图像进行预处理以供tesseract使用,但目前我卡在了车牌检测(矩形检测)上。

我的步骤:

1)原始图像

真实图像

Mat img = new Mat();
img = Imgcodecs.imread("sample_photo.jpg"); 
Imgcodecs.imwrite("preprocess/True_Image.png", img);

2) 灰度

Mat imgGray = new Mat();
Imgproc.cvtColor(img, imgGray, Imgproc.COLOR_BGR2GRAY);
Imgcodecs.imwrite("preprocess/Gray.png", imgGray);

3) 高斯模糊

Mat imgGaussianBlur = new Mat(); 
Imgproc.GaussianBlur(imgGray,imgGaussianBlur,new Size(3, 3),0);
Imgcodecs.imwrite("preprocess/gaussian_blur.png", imgGaussianBlur);  

4)自适应阈值

Mat imgAdaptiveThreshold = new Mat();
Imgproc.adaptiveThreshold(imgGaussianBlur, imgAdaptiveThreshold, 255, CV_ADAPTIVE_THRESH_MEAN_C ,CV_THRESH_BINARY, 99, 4);
Imgcodecs.imwrite("preprocess/adaptive_threshold.png", imgAdaptiveThreshold);

这里应该是第五步,即检测车牌区域(暂时可能不需要去倾斜)。

我使用画图软件从图像中裁剪出所需的区域(在第四步之后),得到了:

plate region

然后我通过tesseract,tess4j进行了OCR识别:

File imageFile = new File("preprocess/adaptive_threshold_AFTER_PAINT.png");
ITesseract instance = new Tesseract();
instance.setLanguage("eng");
instance.setTessVariable("tessedit_char_whitelist", "acekopxyABCEHKMOPTXY0123456789");
String result = instance.doOCR(imageFile); 
System.out.println(result);

并获得了足够好的结果 - "Y841ox EH"(几乎正确)

在第四步之后,我该如何检测和裁剪车牌区域?我需要在1-4步中进行一些改进吗?希望看到一些使用Java + OpenCV(而不是JavaCV)实现的示例。
提前感谢。

编辑(感谢@Abdul Fatir的答案) 好吧,我为那些对这个问题感兴趣的人提供了工作(至少对我来说是这样)的代码示例(Netbeans+Java+OpenCV+Tess4j)。 代码不是最好的,但我只是为了学习而编写它。
http://pastebin.com/H46wuXWn(不要忘记将tessdata文件夹放入项目文件夹中)


2
你可以尝试分析轮廓。然而,使用级联分类器来定位车牌可能更可靠(用白色汽车测试算法并查看其效果)。将车牌矫正为水平状态。在进行tesseract之前,还应该添加一个额外的阶段--将车牌分割成单个字符(垂直投影可能会很好地适用于您的图像质量),然后只将这些字符提供给tesseract。 - Dan Mašek
你能在第四步之后也发布图像吗?我认为你应该能够通过提取轮廓并按大小和高宽比进行过滤来检测车牌边框。如果你有轮廓(因为你知道它是一个矩形,你可以撤销投影变换)。 - Rob Audenaerde
@RobAu,好的,这是链接:http://i.imgur.com/chrNMYX.png - DocC
3个回答

13

以下是我建议您完成此任务的步骤:

  1. 将图像转换为灰度。
  2. 使用3x3或5x5滤波器进行高斯模糊处理。
  3. 使用Sobel滤波器查找垂直边缘。

    Sobel(gray,dst,-1,1,0)

  4. 对结果图像进行阈值处理以获得二进制图像。
  5. 使用适当的结构元素应用形态学闭合操作。
  6. 查找所得图像的轮廓。
  7. 查找每个轮廓的minAreaRect。根据长宽比和最小和最大面积选择矩形。
  8. 对于每个选择的轮廓,查找边缘密度。设置边缘密度的阈值,并选择突破该阈值的矩形作为可能的车牌区域。
  9. 经过此步骤后,将保留少量矩形。可以根据方向或任何您认为合适的标准来筛选它们。
  10. adaptiveThreshold之后截取这些检测到的矩形部分并应用OCR。

a)第5步后的结果

第5步后的结果

b)第7步后的结果。绿色的是所有minAreaRect,红色的是符合以下标准的矩形:长宽比范围(2,12)和面积范围(300,10000)

c)第9步后的结果。选择的矩形。标准:边缘密度>0.5

在此输入图像描述

编辑

对于边缘密度,在上面的示例中我做了以下操作:

  1. 直接将 Canny 边缘检测器应用于输入图像。让CannyED图像为 Ic
  2. 使用 Sobel 过滤器和 Ic 的结果进行乘法。基本上,将 Sobel 和 Canny 图像进行 AND 操作。
  3. 使用一个大的过滤器(我使用 21x21)对结果图像进行高斯模糊。
  4. 使用 OTSU 方法对生成的图像进行阈值处理,你会得到二进制图像。
  5. 对于每个红色矩形,旋转此矩形内部的部分(在二进制图像中)使其竖直。循环遍历矩形像素并计算白色像素数量。(如何旋转?

边缘密度 = 矩形中白色像素的数量 / 矩形中像素的总数

  1. 选择一个边缘密度的阈值。

注意:你也可以使用第 5 步中的二进制图像来计算边缘密度,而不必执行步骤 1 到 3。


谢谢您提供如此详细的答案!我按照您描述的步骤完成了所有工作,只是遗漏了步骤“c”,即“边缘密度”。在这一步之前,算法运行得很好-我稍微调整了阈值和比率(感谢《Mastering OpenCV》第5章https://github.com/MasteringOpenCV/code/blob/master/Chapter5_NumberPlateRecognition/DetectRegions.cpp中的"verifySizes"函数),对于某些照片而言,没有使用边缘密度标准也足够好。您能解释一下如何检查由_minAreaRect_给出的RotatedRec是否符合边缘密度标准吗? - DocC
嗨!请检查已编辑的答案。如果对您有用,请点赞并标记为答案。 :) - Abdul Fatir
是的,我跳过了1-4步(从编辑段落)并强制执行第5步。 我从图像(adaptiveThreshold Mat对象)中裁剪了每个获得的矩形(通常有1-3个“可能”的板)。 然后我计算了白色像素的数量(_countNonZero_)和总像素数量; 得到密度(> =〜0.6 ++密度很好)和所需的矩形。 Tesseract也很好地完成了工作(我认为我会像@Dan建议的那样对每个牌子上的单个字符进行分割,以便更好地识别)。 - DocC

2

实际上,OpenCV有一种特别针对俄罗斯车牌的预训练模型:haarcascade_russian_plate_number

此外,还有一个针对俄罗斯车牌的开源ANPR项目:plate_recognition。它没有使用tesseract,但是它有相当不错的预训练神经网络。


好的,谢谢回复。我已经看过这个项目了 - 它很不错,但是有很多C++和QT(我不擅长)。我猜从车牌上裁剪出每个符号(由级联检测器检测到)并将其传递给tesseract引擎也可以工作,并且使用Java很容易实现。我想为Java解释那个C++项目(或绑定JNI),但是我现在没有那么多时间。 - DocC

1
  • 找出所有连接的组件(即白色区域),并确定其轮廓。
  • 如果根据大小(作为图像的一部分)、比率(宽高比)和白黑比过滤它们,则可以检索候选车牌。
  • 撤销矩形的变换。
  • 移除螺栓。
  • 将图像传递给OCR引擎。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接