具有不同背景颜色和低对比度的 OCR?

问题描述 投票:0回答:1

我正在尝试使用 pytesseract 将图像转换为文本。它对于白色背景和黑色文本的图像效果很好,但对于对比度较低和颜色变化的图像则失败。

我尝试将所有情况整合到一张图片中。在大多数情况下,文本和背景都有单一颜色。对于下面的图像,您会推荐哪种图像处理?是否可以使用某种“局部高对比度”转换?

代码:

import PIL.Image
import numpy as np
import pytesseract

img = PIL.Image.open('ocrtest.png')
ocr_string = pytesseract.image_to_string(img, timeout=30)
print(ocr_string)

结果:

Test one

Test five

Test 6 (not as important)

我在 Gimp 中尝试过曲线,并找到了使用 numpy 的等效曲线。我在图像子集方面取得了一些成功,但它并没有解决一般问题。

# Points found using Curve tool in gimp
# Based on https://stackoverflow.com/a/74279250/106019
lut_x = [226, 229]
lut_y = [  0, 252]
import numpy as np
lut_u8 = np.interp(np.arange(0, 256), lut_x, lut_y).astype(np.uint8)
R, G, B, A = [0, 1, 2, 3]
source = img.split()
out = []
for band in [R, G, B]:
    out.append(source[band].point(lut_u8))
out.append(source[A])
merged_img = PIL.Image.merge('RGBA', out)

python image-processing tesseract python-tesseract
1个回答
0
投票

Tesseract 需要二值化才能工作。那就是有一个黑白图像。

如果给定图像尚未二值化,则默认使用大津的图像。但这是全局二值化。它在不同块中背景和前景色不同的情况下效果不佳。

我不确定你的图像是否只是你用来试图找到最佳参数的酷刑测试。或者它是否是您可能遇到的实际示例。 如果是第一个,请注意将所有这些示例放在单个图像中会使问题变得更加困难。 如果是第二个,那么,了解这个例子的代表性将很有趣。总会有一些带有文字的矩形区域吗?

因为,在第二种情况下,更合适的答案可能是首先尝试隔离每个矩形,然后在每个矩形中单独运行超正方(使用正确的 psm)。

但是,一个通用的答案可能是您需要一种局部二值化技术。 有一些众所周知的(niblack,sauvaula,...) 我刚刚尝试了这两种方法,它们并非在所有情况下都有效。但它们是为现实世界的形象而设计的。

在你的情况下,有了这样的图像,我们可以制作我们自己的局部分割方法。基于以下事实:区域局部由 2 种颜色组成,其中占主导地位的颜色是背景。因此,如果我在某处提取本地窗口,并计算其中的中值颜色,则该中值就是背景(因为超过一半的像素是背景)。所以文字与背景有很大不同

因此进行二值化:

img = np.asarray(PIL.Image.open('ocrtest.png'))
bw=img.mean(axis=2)
bwloc=np.lib.stride_tricks.sliding_window_view(bw, (15,15))
back = np.median(bwloc, axis=(2,3))
binar = np.abs(bw[7:-7,7:-7]-back)<30

我处理灰度图像(

bw=img.mean...
也可以使用PIL直接读取灰度图像)。
bwloc
是一个 4D 数组,每个像素包含一个 15x15 像素的子窗口。所以
back
是每个本地 15x15 窗口的中值。这就是背景。二值化图像是白色(真实)狐狸像素的图像,与背景足够接近(30),而与背景非常不同的图像则为黑色(tesseract 需要白色文本上的黑色)。

请注意,背景是在 15x15 本地子窗口上计算的。我们将其与中间像素的原始值进行比较,从而产生 7 秒的偏移。事实上,我们可以对距图像边框小于 7 像素的像素执行此操作。

二值化图像为

然后,您需要做的另一件事(如果我们想在本地文本的图像上使用超正方体。如果我们首先提取矩形,则会以不同的方式完成)是选择正确的 psm。默认情况下需要一些文本页面,例如书本中的内容。

这里,psm 11 似乎更正确(稀疏文本)

这样做是这样的

ocr_string = pytesseract.image_to_string(binar, timeout=30, config="--psm 12")

结果原样

Test one

Test 6 (not as important)

Test two

Test?

Test &

Test three

Test 9

Test four

Test 10

Test five

并不完美。

7
?
之间存在一些混淆;以及
8
&
之间。

但我猜这就是测试的极限了。我们需要真实的文本来决定如何避免这些。例如,通过使用受限字符集来强制超立方体选择一个数字。

我自制的分割算法当然还可以改进。例如,我先对图像进行灰度化,然后再进行分割。我通常会抱怨“我们不应该这样做”,因为这会忽略颜色信息,这可能很有趣(如果红色和绿色具有相同的灰度强度,则绿色文本上的红色可能是不可见的)。

在用我当地的中位数重新发明轮子之前,我还没有努力让 Sauvola 发挥作用。

© www.soinside.com 2019 - 2024. All rights reserved.