# How to programmatically split the RGB channels and store them separately as a segmentation node

**URL:** <https://discourse.slicer.org/t/how-to-programmatically-split-the-rgb-channels-and-store-them-separately-as-a-segmentation-node/22122>\
**Category:** Support\
**Tags:** segmentation, python\
**Created:** [February 22, 2022, 11:13pm UTC](https://discourse.slicer.org/t/how-to-programmatically-split-the-rgb-channels-and-store-them-separately-as-a-segmentation-node/22122 "2022-02-22T23:13:29Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![PhilipDavis](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/philipdavis/32/14142_2.png) [@PhilipDavis](https://discourse.slicer.org/u/PhilipDavis)\
**Post date:** [February 22, 2022, 11:13pm UTC](https://discourse.slicer.org/t/how-to-programmatically-split-the-rgb-channels-and-store-them-separately-as-a-segmentation-node/22122/1 "2022-02-22T23:13:29Z")

</div>

Hi there,

I’m new to 3D Slicer, as this title mentions, here is a RGB stack TIFF image:  
[RGB STACK](https://drive.google.com/file/d/1rH9DZPQGbinYuE6T_UYZSeLCWzYd-oE7/view?usp=sharing)  
and now I’d like to implement a procedure in python that splits the RGB channels and converts/stores the RGB channels in the three corresponding segmentation nodes.  
The result is similar to the following:

 ![Figure1](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/2/f/2fe2dc1e75c31bf339f1060ef9815df592370788.png)

Each channel will be stored in a corresponding segmentation node.  
Thank you very much for any specific guidance in advance!

---

<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:** [February 23, 2022, 12:54am UTC](https://discourse.slicer.org/t/how-to-programmatically-split-the-rgb-channels-and-store-them-separately-as-a-segmentation-node/22122/2 "2022-02-23T00:54:21Z")

</div>

Segmentations are typically represented as binary labelmap images, so normally you threshold the input scalar (grayscale) image to get the segmentation. Slicer supports fractional labelmaps for segmentations, too, but only a limited set of segmentation tools are supported for those, so I’m not sure if they would work better for you.

I had a look at the image that you provided and the R, G, B components are exactly the same, so what you have is actually single-channel image (the same channel is repeated 3 times).

When I loaded it using the default image loader then only the first component was loaded. So, instead of loading the image using drag-and-drop you can load it as 3 separate volumes using scikit-image using this Python code snippet:

```python
filename = r"c:\Users\andra\Downloads\RGB STACK.tif"

# Install scikit-image
try:
    from skimage import io
except:
    pip_install('scikit-image')
    from skimage import io

# Read the image into numpy array
im = io.imread(filename)

# Create 3 separate volumes
# Useful for segmentation, volume rendering with color mapping, etc.

for component in range(im.shape[3]):
    volumeNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLScalarVolumeNode", f"image-c{component}")
    slicer.util.updateVolumeFromArray(volumeNode, im[:,:,:,component])

# Show in slice views
slicer.util.setSliceViewerLayers(background=volumeNode, fit=True)

# Show volume rendering
vrDisplayNode = slicer.modules.volumerendering.logic().CreateDefaultVolumeRenderingNodes(volumeNode)

```

You can then create 3 segments from them using thresholding, but I’m not sure if this is really what you want to achieve.

For example, if you want to visualize the image then probably volume rendering is more appropriate. With slight adjustment of the opacity and color transfer function you can get a 3D visualization like this:

[https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/d/1/d1bd0608a3369e45de2aa4f6020b50ae1ed93b41.mp4](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/d/1/d1bd0608a3369e45de2aa4f6020b50ae1ed93b41.mp4)

If you acquire volumes that have different data on each channel then you can load it as a single RGB volume like this:

```auto
# Create a single volume
# Useful for actual RGB volumes (that has different values in each channel)
volumeNodeRGB = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLVectorVolumeNode", "image-rgb")
slicer.util.updateVolumeFromArray(volumeNodeRGB, im)

```

If you do want to create segmentation (for example, to measure volumes, count objects, etc.) you can create a segmentation from a numpy array as shown in [this example](https://slicer.readthedocs.io/en/latest/developer_guide/script_repository.html#read-and-write-a-segment-as-a-numpy-array).

---

<div class="post-metadata">

**Author:** ![PhilipDavis](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/philipdavis/32/14142_2.png) [@PhilipDavis](https://discourse.slicer.org/u/PhilipDavis)\
**Post date:** [February 23, 2022, 3:01am UTC](https://discourse.slicer.org/t/how-to-programmatically-split-the-rgb-channels-and-store-them-separately-as-a-segmentation-node/22122/3 "2022-02-23T03:01:40Z")

</div>

Hello @lassoan,  
Thank you so much for your kind and quick reply!

> I had a look at the image that you provided and the R, G, B components are exactly the same.

Sorry about that, this image was processed by using FIJI, I re-uploaded a new image and it should be fine now(It now have different data on each channel): [RGB STACK2](https://drive.google.com/file/d/1IuIFFL9th6D9CxVjfBvgInFlYUbzAoo3/view?usp=sharing)

The script you provided are very useful, after I run it, I got 3 volumeNode: image-c0(red), image-c1(green), image-c2(blue). Therefore I see I can take these volume nodes as ‘Master volume’ and create 3 segmentation nodes/segments separately for them:  
 ![Figure7](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/9/b/9b027388a2707b169a158a0b78dad8783f2b8f46.png)

So what need to be add to the script you provided to do this? ( **To create 3 separate segmentation nodes/segments for these 3 volume nodes programmatically** ) For example, how to create 3 segmentation nodes with separate thresholds:  
Red: 20 Green:30 Blue:40.

Thank you very much!

---

<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:** [February 23, 2022, 6:05am UTC](https://discourse.slicer.org/t/how-to-programmatically-split-the-rgb-channels-and-store-them-separately-as-a-segmentation-node/22122/4 "2022-02-23T06:05:37Z")

</div>

This script creates 3 segments by thresholding each channel, displays the segmentation in 3D, and displays the 3 channels as an RGB image:

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/7/9/79c9c5095ba8e4de2943d7e7d9f714d2167075f6.png)

```python
# Inputs
filename = r"c:\Users\andra\OneDrive\Projects\SlicerTesting2022\20220222-Confocal\RGB STACK2.tif"
channelThresholds = [20, 30, 40]

# Install scikit-image
try:
    import skimage
except:
    pip_install('scikit-image')

from skimage import io
import numpy as np

# Read the image into numpy array
im = io.imread(filename)

# Create segments by thresholding each channel

# Create temporary labelmap volume node (it will store the thresholded image that will be imported into the segmentation)
tempLabelmapVolumeNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLLabelMapVolumeNode")
tempLabelmapVolumeNode.CreateDefaultDisplayNodes()
# Do not set a color node ID. This will make segment names set based on the labelmap volume's name (instead of looking up the segment name in the color node)
tempLabelmapVolumeNode.GetDisplayNode().SetAndObserveColorNodeID("")

segmentationNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
for component in range(im.shape[3]):
    componentImage = im[:,:,:,component]
    componentThreshold = channelThresholds[component]
    thresholdedComponentImage = np.zeros(componentImage.shape)
    thresholdedComponentImage[componentImage > componentThreshold] = 1
    tempLabelmapVolumeNode.SetName(f"Component-{component}") # the volume node's name will be used as segment name
    slicer.util.updateVolumeFromArray(tempLabelmapVolumeNode, thresholdedComponentImage)
    slicer.modules.segmentations.logic().ImportLabelmapToSegmentationNode(tempLabelmapVolumeNode, segmentationNode)

slicer.mrmlScene.RemoveNode(tempLabelmapVolumeNode)

# Show segmentation in 3D
segmentationNode.CreateClosedSurfaceRepresentation()
segmentationNode.GetDisplayNode().SetOpacity(0.6)

# Show the image as an RGB volume in slice views
volumeNodeRGB = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLVectorVolumeNode", "image-rgb")
slicer.util.updateVolumeFromArray(volumeNodeRGB, im)
slicer.util.setSliceViewerLayers(background=volumeNodeRGB, fit=True)

```

---

<div class="post-metadata">

**Author:** ![PhilipDavis](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/philipdavis/32/14142_2.png) [@PhilipDavis](https://discourse.slicer.org/u/PhilipDavis)\
**Post date:** [February 23, 2022, 5:44pm UTC](https://discourse.slicer.org/t/how-to-programmatically-split-the-rgb-channels-and-store-them-separately-as-a-segmentation-node/22122/5 "2022-02-23T17:44:27Z")

</div>

Hello @lassoan,  
Thank you very much for your thoughtful guidance!  
However, after I run the code you just provided above, only one empty segmentaion node was created, it doesn’t contain three separate segments as shown in the figure you provided, and there are no 3D surfaces showing in the render window :

 ![Figure10](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/1/c/1c3d26fbd2d677733b6e9c837cdfe24fdb078211.png)

> **the code I am running**
>
> ```auto
> # Inputs
> filename = r"d:\IMAGE\RGB STACK2.tif"
> channelThresholds = [20, 30, 40]
> 
> # Install scikit-image
> try:
> import skimage
> except:
> pip_install('scikit-image')
> 
> from skimage import io
> import numpy as np
> 
> # Read the image into numpy array
> im = io.imread(filename)
> 
> # Create segments by thresholding each channel
> 
> # Create temporary labelmap volume node (it will store the thresholded image that will be imported into the segmentation)
> tempLabelmapVolumeNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLLabelMapVolumeNode")
> tempLabelmapVolumeNode.CreateDefaultDisplayNodes()
> # Do not set a color node ID. This will make segment names set based on the labelmap volume's name (instead of looking up the segment name in the color node)
> tempLabelmapVolumeNode.GetDisplayNode().SetAndObserveColorNodeID("")
> 
> segmentationNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
> for component in range(im.shape[3]):
> componentImage = im[:,:,:,component]
> componentThreshold = channelThresholds[component]
> thresholdedComponentImage = np.zeros(componentImage.shape)
> tempLabelmapVolumeNode.SetName(f"Component-{component}") # the volume node's name will be used as segment name
> slicer.util.updateVolumeFromArray(tempLabelmapVolumeNode, thresholdedComponentImage)
> slicer.modules.segmentations.logic().ImportLabelmapToSegmentationNode(tempLabelmapVolumeNode, segmentationNode)
> 
> slicer.mrmlScene.RemoveNode(tempLabelmapVolumeNode)
> 
> # Show segmentation in 3D
> segmentationNode.CreateClosedSurfaceRepresentation()
> segmentationNode.GetDisplayNode().SetOpacity(0.6)
> 
> ```

Could you please point out how to fix it? Since in the next steps I need to apply the logical operator effect to these three segments. Thank you so much!

---

<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:** [February 23, 2022, 5:52pm UTC](https://discourse.slicer.org/t/how-to-programmatically-split-the-rgb-channels-and-store-them-separately-as-a-segmentation-node/22122/6 "2022-02-23T17:52:40Z")

</div>

Are you using the latest Slicer Preview Release?

---

<div class="post-metadata">

**Author:** ![PhilipDavis](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/philipdavis/32/14142_2.png) [@PhilipDavis](https://discourse.slicer.org/u/PhilipDavis)\
**Post date:** [February 23, 2022, 6:48pm UTC](https://discourse.slicer.org/t/how-to-programmatically-split-the-rgb-channels-and-store-them-separately-as-a-segmentation-node/22122/7 "2022-02-23T18:48:31Z")

</div>

Hello @lassoan,  
Yes, what I’m using now is the latest Slicer Preview Release for **Windows: 3D Slicer 4.13.0-2022-02-22** :  
Or do I need to uninstall all other versions of Slicer just to keep this one?

 ![Figure11](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/a/a/aa90ecc0afad706705d8c0fc4cb07e3832a517b9.png)

---

<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:** [February 23, 2022, 7:35pm UTC](https://discourse.slicer.org/t/how-to-programmatically-split-the-rgb-channels-and-store-them-separately-as-a-segmentation-node/22122/8 "2022-02-23T19:35:41Z")

</div>

The Slicer version you are using is good. It seems that I missed a line when I copied the code into the post above. I’ve added the missing line, please try [my script](https://discourse.slicer.org/t/how-to-programmatically-split-the-rgb-channels-and-store-them-separately-as-a-segmentation-node/22122/4) again.

---

<div class="post-metadata">

**Author:** ![PhilipDavis](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/philipdavis/32/14142_2.png) [@PhilipDavis](https://discourse.slicer.org/u/PhilipDavis)\
**Post date:** [February 23, 2022, 7:45pm UTC](https://discourse.slicer.org/t/how-to-programmatically-split-the-rgb-channels-and-store-them-separately-as-a-segmentation-node/22122/9 "2022-02-23T19:45:35Z")

</div>

> [@lassoan](#):
>
> `thresholdedComponentImage[componentImage > componentThreshold] = 1`

Thank you very much for your great guidance! It’s working now 👍
