使用 tesseract 检测明亮背景上的白色文本
Detecting white text on a bright background with tesseract
我在明亮背景上阅读白色文本时遇到问题,它可以找到文本本身,但无法正确翻译它。
图片:
老实说,我一直得到的结果是 LanEerus
,相距不远。
我想知道什么图像预处理可以解决这个问题?我正在使用 photoshop 对其进行手动预处理,然后再尝试使用代码进行处理,以找到最先起作用的方法。
我试过将其设为位图,但这会使文本的 边框 非常糟糕,导致 tesseract 只是将其转换为随机字符。
反转颜色 and/or 灰度似乎也无济于事。
有人有什么想法吗?我知道对于这个案例的文本来说,这是一个非常糟糕的背景。相信我,我希望背景不一样!
我的测试代码:
File file = new File("C:\tess\lando.png");
ITesseract tess = new Tesseract();
tess.setDatapath("tessdata");
System.out.println(tess.doOCR(file));
编辑
我已通读 Improving the quality 但无法使这些提示起作用。
编辑 2
使用 OpenCV 对图像进行灰度、反色、高斯模糊和自适应阈值预处理后。我得到了图像的结果,但没有更好的阅读。如果有的话,更糟..
这是一种可能的解决方案。这是在 Python 中,但对于 Java 端口来说应该足够清楚了。我们将应用一种名为 获得除法 的方法。这个想法是您尝试构建背景模型,然后通过该模型对每个输入像素进行加权。在大部分图像期间,输出增益应该相对恒定。这将消除大部分背景颜色变化。我们可以使用 morphological
链来稍微清理一下结果,让我们看一下代码:
# imports:
import cv2
import numpy as np
# OCR imports:
from PIL import Image
import pyocr
import pyocr.builders
# image path
path = "D://opencvImages//"
fileName = "c552h.png"
# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)
# Get local maximum:
kernelSize = 5
maxKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))
localMax = cv2.morphologyEx(inputImage, cv2.MORPH_CLOSE, maxKernel, None, None, 1, cv2.BORDER_REFLECT101)
# Perform gain division
gainDivision = np.where(localMax == 0, 0, (inputImage/localMax))
# Clip the values to [0,255]
gainDivision = np.clip((255 * gainDivision), 0, 255)
# Convert the mat type from float to uint8:
gainDivision = gainDivision.astype("uint8")
第一步是应用增益除法,你需要的操作很简单:一个带有大矩形structuring element
的形态学closing
和一些数据类型转换,后面的要小心。这是应用该方法后您应该看到的图像:
太棒了,背景都快没了。让我们使用 Otsu 的阈值处理得到一个二值图像:
# Convert RGB to grayscale:
grayscaleImage = cv2.cvtColor(gainDivision, cv2.COLOR_BGR2GRAY)
# Get binary image via Otsu:
_, binaryImage = cv2.threshold(grayscaleImage, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
这是二进制图像:
我们有一个很好的文本边缘图像。如果我们 Flood-Fill
背景为白色,我们可以获得黑色背景和白色文本。但是,我们应该注意字符,因为如果一个字符坏了,Flood-Fill
操作会把它擦掉。让我们首先通过应用形态 closing
:
来确保我们的字符是封闭的
# Set kernel (structuring element) size:
kernelSize = 3
# Set morph operation iterations:
opIterations = 1
# Get the structuring element:
morphKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))
# Perform closing:
binaryImage = cv2.morphologyEx( binaryImage, cv2.MORPH_CLOSE, morphKernel, None, None, opIterations, cv2.BORDER_REFLECT101 )
这是生成的图像:
如您所见,边缘更加坚固,最重要的是,边缘是闭合的。现在,我们可以 Flood-Fill
将背景设为白色。这里,Flood-Fill
种子点位于图像原点(x = 0
,y = 0
):
# Flood fill (white + black):
cv2.floodFill(binaryImage, mask=None, seedPoint=(int(0), int(0)), newVal=(255))
我们得到这张图片:
我们快到了。如您所见,某些字符(例如“a”、“d”和“o”)内部的空洞未被填充 - 这会对 OCR
产生噪音。让我们尝试填充它们。我们可以利用这些孔都是父轮廓的 children 这一事实。我们可以隔离子轮廓,然后再次应用 Flood-Fill
来填充它们。但首先,不要忘记反转图像:
# Invert image so target blobs are colored in white:
binaryImage = 255 - binaryImage
# Find the blobs on the binary image:
contours, hierarchy = cv2.findContours(binaryImage, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# Process the contours:
for i, c in enumerate(contours):
# Get contour hierarchy:
currentHierarchy = hierarchy[0][i][3]
# Look only for children contours (the holes):
if currentHierarchy != -1:
# Get the contour bounding rectangle:
boundRect = cv2.boundingRect(c)
# Get the dimensions of the bounding rect:
rectX = boundRect[0]
rectY = boundRect[1]
rectWidth = boundRect[2]
rectHeight = boundRect[3]
# Get the center of the contour the will act as
# seed point to the Flood-Filling:
fx = rectX + 0.5 * rectWidth
fy = rectY + 0.5 * rectHeight
# Fill the hole:
cv2.floodFill(binaryImage, mask=None, seedPoint=(int(fx), int(fy)), newVal=(0))
# Write result to disk:
cv2.imwrite("text.png", binaryImage, [cv2.IMWRITE_PNG_COMPRESSION, 0])
这是生成的掩码:
酷,让我们应用 OCR
。我正在使用 pyocr
:
txt = tool.image_to_string(
Image.open("text.png"),
lang=lang,
builder=pyocr.builders.TextBuilder()
)
print(txt)
输出:
Landorus
我在明亮背景上阅读白色文本时遇到问题,它可以找到文本本身,但无法正确翻译它。
图片:
老实说,我一直得到的结果是 LanEerus
,相距不远。
我想知道什么图像预处理可以解决这个问题?我正在使用 photoshop 对其进行手动预处理,然后再尝试使用代码进行处理,以找到最先起作用的方法。
我试过将其设为位图,但这会使文本的 边框 非常糟糕,导致 tesseract 只是将其转换为随机字符。
反转颜色 and/or 灰度似乎也无济于事。
有人有什么想法吗?我知道对于这个案例的文本来说,这是一个非常糟糕的背景。相信我,我希望背景不一样!
我的测试代码:
File file = new File("C:\tess\lando.png");
ITesseract tess = new Tesseract();
tess.setDatapath("tessdata");
System.out.println(tess.doOCR(file));
编辑
我已通读 Improving the quality 但无法使这些提示起作用。
编辑 2
使用 OpenCV 对图像进行灰度、反色、高斯模糊和自适应阈值预处理后。我得到了图像的结果,但没有更好的阅读。如果有的话,更糟..
这是一种可能的解决方案。这是在 Python 中,但对于 Java 端口来说应该足够清楚了。我们将应用一种名为 获得除法 的方法。这个想法是您尝试构建背景模型,然后通过该模型对每个输入像素进行加权。在大部分图像期间,输出增益应该相对恒定。这将消除大部分背景颜色变化。我们可以使用 morphological
链来稍微清理一下结果,让我们看一下代码:
# imports:
import cv2
import numpy as np
# OCR imports:
from PIL import Image
import pyocr
import pyocr.builders
# image path
path = "D://opencvImages//"
fileName = "c552h.png"
# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)
# Get local maximum:
kernelSize = 5
maxKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))
localMax = cv2.morphologyEx(inputImage, cv2.MORPH_CLOSE, maxKernel, None, None, 1, cv2.BORDER_REFLECT101)
# Perform gain division
gainDivision = np.where(localMax == 0, 0, (inputImage/localMax))
# Clip the values to [0,255]
gainDivision = np.clip((255 * gainDivision), 0, 255)
# Convert the mat type from float to uint8:
gainDivision = gainDivision.astype("uint8")
第一步是应用增益除法,你需要的操作很简单:一个带有大矩形structuring element
的形态学closing
和一些数据类型转换,后面的要小心。这是应用该方法后您应该看到的图像:
太棒了,背景都快没了。让我们使用 Otsu 的阈值处理得到一个二值图像:
# Convert RGB to grayscale:
grayscaleImage = cv2.cvtColor(gainDivision, cv2.COLOR_BGR2GRAY)
# Get binary image via Otsu:
_, binaryImage = cv2.threshold(grayscaleImage, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
这是二进制图像:
我们有一个很好的文本边缘图像。如果我们 Flood-Fill
背景为白色,我们可以获得黑色背景和白色文本。但是,我们应该注意字符,因为如果一个字符坏了,Flood-Fill
操作会把它擦掉。让我们首先通过应用形态 closing
:
# Set kernel (structuring element) size:
kernelSize = 3
# Set morph operation iterations:
opIterations = 1
# Get the structuring element:
morphKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))
# Perform closing:
binaryImage = cv2.morphologyEx( binaryImage, cv2.MORPH_CLOSE, morphKernel, None, None, opIterations, cv2.BORDER_REFLECT101 )
这是生成的图像:
如您所见,边缘更加坚固,最重要的是,边缘是闭合的。现在,我们可以 Flood-Fill
将背景设为白色。这里,Flood-Fill
种子点位于图像原点(x = 0
,y = 0
):
# Flood fill (white + black):
cv2.floodFill(binaryImage, mask=None, seedPoint=(int(0), int(0)), newVal=(255))
我们得到这张图片:
我们快到了。如您所见,某些字符(例如“a”、“d”和“o”)内部的空洞未被填充 - 这会对 OCR
产生噪音。让我们尝试填充它们。我们可以利用这些孔都是父轮廓的 children 这一事实。我们可以隔离子轮廓,然后再次应用 Flood-Fill
来填充它们。但首先,不要忘记反转图像:
# Invert image so target blobs are colored in white:
binaryImage = 255 - binaryImage
# Find the blobs on the binary image:
contours, hierarchy = cv2.findContours(binaryImage, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# Process the contours:
for i, c in enumerate(contours):
# Get contour hierarchy:
currentHierarchy = hierarchy[0][i][3]
# Look only for children contours (the holes):
if currentHierarchy != -1:
# Get the contour bounding rectangle:
boundRect = cv2.boundingRect(c)
# Get the dimensions of the bounding rect:
rectX = boundRect[0]
rectY = boundRect[1]
rectWidth = boundRect[2]
rectHeight = boundRect[3]
# Get the center of the contour the will act as
# seed point to the Flood-Filling:
fx = rectX + 0.5 * rectWidth
fy = rectY + 0.5 * rectHeight
# Fill the hole:
cv2.floodFill(binaryImage, mask=None, seedPoint=(int(fx), int(fy)), newVal=(0))
# Write result to disk:
cv2.imwrite("text.png", binaryImage, [cv2.IMWRITE_PNG_COMPRESSION, 0])
这是生成的掩码:
酷,让我们应用 OCR
。我正在使用 pyocr
:
txt = tool.image_to_string(
Image.open("text.png"),
lang=lang,
builder=pyocr.builders.TextBuilder()
)
print(txt)
输出:
Landorus