使用python和opencv检测图像中的文本区域

时间:2016-06-12 06:12:46

标签: python opencv image-processing ocr

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

我是图像处理的新手,所以任何想法如何做到这一点将不胜感激。

building blueprint with labeled rooms

4 个答案:

答案 0 :(得分:36)

有多种方法可以检测图像中的文字。

我建议您查看this question here,因为它也可以回答您的情况。虽然它不在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撰写的"Detecting Text in Natural Scenes with Stroke Width Transform "所示。说实话,如果这和我认为的一样快,可靠,那么这种方法比我的代码更有效。您仍然可以使用上面的代码删除蓝图设计,并且可能有助于swt算法的整体性能。

Here is a c library实现了他们的算法,但据说这是非常原始的,文档说明是不完整的。显然,为了将这个库与python一起使用,需要一个包装器,目前我没有看到正式提供的。

我链接的图书馆是CCV。它是一个旨在用于您的应用程序的库,而不是重新创建算法。因此,这是一个可以使用的工具,这违背了OP希望从“第一原则”制定它,如评论中所述。如果您不想自己编写算法代码,仍然有用,知道它存在。

Home Brewed非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版本,我决定最后将其包括在内。可以通过简单的谷歌搜索找到场景文本检测的文档。

用于文本检测的opencv模块还附带文本识别功能,可实现tessaract,这是一个免费的开源文本识别模块。 tessaract的垮台,以及opencv的场景文本识别模块,它不像商业应用程序那样精致,使用起来很耗时。因此降低了它的性能,但是它可以自由使用,所以如果你想要文本识别的话,它是我们得到的最好的,如果你想要文本识别。

链接:

老实说,我缺乏opencv和图像处理方面的经验和专业知识,以便提供实现文本检测模块的详细方法。与SWT算法相同。在过去的几个月里,我刚刚介绍了这些内容,但随着我的了解,我将编辑这个答案。

答案 1 :(得分:4)

enter image description here

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

  • 将图像转换为灰度和高斯模糊
  • 自适应阈值
  • 扩张
  • 查找轮廓并使用轮廓区域进行过滤
  • 突出显示ROI和/或提取ROI

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

enter image description here

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

接下来,我们将文本放大为单个轮廓

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

如果愿意,我们还可以使用Numpy切片来提取并保存每个投资回报率

enter image description here

代码

import cv2

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)

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

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()

答案 2 :(得分:0)

EAST进入图片以来,在OpenCV中检测文本区域非常简单。文本检测器不仅准确,而且还可以在720p图像上以约13 FPS的速度实时运行。 入门指南here

答案 3 :(得分:0)