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

Amr*_*hna 5 python opencv bounding-box image-stitching mser

我正在使用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)) 
Run Code Online (Sandbox Code Playgroud)

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

但是如何在OpenCV中合并这些区域.因为我是openCV的新手,所以我错过了这个.任何帮助,将不胜感激.

附加样本图像 在此输入图像描述

期望的输出如下 在此输入图像描述

另一条线 在此输入图像描述

另一条线 在此输入图像描述

Gan*_*ata 6

如果您特别喜欢使用MSER,则如上所述,可以使用启发式方法将区域与附近的y坐标相结合。以下方法可能不是有效的方法,我将尝试对其进行优化,但是它可能使您对如何解决该问题有所了解。

  1. 首先,让我们绘制由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)
    
    Run Code Online (Sandbox Code Playgroud)

    这给我们- MSER检测到的bbox

  2. 现在,从bbox中可以明显看出,即使在一行中,高度也有很大的变化。因此,为了将边界bbox聚集在一行中,我们将需要一个间隔。我无法提供万无一失的方法,因此我使用了给定bbox的所有高度的中位数的一半,这在给定的情况下效果很好。

    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
    
    Run Code Online (Sandbox Code Playgroud)
  3. 现在,要对边界框进行分组,给定y坐标的特定间隔(此处为中值高度),我正在修改一个我在stackoverflow上发现的代码段(找到后将添加源代码)。此函数接收一个列表以及一个特定的间隔作为输入,并返回一组列表,其中每个组都包含边界框,这些边界框的y坐标的绝对差小于或等于该间隔。请注意,可迭代的/列表需要根据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
    
    Run Code Online (Sandbox Code Playgroud)
  4. 因此,在对边界框进行分组之前,需要根据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)
    
    Run Code Online (Sandbox Code Playgroud)

    最终结果图像-

    Lines_combined

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


Hea*_*rab 5

也许甚至像扩张侵蚀这样原始的东西也可以在你的情况下发挥作用?例如,如果我使用erodedilate对原始图像使用操作,然后操作,并且主要在水平方向,例如:

img = cv2.erode(img, np.ones((1, 20)))
img = cv2.dilate(img, np.ones((1, 22)))
Run Code Online (Sandbox Code Playgroud)

结果是这样的:

在此输入图像描述

因此,如果我们在原始图像上绘制它,它就会变成:

在此输入图像描述

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