使用 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" ,其中为图像的每个邻域计算不同的阈值。有很多方法可以做到这一点,但请查看示例:
- Adaptive gaussian thresholding in OpenCV 与
cv2.adaptiveThreshold(...,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,...)
- Local Otsu's method
- Local adaptive histogram equalization
您遇到的另一个问题是线条不直。根据我的经验,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 文字太琐碎了。"
所以似乎不需要白底黑字,反之亦然。
Scanning at 300 dpi (dots per inch) is not officially a standard for OCR (optical character recognition), but it is considered the gold standard.
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)
我正在尝试开发一个应用程序,该应用程序使用 Tesseract 识别 phone 摄像头拍摄的文档中的文本。我正在使用 OpenCV 对图像进行预处理以更好地识别,应用高斯模糊和阈值方法进行二值化,但结果很糟糕。
Here 是我用于测试的图像:
和here预处理图像:
我可以使用哪些其他滤镜来使图像对于 Tesseract 更具可读性?
我在这里描述了为 Tesseract 准备图像的一些技巧: Using tesseract to recognize license plates
在您的示例中,发生了几件事...
您需要将文本设为黑色,将图像的其余部分设为白色(而非 相反)。这就是调整字符识别的原因。灰度是可以,只要背景大部分是全白,文字大部分是全黑;文本的边缘可能是灰色的(抗锯齿)并且可能有助于识别(但不一定 - 您必须进行试验)
您看到的一个问题是,在图像的某些部分,文本确实是 "thin"(并且在阈值处理后字母中的间隙出现),而在其他部分确实是 "thin" "thick"(字母开始合并)。 Tesseract 不会喜欢那样 :) 发生这种情况是因为输入图像的光照不均匀,所以单个阈值并不适用于所有地方。解决方案是 "locally adaptive thresholding" ,其中为图像的每个邻域计算不同的阈值。有很多方法可以做到这一点,但请查看示例:
- Adaptive gaussian thresholding in OpenCV 与
cv2.adaptiveThreshold(...,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,...)
- Local Otsu's method
- Local adaptive histogram equalization
您遇到的另一个问题是线条不直。根据我的经验,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 文字太琐碎了。"
所以似乎不需要白底黑字,反之亦然。
Scanning at 300 dpi (dots per inch) is not officially a standard for OCR (optical character recognition), but it is considered the gold standard.
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)