You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 14:27:44