如何使用Python OpenCV在OCR之前清理图像?

9

我一直在尝试处理OCR图像: (那些线)

enter image description here

我需要移除这些线以便进一步处理图像,我已经接近成功了,但很多时候阈值会让文本的清晰度降低太多:

    copy = img.copy()
    blur = cv2.GaussianBlur(copy, (9,9), 0)
    thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,11,30)

    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
    dilate = cv2.dilate(thresh, kernel, iterations=2)

    cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]

    for c in cnts:
        area = cv2.contourArea(c)
        if area > 300:
            x,y,w,h = cv2.boundingRect(c)
            cv2.rectangle(copy, (x, y), (x + w, y + h), (36,255,12), 3)

编辑:此外,使用常数将不能处理字体更改的情况。有通用的方法可以解决这个问题吗?

2
其中一些行或其片段具有与法律文本相同的特征,如果不加区分地删除它们,将会破坏有效文本。如果情况如此,您可以关注它们比字符更长且有些孤立的事实。因此,第一步可能是估计字符的大小和接近程度。 - user1196549
@YvesDaoust 如何找到字符的相似度?(因为仅根据大小过滤往往会与字符混淆) - K41F4r
1
你可以找到每个blob到其最近邻的距离。然后通过距离的直方图分析,你可以找到“接近”和“分开”之间的阈值(类似于分布的模式),或者在“包围”和“孤立”之间。 - user1196549
如果多个小线条彼此靠近,它们最接近的邻居会是另一条小线吗?计算到所有其他斑点的平均距离是否太昂贵? - K41F4r
“不是最近的邻居是另一个小线吗?”:好反驳,尊敬的法官。实际上,一堆紧密短线段与合法文本没有区别,尽管排列方式完全不可能。您可能需要重新组合断开的行片段。我不确定平均距离是否能拯救您。 - user1196549
另一个选择是将碎片分类为类似于行片段和字符,并且丢弃没有接近字符的邻居的行片段。您还可以对所有片段进行盲目OCR,并使用识别结果组合到距离以丢弃杂乱无章的内容。 - user1196549
1个回答

16
这里有一个想法。我们将这个问题分成几个步骤:
1. 确定矩形轮廓的平均面积。我们进行阈值处理,然后找到轮廓并使用轮廓的边界矩形面积进行过滤。我们之所以这样做,是因为观察到任何典型的字符只会那么大,而大噪声将跨越更大的矩形区域。然后我们确定平均面积。
2. 删除大的异常轮廓。我们再次迭代轮廓,并通过填充轮廓来删除大的轮廓,如果它们比平均轮廓面积大5倍。我们不使用固定的阈值面积,而是使用这个动态阈值来提高鲁棒性。
3. 用垂直核膨胀以连接字符。这个想法是利用字符在列中对齐的观察结果。通过用垂直核膨胀,我们将文本连接在一起,因此噪声将不包括在这个组合轮廓中。
4. 删除小的噪声。现在保留的文本已经连接了,我们找到轮廓并删除任何小于平均轮廓面积4倍的轮廓。
5. 位运算重构图像。由于我们只在掩模上保留所需的轮廓,因此我们进行位运算以保留文本并得到结果。
这是一个过程的可视化:
我们使用 大津阈值 获得二进制图像,然后 查找轮廓 来确定平均矩形轮廓面积。从这里开始,我们通过 填充轮廓 来去除被突出显示为绿色的大离群轮廓。

enter image description here enter image description here

下一步,我们构建一个垂直卷积核并进行膨胀操作以连接字符。此步骤将所有需要保留的文本连接起来,并将噪声隔离为单独的块。

enter image description here

现在我们找到轮廓并使用轮廓面积进行过滤,以消除小噪声。

enter image description here

这里是所有被去除的噪点,用绿色标出

enter image description here

结果

enter image description here

代码

import cv2

# Load image, grayscale, and Otsu's threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Determine average contour area
average_area = [] 
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    area = w * h
    average_area.append(area)

average = sum(average_area) / len(average_area)

# Remove large lines if contour area is 5x bigger then average contour area
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    area = w * h
    if area > average * 5:  
        cv2.drawContours(thresh, [c], -1, (0,0,0), -1)

# Dilate with vertical kernel to connect characters
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,5))
dilate = cv2.dilate(thresh, kernel, iterations=3)

# Remove small noise if contour area is smaller than 4x average
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    area = cv2.contourArea(c)
    if area < average * 4:
        cv2.drawContours(dilate, [c], -1, (0,0,0), -1)

# Bitwise mask with input image
result = cv2.bitwise_and(image, image, mask=dilate)
result[dilate==0] = (255,255,255)

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

注意: 传统图像处理仅限于阈值处理、形态学操作和轮廓过滤(轮廓近似、面积、长宽比或斑点检测)。由于输入图像可能会根据字符文本大小而变化,因此找到一个单一的解决方案是相当困难的。您可能需要研究使用机器/深度学习训练自己的分类器以获得动态解决方案。


1
如果使用更大的字体,这会删除文本吗? - K41F4r
是的,可能需要调整阈值区域值。为了更具动态性的方法,一个想法是确定平均字符区域,然后将其用作阈值。 - nathancy
似乎太具体化了,使用平均面积仍然会经常删除文本,这会使OCR的结果变得更糟。 - K41F4r
@C493d请检查更新,现在它使用动态字符区域来确定要删除哪些噪声轮廓,因此它应该适用于具有较大字体的图像。 - nathancy
1
使用传统的图像处理技术找到适用于所有情况的解决方案是相当困难的。您可能需要考虑使用深度学习来训练自己的分类器。祝你好运! - nathancy
显示剩余2条评论

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