如何基于Active Contour结果点从掩码绘制正确形状的多边形?
解决Active Contour结果点绘制多边形顺序混乱的问题
看起来你遇到的问题是主动轮廓返回的轮廓点是无序的,而skimage.draw.polygon要求输入的点是按多边形的连续顶点顺序排列的(顺时针或逆时针沿着轮廓走),直接按行读取的无序点会导致填充出不符合预期的形状。
问题根源
active_contour返回的snake数组里的点,并不是沿着轮廓的连续顺序排列的——它们只是轮廓上的所有点,但顺序是随机的(或者说按原始mask的行遍历顺序)。当你直接把这样的点传给polygon函数时,它会按给定的点顺序依次连接,自然会画出混乱的填充区域,而不是你预期的类圆形。
解决方案:对轮廓点按极角排序
我们可以通过计算每个点相对于轮廓质心的极角,然后按极角从小到大(或从大到小)排序,这样就能把无序的点整理成沿着轮廓连续的顺序。具体步骤如下:
- 计算轮廓点的质心(所有点的坐标平均值)
- 计算每个点相对于质心的极角(用
np.arctan2函数) - 按极角排序所有点,得到有序的轮廓顶点序列
- 用排序后的点调用
polygon函数
修改后的完整代码
import numpy as np from skimage.io import imread, imsave import skimage.filters as filters from skimage.segmentation import active_contour from scipy.sparse import csr_matrix import matplotlib.pyplot as plt from skimage.draw import polygon #--------------------------------- img = imread('image.jpg') mask = imread('label.png') def activeContour(img, mask, alpha=0.015, beta=10, gamma=0.001, iterations=500, max_step=0.5): lb_list = np.unique(mask) Result = np.zeros_like(img) for lb in lb_list: if(lb != 0): s = np.where(mask == lb) r = s[0].astype(np.uint8) c = s[1].astype(np.uint8) init = np.array([r, c]).T #-------PreProcessing---------------------------- img2 = filters.gaussian(img, 3) #------- Active Contour ------------------------ snake = active_contour(img2, init, alpha=alpha, beta=beta, gamma=gamma, coordinates='rc', max_px_move=max_step, max_iterations=iterations) #------------------------------------------------ vals = lb*np.ones_like(snake[:, 0],dtype=np.uint8) label = csr_matrix((vals, (snake[:, 1].astype(np.uint8), snake[:, 0].astype(np.uint8))), shape=(img.shape[0], img.shape[1])).toarray() Result = Result + label return Result res = activeContour(img, mask, alpha=0, beta= 10, gamma= 0.0000001) labels = np.unique(res) ctr = 25 for label in labels: if label != 0: # 重新创建空图,避免覆盖原始图像 display_img = np.zeros_like(img) s = np.where(res == label) r = s[0].astype(np.float32) c = s[1].astype(np.float32) # --- 关键:对轮廓点按极角排序 --- # 1. 计算质心 centroid_r = np.mean(r) centroid_c = np.mean(c) # 2. 计算每个点相对于质心的极角 angles = np.arctan2(r - centroid_r, c - centroid_c) # 3. 按极角排序,得到有序的点索引 sorted_indices = np.argsort(angles) # 4. 重新排序r和c sorted_r = r[sorted_indices] sorted_c = c[sorted_indices] # 用排序后的点绘制多边形 rr, cc = polygon(sorted_r, sorted_c) # 确保坐标在图像范围内(避免越界) rr = np.clip(rr, 0, display_img.shape[0]-1) cc = np.clip(cc, 0, display_img.shape[1]-1) display_img[rr, cc] = 2*ctr plt.imshow(display_img, cmap='gray') plt.title(f'Label {label}') plt.axis('off') plt.show() ctr += 1
关键修改说明
- 点排序逻辑:通过质心和极角排序,把无序的轮廓点整理成沿着轮廓连续的顺序,这样
polygon函数就能正确识别出闭合的类圆形区域。 - 修正原代码的小问题:原代码循环内
img = np.zeros_like(img)会覆盖原始图像,改成display_img避免这个问题。 - 坐标裁剪:用
np.clip确保生成的多边形坐标不会超出图像边界,避免索引错误。
补充说明
如果你的轮廓是复杂形状(不是简单类圆形),极角排序依然有效——只要轮廓是单连通的,这种方法都能把点整理成正确的顺序。如果是多连通区域,你可能需要先用skimage.measure.find_contours提取每个连通分量的轮廓,再分别处理。
内容的提问来源于stack exchange,提问作者Bilal
相关产品推荐
相关产品推荐

