Skip to content

Commit 154d742

Browse files
Address integer overflow when labeling background (#387)
See #391 (review)
1 parent 22f46f8 commit 154d742

File tree

1 file changed

+22
-2
lines changed

1 file changed

+22
-2
lines changed

OpenLIFULib/OpenLIFULib/skinseg.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Skin segmentation tools"""
22

3+
import logging
4+
import vtk
35
from OpenLIFULib.lazyimport import openlifu_lz
46
from slicer import vtkMRMLScalarVolumeNode, vtkMRMLModelNode
57
from 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+
1933
def 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

Comments
 (0)