# Updating segmentationNode with oversampled labelmap

**URL:** <https://discourse.slicer.org/t/updating-segmentationnode-with-oversampled-labelmap/29733>\
**Category:** Development\
**Created:** [May 30, 2023, 6:18pm UTC](https://discourse.slicer.org/t/updating-segmentationnode-with-oversampled-labelmap/29733 "2023-05-30T18:18:46Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![danpak94](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/danpak94/32/18310_2.png) [@danpak94](https://discourse.slicer.org/u/danpak94)\
**Post date:** [May 30, 2023, 6:18pm UTC](https://discourse.slicer.org/t/updating-segmentationnode-with-oversampled-labelmap/29733/1 "2023-05-30T18:18:46Z")

</div>

Hello,

I’m trying to do the following:  
(1) oversample a labelmap  
(2) modify it in the higher resolution voxel space  
(3) update the segmentation node with the modified labelmap

I figured out (1) and (2), but I couldn’t find info on (3).

Any help would be greatly appreciated.

Below is a demo of what I mean:

```auto
def apply_oversampling_on_segmentationNode(segmentationNode, inputVolumeNode, oversampling_factor):
    segmentationGeometryLogic = slicer.vtkSlicerSegmentationGeometryLogic()
    if segmentationGeometryLogic.GetOversamplingFactor() != oversampling_factor:
        segmentationGeometryLogic.SetInputSegmentationNode(segmentationNode)
        segmentationGeometryLogic.SetSourceGeometryNode(inputVolumeNode)
        segmentationGeometryLogic.SetOversamplingFactor(oversampling_factor)
        segmentationGeometryLogic.CalculateOutputGeometry()
        geometryImageData = segmentationGeometryLogic.GetOutputGeometryImageData()
        geometryString = slicer.vtkSegmentationConverter.SerializeImageGeometry(geometryImageData)
        segmentationNode.GetSegmentation().SetConversionParameter(slicer.vtkSegmentationConverter.GetReferenceImageGeometryParameterName(), geometryString)
        segmentationGeometryLogic.ResampleLabelmapsInSegmentationNode()
    else:
        geometryImageData = segmentationGeometryLogic.GetOutputGeometryImageData()
    return geometryImageData

def get_dims_spacing_origin_from_vtkImageData(geometryImageData):
    dims = geometryImageData.GetDimensions()
    spacing = geometryImageData.GetSpacing()
    origin = geometryImageData.GetOrigin()
    return dims, spacing, origin

def modifySegment(segmentArray):
    # some random modification
    return np.clip(segmentArray + np.random.rand(*segmentArray.shape), 0, 1)

# (1) works
geometryImageData = apply_oversampling_on_segmentationNode(segmentationNode, inputVolumeNode, oversampling_factor=3)
dims, spacing, origin = get_dims_spacing_origin_from_vtkImageData(geometryImageData) # I can use this to get the correct closed surface representation

# (2) works
segmentId = segmentationNode.GetSegmentation().GetSegmentIdBySegmentName(segment_name)
segmentArray = slicer.util.arrayFromSegmentInternalBinaryLabelmap(segmentationNode, segmentId)

newSegmentArray = modifySegment(segmentArray.copy())

# (3) how do I do this properly??
slicer.util.updateSegmentBinaryLabelmapFromArray(newSegmentArray, segmentationNode, segmentId, referenceVolumeNode=inputVolumeNode)

```

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [May 30, 2023, 7:36pm UTC](https://discourse.slicer.org/t/updating-segmentationnode-with-oversampled-labelmap/29733/2 "2023-05-30T19:36:15Z")

</div>

See a complete solution in this topic (using vtkSlicerSegmentationGeometryLogic):

> [@Resample segments in segmentation using Python scripting](https://discourse.slicer.org/t/resample-segments-in-segmentation-using-python-scripting/26411/4):
>
> This should resample all the segments in the segmentation using the current geometry: segmentationNode = getNode('Segmentation') segGeomLogic = slicer.vtkSlicerSegmentationGeometryLogic() segGeomLogic.SetInputSegmentationNode(segmentationNode) segGeomLogic.SetSourceGeometryNode(segmentationNode) segGeomLogic.CalculateOutputGeometry() segGeomLogic.ResampleLabelmapsInSegmentationNode()

---

<div class="post-metadata">

**Author:** ![danpak94](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/danpak94/32/18310_2.png) [@danpak94](https://discourse.slicer.org/u/danpak94)\
**Post date:** [May 30, 2023, 8:39pm UTC](https://discourse.slicer.org/t/updating-segmentationnode-with-oversampled-labelmap/29733/3 "2023-05-30T20:39:13Z")

</div>

Thank you for the response.

This actually looks like a repeat of my posted code: `apply_oversampling_on_segmentationNode`

I want to add a new _modified_ oversampled labelmap to segmentationNode.

How can I do this properly?

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [May 30, 2023, 8:53pm UTC](https://discourse.slicer.org/t/updating-segmentationnode-with-oversampled-labelmap/29733/4 "2023-05-30T20:53:47Z")

</div>

Once the segmentation has the resolution that you need, I would recommend to import the new segments from a labelmap node and delete the old segment (or use Logical operators effect’s Copy method).

You could use `slicer.vtkOrientedImageDataResample.ModifyImage` to manipulate an existing segment, but it is very hard to do it correctly due to multiple segments sharing a labelmap and further complicated by masking settings.

---

<div class="post-metadata">

**Author:** ![danpak94](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/danpak94/32/18310_2.png) [@danpak94](https://discourse.slicer.org/u/danpak94)\
**Post date:** [June 3, 2023, 1:18am UTC](https://discourse.slicer.org/t/updating-segmentationnode-with-oversampled-labelmap/29733/5 "2023-06-03T01:18:32Z")

</div>

I am able to get the correct resolution using `segGeomLogic.ResampleLabelmapsInSegmentationNode()`, but when I try to edit the segment with the new resolution (brush, erase, etc.), it seems to return to the original resolution.

How can I prevent this from happening?

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [June 3, 2023, 3:29am UTC](https://discourse.slicer.org/t/updating-segmentationnode-with-oversampled-labelmap/29733/6 "2023-06-03T03:29:27Z")

</div>

Make sure the master (source) representation is binary labelmap. If it is then give us more information so that we can reproduce what your see and can tell if it is the expected behaviour or something is wrong.

---

<div class="post-metadata">

**Author:** ![danpak94](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/danpak94/32/18310_2.png) [@danpak94](https://discourse.slicer.org/u/danpak94)\
**Post date:** [June 3, 2023, 11:39am UTC](https://discourse.slicer.org/t/updating-segmentationnode-with-oversampled-labelmap/29733/7 "2023-06-03T11:39:30Z")

</div>

If you are referring to the master representation setting as shown below, I believe I do have binary labelmap as my master representation.  
 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/b/2/b2852fc9c21cf0e181725a12ac60bd34ab482a33.png)

I’m trying to do the following:

1. Resample label maps programmatically using the code below
2. Edit the resulting segments with segment editor (brush, erase, etc.)

Expected behavior: Labelmap geometry stays as resampled  
Observed behavior: Labelmap geometry converts back to the original geometry before resampling  
Test: Specifying Geometry using this button ( ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/b/9/b912a202d86be4d7203deb28ae131957df5a8846.png)) results in the expected behavior

```auto
segmentationGeometryLogic = slicer.vtkSlicerSegmentationGeometryLogic()
segmentationGeometryLogic.SetInputSegmentationNode(segmentationNode)
segmentationGeometryLogic.SetSourceGeometryNode(inputVolumeNode)
segmentationGeometryLogic.SetOversamplingFactor(oversampling_factor)
segmentationGeometryLogic.CalculateOutputGeometry()
geometryImageData = segmentationGeometryLogic.GetOutputGeometryImageData()
segmentationGeometryLogic.ResampleLabelmapsInSegmentationNode()

```

---

<div class="post-metadata">

**Author:** ![danpak94](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/danpak94/32/18310_2.png) [@danpak94](https://discourse.slicer.org/u/danpak94)\
**Post date:** [June 4, 2023, 12:01pm UTC](https://discourse.slicer.org/t/updating-segmentationnode-with-oversampled-labelmap/29733/8 "2023-06-04T12:01:01Z")

</div>

With respect to this suggestion:

1. I successfully created a new segment using `slicer.modules.segmentations.logic().ImportLabelmapToSegmentationNode(labelmap_vtkImageData, segmentationNode, segmentName)`.

2. But the stored labelmap is clipped to the effective extent. What’s the best way to retrieve the full extent numpy array from the original vtkImageData?

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [June 4, 2023, 12:06pm UTC](https://discourse.slicer.org/t/updating-segmentationnode-with-oversampled-labelmap/29733/9 "2023-06-04T12:06:56Z")

</div>

> [@danpak94](#):
>
> What’s the best way to retrieve the full extent numpy array from the original vtkImageData?

I would use convenience functions in `slicer.util` or specify reference volume for the segmentation export.

---

<div class="post-metadata">

**Author:** ![danpak94](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/danpak94/32/18310_2.png) [@danpak94](https://discourse.slicer.org/u/danpak94)\
**Post date:** [June 5, 2023, 1:13pm UTC](https://discourse.slicer.org/t/updating-segmentationnode-with-oversampled-labelmap/29733/10 "2023-06-05T13:13:01Z")

</div>

Note to myself and maybe future developers:

1. I tried to directly work with vtkImageData without creating a new volume node, but everything worked much better if I just create a volume node for the reference geometry.

Updating labelmap:  
` slicer.util.updateSegmentBinaryLabelmapFromArray(segmentArray.transpose([2,1,0]), segmentationNode, segmentId, referenceVolumeNode=geometryVolumeNode)`

Retrieving labelmap numpy array:  
`segmentArray = slicer.util.arrayFromSegmentBinaryLabelmap(segmentationNode, segmentId, referenceVolumeNode=geometryVolumeNode)`

1. For labelmaps with different resolutions, it’s much easier if I just create separate segmentationNodes.

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [June 5, 2023, 3:35pm UTC](https://discourse.slicer.org/t/updating-segmentationnode-with-oversampled-labelmap/29733/11 "2023-06-05T15:35:05Z")

</div>

Thanks for sharing your experience. Indeed, it makes things simpler if the reference image geometry that is set in the segmentation is the same as the current segmentation geometry.
