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

如何用Python从2D和3D骨架图像中提取邻接表及相关信息?

问题描述

我已获取2D和3D骨架化图像,现需在Python中对这些骨架进行分析,获取以下信息:

  1. 节点位置
  2. 邻接表
  3. 连通性(图像中连续骨架的数量)

请问是否有可行的实现方法?


可行实现方案

一、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.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 23:05:27