使用python和opencv检测图像中的文本区域
computer-vision
image-processing
ocr
opencv
9
0

我想使用python 2.7和opencv 2.4.9检测图像的文本区域,并在其周围绘制一个矩形区域。如下面的示例图片所示。

我是图像处理的新手,因此任何实现此方法的想法将不胜感激。

带标签房间的建筑蓝图

参考资料:
Stack Overflow
收藏
评论
共 2 个回答
高赞 时间 活跃

检测图像中的文本有多种方法。

我建议在这里查看此问题 ,因为它也可以回答您的问题 。尽管它不在python中,但是代码可以轻松地从c ++转换为python(只需查看API并将方法从c ++转换为python,就不难了。当我针对自己的独立问题尝试使用它们的代码时,我自己做的) 。这里的解决方案可能不适用于您的情况,但我建议您尝试一下。

如果要执行此操作,请执行以下过程:

准备图像:如果您要编辑的所有图像都大致与您提供的图像相似,则实际设计由一系列灰色组成,文本始终为黑色。我首先将所有不是黑色(或已经是白色)的内容涂白。这样做将只留下黑色文本。

# must import if working with opencv in python
import numpy as np
import cv2

# removes pixels in image that are between the range of
# [lower_val,upper_val]
def remove_gray(img,lower_val,upper_val):
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    lower_bound = np.array([0,0,lower_val])
    upper_bound = np.array([255,255,upper_val])
    mask = cv2.inRange(gray, lower_bound, upper_bound)
    return cv2.bitwise_and(gray, gray, mask = mask)

现在,您所拥有的只是黑色文本,目标是得到那些盒子。如前所述,有多种解决方法。

笔划宽度变换(SWT)

查找文本区域的典型方法:您可以通过使用笔触宽度变换来查找文本区域,如Boris Epshtein,Eyal Ofek和Yonatan Wexler所著的通过笔触宽度变换检测自然场景中的文本”中所述 。老实说,如果这与我所相信的一样快速和可靠,那么该方法比下面的代码更有效。不过,您仍然可以使用上面的代码删除蓝图设计,这可能有助于swt算法的整体性能。

这是一个实现其算法的ac库 ,但据说它很原始,文档不完整。显然,将需要一个包装器才能将该库与python一起使用,目前我看不到官方提供的包装器。

我链接的库是CCV 。它是一个应在您的应用程序中使用的库,而不是重新创建算法。因此,这是要使用的一种工具,这与OP出于从“第一原则”中使之成为现实的想法不符,正如评论中所述。如果您不想自己编写算法,知道它的存在仍然很有用。


自制非SWT方法

如果您有每个图像的元数据(例如,在xml文件中),该数据指出了每个图像中标记了多少个房间,则可以访问该xml文件,获取有关图像中有多少个标签的数据,然后将其存储一些变量中的数字num_of_labels 。现在,将您的图像放入一个while循环中,该循环以您指定的设定速率腐蚀,在每个循环中找到图像的外部轮廓,并在与num_of_labels具有相同数量的外部轮廓时停止循环。然后只需找到每个轮廓的边界框即可。

# erodes image based on given kernel size (erosion = expands black areas)
def erode( img, kern_size = 3 ):
    retval, img = cv2.threshold(img, 254.0, 255.0, cv2.THRESH_BINARY) # threshold to deal with only black and white.
    kern = np.ones((kern_size,kern_size),np.uint8) # make a kernel for erosion based on given kernel size.
    eroded = cv2.erode(img, kern, 1) # erode your image to blobbify black areas
    y,x = eroded.shape # get shape of image to make a white boarder around image of 1px, to avoid problems with find contours.
    return cv2.rectangle(eroded, (0,0), (x,y), (255,255,255), 1)

# finds contours of eroded image
def prep( img, kern_size = 3 ):    
    img = erode( img, kern_size )
    retval, img = cv2.threshold(img, 200.0, 255.0, cv2.THRESH_BINARY_INV) #   invert colors for findContours
    return cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) # Find Contours of Image

# given img & number of desired blobs, returns contours of blobs.
def blobbify(img, num_of_labels, kern_size = 3, dilation_rate = 10):
    prep_img, contours, hierarchy = prep( img.copy(), kern_size ) # dilate img and check current contour count.
    while len(contours) > num_of_labels:
        kern_size += dilation_rate # add dilation_rate to kern_size to increase the blob. Remember kern_size must always be odd.
        previous = (prep_img, contours, hierarchy)
        processed_img, contours, hierarchy = prep( img.copy(), kern_size ) # dilate img and check current contour count, again.
    if len(contours) < num_of_labels:
        return (processed_img, contours, hierarchy)
    else:
        return previous

# finds bounding boxes of all contours
def bounding_box(contours):
    bBox = []
    for curve in contours:
        box = cv2.boundingRect(curve)
    bBox.append(box)
    return bBox

通过上述方法得到的盒子将在标签周围留有空间,如果将盒子应用于原始图像,则这可能包括原始设计的一部分。为避免这种情况,请通过新找到的框来设置感兴趣的区域并修剪空白区域。然后将该roi的形状另存为新框。

也许您无法知道图像中将包含多少个标签。如果是这种情况,那么我建议您尝试使用腐蚀值,直到找到最适合您的情况的腐蚀值并获得所需的斑点为止。

或者,您也可以在删除设计后尝试在其余内容上找到轮廓,然后根据边界框彼此之间的距离将边界框组合成一个矩形。

找到盒子后,只需将这些盒子用于原始图像即可。


OpenCV 3中的场景文本检测模块

如您对问题的评论中所述,opencv 3中已经存在一种场景文本检测(而非文档文本检测)的方法。我知道您没有能力切换版本,但是对于那些有相同问题且不受限制的版本对于较旧的opencv版本,我决定在末尾添加它。可以通过简单的Google搜索找到有关场景文本检测的文档。

用于文本检测的opencv模块还带有实现tessaract的文本识别,这是一个免费的开源文本识别模块。 tessaract的衰落,因此opencv的场景文本识别模块的不足在于它不如商业应用程序那么完善,并且使用起来很耗时。因此降低了它的性能,但是却免费使用,因此,如果您也想要文本识别,那是我们最好的而无需花钱的方法。

链接:

老实说,我缺乏在opencv和图像处理方面的经验和专业知识,无法提供实现其文本检测模块的详细方法。与SWT算法相同。过去几个月,我才刚接触到这些东西,但是随着我学到更多,我将编辑此答案。

收藏
评论

在此处输入图片说明

这是仅使用阈值和轮廓滤波的简单图像处理方法:

  1. 获取二进制图像。加载图像,转换为灰度, 高斯模糊自适应阈值

  2. 合并相邻的文本。我们创建一个矩形结构核,然后膨胀以形成单个轮廓

  3. 过滤文本轮廓。我们找到轮廓并使用轮廓区域进行过滤。从这里我们可以用cv2.rectangle绘制边界框


使用此原始输入图像(删除的红线)

在此处输入图片说明

在将图像转换为灰度和高斯模糊之后,我们自适应阈值以获得二进制图像

接下来,我们将文本合并为单个轮廓

从这里我们找到轮廓并使用最小阈值区域进行过滤(以防噪声很小)。这是结果

如果我们愿意,我们还可以使用Numpy切片来提取并保存每个ROI

在此处输入图片说明

import cv2

# Load image, grayscale, Gaussian blur, adaptive threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (9,9), 0)
thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,11,30)

# Dilate to combine adjacent text contours
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
dilate = cv2.dilate(thresh, kernel, iterations=4)

# Find contours, highlight text areas, and extract ROIs
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]

ROI_number = 0
for c in cnts:
    area = cv2.contourArea(c)
    if area > 10000:
        x,y,w,h = cv2.boundingRect(c)
        cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 3)
        # ROI = image[y:y+h, x:x+w]
        # cv2.imwrite('ROI_{}.png'.format(ROI_number), ROI)
        # ROI_number += 1

cv2.imshow('thresh', thresh)
cv2.imshow('dilate', dilate)
cv2.imshow('image', image)
cv2.waitKey()
收藏
评论
新手导航
  • 社区规范
  • 提出问题
  • 进行投票
  • 个人资料
  • 优化问题
  • 回答问题

关于我们

常见问题

内容许可

联系我们

@2020 AskGo
京ICP备20001863号