如何使用OCR技术在图片中检测下标数字?

10

我正在使用tesseract进行OCR,通过pytesseract绑定。不幸的是,当尝试提取包括下标式数字的文本时,下标数字被解释为字母。

例如,在基本图像中:

enter image description here

我想提取文本作为“CH3”,即我不关心数字3在图像中是下标。

我使用tesseract的尝试是:

import cv2
import pytesseract

img = cv2.imread('test.jpeg')

# Note that I have reduced the region of interest to the known 
# text portion of the image
text = pytesseract.image_to_string(
    img[200:300, 200:320], config='-l eng --oem 1 --psm 13'
)
print(text)

不幸的是,这将会错误地输出

'CHs'

根据psm参数的不同,也可能会得到'CHa'

我怀疑这个问题与文本的“基线”在整行中不一致有关,但我不确定。

如何准确地从这种类型的图像中提取文本?

更新-2020年5月19日

在看到Achintha Ihalage的答案之后(该答案没有为tesseract提供任何配置选项),我探索了psm选项。

由于感兴趣的区域已知(在这种情况下,我使用EAST检测来定位文本的边界框),因此tesseractpsm配置选项,在我的原始代码中将文本视为单行,可能不是必需的。针对上面给出的边界框的感兴趣区域运行image_to_string即可得到输出。

CH

3

它当然可以轻易地被处理,以得到 CH3

3个回答

4
这是因为下标的字体太小了。您可以使用Python软件包(例如cv2PIL)调整图像大小,并使用调整后的图像进行OCR,如下所示的代码。
import pytesseract
import cv2

img = cv2.imread('test.jpg')
img = cv2.resize(img, None, fx=2, fy=2)  # scaling factor = 2

data = pytesseract.image_to_string(img)
print(data)

输出:

CH3

缩放是我会尝试的几个方法之一,它在这里似乎有效,但可能并不适用于每个图像。其他步骤包括尝试膨胀操作以及使用一组下标字符专门训练模型。 - Matt L.
谢谢。最好提供一个通用解决方案。@MattL. 你能否在答案中详细说明你建议的额外步骤? - dspencer
一个通用的解决方案是使用(可能)包含正常和下标字体的数千个图像来训练CNN模型。您还可以通过使用自己的数据训练ResNet或VGGNet架构来实现更高的准确性,这显然更加繁琐。 - Achintha Ihalage

3
您想在将图像输入到tesseract进行OCR之前对其进行预处理,以提高精度。我在这里使用了PIL和cv2的组合来完成此操作,因为cv2具有良好的滤波器用于模糊/噪声去除(膨胀、腐蚀、阈值),而PIL可以轻松增强对比度(区分文本和背景),我想展示如何使用任意一种预处理方法...(不过,两者一起使用并非100%必要,如下所示)。您可以更优雅地表达这个想法-这只是一个大致的概念。
import cv2
import pytesseract
import numpy as np
from PIL import Image, ImageEnhance


img = cv2.imread('test.jpg')

def cv2_preprocess(image_path):
  img = cv2.imread(image_path)

  # convert to black and white if not already
  img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

  # remove noise
  kernel = np.ones((1, 1), np.uint8)
  img = cv2.dilate(img, kernel, iterations=1)
  img = cv2.erode(img, kernel, iterations=1)

  # apply a blur 
  # gaussian noise
  img = cv2.threshold(cv2.GaussianBlur(img, (9, 9), 0), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

  # this can be used for salt and pepper noise (not necessary here)
  #img = cv2.adaptiveThreshold(cv2.medianBlur(img, 7), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)

  cv2.imwrite('new.jpg', img)
  return 'new.jpg'

def pil_enhance(image_path):
  image = Image.open(image_path)
  contrast = ImageEnhance.Contrast(image)
  contrast.enhance(2).save('new2.jpg')
  return 'new2.jpg'


img = cv2.imread(pil_enhance(cv2_preprocess('test.jpg')))


text = pytesseract.image_to_string(img)
print(text)

输出:

CH3

cv2的预处理会生成如下图所示的图片: enter image description here

使用PIL进行增强后,你会得到如下结果:

enter image description here

在这个具体的例子中,实际上在执行完cv2_preprocess步骤之后就可以停止了,因为这已经足够清晰易懂了。

img = cv2.imread(cv2_preprocess('test.jpg'))
text = pytesseract.image_to_string(img)
print(text)

输出:

CH3

但如果你正在处理的东西不一定以白色背景为开头(即灰度转换成浅灰色,而不是白色),我发现PIL步骤在这里真的很有帮助。

主要的重点是提高tesseract的准确性的方法通常包括:

  1. 修复DPI(重新缩放)
  2. 修复图像的亮度/噪音
  3. 修复文本大小/行数(倾斜/扭曲文本)

做其中一个或者所有三个都会有所帮助……但亮度/噪音比其他两个更具有普适性(至少从我的经验来看)。


2
我认为这种方式更适合一般情况。
import cv2
import pytesseract
from pathlib import Path

image = cv2.imread('test.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]  # (suitable for sharper black and white pictures
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]  # is OpenCV2.4 or OpenCV3
result_list = []
for c in contours:
    x, y, w, h = cv2.boundingRect(c)
    area = cv2.contourArea(c)
    if area > 200:
        detect_area = image[y:y + h, x:x + w]
        # detect_area = cv2.GaussianBlur(detect_area, (3, 3), 0)
        predict_char = pytesseract.image_to_string(detect_area, lang='eng', config='--oem 0 --psm 10')
        result_list.append((x, predict_char))
        cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), thickness=2)

result = ''.join([char for _, char in sorted(result_list, key=lambda _x: _x[0])])
print(result)  # CH3


output_dir = Path('./temp')
output_dir.mkdir(parents=True, exist_ok=True)
cv2.imwrite(f"{output_dir/Path('image.png')}", image)
cv2.imwrite(f"{output_dir/Path('clean.png')}", thresh)

更多参考资料

我强烈建议您参考以下示例,这些对OCR非常有用。

  1. 使用opencv获取图像中所有文本的位置
  2. 使用YOLO或其他图像识别技术识别图像中所有字母数字文本

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接