使用 opencv 获取图像中存在的所有轮廓的位置,但跳过文本

Get the location of all contours present in image using opencv, but skipping text

我想检索下图中的所有轮廓,但忽略文本。

图片:

当我试图找到当前图像的轮廓时,我得到以下信息:

我不知道该怎么做,因为我是使用 OpenCV 和图像处理的新手。我想忽略文本,我该如何实现?如果无法忽略,但可以在文本周围制作一个边界框,那也很好。

编辑:

我需要匹配的条件:

我会推荐使用洪水填充,找到每个颜色区域的种子点,洪水填充它以忽略其中的文本值。希望对您有所帮助!

参考这里使用floodfill的例子:https://www.programcreek.com/python/example/89425/cv2.floodFill

下面的示例从上面的 link 复制而来[​​=12=]

def fillhole(input_image):
'''
input gray binary image  get the filled image by floodfill method
Note: only holes surrounded in the connected regions will be filled.
:param input_image:
:return:
'''
im_flood_fill = input_image.copy()
h, w = input_image.shape[:2]
mask = np.zeros((h + 2, w + 2), np.uint8)
im_flood_fill = im_flood_fill.astype("uint8")
cv.floodFill(im_flood_fill, mask, (0, 0), 255)
im_flood_fill_inv = cv.bitwise_not(im_flood_fill)
img_out = input_image | im_flood_fill_inv
return img_out

这是 Python/OpenCV 中的一种方法。

  • 读取输入
  • 转换为灰度
  • 获取 Canny 边
  • 应用形态关闭以确保它们是封闭的
  • 获取所有等高线层级
  • 过滤轮廓以仅保留周界中高于阈值的轮廓
  • 根据输入绘制等高线
  • 在黑色背景上绘制每个轮廓
  • 保存结果

输入:

import numpy as np
import cv2

# read input
img = cv2.imread('short_title.png')

# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# get canny edges
edges = cv2.Canny(gray, 1, 50)

# apply morphology close to ensure they are closed
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
edges = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)

# get contours
contours = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
contours = contours[0] if len(contours) == 2 else contours[1]

# filter contours to keep only large ones
result = img.copy()
i = 1
for c in contours:
    perimeter = cv2.arcLength(c, True)
    if perimeter > 500: 
        cv2.drawContours(result, c, -1, (0,0,255), 1)
        contour_img = np.zeros_like(img, dtype=np.uint8)
        cv2.drawContours(contour_img, c, -1, (0,0,255), 1)
        cv2.imwrite("short_title_contour_{0}.jpg".format(i),contour_img)
        i = i + 1

# save results
cv2.imwrite("short_title_gray.jpg", gray)
cv2.imwrite("short_title_edges.jpg", edges)
cv2.imwrite("short_title_contours.jpg", result)

# show images
cv2.imshow("gray", gray)
cv2.imshow("edges", edges)
cv2.imshow("result", result)
cv2.waitKey(0)

灰度:

边缘:

输入的所有轮廓:

轮廓 1:

轮廓 2:

轮廓 3:

轮廓 4:

这里有两个擦除文本的选项:

  • 使用 pytesseract OCR。
  • 寻找白色(和小)连通分量。

两种解决方案都构建了一个遮罩,扩大遮罩并使用 cv2.inpaint 擦除文本。


使用 pytesseract:

  • 使用 pytesseract.image_to_boxes 查找文本框。
  • 255 填充掩码中的方框。

代码示例:

import cv2
import numpy as np
from pytesseract import pytesseract, Output

# Tesseract path
pytesseract.tesseract_cmd = "C:\Program Files\Tesseract-OCR\tesseract.exe"

img = cv2.imread('ShortAndInteresting.png')

# 
boxes = pytesseract.image_to_boxes(img, lang='eng', config=' --psm 6')  # Run tesseract, returning the bounding boxes

h, w, _ = img.shape # assumes color image
mask = np.zeros((h, w), np.uint8)

# Fill the bounding boxes on the image
for b in boxes.splitlines():
    b = b.split(' ')
    mask = cv2.rectangle(mask, (int(b[1]), h - int(b[2])), (int(b[3]), h - int(b[4])), 255, -1)

mask = cv2.dilate(mask, np.ones((5, 5), np.uint8))  # Dilate the boxes in the mask
   
clean_img = cv2.inpaint(img, mask, 2, cv2.INPAINT_NS)  # Remove the text using inpaint (replace the masked pixels with the neighbor pixels).

# Show mask and clean_img for testing
cv2.imshow('mask', mask)
cv2.imshow('clean_img', clean_img)
cv2.waitKey()
cv2.destroyAllWindows()

掩码:


寻找白色(和小)连通分量:

  • 使用 mask = cv2.inRange(img, (230, 230, 230), (255, 255, 255)) 查找文本(假设文本为白色)。
  • 使用 cv2.connectedComponentsWithStats(mask, 4)
  • 在掩码中查找连通分量
  • 从蒙版中移除大组件 - 用零填充大面积组件。

代码示例:

import cv2
import numpy as np

img = cv2.imread('ShortAndInteresting.png')

mask = cv2.inRange(img, (230, 230, 230), (255, 255, 255))

nlabel, labels, stats, centroids = cv2.connectedComponentsWithStats(mask, 4)  # Finding connected components with statistics

# Remove large components from the mask (fill components with large area with zeros).
for i in range(1, nlabel):
    area = stats[i, cv2.CC_STAT_AREA]  # Get area
    if area > 1000:
        mask[labels == i] = 0  # Remove large connected components from the mask (fill with zero)

mask = cv2.dilate(mask, np.ones((5, 5), np.uint8))  # Dilate the text in the maks

cv2.imwrite('mask2.png', mask)

clean_img = cv2.inpaint(img, mask, 2, cv2.INPAINT_NS)  # Remove the text using inpaint (replace the masked pixels with the neighbor pixels).

# Show mask and clean_img for testing
cv2.imshow('mask', mask)
cv2.imshow('clean_img', clean_img)
cv2.waitKey()
cv2.destroyAllWindows()

掩码:

干净的图像:


注:

  • 我的假设是您知道如何将图像分割成轮廓,唯一的问题是文本的呈现。