首页 文章

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

提问于
浏览
112

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

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

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

12 回答

  • 16
    • 修复DPI(如果需要)300 DPI是最小的

    • 修复文字大小(例如12磅应该没问题)

    • 尝试修复文本行(偏移校正和去扭曲文本)

    • 尝试修复图像的照明(例如图像没有暗部)

    • 二值化和去噪图像

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

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

  • 13

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

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

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

    Gimp是你的朋友 .

  • 64

    提高图像可读性的三点: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;
            }
    

    输入图像

    INPUT IMAGE

    输出图像
    OUTPUT IMAGE

  • 6

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

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

    尝试不同的插值模式 . 帖子https://stackoverflow.com/a/4756906/146003给了我很多帮助 .

  • 12

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

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

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

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

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

  • 27

    上面的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;
    }
    
  • 78

    如果图像上的光照不均匀,则自适应阈值处理很重要 . 我在这篇文章中提到了使用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

  • 0

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

    • 重新缩放图像(建议您使用DPI低于300 dpi的图像):
    img = cv2.resize(img, None, fx=1.2, fy=1.2, interpolation=cv2.INTER_CUBIC)
    
    • 将图像转换为灰度:
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    • 应用膨胀和侵蚀来消除噪音(您可以根据数据集使用内核大小):
    kernel = np.ones((1, 1), np.uint8)
    img = cv2.dilate(img, kernel, iterations=1)
    img = cv2.erode(img, kernel, iterations=1)
    
    • 应用模糊,这可以通过使用以下行之一来完成(每个行都有其优点和缺点,但是,中值模糊和双边滤波通常比高斯模糊执行得更好 . ):
    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)
    

    我最近写了一篇非常简单的Tesseract指南,但是它应该让你能够编写你的第一个OCR脚本,并清除我遇到的一些障碍,当事情不那么清晰时我会喜欢在文档中 .

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

  • 5

    Tesseract文档通过图像处理步骤包含how to improve the OCR quality的一些很好的细节 .

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

    更重要的是,Tesseract 4中的new neural network system产生了更好的OCR结果 - 通常,特别是对于有一些噪声的图像 . 它使用 --oem 1 启用,例如如:

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

    (此示例选择德语)

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

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

  • 6

    我这样做是为了从一个文字不是很小的图像中获得好的结果 .

    • 将模糊应用于原始图像 .

    • 应用自适应阈值 .

    • 应用锐化效果 .

    如果仍未获得良好效果,请将图像缩放至150%或200% .

  • 2

    使用任何OCR引擎从图像文档中读取文本有许多问题,以便获得良好的准确性 . 所有案例都没有固定的解决方案,但这里有一些应该考虑改进OCR结果的事情 .

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

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

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

    这篇文章OCR application是一个示例案例,其中可以应用OCR结果的一些图像预处理和后处理以获得更好的OCR准确度 .

  • 2

    文本识别取决于多种因素,以产生高质量的输出 . OCR输出高度依赖于输入图像的质量 . 这就是每个OCR引擎提供有关输入图像质量及其大小的指导原因 . 这些指南可帮助OCR引擎产生准确的结果 .

    我在python中写了一篇关于图像处理的详细文章 . 请点击以下链接获取更多解释 . 还添加了python源代码来实现这些过程 .

    如果您对此主题有任何建议或更好的想法,请撰写评论以改进它 .

    https://medium.com/cashify-engineering/improve-accuracy-of-ocr-using-image-preprocessing-8df29ec3a033

相关问题