合并 MSER 中的区域以识别 OCR 中的文本行

Posted

技术标签:

【中文标题】合并 MSER 中的区域以识别 OCR 中的文本行【英文标题】:Merging regions in MSER for identifying text lines in OCR 【发布时间】:2018-07-14 22:01:16 【问题描述】:

我正在使用 MSER 来识别 MSER 中的文本区域。我正在使用以下代码来提取区域并将它们保存为图像。目前,每个识别的区域都保存为单独的图像。但是,我想合并属于合并为单个图像的文本行的区域。

import cv2

img = cv2.imread('newF.png')
mser = cv2.MSER_create()


img = cv2.resize(img, (img.shape[1]*2, img.shape[0]*2))

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
vis = img.copy()

regions = mser.detectRegions(gray)
hulls = [cv2.convexHull(p.reshape(-1, 1, 2)) for p in regions[0]]
cv2.polylines(vis, hulls, 1, (0,255,0)) 

如何将属于单行的图像拼接在一起?我得到的逻辑将主要基于一些用于识别具有附近 y 坐标的区域的启发式方法。

但是如何在 OpenCV 中准确地合并区域。我错过了这一点,因为我是 openCV 的新手。任何帮助将不胜感激。

附加示例图像

所需的输出如下

另一行

另一行

【问题讨论】:

你的预期输出是什么? 计算二值图像中每一行的白色像素数,以及在白色分布中发现尖峰的段。 检查this @AmrithKrishna 您能否说明为什么选择腐蚀/膨胀的答案作为接受的答案? @GaneshTata- 非常感谢您的回答。首先,由于斑点的高度不同,中值高度通常不起作用。不同长度的斑点主要是由于语言特定的属性(如连字、标记等)而出现的。现在接受答案的想法与这些无关。通过稍作调整的膨胀和腐蚀,我得到了预期的结果。假设这两种方法或多或少地实现了相同的结果,按照奥卡姆剃刀的说法,应该首选更简单的方法。因此优先考虑当前接受的答案。 【参考方案1】:

如果您特别喜欢使用 MSER,那么正如您所提到的,可以使用将区域与附近 y 坐标相结合的启发式方法。以下方法可能效率不高,我会尝试优化它,但它可能会让您了解如何解决问题。

    首先,让我们绘制由 MSER 确定的所有 bbox:

    coordinates, bboxes = mser.detectRegions(gray)
    for bbox in bboxes:
        x, y, w, h = bbox
        cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
    

    这给了我们 -

    现在,从 bbox 中可以明显看出,高度变化很大,即使在一行中也是如此。因此,为了在一行中聚集边界 bbox,我们必须提出一个区间。我想不出万无一失的东西,所以我选择了 给定 bbox 的所有高度的中值的一半,这对于给定的情况很有效。 p>

    bboxes_list = list()
    heights = list()
    for bbox in bboxes:
        x, y, w, h = bbox
        bboxes_list.append([x, y, x + w, y + h])  # Create list of bounding boxes, with each bbox containing the left-top and right-bottom coordinates
        heights.append(h)
    heights = sorted(heights)  # Sort heights
    median_height = heights[len(heights) / 2] / 2  # Find half of the median height
    

    现在,为了对边界框进行分组,给定 y 坐标的特定间隔(这里是中值高度),我正在修改我曾经在 *** 上找到的一个 sn-p(我将添加源一次找到它 )。此函数接受一个列表以及一个特定的区间作为输入,并返回一个组列表,其中每个组包含边界框,其 y 坐标的绝对差小于或等于该区间。请注意,iterable / list 需要根据 y 坐标进行排序。

    def grouper(iterable, interval=2):
        prev = None
        group = []
        for item in iterable:
            if not prev or abs(item[1] - prev[1]) <= interval:
                group.append(item)
            else:
                yield group
                group = [item]
            prev = item
        if group:
            yield group
    

    因此,在对边界框进行分组之前,需要根据 y 坐标对它们进行排序。分组后,我们遍历每个组,并确定绘制覆盖给定组中所有边界框的边界框所需的最小 x 坐标、最小 y 坐标、最大 x 坐标和最大 y 坐标。

    bboxes_list = sorted(bbox_mod, key=lambda k: k[1])  # Sort the bounding boxes based on y1 coordinate ( y of the left-top coordinate )
    combined_bboxes = grouper(bboxes_list, median_height)  # Group the bounding boxes
    for group in combined_bboxes:
        x_min = min(group, key=lambda k: k[0])[0]  # Find min of x1
        x_max = max(group, key=lambda k: k[2])[2]  # Find max of x2
        y_min = min(group, key=lambda k: k[1])[1]  # Find min of y1
        y_max = max(group, key=lambda k: k[3])[3]  # Find max of y2
        cv2.rectangle(img, (x_min, y_min), (x_max, y_max), (0, 255, 0), 2)
    

    最终生成的图像 -

再次,我想重申一个事实,即他们可能是进一步优化这种方法的方法。目的是让您了解如何解决此类问题。

【讨论】:

【参考方案2】:

也许甚至像扩张腐蚀这样原始的东西也可以在你的情况下工作?例如,如果我在原始图像上使用erode 操作,然后使用dilate 操作,并且主要是在水平方向上,例如。 g.:

img = cv2.erode(img, np.ones((1, 20)))
img = cv2.dilate(img, np.ones((1, 22)))

结果是这样的:

因此,如果我们将其绘制在原始图像上,它将变为:

我没有像您那样调整原始图像的大小(可能是为了检测那些单独的小点和东西)。不理想(我不知道 MSER 是如何工作的),但是通过足够的调整,也许您甚至可以使用此连接组件的简单检测?

【讨论】:

以上是关于合并 MSER 中的区域以识别 OCR 中的文本行的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中计算文本文件中的文本行数时出错

如何在 C++ 程序中的 2 个特定字符之间比较 2 个文件中的文本行

m基于MSER最大稳定极值区域和SVM的交通标志检测识别算法的matlab仿真

根据javascript中的文本行数更改textarea的高度[重复]

OpenCV实战——使用MSER提取特征区域

C 语言文件操作 ( 配置文件读写 | 写出或更新配置文件 | 逐行遍历文件文本数据 | 获取文件中的文本行 | 查询文本行数据 | 追加文件数据 | 使用占位符方式拼接字符串 )