image processing,以提高tesseract OCR的准确性

我一直在使用tesseract将文档转换为文本。 文档的质量范围很广,我正在寻找什么样的image processing可以改善结果的提示。 我注意到,高度像素化的文本(例如由传真机生成的文本)对于处理来说特别困难 – 大概所有那些锯齿状的边缘与字符混淆了形状识别algorithm。

什么样的image processing技术可以提高准确度? 我一直在使用高斯模糊平滑像素化的图像,并看到一些小的改进,但我希望有一个更具体的技术,会产生更好的结果。 说一个filter,调整为黑白图像,这将平滑不规则的边缘,其次是一个filter,这将增加对比度,使字符更清晰。

对于在image processing方面是新手的人来说,任何一般的提示?

  1. 修复DPI(如果需要)300 DPI最小
  2. 修正文本大小(例如12点应该没关系)
  3. 尝试修复文本行(歪斜和dewarp文本)
  4. 尝试修复图像的照明(例如,没有图像的黑暗部分
  5. 二值化和去噪图像

没有适用于所有情况的通用命令行(有时需要模糊和锐化图像)。 但是你可以试试Fred的ImageMagick脚本中的TEXTCLEANER 。

如果你不是命令行的粉丝,也许你可以尝试使用opensource scantailor.sourceforge.net或商业书籍记者 。

我绝不是一个OCR专家。 但是本周我需要把文本转换成jpg。

我从彩色的RGB 445×747像素jpg开始。 我马上就试了一下,这个程序几乎没有改变。 然后我去了GIMP,做了以下。 图像>模式>灰度图像>比例尺图像> 1191×2000像素滤镜>增强>半径为6.8,数量= 2.69,阈值= 0的不清晰蒙版。然后以100%质量保存为新的jpg。

Tesseract然后能够提取所有的文本到一个.txt文件

Gimp是你的朋友。

提高图像可读性的三点:1)调整图像的高度和宽度(将图像的高度和宽度乘以0.5和1和2)。 2)将图像转换为灰度格式(黑白)。 3)去除噪点像素,更清晰(过滤图像)。

请参考以下代码:

//Resize public Bitmap Resize(Bitmap bmp, int newWidth, int newHeight) { Bitmap temp = (Bitmap)bmp; Bitmap bmap = new Bitmap(newWidth, newHeight, temp.PixelFormat); double nWidthFactor = (double)temp.Width / (double)newWidth; double nHeightFactor = (double)temp.Height / (double)newHeight; double fx, fy, nx, ny; int cx, cy, fr_x, fr_y; Color color1 = new Color(); Color color2 = new Color(); Color color3 = new Color(); Color color4 = new Color(); byte nRed, nGreen, nBlue; byte bp1, bp2; for (int x = 0; x < bmap.Width; ++x) { for (int y = 0; y < bmap.Height; ++y) { fr_x = (int)Math.Floor(x * nWidthFactor); fr_y = (int)Math.Floor(y * nHeightFactor); cx = fr_x + 1; if (cx >= temp.Width) cx = fr_x; cy = fr_y + 1; if (cy >= temp.Height) cy = fr_y; fx = x * nWidthFactor - fr_x; fy = y * nHeightFactor - fr_y; nx = 1.0 - fx; ny = 1.0 - fy; color1 = temp.GetPixel(fr_x, fr_y); color2 = temp.GetPixel(cx, fr_y); color3 = temp.GetPixel(fr_x, cy); color4 = temp.GetPixel(cx, cy); // Blue bp1 = (byte)(nx * color1.B + fx * color2.B); bp2 = (byte)(nx * color3.B + fx * color4.B); nBlue = (byte)(ny * (double)(bp1) + fy * (double)(bp2)); // Green bp1 = (byte)(nx * color1.G + fx * color2.G); bp2 = (byte)(nx * color3.G + fx * color4.G); nGreen = (byte)(ny * (double)(bp1) + fy * (double)(bp2)); // Red bp1 = (byte)(nx * color1.R + fx * color2.R); bp2 = (byte)(nx * color3.R + fx * color4.R); nRed = (byte)(ny * (double)(bp1) + fy * (double)(bp2)); bmap.SetPixel(x, y, System.Drawing.Color.FromArgb (255, nRed, nGreen, nBlue)); } } bmap = SetGrayscale(bmap); bmap = RemoveNoise(bmap); return bmap; } //SetGrayscale public Bitmap SetGrayscale(Bitmap img) { Bitmap temp = (Bitmap)img; Bitmap bmap = (Bitmap)temp.Clone(); Color c; for (int i = 0; i < bmap.Width; i++) { for (int j = 0; j < bmap.Height; j++) { c = bmap.GetPixel(i, j); byte gray = (byte)(.299 * cR + .587 * cG + .114 * cB); bmap.SetPixel(i, j, Color.FromArgb(gray, gray, gray)); } } return (Bitmap)bmap.Clone(); } //RemoveNoise public Bitmap RemoveNoise(Bitmap bmap) { for (var x = 0; x < bmap.Width; x++) { for (var y = 0; y < bmap.Height; y++) { var pixel = bmap.GetPixel(x, y); if (pixel.R < 162 && pixel.G < 162 && pixel.B < 162) bmap.SetPixel(x, y, Color.Black); else if (pixel.R > 162 && pixel.G > 162 && pixel.B > 162) bmap.SetPixel(x, y, Color.White); } } return bmap; } 

input图像
输入图像

输出图像 输出图像

这是有点前,但它仍然可能是有用的。

我的经验表明,在传递给tesseract之前调整内存中的图像有时会有所帮助。

尝试不同的插值模式。 posthttps://stackoverflow.com/a/4756906/146003帮了我很多。

Capture2Text项目的源代码对我来说非常有帮助。 http://sourceforge.net/projects/capture2text/files/Capture2Text/

顺便说一句:这是作者分享这样一个艰苦的algorithm的荣誉。

请特别注意文件Capture2Text \ SourceCode \ leptonica_util \ leptonica_util.c – 这是此实用程序的图像预处理的本质。

如果您将运行二进制文件,您可以在Capture2Text \ Output \文件夹的过程之前/之后检查图像转换。

PS提到的解决scheme使用Tesseract进行OCR和Leptonica预处理。

上面的Sathyaraj代码的Java版本:

 // Resize public Bitmap resize(Bitmap img, int newWidth, int newHeight) { Bitmap bmap = img.copy(img.getConfig(), true); double nWidthFactor = (double) img.getWidth() / (double) newWidth; double nHeightFactor = (double) img.getHeight() / (double) newHeight; double fx, fy, nx, ny; int cx, cy, fr_x, fr_y; int color1; int color2; int color3; int color4; byte nRed, nGreen, nBlue; byte bp1, bp2; for (int x = 0; x < bmap.getWidth(); ++x) { for (int y = 0; y < bmap.getHeight(); ++y) { fr_x = (int) Math.floor(x * nWidthFactor); fr_y = (int) Math.floor(y * nHeightFactor); cx = fr_x + 1; if (cx >= img.getWidth()) cx = fr_x; cy = fr_y + 1; if (cy >= img.getHeight()) cy = fr_y; fx = x * nWidthFactor - fr_x; fy = y * nHeightFactor - fr_y; nx = 1.0 - fx; ny = 1.0 - fy; color1 = img.getPixel(fr_x, fr_y); color2 = img.getPixel(cx, fr_y); color3 = img.getPixel(fr_x, cy); color4 = img.getPixel(cx, cy); // Blue bp1 = (byte) (nx * Color.blue(color1) + fx * Color.blue(color2)); bp2 = (byte) (nx * Color.blue(color3) + fx * Color.blue(color4)); nBlue = (byte) (ny * (double) (bp1) + fy * (double) (bp2)); // Green bp1 = (byte) (nx * Color.green(color1) + fx * Color.green(color2)); bp2 = (byte) (nx * Color.green(color3) + fx * Color.green(color4)); nGreen = (byte) (ny * (double) (bp1) + fy * (double) (bp2)); // Red bp1 = (byte) (nx * Color.red(color1) + fx * Color.red(color2)); bp2 = (byte) (nx * Color.red(color3) + fx * Color.red(color4)); nRed = (byte) (ny * (double) (bp1) + fy * (double) (bp2)); bmap.setPixel(x, y, Color.argb(255, nRed, nGreen, nBlue)); } } bmap = setGrayscale(bmap); bmap = removeNoise(bmap); return bmap; } // SetGrayscale private Bitmap setGrayscale(Bitmap img) { Bitmap bmap = img.copy(img.getConfig(), true); int c; for (int i = 0; i < bmap.getWidth(); i++) { for (int j = 0; j < bmap.getHeight(); j++) { c = bmap.getPixel(i, j); byte gray = (byte) (.299 * Color.red(c) + .587 * Color.green(c) + .114 * Color.blue(c)); bmap.setPixel(i, j, Color.argb(255, gray, gray, gray)); } } return bmap; } // RemoveNoise private Bitmap removeNoise(Bitmap bmap) { for (int x = 0; x < bmap.getWidth(); x++) { for (int y = 0; y < bmap.getHeight(); y++) { int pixel = bmap.getPixel(x, y); if (Color.red(pixel) < 162 && Color.green(pixel) < 162 && Color.blue(pixel) < 162) { bmap.setPixel(x, y, Color.BLACK); } } } for (int x = 0; x < bmap.getWidth(); x++) { for (int y = 0; y < bmap.getHeight(); y++) { int pixel = bmap.getPixel(x, y); if (Color.red(pixel) > 162 && Color.green(pixel) > 162 && Color.blue(pixel) > 162) { bmap.setPixel(x, y, Color.WHITE); } } } return bmap; } 

如果照明在图像上不均匀,则自适应阈值处理非常重要。 我在这篇文章中提到了使用GraphicsMagic的预处理: https : //groups.google.com/forum/#!topic/tesseract-ocr/jONGSChLRv4

GraphicsMagic还具有线性时间自适应阈值的-latfunction,我将尽快尝试。

另一种使用OpenCV进行阈值处理的方法在这里描述: http : //docs.opencv.org/trunk/doc/py_tutorials/py_imgproc/py_thresholding/py_thresholding.html

我做了这些,以获得良好的结果出来的文字不是很小的图像。

  1. 将模糊应用于原始图像。
  2. 应用自适应阈值。
  3. 应用锐化效果。

如果仍然没有得到好的结果,将图像缩放到150%或200%。

Tesseract文档包含一些关于如何通过image processing步骤提高OCR质量的好的细节。

在某种程度上,Tesseract自动应用它们。 也可以告诉Tesseract写一个中间图像进行检查,即检查内部image processing的工作情况(在上面的参考文献中searchtessedit_write_images )。

更重要的是,Tesseract 4中的新型neural network系统可以产生更好的OCR结果 – 总的来说,尤其是对于有噪声的图像。 它使用--oem 1启用,例如im:

 $ tesseract --oem 1 -l deu page.png result pdf 

(这个例子select德语)

因此,在应用一些自定义的预处理image processing步骤之前,首先testing您使用新的Tesseract LSTM模式的距离是多less。

(截至2017年末,Tesseract 4尚未稳定发布,但开发版本可用)

使用任何OCR引擎从图像文档中读取文本有很多问题,以获得良好的准确性。 所有的情况都没有固定的解决scheme,但是这里有一些应该考虑的事情来改善OCR结果。

1)由于图像质量差/背景区域中的不需要的元素/斑点而出现噪声。 这需要一些预处理操作,例如可以使用高斯滤波器或普通中值滤波器方法容易地完成噪声去除。 这些在OpenCV中也是可用的。

2)图像方向错误:由于方向错误,OCR引擎无法正确分割图像中的线条和文字,从而导致精度最差。

3)线条的存在:在进行单词或线条分割时,OCR引擎有时也会尝试将单词和单词合并在一起,从而处理错误的内容,从而给出错误的结果。 还有其他的问题,但这些是基本的。

此OCR应用程序是一个示例情况,其中一些图像预处理和OCR结果后处理可应用于获得更好的OCR准确性。