如何用Python从2D和3D骨架图像中提取邻接表及相关信息?
问题描述
我已获取2D和3D骨架化图像,现需在Python中对这些骨架进行分析,获取以下信息:
- 节点位置
- 邻接表
- 连通性(图像中连续骨架的数量)
请问是否有可行的实现方法?
可行实现方案
一、2D骨架分析(基于OpenCV + scikit-image + NetworkX)
1. 节点位置提取
骨架中的节点通常是分支点(邻域连通数≥3)和端点(邻域连通数=1),通过遍历骨架像素的8邻域判断:
import cv2 import numpy as np from skimage.morphology import skeletonize # 读取并预处理2D骨架图像(转为二值图,骨架为白色) img = cv2.imread('2d_skeleton.png', cv2.IMREAD_GRAYSCALE) _, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) skeleton = skeletonize(binary//255).astype(np.uint8) * 255 # 定义8邻域坐标偏移 neighbors = [(-1,-1), (-1,0), (-1,1), (0,-1), (0,1), (1,-1), (1,0), (1,1)] nodes = [] h, w = skeleton.shape # 遍历所有非边缘骨架像素 for y in range(1, h-1): for x in range(1, w-1): if skeleton[y, x] == 255: # 统计邻域内的骨架像素数量 neighbor_count = sum([1 for dy, dx in neighbors if skeleton[y+dy, x+dx] == 255]) # 端点或分支点标记为节点 if neighbor_count == 1 or neighbor_count >= 3: nodes.append((x, y))
2. 邻接表构建
基于节点间的连通路径,用BFS遍历构建图结构并导出邻接表:
import networkx as nx def find_skeleton_path(start, end, skeleton): # BFS查找两点间的骨架连通路径 visited = set() queue = [start] visited.add(start) parent_map = {} found = False while queue: curr_y, curr_x = queue.pop(0) if (curr_y, curr_x) == end: found = True break # 遍历邻域 for dy, dx in neighbors: ny, nx = curr_y + dy, curr_x + dx if 0 <= ny < h and 0 <= nx < w and skeleton[ny, nx] == 255 and (ny, nx) not in visited: visited.add((ny, nx)) parent_map[(ny, nx)] = (curr_y, curr_x) queue.append((ny, nx)) # 回溯生成路径 if found: path = [] curr = end while curr in parent_map: path.append(curr) curr = parent_map[curr] path.append(start) return path[::-1] return None # 初始化图并添加节点 skeleton_graph = nx.Graph() skeleton_graph.add_nodes_from(nodes) # 遍历节点对,添加连通边 for i in range(len(nodes)): for j in range(i+1, len(nodes)): path = find_skeleton_path(nodes[i], nodes[j], skeleton) if path: # 边权重设为路径长度(像素数-1) skeleton_graph.add_edge(nodes[i], nodes[j], length=len(path)-1) # 导出邻接表 adjacency_list = nx.to_dict_of_lists(skeleton_graph)
3. 连通性统计
用OpenCV的连通组件分析直接获取连续骨架数量:
# 8连通规则分析骨架的连通组件 num_labels, _, _, _ = cv2.connectedComponentsWithStats(skeleton, connectivity=8) # 减去背景对应的1个组件 connected_skeleton_count = num_labels - 1 print(f"2D连续骨架数量:{connected_skeleton_count}")
二、3D骨架分析(基于scipy.ndimage + skimage + NetworkX)
3D骨架处理逻辑与2D一致,邻域扩展为26邻域(三维空间全方向):
1. 节点位置提取
import numpy as np from scipy.ndimage import generate_binary_structure from skimage.morphology import skeletonize_3d # 读取3D二值数据并生成骨架(示例:skeleton_3d为三维数组,骨架体素值为1) # binary_3d = np.load("3d_binary_data.npy") # skeleton_3d = skeletonize_3d(binary_3d) # 定义26邻域结构 struct_3d = generate_binary_structure(3, 3) neighbors_3d = [(dz, dy, dx) for dz in (-1,0,1) for dy in (-1,0,1) for dx in (-1,0,1) if not (dz==0 and dy==0 and dx==0)] nodes_3d = [] z_size, y_size, x_size = skeleton_3d.shape # 遍历非边缘的骨架体素 for z in range(1, z_size-1): for y in range(1, y_size-1): for x in range(1, x_size-1): if skeleton_3d[z, y, x] == 1: # 统计邻域内的骨架体素数量 neighbor_count = np.sum(skeleton_3d[z-1:z+2, y-1:y+2, x-1:x+2]) - 1 # 端点或分支点标记为节点 if neighbor_count == 1 or neighbor_count >= 3: nodes_3d.append((z, y, x))
2. 邻接表构建
import networkx as nx def find_3d_skeleton_path(start, end, skeleton): visited = set() queue = [start] visited.add(start) parent_map = {} found = False while queue: curr_z, curr_y, curr_x = queue.pop(0) if (curr_z, curr_y, curr_x) == end: found = True break # 遍历26邻域 for dz, dy, dx in neighbors_3d: nz, ny, nx = curr_z + dz, curr_y + dy, curr_x + dx if 0 <= nz < z_size and 0 <= ny < y_size and 0 <= nx < x_size and skeleton[nz, ny, nx] == 1 and (nz, ny, nx) not in visited: visited.add((nz, ny, nx)) parent_map[(nz, ny, nx)] = (curr_z, curr_y, curr_x) queue.append((nz, ny, nx)) if found: path = [] curr = end while curr in parent_map: path.append(curr) curr = parent_map[curr] path.append(start) return path[::-1] return None # 构建3D骨架图 skeleton_graph_3d = nx.Graph() skeleton_graph_3d.add_nodes_from(nodes_3d) # 添加连通边 for i in range(len(nodes_3d)): for j in range(i+1, len(nodes_3d)): path = find_3d_skeleton_path(nodes_3d[i], nodes_3d[j], skeleton_3d) if path: skeleton_graph_3d.add_edge(nodes_3d[i], nodes_3d[j], length=len(path)-1) # 导出邻接表 adjacency_list_3d = nx.to_dict_of_lists(skeleton_graph_3d)
3. 连通性统计
用scipy的连通组件分析获取3D连续骨架数量:
from scipy.ndimage import label # 26连通规则分析3D骨架的连通组件 _, num_components = label(skeleton_3d, structure=struct_3d) print(f"3D连续骨架数量:{num_components}")
关键注意事项
- 输入必须是二值化数据:骨架为前景(255或1),背景为0,否则需先做阈值分割处理。
- 3D数据处理需注意内存限制,大体积数据建议分块处理。
- 节点定义可按需调整:比如仅保留分支点,或增加其他特征(如曲率)筛选节点。
内容的提问来源于stack exchange,提问作者Mehdi MA.
相关产品推荐
相关产品推荐

