将 3D 对象切片/栅格化到图像堆栈时,如何标记轮廓的内/外?

How to label inner/outer of contour when slice/rasterize 3D objects to image stack?

提问人:vscv 提问时间:2/3/2023 最后编辑:vscv 更新时间:3/23/2023 访问量:307

问:

对于 3D 打印,我们将数字对象切成图像堆栈,以便使用 3D 打印机逐层堆叠它们。切片完成后,如何标记内/外以设置实心部分?

  1. STL 模型:

在此处输入图像描述

  1. 切片:

在此处输入图像描述

  1. 一个图像堆栈的示例(切片):

在此处输入图像描述

但需要保留或标记轮廓的内/外轮廓,比如说内部是黑色的,这样 3D 打印机就会打印出来并跳过白色的外部。目标填充在轮廓内部,如下图所示:

在此处输入图像描述

尝试 1

import pyvista as pv

mesh = pv.read('./haus.stl')
slices = mesh.slice_along_axis(n=20, axis='z', progress_bar=True) 

# show single slice with camera setting
slices[15].plot(cpos=[0, 1, 1], line_width=5, parallel_projection=True,)

# save slices (outcome is as step.3 image stack)
for i in range(20):
    p = pv.Plotter(off_screen=True)
    p.add_mesh(slices[i])
    p.camera_position = 'zy'
    p.enable_parallel_projection()
    im_name = "im_slice_" + str(i) + ".jpg"
    p.screenshot(im_name)


# Try voxelize (as ans from https://stackoverflow.com/questions/75300529)
voxels = pv.voxelize(mesh, density=mesh.length / 100)

# Try pv.Plane() (not test yet)
plane=pv.Plane()
plane.compute_implicit_distance(mesh, inplace=True)
np.sign(plane.point_data['implicit_distance'])
#i_resolution=?, j_resolution=?


# Try vtk (not test yet)
# https://stackoverflow.com/questions/68191368

体素化模型:在此处输入图像描述

体素切片:在此处输入图像描述

但似乎不太合适。需要构建一个非常精细的网格来恢复边界。voxelize sliced

尝试 2 个 VTK 示例

显示 STL:

在此处输入图像描述

只需添加 STL 读取器和映射器:

filename = './haus.stl'
    
reader = vtkSTLReader()
reader.SetFileName(filename)
reader.Update()

stlMapper = vtk.vtkPolyDataMapper()
stlMapper.SetInputConnection(reader.GetOutputPort())
polydata = stlMapper
print("Get GetOrigin", polydata.GetCenter()) 
sphereSource = reader

切片结果:

在此处输入图像描述

尝试 2 几乎完成了工作,但无法弄清楚 SetExtent/SetOrigin 效果。输出图像都适合轮廓的尺寸,因此每个输出图像 WXH 都不相同。

尝试 3 个 3D Silcer 示例

仅更改某些代码,如下所示:

inputModelFile = "./data/haus.stl"
outputDir = "./outputs/"
...
for i in range(80,140, 10):
  imageio.imwrite(f"{outputDir}/image_{i:03}.jpg", 255 - outputLabelmapVolumeArray[i]) # Inverting Colors

在此处输入图像描述

结果似乎是可以接受的,但需要将来修改一些代码以匹配分辨率、位置、间距等。那么,有没有一种更精简、更高效的方法来自动化类似的工作呢?

python vtk 光栅化 3D 打印 图像切片器

评论


答:

1赞 mmusy 2/4/2023 #1

您可能想尝试 和 的组合,例如:vtkFeatureEdgesvtkStrippervtkTriangleFilter

from vedo import *
msh = Mesh('https://vedo.embl.es/examples/data/cow.vtk')
slices = []
for z in np.arange(-.50, .50, .15):
    line = msh.clone().cut_with_plane(origin=(0,0,z), normal='z')
    cap = line.cap(True)
    slices.append(cap)
show(slices, msh.alpha(0.1), axes=1)

在此处输入图像描述

In case you want to create a set of images or a tiff stack (instead of a polygonal mesh), you can do it with:

from vedo import *

surf = Mesh("https://vedo.embl.es/examples/data/cow.vtk")
surf.compute_normals()

# write a tiff stack
vol = surf.binarize(spacing=(0.02, 0.02, 0.02))
vol.alpha([0,0.6]).c('blue5')
vol.write("image.tif")

# write a numpy array and/or a set of png files
arr = vol.tonumpy()
pic = Picture(arr[:,:,55])
pic.write("slice55.png")

show(vol, pic, N=2, sharecam=False, axes=1)

在此处输入图像描述

Finally you can inspect the tiff file from command line:

vedo --slicer3d image.tif

在此处输入图像描述

评论

0赞 vscv 2/6/2023
Thanks, @mmusy. It did the job. But it has the same issue as Try-2 VTK, "The output image all fit to the contours' dimensions, so each output image WXH is not identical." is it possible manually control the space padding for each plane to fit the output image size?
0赞 mmusy 2/6/2023
You don't need padding, the contour coordinates are absolute, they are not shifted wrt the original shape. The "padding" is trivially given by the difference of the bounds.
0赞 vscv 3/21/2023
can you get the output of the sliced image, just for example?
0赞 mmusy 3/22/2023
yes - you can write it to disk with eg. .cap.write("capmesh.stl")
0赞 vscv 3/23/2023
Thanks, @mmusy, I mean the "image" output as the original question. It would be great if you could provide the workable code.