我正在尝试使用 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)
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 发挥作用。