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

蛇形通道网格沿骨架离散点尺寸提取与标量映射优化求助

蛇形通道表面网格的尺寸提取与标量映射优化

我有一个带渐变蛇形通道的表面网格(STL文件):
蛇形通道3D视图

需要完成以下操作:

  • 获取沿通道骨架(长度方向)离散点处的通道尺寸(宽度、高度)
  • 根据宽度和高度计算标量值,公式为 calc_scalar(width, height) = 1/width + 1/height
  • 将标量值映射回体素表示或切片中

当前实现存在的问题:
我使用PyVista、Scikit-Image形态学模块、Scipy ndimage和Matplotlib完成了部分工作,但仅提取了宽度(未获取高度),且将标量映射到2D切片的过程速度极慢、效果不完善。相关代码如下:

#!/usr/bin/env python
# coding: utf-8

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

import pyvista as pv

import skimage as ski
from skimage.morphology import skeletonize, medial_axis

from scipy.ndimage import distance_transform_edt
import copy

# The function to calculate the scalar from the channel width and height
def calc_scalar(width, height):
    return 1/width + 1/height

# Some constants
mesh_res = 2400
figsize_mult = 15
image_type = 'png'
savefig_dpi = 1200
thresh = .95
thresh_dist = 9
atol = 16

# Colors maps
cmap_greys = cmap = plt.get_cmap("Greys", 2)
cmap_coolwarm = copy.copy(mpl.cm.coolwarm)
cmap_coolwarm.set_bad(color='black', alpha=1.)

# Import the STL mesh
mesh = pv.read("serpent.stl")
mesh_length = mesh.bounds[1] - mesh.bounds[0]
mesh_width = mesh.bounds[3] - mesh.bounds[2]
print(f"Mesh's bounding box dimensions:\n  Length x Width: {mesh_length:.3f} mm x {mesh_width:.3f} mm")

# Make voxels (volume) out of the surface mesh
voxels = pv.voxelize(mesh, density=mesh.length/mesh_res)
voxels['dummy'] = np.zeros(voxels.GetNumberOfCells())

# Slice the voxels
data = voxels.ctp().slice('z', generate_triangles=True)
tri = data.faces.reshape((-1,4))[:,1:]
u = data.active_scalars

# Plot the slice and save as a raster image
fig, ax = plt.subplots(figsize=(figsize_mult*mesh_length/25.4, figsize_mult*mesh_width/25.4))
ax.tricontourf(data.points[:,0], data.points[:,1], tri, u, cmap=cmap_greys, vmin=0, vmax=1)
ax = plt.gca()
ax.set_aspect('equal')
ax.set_facecolor('k')
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.set_ylim(-.55*mesh_width, .55*mesh_width)
plt.tight_layout()
plt.savefig(f'serpent bw.{image_type}', dpi=savefig_dpi, bbox_inches='tight', facecolor='black')

# Load back the raster image using scikit.image
# Do some clean-up (Gaussian filter, threshold)
im = ski.io.imread(f'serpent bw.{image_type}', as_gray=True)
blurred_im = ski.filters.gaussian(im, sigma=1.0)
binary_mask = blurred_im > thresh

# Get the skeletons and the distance
# Somehow, the different functions yield somewhat different skeletons
skel, distance = medial_axis(binary_mask, return_distance=True)
skeleton = skeletonize(binary_mask)
dist_on_skel = distance * skel

# Yet another, cleaner, skeleton from Scipy ndimage
im_edt, indices = distance_transform_edt(im, return_indices=True)
im_edt_skel = im_edt * skeleton
sc_im_edt = np.where(skeleton > 0, calc_scalar(im_edt, 1), 0)

# Scan the array searching for the skeleton.
# Compute the scalar value at each skeleton point.
# Fill the channel's width with the same value.
# Super slow...
sc_im_edt_cp = np.full_like(sc_im_edt, np.nan)
for i, r in enumerate(sc_im_edt):
    for j, p in enumerate(r):
        if ~np.isnan(p) and p >=.1:
            y = indices[0][i][j]
            x = indices[1][i][j]
            sc_im_edt_cp[i][j] = p
            if x == j:
                # print(f"(j, i): ({j}, {i}); (x, y): ({x}, {y})")
                for y1 in np.arange(np.abs(y - i)):
                    # print(f"(x1, y1) = ({x}, {y + y1})")
                    try:
                        sc_im_edt_cp[i + y1][j] = p
                    except IndexError:
                        pass
                    try:
                        sc_im_edt_cp[i - y1][j] = p
                    except IndexError:
                        pass
            elif y == i:
                # print(f"(j, i): ({j}, {i}); (x, y): ({x}, {y})")
                for x1 in np.arange(np.abs(x - j)):
                    # print(f"(x1, y1) = ({x + x1}, {y})")
                    try:
                        sc_im_edt_cp[i][j + x1] = p
                    except IndexError:
                        pass
                    try:
                        sc_im_edt_cp[i][j - x1] = p
                    except IndexError:
                        pass
            else:
                m = np.nan
                ## in which cadran the boundary is located
                if x < j:
                    m = (i - y) / (j - x)
                elif x > j:
                    m = (y - i) / (x - j)
                b = y - m * x
                # print(f"(j, i): ({j}, {i}); (x, y): ({x}, {y}), (m, b): ({m}, {b})")
                for y1 in np.arange(np.abs(y - i)):
                    for x1 in np.arange(np.abs(x - j)):
                        if m < 0:
                            if np.isclose(i + y1, m * (j - x1) + b, atol=atol):
                                # print(f"(x', y') = ({j + x1}, {i + y1}), m.x1+b: {m*(j+x1)+b}")
                                try:
                                    sc_im_edt_cp[i + y1][j - x1] = p
                                except IndexError:
                                    pass
                            if np.isclose(i - y1, m * (j + x1) + b, atol=atol):
                                # print(f"(x', y') = ({j - x1}, {i - y1}), m.x1+b: {m*(j-x1)+b}")
                                try:
                                    sc_im_edt_cp[i - y1][j + x1] = p
                                except IndexError:
                                    pass
                        else:
                            if np.isclose(i + y1, m * (j + x1) + b, atol=atol):
                                # print(f"(x', y') = ({j + x1}, {i + y1}), m.x1+b: {m*(j+x1)+b}")
                                try:
                                    sc_im_edt_cp[i + y1][j + x1] = p
                                except IndexError:
                                    pass
                            if np.isclose(i - y1, m * (j - x1) + b, atol=atol):
                                # print(f"(x', y') = ({j - x1}, {i - y1}), m.x1+b: {m*(j-x1)+b}")
                                try:
                                    sc_im_edt_cp[i - y1][j - x1] = p
                                except IndexError:
                                    pass

# Plot the result and save the raster image
fig, ax = plt.subplots(figsize=(12, 8))
ax.imshow(sc_im_edt_cp, cmap=cmap_coolwarm)
ax.contour(im, [0.5], colors='gray')
ax.axis('off')
plt.tight_layout()
plt.savefig(f'serpent sc.{image_type}', dpi=savefig_dpi, bbox_inches='tight', facecolor='black')

当前效果不完善的输出结果:
标量值映射到蛇形通道的结果

我肯定是在重复造轮子,这类问题应该有更高效的标准实现方法,求各位给出优化建议。


内容的提问来源于stack exchange,提问作者François

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 03:35:55