图像骨架提取后毛刺去除及二值图像长度计算技术问询
问题描述
我有一张二值图像,使用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
相关产品推荐
相关产品推荐

