如何尽可能提高tesseract ocr对文本识别的可靠性?

How can I maximise the reliability of tesseract ocr for text recognition as much as possible?

我正在尝试从游戏 (starbase) 中的一家商店收集数据,以便将数据提供给网站,以便能够将它们显示为烛台图

到目前为止,我已经开始使用 Tesseract OCR 5.0.0,但我一直运行遇到问题,因为我无法可靠地获取值

我看到可以对图像进行预处理以提高可靠性,但我 运行 遇到了瓶颈,因为我对 Tesseract 和 OpenCV 不够熟悉,不知道该怎么做更多

请注意,由于这是游戏中的 UI 图像将非常稳定,因为没有颜色变化/光线变化/字体大小变化/ ...从技术上讲,我只需要让它工作一次,仅此而已

以下是我到目前为止采取的步骤和结果:

我已经开始只获取我感兴趣的 UI 部分的屏幕,以便尽可能地消除混乱

然后我设置了一个阈值,如图所示(我也会在进行自动化时使用裁剪部分,但我还没有),将语言设置为英语和 psm 6 的参数给了我以下代码:

import cv2
import pytesseract


def clean_text(text):
    ret = text.replace("\n\n", "\n")  # remove the blank lines
    return ret


pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract'
img = cv2.imread('screens/ressources_list_array_1.png', 0)
thresh = 255 - cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

print("======= Output")
print(clean_text(pytesseract.image_to_string(thresh, lang='eng', config='--psm 6')))

cv2.imshow('thresh', thresh)
cv2.waitKey()

这是我得到的输出示例:

======= Output
Aegisium Ore 4490 456
Ajatite Ore 600 332
Arkanium Ore 84999 53
Bastium Ore 2350 421
Charodium Ore 5 280 366
Corazium Ore 39 896 212
Exorium Ore 5 380 112
Ice 980 141
Karnite Crystal ele) 111
Kutonium Ore 14 000 215
Lukium Ore 31 000 158
Nhurgite Crystal 3144 64
Surtrite Crystal 4198 70
Valkite Ore 545 150
Vokarium Ore 1850 415
Ymrium Ore 69 899 60

主要有两个问题:
1 - 它不够可靠,你可以看到它混淆了 6 000ele)
2 - 没有正确理解数字的开始和结束位置,导致难以区分 2 列

我想我可以通过将图像进一步分成 3 列来解决第二个问题,但我不确定它是否不会对 CPU / GPU 使用率造成很大影响 我会最好避免

我还找到了 OpenCV 的文档,它显示了所有可能的 Image processing methods 但是有很多,我不确定 可以用来进一步提高可靠性

非常感谢任何帮助

Pytesseract 本身并不能很好地处理 table 检测 - table 格式不会保留在输出中,这会使其难以解析,如中所示你的输出。

因此,将 table 拆分为不同的列,对每个列执行 OCR,然后重新加入列会有所帮助。这样速度较慢,但​​更准确。

膨胀可以提供帮助,它可以将白色像素添加到现有的白色区域(使用您当前拥有的阈值和图像)。这扩大了数字的狭窄区域。

根据我的经验,提高准确性通常意味着将 table 分成不同的部分,以及测试不同的阈值和扩张设置。

import cv2
import numpy as np
import pandas as pd
def read_img(img):
    '''
    Read in a grayscale image.
    '''
    img = cv2.imread(img)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    return img
img = read_img("img_path.png")
thresh = 255 - cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1] # your current threshold
dilated = cv2.dilate(thresh, np.ones((3,1)), iterations=1) # dilate vertically (don't want to smudge the numbers together)
cols = []
for i, v in enumerate([dilated[:,0:200],thresh[:,200:500],dilated[:,800:900]]): # split image into columns by array slicing
    # Note that the middle column isn't dilated, when so, a decimal point is found
    config_options = '--psm 6'
    cols.append(clean_text(pytesseract.image_to_string(v, lang='eng', config=config_options)).split('\n'))
pd.DataFrame(cols).T
                   0       1    2
0       Aegisium Ore    4490  456
1        Ajatite Ore     600  332
2       Arkanlum Ore   84999   53
3        Bastium Ore    2350  421
4      Charodium Ore   5 280  366
5       Corazium Ore  39 896  212
6        Exorlum Ore   5 380  112
7                Ice     980  141
8    Karnite Crystal   6 000  111
9       Kutonlum Ore  14 000  215
10        Lukium Ore  31 000  158
11  Nhurgite Crystal    3144   64
12  Surtrite Crystal    4198   70
13       Valkite Ore     545  150
14      Vokarlum Ore    1850  415
15        Ymrium Ore  69 899   60

np.ones提供了一个kernel供dilation使用。 Documentation.

最后,根据您的用例,AWS Textract 可以很好地解析 table 和数字,并且他们在文档中提供示例 Python 代码以连接到 API,至少对我来说效果很好。希望其中一些内容有所帮助。

您的代码实际上运行良好。为了提高性能,您需要提供 tesseract 阈值图像的负片。 Tesseract 更喜欢白色背景上的黑色文本。不知道为什么。

您的阈值命令已经在生成“双重”负片图像。首先是 255 - ,其次是 cv2.THRESH_BINARY_INV。要修复,您可以

  1. 移除255 -
  2. cv2.THRESH_BINARY_INV 更改为 cv2.THRESH_BINARY 参数。

此更改后,您的文字将被完美检测:

======= Output
Aegisium Ore 4490 456
Ajatite Ore 600 332
Arkanium Ore 84999 53
Bastium Ore 2350 421
Charodium Ore 5 280 366
Corazium Ore 39 896 212
Exorium Ore 5 380 112
Ice 980 141
Karnite Crystal 6 000 111
Kutonium Ore 14 000 215
Lukium Ore 31 000 158
Nhurgite Crystal 3144 64
Surtrite Crystal 4198 70
Valkite Ore 545 150
Vokarium Ore 1850 415
Ymrium Ore 69 899 60
♀

好吧,最后还有那个特别时髦的角色。

关于无法区分列的问题,将图像拆分为三列的计算量应该不会太大,并且可能会更容易编写代码。

如果您希望以不同的方式解决列的问题,并且如果第三列的值始终小于 1000,那么您可以准确地检测出数字属于哪一列。