使用 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()
掩码:
干净的图像:
注:
- 我的假设是您知道如何将图像分割成轮廓,唯一的问题是文本的呈现。
我想检索下图中的所有轮廓,但忽略文本。
图片:
当我试图找到当前图像的轮廓时,我得到以下信息:
我不知道该怎么做,因为我是使用 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()
掩码:
干净的图像:
注:
- 我的假设是您知道如何将图像分割成轮廓,唯一的问题是文本的呈现。