问题是cv2.drawContours
填充闭合轮廓的整个内部部分,无论是否存在内部轮廓。
我们可以从白色轮廓开始,然后用黑色填充没有父项的轮廓,而不是用白色填充没有父项的轮廓。
假设我们知道内部应该是黑色的,我们可以应用以下阶段:
- 使用cv2.RETR_EXTERNAL查找轮廓,并用白色填充外部轮廓。
- 使用 cv2.RETR_TREE 查找轮廓。
- 迭代轮廓层次结构,并仅用黑色填充没有子轮廓的轮廓(用黑色填充最内部的轮廓)。
代码示例:
import numpy as np
import cv2
# Load the PNG image
img = cv2.imread('slice.png')
# Convert the image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Threshold the image to create a binary image
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)
# Find the outer contours in the binary image (using cv2.RETR_EXTERNAL)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Create a blank image with the same dimensions as the original image
filled_img = np.zeros(img.shape[:2], dtype=np.uint8)
# Fill the outer contour with white color
cv2.drawContours(filled_img, contours, -1, 255, cv2.FILLED)
# Find contours with hierarchy, this time use cv2.RETR_TREE
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# Iterate over the contours and their hierarchies
for i, contour in enumerate(contours):
# Check if the contour has no child
if hierarchy[0][i][2] < 0:
# If contour has no child, fill the contour with black color
cv2.drawContours(filled_img, [contour], -1, 0, cv2.FILLED)
# Display the result
cv2.imshow('Original Image', img)
cv2.imshow('Filled Regions', filled_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result filled_img
:
Note:
如果我们不知道最内部轮廓的颜色,我们可以在黑色背景上绘制白色轮廓,并将结果用作蒙版 - 使用蒙版复制输入图像的原始内容。
Update:
支持没有子项的轮廓:
为了支持有子轮廓和没有子轮廓的轮廓,我们可以填充黑色,仅填充符合这两个条件的轮廓:
- 轮廓没有子轮廓。
- 轮廓有一个祖父母轮廓(寻找祖父母而不是父轮廓,因为空轮廓有一个内轮廓,而其父轮廓是外轮廓)。
代码示例:
import numpy as np
import cv2
# Load the PNG image
img = cv2.imread('slice.png')
# Convert the image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Threshold the image to create a binary image
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)
# Find the outer contours in the binary image (using cv2.RETR_EXTERNAL)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Create a blank image with the same dimensions as the original image
filled_img = np.zeros(img.shape[:2], dtype=np.uint8)
# Fill the outer contour with white color
cv2.drawContours(filled_img, contours, -1, 255, cv2.FILLED)
# Find contours with hierarchy, this time use cv2.RETR_TREE
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# Iterate over the contours and their hierarchies
for i, contour in enumerate(contours):
has_grandparent = False
has_parent = hierarchy[0][i][3] >= 0
if has_parent:
# Check if contour has a grandparent
parent_idx = hierarchy[0][i][3]
has_grandparent = hierarchy[0][parent_idx][3] >= 0
# Check if the contour has no child
if hierarchy[0][i][2] < 0 and has_grandparent:
# If contour has no child, fill the contour with black color
cv2.drawContours(filled_img, [contour], -1, 0, cv2.FILLED)
# Display the result
cv2.imshow('Original Image', img)
cv2.imshow('Filled Regions', filled_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Update:
在较小的层次结构轮廓内部填充白色:
在用黑色填充轮廓之前,我们可以检查指定轮廓内部是否有黑色像素。
仅当它没有孩子、有祖父母并且内部有黑色时才填充黑色。
为了测试内部是否有黑色像素,我们可以在临时图像上绘制轮廓(白色)。
然后检查最小值是否为 0(绘制轮廓为白色的值)。
tmp = np.zeros_like(thresh)
cv2.drawContours(tmp, [contour], -1, 255, cv2.FILLED)
has_innder_black_pixels = (thresh[tmp==255].min() == 0)
代码示例:
import numpy as np
import cv2
# Load the PNG image
img = cv2.imread('slice.png')
# Convert the image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Threshold the image to create a binary image
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)
# Find the outer contours in the binary image (using cv2.RETR_EXTERNAL)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Create a blank image with the same dimensions as the original image
filled_img = np.zeros(img.shape[:2], dtype=np.uint8)
# Fill the outer contour with white color
cv2.drawContours(filled_img, contours, -1, 255, cv2.FILLED)
# Find contours with hierarchy, this time use cv2.RETR_TREE
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# Iterate over the contours and their hierarchies
for i, contour in enumerate(contours):
has_grandparent = False
has_parent = hierarchy[0][i][3] >= 0
if has_parent:
# Check if contour has a grandparent
parent_idx = hierarchy[0][i][3]
has_grandparent = hierarchy[0][parent_idx][3] >= 0
# Draw the contour over temporary image first (for testing if it has black pixels inside).
tmp = np.zeros_like(thresh)
cv2.drawContours(tmp, [contour], -1, 255, cv2.FILLED)
has_innder_black_pixels = (thresh[tmp==255].min() == 0) # If the minimum value is 0 (value where draw contour is white) then the contour has black pixels inside
if hierarchy[0][i][2] < 0 and has_grandparent and has_innder_black_pixels:
# If contour has no child and has a grandparent and it has black inside, fill the contour with black color
cv2.drawContours(filled_img, [contour], -1, 0, cv2.FILLED)
# Display the result
cv2.imshow('Original Image', img)
cv2.imshow('Filled Regions', filled_img)
cv2.waitKey(0)
cv2.destroyAllWindows()