如何使用 OCR 检测图像中的下标数字?

Posted

技术标签:

【中文标题】如何使用 OCR 检测图像中的下标数字?【英文标题】:How to detect subscript numbers in an image using OCR? 【发布时间】:2020-09-02 12:11:54 【问题描述】:

我通过pytesseract 绑定使用tesseract 进行OCR。不幸的是,我在尝试提取包含下标样式数字的文本时遇到了困难——下标数字被解释为一个字母。

例如,在基本图像中:

我想将文本提取为“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'

也可以获取'CHa',具体取决于psm 参数。

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

我怎样才能准确地从这种类型的图像中提取文本?

更新 - 2020 年 5 月 19 日

在看到 Achintha Ihalage 的回答后,它没有为 tesseract 提供任何配置选项,我探索了 psm 选项。

由于感兴趣区域是已知的(在本例中,我使用 EAST 检测来定位文本的边界框),tesseractpsm 配置选项,在我的原始代码中将文本视为单行,可能没有必要。对上面边界框给出的感兴趣区域运行image_to_string 会得到输出

CH

3

当然可以很容易地处理得到CH3

【问题讨论】:

【参考方案1】:

这是因为下标字体太小了。您可以使用cv2PIL 等python 包调整图像大小,并将调整后的图像用于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

【讨论】:

缩放是我会尝试的几件事之一,它似乎在这里工作,但可能不适用于每张图像。其他步骤包括玩扩张和专门使用一组下标字符训练模型。 谢谢。一个通用的解决方案将是首选。 @MattL。您能否在答案中扩展您建议的其他步骤? 一般的解决方案是用(可能)1000 张包含正常字体和下标字体的图像来训练 CNN 模型。通过使用自己的数据训练 ResNet 或 VGGNet 架构,您还可以通过迁移学习获得更高的准确性。显然,这些更乏味。【参考方案2】:

您希望在将图像输入tesseract 之前对图像进行预处理,以提高 OCR 的准确性。我在这里使用PILcv2 的组合来执行此操作,因为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 预处理生成的图像如下所示:

PIL 的增强功能为您提供:

在这个具体示例中,您实际上可以在 cv2_preprocess 步骤之后停止,因为这对读者来说已经足够清楚了:

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

输出:

CH3

但是,如果您正在处理的东西不一定以白色背景开始(即灰度缩放转换为浅灰色而不是白色)- 我发现 PIL 步骤确实有帮助。

重点是提高tesseract准确率的方法通常是:

    修复 DPI(重新缩放) 修复图像的亮度/噪点 修复 tex 大小/线条 (倾斜/扭曲文本)

执行其中一项或全部三项会有所帮助...但亮度/噪音可能比其他两项更普遍(至少根据我的经验)。

【讨论】:

【参考方案3】:

我认为这种方式可以更适合一般情况。

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 很有帮助。

    Get the location of all text present in image using opencv Using YOLO or other image recognition techniques to identify all alphanumeric text present in images

【讨论】:

以上是关于如何使用 OCR 检测图像中的下标数字?的主要内容,如果未能解决你的问题,请参考以下文章

从android中的图像中检测数字

如何使用 OpenCV 进行 OCR 和文本检测识别

如何根据图像质量确定使用哪种 OCR 方法

使用Python进行视频流OCR

如何使用光学字符识别解析数字 4

如何使用不同颜色和字体的文本改进图像的 OCR?