三维成像数据中的物体分割,不能简单的先逐层分析每个slice,再堆叠回去。因此这里再专门提供一个三维点簇状信号分割的函数。

之前已经介绍过怎么用 watershed 分水岭算法来解决点簇状信号的分割问题,对于三维数据来说,思路是一样的,而且得益于我调用的函数能够支持高维数据的处理,所以稍作修改就能实现对三维成像数据的实例分割。

效果如下:

2025-05-16T08:37:37.png

具体代码如下:

import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams['font.family'] = 'DengXian'
plt.rcParams['axes.unicode_minus'] = False

from skimage import io
import numpy as np
from scipy import ndimage as ndi
from skimage.segmentation import watershed
from skimage.feature import peak_local_max
from skimage import filters
from skimage.color import label2rgb


def wat_seg_3d(volume,
               threshold,
               peak_min_distance=3):
    '''
    volume: 三维图像 (NumPy array, typically Z, Y, X order, but depends on your data)
    peak_min_distance: 决定了搜索peak的范围,在每个维度上,例如 3 表示在 3x3x3 的邻域内
    返回一张label图像,背景为0,目标已按region id 进行赋值, 且为uint16
    '''

    # 图像二值化,使用在二维分割或目视检查的自定义threshold
    binary = volume > threshold

    # 距离变换 (distance_transform_edt works on N-dim)
    # It computes the Euclidean distance from non-zero (foreground) pixels to zero (background) pixels.
    distance = ndi.distance_transform_edt(binary)

    # 准备标记点 (peak_local_max works on N-dim)
    # Finds local maxima in the original intensity volume as potential markers
    coords = peak_local_max(volume,
                            min_distance=peak_min_distance,
                            threshold_abs=threshold, # Use the determined threshold
                            exclude_border=False) # Decide if peaks on borders should be considered

    # Initialize markers array with zeros (undetermined)
    markers = np.zeros(shape=volume.shape, dtype=np.int32)

    # 将局部极大值位置设置为前景标记点
    # 我们从标签 2 开始标记前景(标签 1 保留给背景,标签 0 是未确定区域)
    # 使用 enumerate 为每个局部极大值赋予唯一的标签
    # coords will be an array of shape (N, 3) for 3D, where N is the number of peaks
    for i, coord in enumerate(coords):
        # coord is a tuple/list of indices (z, y, x) for 3D
        # Need to use tuple indexing for N-dim arrays
        markers[tuple(coord)] = i + 2 # Labels start from 2, 3, 4...

    # 确定“确定无疑的背景”区域,直接设定阈值 (np.where works on N-dim)
    inds = np.where(volume <= threshold)
    markers[inds] = 1 # 将确定背景区域设置为标签 1

    # 应用分水岭算法 (watershed works on N-dim)
    # Use the negative distance map as the 'surface' for the watershed
    labels = watershed(-distance, markers)

    # Output formatting: shift labels so background is 0
    # The watershed background label is typically 1 if provided as a marker.
    # Subtracting 1 makes marker 1 -> label 0 (background), marker 2 -> label 1, etc.
    rlabel = labels - 1

    # Ensure output type is uint16
    rlabel = rlabel.astype('uint16')

    return rlabel
最后修改:2025 年 05 月 21 日
请大力赞赏以支持本站持续运行!