使用 OpenCV 为 Tesseract OCR 预处理图像

Preprocessing image for Tesseract OCR with OpenCV

我正在尝试开发一个应用程序,该应用程序使用 Tesseract 识别 phone 摄像头拍摄的文档中的文本。我正在使用 OpenCV 对图像进行预处理以更好地识别,应用高斯模糊和阈值方法进行二值化,但结果很糟糕。

Here 是我用于测试的图像:

here预处理图像:

我可以使用哪些其他滤镜来使图像对于 Tesseract 更具可读性?

我在这里描述了为 Tesseract 准备图像的一些技巧: Using tesseract to recognize license plates

在您的示例中,发生了几件事...

您需要将文本设为黑色,将图像的其余部分设为白色(而非 相反)。这就是调整字符识别的原因。灰度可以,只要背景大部分是全白,文字大部分是全黑;文本的边缘可能是灰色的(抗锯齿)并且可能有助于识别(但不一定 - 您必须进行试验)

您看到的一个问题是,在图像的某些部分,文本确实是 "thin"(并且在阈值处理后字母中的间隙出现),而在其他部分确实是 "thin" "thick"(字母开始合并)。 Tesseract 不会喜欢那样 :) 发生这种情况是因为输入图像的光照不均匀,所以单个阈值并不适用于所有地方。解决方案是 "locally adaptive thresholding" ,其中为图像的每个邻域计算不同的阈值。有很多方法可以做到这一点,但请查看示例:

您遇到的另一个问题是线条不直。根据我的经验,Tesseract 可以处理非常有限 度的非直线(百分之几的透视变形、倾斜或偏斜),但它不适用于 波浪行。如果可以,请确保源图像有直线 :) 不幸的是,对此没有简单的现成答案;您必须查看研究文献并自己实施一种最先进的算法(并在可能的情况下将其开源 - 确实需要一个开源解决方案)。 Google 学术搜索“curved line OCR extraction”会让你开始,例如:

最后:我认为与使用 C++ 中的 OpenCV 相比,使用 python 生态系统(ndimage、skimage)会更好。 OpenCV python 包装器可以用于简单的东西,但是对于您尝试做的事情,它们将无法完成工作,您需要获取许多 OpenCV 中没有的部分(当然您可以混合和比赛)。在 C++ 中实现诸如曲线检测之类的东西将比在 python 中花费更长的数量级(* 即使你不知道 python 也是如此)。

祝你好运!

注意:这应该是我回答的对Alex的评论,但是它太长了所以我把它作为答案。

从 "An Overview of the Tesseract OCR engine, by Ray Smith, Google Inc." 在 https://github.com/tesseract-ocr/docs/blob/master/tesseracticdar2007.pdf

"处理遵循传统 step-by-step 流水线,但有些阶段在他们的 一天,甚至现在可能仍然如此。第一步是 一个连接的组件分析,其中的轮廓 组件被存储。这是一个计算 当时昂贵的设计决策,但有一个 显着优势:通过检查嵌套 轮廓,以及 child 和 grandchild 的数量 轮廓,很容易检测到反文字和 识别它就像 black-on-white 文本一样容易。立方体 可能是第一个能够处理的 OCR 引擎 white-on-black 文字太琐碎了。"

所以似乎不需要白底黑字,反之亦然。

  1. Scanning at 300 dpi (dots per inch) is not officially a standard for OCR (optical character recognition), but it is considered the gold standard.

  2. Converting image to Greyscale improves accuracy in reading text in general.

我编写了一个模块来读取图像中的文本,然后处理图像以获得 OCR 的最佳结果,Image Text Reader

import tempfile

import cv2
import numpy as np
from PIL import Image

IMAGE_SIZE = 1800
BINARY_THREHOLD = 180

def process_image_for_ocr(file_path):
    # TODO : Implement using opencv
    temp_filename = set_image_dpi(file_path)
    im_new = remove_noise_and_smooth(temp_filename)
    return im_new

def set_image_dpi(file_path):
    im = Image.open(file_path)
    length_x, width_y = im.size
    factor = max(1, int(IMAGE_SIZE / length_x))
    size = factor * length_x, factor * width_y
    # size = (1800, 1800)
    im_resized = im.resize(size, Image.ANTIALIAS)
    temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.jpg')
    temp_filename = temp_file.name
    im_resized.save(temp_filename, dpi=(300, 300))
    return temp_filename

def image_smoothening(img):
    ret1, th1 = cv2.threshold(img, BINARY_THREHOLD, 255, cv2.THRESH_BINARY)
    ret2, th2 = cv2.threshold(th1, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    blur = cv2.GaussianBlur(th2, (1, 1), 0)
    ret3, th3 = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    return th3

def remove_noise_and_smooth(file_name):
    img = cv2.imread(file_name, 0)
    filtered = cv2.adaptiveThreshold(img.astype(np.uint8), 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 41,
                                     3)
    kernel = np.ones((1, 1), np.uint8)
    opening = cv2.morphologyEx(filtered, cv2.MORPH_OPEN, kernel)
    closing = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, kernel)
    img = image_smoothening(img)
    or_image = cv2.bitwise_or(img, closing)
    return or_image

您可以通过更改 --psm 和 --oem 值来尝试 OCR 的配置,在您的具体情况下,我建议使用

--psm 3 --OEM 2

您还可以查看以下内容link了解更多详情 here

我猜你已经使用了二值化的通用方法,这就是整个图像没有统一二值化的原因。您可以使用自适应阈值技术进行二值化。还可以做一些歪斜校正、透视校正、去噪等效果更好。

参考此媒体 article,了解上述技术以及代码示例。

对于像您这样的波浪形文本,在 GitHub 上有这个很棒的 Python 代码,它将文本转换为直线:https://github.com/tachylatus/page_dewarp.git (this is the most updated version of MZucker's original post and the mechanics are explained here:https://mzucker.github.io/2016/08/15/page-dewarping.html)