11"""Skin segmentation tools"""
22
3+ import logging
4+ import vtk
35from OpenLIFULib .lazyimport import openlifu_lz
46from slicer import vtkMRMLScalarVolumeNode , vtkMRMLModelNode
57from OpenLIFULib .coordinate_system_utils import get_IJK2RAS
@@ -16,6 +18,18 @@ def generate_skin_mesh(volume_node:vtkMRMLScalarVolumeNode) -> vtkMRMLModelNode:
1618 skin_node .SetAndObservePolyData (skin_mesh )
1719 return skin_node
1820
21+ def cast_volume_to_float (volume_node :vtkMRMLScalarVolumeNode ) -> None :
22+ """Converts a volume node to float, replacing the underlying vtkImageData."""
23+ image_cast = vtk .vtkImageCast ()
24+ image_cast .SetInputData (volume_node .GetImageData ())
25+ image_cast .SetOutputScalarTypeToDouble ()
26+ image_cast .Update ()
27+ volume_node .SetAndObserveImageData (image_cast .GetOutput ())
28+
29+ # I am not certain that the display node will know to update itself to handle the new image data type,
30+ # so I hope poking `CreateDefaultDisplayNodes` here makes it do the right thing. If it's not needed then it's harmless anyway:
31+ volume_node .CreateDefaultDisplayNodes ()
32+
1933def threshold_volume_by_foreground_mask (volume_node :vtkMRMLScalarVolumeNode ) -> None :
2034 """Compute the foreground mask for a loaded volume and threshold the volume to strip out the background.
2135 This modifies the values of the background region in the volume and sets them to 1 less than the minimum value in the volume.
@@ -24,8 +38,14 @@ def threshold_volume_by_foreground_mask(volume_node:vtkMRMLScalarVolumeNode) ->
2438 """
2539 volume_array = slicer .util .arrayFromVolume (volume_node )
2640 volume_array_min = volume_array .min ()
41+
42+ background_value = volume_array_min - 1
43+ if background_value < volume_node .GetImageData ().GetScalarTypeMin (): # e.g. if volume_array_min is 0 and it's an unsigned int type
44+ logging .info ("Casting volume to float for the sake of `threshold_volume_by_foreground_mask`." )
45+ cast_volume_to_float (volume_node )
46+
2747 foreground_mask = openlifu_lz ().seg .skinseg .compute_foreground_mask (volume_array )
28- slicer .util .arrayFromVolume (volume_node )[~ foreground_mask ] = volume_array_min - 1
48+ slicer .util .arrayFromVolume (volume_node )[~ foreground_mask ] = background_value
2949 volume_node .GetDisplayNode ().SetThreshold (volume_array_min ,volume_array .max ())
3050 volume_node .GetDisplayNode ().SetApplyThreshold (1 )
3151 volume_node .GetDisplayNode ().SetAutoThreshold (0 )
@@ -36,4 +56,4 @@ def load_volume_and_threshold_background(volume_filepath) -> vtkMRMLScalarVolume
3656 volume_node = slicer .util .loadVolume (volume_filepath )
3757 with BusyCursor ():
3858 threshold_volume_by_foreground_mask (volume_node )
39- return volume_node
59+ return volume_node
0 commit comments