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

图像骨架提取后毛刺去除及二值图像长度计算技术问询

问题描述

我有一张二值图像,使用Skimage库的skeletonize方法(zhang与lee两种方式)提取骨架线后,边缘存在大量毛刺,需要去除这些毛刺,并计算二值图像的长度,期望效果如图(c)所示。

现有代码

import skimage as ski
import skimage.io as io
import matplotlib.pyplot as plt
import numpy as np
from skimage.morphology import skeletonize
import matplotlib
matplotlib.rc("font",family='Microsoft YaHei')
image_path=r"./1089_8.jpg"
image=io.imread(image_path,as_gray=True)
skeleton1=skeletonize(image,method='zhang')
skeleton2=skeletonize(image,method='lee')
fig,axes=plt.subplots(nrows=1,ncols=3,figsize=(8, 4), sharex=True, sharey=True)
ax=axes.ravel()
ax[0].imshow(image, cmap=plt.cm.gray)
ax[0].axis('off')
ax[0].set_title('original', fontsize=20)
ax[1].imshow(skeleton1, cmap=plt.cm.gray)
ax[1].axis('off')
ax[1].set_title('Zhang_method', fontsize=20)
ax[2].imshow(skeleton2,cmap=plt.cm.gray)
ax[2].axis('off')
ax[2].set_title('lee_method',fontsize=20)
fig.tight_layout()
plt.show()

解决方案

1. 去除骨架毛刺

骨架边缘的毛刺多为短分支端点,可通过以下两种方式去除,其中迭代修剪端点的方法更精准,不会破坏主骨架结构:

方法一:迭代修剪端点

识别骨架中仅存在1个邻域像素的端点,逐次删除这些端点,直到毛刺被清除:

from skimage.morphology import binary_erosion

def trim_skeleton(skeleton, iterations=3):
    trimmed = skeleton.copy()
    for _ in range(iterations):
        neighbors = np.zeros_like(trimmed)
        # 统计每个骨架像素的8邻域骨架像素数量
        for i in range(1, trimmed.shape[0]-1):
            for j in range(1, trimmed.shape[1]-1):
                if trimmed[i,j] == 1:
                    # 减去自身,仅统计邻域内的骨架像素数
                    count = np.sum(trimmed[i-1:i+2, j-1:j+2]) - 1
                    neighbors[i,j] = count == 1
        # 删除端点像素
        trimmed[neighbors] = 0
    return trimmed

方法二:筛选长连通区域

标记骨架中的所有连通区域,保留面积大于设定阈值的区域,直接过滤短毛刺:

from skimage.morphology import label
from skimage.measure import regionprops

def prune_skeleton(skeleton, min_length=10):
    labeled_skeleton = label(skeleton)
    pruned = np.zeros_like(skeleton)
    for region in regionprops(labeled_skeleton):
        if region.area >= min_length:
            pruned[labeled_skeleton == region.label] = 1
    return pruned

2. 计算骨架长度

通过累加相邻骨架像素的欧氏距离计算长度,若已知图像的实际像素尺寸,可乘以对应系数转换为物理长度:

def calculate_skeleton_length(skeleton, pixel_size=1):
    # 获取所有骨架像素的坐标
    y, x = np.where(skeleton == 1)
    length = 0
    # 遍历相邻像素计算距离并累加
    for i in range(1, len(y)):
        dy = y[i] - y[i-1]
        dx = x[i] - x[i-1]
        distance = np.sqrt(dy**2 + dx**2)
        length += distance
    return length * pixel_size

完整整合代码

import skimage as ski
import skimage.io as io
import matplotlib.pyplot as plt
import numpy as np
from skimage.morphology import skeletonize
from skimage.measure import regionprops, label
import matplotlib
matplotlib.rc("font",family='Microsoft YaHei')

def trim_skeleton(skeleton, iterations=3):
    trimmed = skeleton.copy()
    for _ in range(iterations):
        neighbors = np.zeros_like(trimmed)
        for i in range(1, trimmed.shape[0]-1):
            for j in range(1, trimmed.shape[1]-1):
                if trimmed[i,j] == 1:
                    count = np.sum(trimmed[i-1:i+2, j-1:j+2]) - 1
                    neighbors[i,j] = count == 1
        trimmed[neighbors] = 0
    return trimmed

def calculate_skeleton_length(skeleton, pixel_size=1):
    y, x = np.where(skeleton == 1)
    length = 0
    for i in range(1, len(y)):
        dy = y[i] - y[i-1]
        dx = x[i] - x[i-1]
        distance = np.sqrt(dy**2 + dx**2)
        length += distance
    return length * pixel_size

image_path=r"./1089_8.jpg"
image=io.imread(image_path,as_gray=True)
# 确保图像为二值化状态(根据实际情况调整阈值)
binary_image = image > 0.5

skeleton1=skeletonize(binary_image,method='zhang')
skeleton2=skeletonize(binary_image,method='lee')

# 修剪毛刺
trimmed_skeleton1 = trim_skeleton(skeleton1, iterations=3)
trimmed_skeleton2 = trim_skeleton(skeleton2, iterations=3)

# 计算长度
length_zhang = calculate_skeleton_length(trimmed_skeleton1)
length_lee = calculate_skeleton_length(trimmed_skeleton2)

# 可视化对比
fig,axes=plt.subplots(nrows=2,ncols=3,figsize=(12, 8), sharex=True, sharey=True)
ax=axes.ravel()

ax[0].imshow(image, cmap=plt.cm.gray)
ax[0].axis('off')
ax[0].set_title('原始图像', fontsize=15)

ax[1].imshow(skeleton1, cmap=plt.cm.gray)
ax[1].axis('off')
ax[1].set_title('Zhang原始骨架', fontsize=15)

ax[2].imshow(trimmed_skeleton1,cmap=plt.cm.gray)
ax[2].axis('off')
ax[2].set_title(f'Zhang修剪后\n长度:{length_zhang:.2f}px', fontsize=15)

ax[3].imshow(image, cmap=plt.cm.gray)
ax[3].axis('off')
ax[3].set_title('原始图像', fontsize=15)

ax[4].imshow(skeleton2, cmap=plt.cm.gray)
ax[4].axis('off')
ax[4].set_title('Lee原始骨架', fontsize=15)

ax[5].imshow(trimmed_skeleton2,cmap=plt.cm.gray)
ax[5].axis('off')
ax[5].set_title(f'Lee修剪后\n长度:{length_lee:.2f}px', fontsize=15)

fig.tight_layout()
plt.show()

内容的提问来源于stack exchange,提问作者Abukeram

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 04:25:15