在进行字符识别之前使用 OpenCV 进行图像预处理(tesseract)
Posted
技术标签:
【中文标题】在进行字符识别之前使用 OpenCV 进行图像预处理(tesseract)【英文标题】:Image preprocessing with OpenCV before doing character recognition (tesseract) 【发布时间】:2016-09-15 02:05:08 【问题描述】:我正在尝试开发用于车牌识别的简单 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);
这里应该是第 5 步,即检测车牌区域(现在可能甚至没有纠偏)。
我用 Paint 从图像中裁剪出所需区域(在第 4 步之后),并得到:
然后我做了 OCR(通过 tesseract、tess4j):
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”(几乎是真的)
如何在第 4 步后检测和裁剪板块区域?我是否需要分 1-4 个步骤进行一些更改(改进)?希望看到一些通过 Java + OpenCV(不是 JavaCV)实现的示例。 提前致谢。
编辑(感谢@Abdul Fatir 的回答) 好吧,我为那些对这个问题感兴趣的人提供了工作(至少对我来说)代码示例(Netbeans+Java+OpenCV+Tess4j)。代码不是最好的,但我只是为了学习而写的。 http://pastebin.com/H46wuXWn (别忘了把 tessdata 文件夹放到你的项目文件夹中)
【问题讨论】:
您可以尝试分析轮廓。然而,使用cascade classifier 来定位车牌可能更可靠(用白色汽车测试你的算法,看看它是如何工作的)。将板歪斜,使其水平。您还应该在 tesseract 之前添加一个额外的阶段——将车牌分割成单个字符(考虑到图像的质量,垂直投影可能会很好地工作)并且只将它们提供给 tesseract.. 你能在第 4 步之后发布图片吗?我认为您应该能够通过提取轮廓并根据尺寸和 h/w 比过滤它们来检测板边界。如果你有轮廓(因为你知道它是一个矩形,你可以撤消投影变换) @RobAu,当然:i.imgur.com/chrNMYX.png 【参考方案1】:以下是我建议您执行此任务的方法。
-
转换为灰度。
使用 3x3 或 5x5 滤镜进行高斯模糊。
应用 Sobel 过滤器来查找垂直边缘。
Sobel(gray, dst, -1, 1, 0)
minAreaRect
。根据纵横比以及最小和最大面积选择矩形。
对于每个选定的轮廓,找出边缘密度。设置边缘密度阈值并选择超出该阈值的矩形作为可能的板块区域。
此后将保留几个矩形。您可以根据方向或您认为合适的任何标准过滤它们。
在adaptiveThreshold
之后从图像中剪切这些检测到的矩形部分并应用OCR。
a) 第 5 步后的结果
b) 第 7 步后的结果。绿色是所有 minAreaRect
s,红色是满足以下条件的结果:纵横比范围 (2,12) & 面积范围 (300,10000)
c) 第 9 步后的结果。选定的矩形。标准:边缘密度 > 0.5
编辑
对于边缘密度,我在上述示例中所做的如下。
-
将 Canny 边缘检测器直接应用于输入图像。让 cannyED 图像为 Ic。
Sobel 滤波器和 Ic 的结果相乘。基本上,对 Sobel 和 Canny 图像进行 AND。
高斯模糊使用大滤镜生成的图像。我使用的是 21x21。
使用 OTSU 的方法对生成的图像进行阈值处理。你会得到一个二值图像
对于每个红色矩形,旋转该矩形内的部分(在二值图像中)使其直立。循环遍历矩形的像素并计算白色像素。 (How to rotate?)
边缘密度 = 矩形中的白色像素数/总数。矩形中的像素数
-
选择边缘密度的阈值。
注意:您也可以使用步骤 5 中的二值图像来计算边缘密度,而不是执行步骤 1 到 3。
【讨论】:
感谢您提供如此详细的答案!我做了你描述的所有事情,除了步骤“c”,即“边缘密度”。直到这一步算法运行良好 - 我玩了一点阈值和比率(感谢“掌握 OpenCV 第 5 章”github.com/MasteringOpenCV/code/blob/master/… 特别是“verifySizes”功能),对于某些照片,它在没有边缘密度标准的情况下运行得很好。你能解释一下我如何检查 RotatedRec(由 minAreaRect 给出)的边缘密度标准吗? 嗨!请检查编辑后的答案。如果对您有用,请投票并标记为答案。 :) 是的,我跳过了 1-4 个步骤(从 Edit 段落)并强制执行第 5 步。我裁剪了每个获得的矩形(通常有 1-3 个“可能的”板) 来自图像(adaptiveThreshold Mat 对象)。然后我计算白色像素的数量(countNonZero)和像素总数;得到密度( >= ~0.6++ 密度很好)和需要的矩形。 Tesseract 也做得很好(我想我会在一个盘子上分割每个单独的字符以便进一步识别,就像@Dan 建议的那样)。【参考方案2】:其实OpenCV有专门针对俄罗斯车牌的预训练模型:haarcascade_russian_plate_number
还有俄罗斯车牌的开源 ANPR 项目:plate_recognition。它没有使用 tesseract,但它具有相当好的预训练神经网络。
【讨论】:
好的,谢谢您的回复。我已经看过这个项目——它很好,但是很多 C++ 和 QT(我不擅长)。我想从盘子中裁剪每个符号(由级联检测到)并将其传递给 tesseract-engine 也可以工作,并且很容易用 Java 制作。我想为 Java 解释那个 C++ 项目(或绑定 JNI),但我现在没有那么多时间。【参考方案3】: 您找到所有连接的组件(白色区域)并确定它们的轮廓。 如果您根据尺寸(作为图像的一部分)、比例(宽高)和白/黑比例过滤它们以检索候选车牌。 撤消矩形的变换 拆下螺栓 将图像传递给 OCR 引擎。【讨论】:
以上是关于在进行字符识别之前使用 OpenCV 进行图像预处理(tesseract)的主要内容,如果未能解决你的问题,请参考以下文章
进行 OCR 之前的预处理(tesseract、OpenCV)
使用 OpenCV 为 Tesseract OCR 预处理七段图像