图像处理,以提高tesseract OCR的准确性

use*_*902 127 ocr tesseract image-processing

我一直在使用tesseract将文档转换为文本.文档的质量范围非常广泛,我正在寻找有关哪种图像处理可能会改善结果的提示.我注意到高度像素化的文本 - 例如由传真机生成的文本 - 对于tesseract来说特别难以处理 - 可能是角色的所有锯齿状边缘都会混淆形状识别算法.

什么样的图像处理技术可以提高准确度?我一直在使用高斯模糊来平滑像素化图像并看到一些小的改进,但我希望有更具体的技术可以产生更好的结果.假设一个过滤器被调整为黑白图像,这将平滑不规则的边缘,然后是一个过滤器,它会增加对比度,使角色更加清晰.

对于图像处理新手的任何一般提示?

use*_*678 89

  1. 修复DPI(如果需要)300 DPI是最小的
  2. 修复文字大小(例如12磅应该没问题)
  3. 尝试修复文本行(偏移校正和去扭曲文本)
  4. 尝试修复图像的照明(例如没有图像的暗部)
  5. 二值化和去噪图像

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

如果您不是命令行的粉丝,也许您可​​以尝试使用opensource scantailor.sourceforge.net或商业bookrestorer.

  • 还有关于如何执行此操作的说明指南:https://code.google.com/p/tesseract-ocr/wiki/ImproveQuality (6认同)
  • @iljau自从搬到github.维基页面位于:https://github.com/tesseract-ocr/tesseract/wiki/ImproveQuality (6认同)
  • Tesseract 文档再次移动至 https://tesseract-ocr.github.io/tessdoc/ImproveQuality (6认同)
  • 注意,链接的脚本似乎只是linux. (2认同)

小智 68

我绝不是OCR专家.但本周我需要将文本转换为jpg.

我开始使用彩色RGB 445x747像素jpg.我立即尝试了tesseract,程序几乎没有转换.然后我进入GIMP并做了以下事情.图像>模式>灰度图像>比例图像> 1191x2000像素滤镜>增强>非锐化蒙版,其值为半径= 6.8,量= 2.69,阈值= 0然后我以100%质量保存为新的jpg.

然后,Tesseract能够将所有文本提取到.txt文件中

Gimp是你的朋友.

  • +1我跟着你的步骤,我得到了很大的改进.谢谢 (11认同)

Sat*_*amy 29

提高图像可读性的三点: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 * c.R + .587 * c.G + .114 * c.B);

                    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;
        }
Run Code Online (Sandbox Code Playgroud)

输入图像
输入图像

输出图像 输出图像


Atm*_*ons 16

这在一定程度上,但它仍然可能有用.

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

尝试不同的插值模式.帖子/sf/answers/332983451/对我帮助很大.


Wis*_*man 14

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

顺便说一句:感谢它分享这样一个艰苦的算法的作者.

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

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

PS提到的解决方案使用Tesseract for OCR和Leptonica进行预处理.


bka*_*glu 13

根据经验,我通常使用OpenCV库应用以下图像预处理技术:

  1. 重新缩放图像(如果您使用的DPI小于300 dpi的图像,建议使用此图像):

    img = cv2.resize(img, None, fx=1.2, fy=1.2, interpolation=cv2.INTER_CUBIC)
    
    Run Code Online (Sandbox Code Playgroud)
  2. 将图像转换为灰度:

    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    Run Code Online (Sandbox Code Playgroud)
  3. 应用膨胀和侵蚀来消除噪音(您可以根据数据集使用内核大小):

    kernel = np.ones((1, 1), np.uint8)
    img = cv2.dilate(img, kernel, iterations=1)
    img = cv2.erode(img, kernel, iterations=1)
    
    Run Code Online (Sandbox Code Playgroud)
  4. 应用模糊,可以通过使用以下行之一来完成(每个行都有其优点和缺点,但是,中值模糊和双边滤波器通常比高斯模糊执行得更好.):

    cv2.threshold(cv2.GaussianBlur(img, (5, 5), 0), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
    
    cv2.threshold(cv2.bilateralFilter(img, 5, 75, 75), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
    
    cv2.threshold(cv2.medianBlur(img, 3), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
    
    cv2.adaptiveThreshold(cv2.GaussianBlur(img, (5, 5), 0), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)
    
    cv2.adaptiveThreshold(cv2.bilateralFilter(img, 9, 75, 75), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)
    
    cv2.adaptiveThreshold(cv2.medianBlur(img, 3), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)
    
    Run Code Online (Sandbox Code Playgroud)

我最近写了一篇非常简单的Tesseract指南,但它应该让你能够编写你的第一个OCR脚本,并清除我在文档中不太清楚的情况下遇到的一些障碍.

如果您想查看它们,我在这里与您分享链接:


小智 12

上面的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;
}
Run Code Online (Sandbox Code Playgroud)


rle*_*eir 6

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

GraphicsMagic还具有线性时间自适应阈值的-lat功能,我将尽快尝试.

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

  • OpenCV链接已更改.在OpenCV文档中,它是[OpenCV-Python教程> OpenCV中的图像处理>图像阈值处理](http://docs.opencv.org/master/d7/d4d/tutorial_py_thresholding.html) (2认同)

max*_*zig 5

Tesseract文档包含一些有关如何通过图像处理步骤提高OCR质量的详细信息.

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

更重要的是,Tesseract 4中的新神经网络系统产生了更好的OCR结果 - 通常,特别是对于具有一些噪声的图像.它启用--oem 1,例如:

$ tesseract --oem 1 -l deu page.png result pdf
Run Code Online (Sandbox Code Playgroud)

(此示例选择德语)

因此,在应用一些自定义预处理图像处理步骤之前,首先测试使用新Tesseract LSTM模式的距离是有意义的.

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