在 Python 中用交替颜色填充轮廓

2024-04-22

我正在尝试获得一种用交替颜色填充图像轮廓的算法:首先是白色,然后是黑色,然后再次白色,然后再次黑色......就像下图所示:

到目前为止,我所取得的成就是用白色填充图像的轮廓,然后用黑色保留图像内部的轮廓,代码如下:

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

但是一旦我得到了几个轮廓,它就不起作用了

我看到了另一个关于类似问题的问题(在Python中查找二进制图像中两个轮廓之间边界的所有像素 https://stackoverflow.com/questions/75925620/find-all-pixels-bounded-between-two-contours-in-a-binary-image-in-python),但我无法找到问题的概括


我的这个答案基于我之前给出的答案:在Python中查找二进制图像中两个轮廓之间边界的所有像素 https://stackoverflow.com/a/75928327

基本上:

  • apply findContours
  • 有选择地构建新的轮廓集

我们有边界,就是那些封闭的白线。每个边界都有一个外轮廓(“轮廓”)和内轮廓(“内联”)。

两个边界之间的空间我称之为“流明”。它由外边界的内线和内边界的轮廓界定。

轮廓层次:

  • 级别 0:轮廓边界 1
  • 级别 1:内联边界 1 = 流明 0 轮廓
  • 级别 2:轮廓边界 2 = 内嵌流明 0
  • 级别 3:内联边界 2 = 内腔 1 轮廓
  • 级别 4:轮廓边界 3 = 内腔 1 内联
  • 级别 5:内联边界 3 = 内腔 2 轮廓
  • 级别 6:轮廓边界 4 = 内腔 2 内联
  • ...

您想要填充交替的亮度 (0, 2, 4, ...)。这需要轮廓 1+2、5+6、9+10...

如果您想用某些东西填充所有发光体,只需选择正确的发光体并给它们合适的颜色即可。

contours, hierarchy = cv.findContours(im, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
hierarchy = hierarchy.squeeze(0) # vector<Vec4i> -> row vector of Vec4i, so (1,N,4), weird shape
ncontours = len(contours)
# extend hierarchy to include nesting level

(NEXT, PREV, CHILD, PARENT, LEVEL) = range(5)
# first four are defined by OpenCV in the docs of `findContours`
# fifth is my own addition

hierarchy = np.hstack((
    hierarchy[:,:4],
    np.full((ncontours, 1), -1, dtype=np.int32) # level
))

def get_level(i):
    if hierarchy[i, LEVEL] != -1:
        return hierarchy[i, LEVEL]
    else:
        level = 0 if (hierarchy[i, PARENT] == -1) else 1 + get_level(hierarchy[i, PARENT])
        hierarchy[i, LEVEL] = level
        return level

for i in range(ncontours):
    hierarchy[i, LEVEL] = get_level(i)
#predicate to decide which contours to keep

def do_include(index, mod=2, offset=0):
    level = hierarchy[index, LEVEL]
    # level: 1+2, 5+6, 9+10, ...
    # lumen:  0,   2,   4, ...
    lumen = (level-1) // 2
    if lumen < 0: return False # outermost contour, not the outline of any lumen (is inline of background)
    return lumen % mod == offset

even_lumina = [ contours[i] for i in range(len(contours)) if do_include(i, 2, 0) ]
odd_lumina = [ contours[i] for i in range(len(contours)) if do_include(i, 2, 1) ]
composite = cv.cvtColor(im, cv.COLOR_GRAY2BGR)

cv.drawContours(image=composite, contours=even_lumina, contourIdx=-1, color=(0,0,255), thickness=cv.FILLED)
cv.drawContours(image=composite, contours=odd_lumina, contourIdx=-1, color=(255,0,0), thickness=cv.FILLED)

composite[im > 0] = 255 # overlay boundaries for neatness
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Python 中用交替颜色填充轮廓 的相关文章

随机推荐