How to use Screen Capture Module to save slices as .png files one-by-one along one axial?

Hi all,this is the thing I want to do:
I need to save all the ROI slice png files along one axial.For example,I need to save the slices from -20 to 20 along I-S axial according to the volume spacing information.
image
Manually,I first change the ROI I-S range -20 to -19.90 as follows
image
Then,I take a screen shot and save the png picture
Next,I change the ROI I-S range -19.90 to -19.80 as follows
image
Then,I take a screen shot and save the png picture again.
So, I need to repeat this operation 400 times to save all the slices along one axial which is super-boring and time waste.
Is there any way to do it automatically?Thank you for any possible advice in advance.

Hi,

I would recommend using the “Utilities” ->“Screen Capture” extension for this purpose.

To capture 31 images of a a series the “Red” view you could use:

image

Sadly,this is Red slice is not the slice after volume rendering.The slice is controlled by window level but what I did is to get the slice after ray casting.

Slicer 4.13.0:
For recording the 3D view you can go to “Sequences”, create a new SequenceBrowser, select “Camera” as a Proxy node, and record the sequence that you want to generate the PNG file series from.

image

Then go to the “Screen Capture” extension, select animation mode → sequence, select the SequenceBrowser you created before and use “output type” → image series. Press “Capture” to create the PNG files.

image

Thanks Rudolf,
What did you mean by record the sequence?I need to snapshot along one axial from bottom to top and if the volume is a 300x300x400 cube and I use the I-S orientation ,so I need to record 400 slice png files.Can you describe it in detail?Thanks for your help.

Please post a screenshot of what you want to record exactly, maybe two of the png that you created, and tell us what you did in between to switch from png1 to png2.

Volume rendering creates a 2D image by casting rays through a 3D volume and applying transfer functions for opacity and color for voxels encountered along each ray. If I understand what you are trying to accomplish, you are clipping the volume rendering so that it shows data from only one axial slice at a time, and you are trying to go slice by slice and capture the volume rendering data into a stack of png images.

If you are interested in slice-by-slice data, then you don’t need volume rendering at all because ray casting is unnecessary. You can just convert your image volume into png files one slice at a time, using the same kind of transfer functions to translate image voxel values into png pixel values. This can be fairly easily accomplished with a small amount of python code.

If you just want to visualize your data (e.g. with color and/or transparency) and don’t actually need the individual png files, you may also be able to accomplish your goal by creating a custom lookup table which you can apply to the volume, and then all slice views will show the colorized data.

What is your ultimate goal? What do you want to do with the png files if you could generate them?

Thanks Mike,
You are very clever.My ultimate goal is to get the visualized data after ray casting.I want all the slice png files because I want to stack these 2D image back to a 3D volume.
Since I have tried to accomplish this goal directly several times by rendering myself using python code,I was stucked with creating lookup table and I can’t figure out how to apply the transfer functions to raw volume.

Thanks Rudolf,
Please let me show you two steps about what I am doing right now.
In the begining,I apply transfer functions such as Scalar Opacity Mapping and Gradient Opacity to my raw volume and become like this:
2021-07-28-Scene
I check the Volume information and this is a 300x300x400 volume and the spacing is 0.1mm so I need to slice by 0.1mm along one axial at one time.Then I go to volume rendering module and begin to slice the ROI.
image
First,I select the I-S orientation and do one slice like this:
image
Then I take a 3D screen shot:
image
Next,I change the I-S Range in ROI,like this:
image
Then,I take anther screen shot.
0.1mm one step,the whole process continues when I reach say I-S Range 6.9 to 7.0 which means I have sliced all the png files from bottom to top.

Thank you.

This can not be done with the Screen Capture Module alone and will probably need a small Python program that (1) iterates through the I-S range values of Volume Rendering and (2) takes a screenshot of the 3D window with every new value pair.

It is quite easy to involve Slicer extensions in Python programs. See this link in Slicer’s script repository for an example on how to programmatically call Volume Rendering and this link on how to save screenshots in a Python script.

Are you sure your workflow can not be simplified by just segmenting your structures with the “Segment Editor”? This extension offers excellent effects which can solve nearly every segmentation problem and you would not need to “stack” anything.

1 Like

@user4 It appears you are just trying to resample your volume along a newly defined set of axes? See some details at Overview | 3D Slicer segmentation recipes where there is a “Option B. Create resampled volume with rotated axes” section.

1 Like

Stacking the set of screen captures you would gather using the procedure you outline, @user4, would not result in a 3D volume which corresponds to the original image volume geometry. Because you are viewing the the slices from an oblique angle in the 3D view, the series of png images you would get out would be like frames of a movie showing the volume rendering of each slice from exactly the current perspective. Perhaps that is what you want, but I suspect not, because this is not what you would accomplish by applying transfer functions to the raw volume.

I noticed that this question is probably related to the prior discussion here: How to save the volume pixel data in display area?

If your goal is to get a vessel mask, the segmentation approach suggested by @rbumm is likely to be the best one. Find a segmentation approach which identifies the voxels you want to include in your mask, convert the mask segment to a labelmap volume, and then use Mask Scalar Volume to apply the mask to your original volume. There are many segmentation approaches which look like they would be easily successful for the data you have. A few suggestions of possible approaches would be:

  • thresholding followed by getting rid of small islands
  • painting with a large brush and a restricted intensity range
  • grow from seeds

You can get a sense of the segmentation tools available and how to use them here: Segment editor — 3D Slicer documentation

1 Like

Thanks Mike,I do this thing with a fixed perspective in display area and I will not change it.
Later I will try it with the segmentation approach.

I have seen through the script repository and still don’t know how to iterate through the I-S range.Can you show me some detailed code or something? It will be really really helpful!!

This step is already completed and I have sucessfully taken shots using python code.

I do not have a complete working / sharable recipe for that, but you would probably need to limit volume rendering to a specific ROI like shown here.
Generally, I would recommend following the segmentation approach.

To iterate through the I-S range, you can set up the first range interactively (as you have already done). Then, you can iterate through positions by getting the ROI node and calling roi.SetCenterPositionWorld( newPos ).

import numpy as np
roiName = 'R' # replace this with whatever your ROI is named
roi = getNode(roiName)
startingRoiCenter = np.zeros(3) # allocate position vector
roi.GetCenterPositionWorld(StartingRoiCenter) # fill in position vector
for centerSuperiorOffset in range(0,40,0.01):
    newRoiCenter = startingRoiCenter + np.array([0, 0, centerSuperiorOffset])
    roi.SetCenterPositionWorld(newRoiCenter)
    # Insert your screen capture code here
1 Like

Thanks Mike very very much for this code!
image

I haven’t seen your method but otherwise found this:
image
I think this Get/Set method is what you mean?

So I have completed this code as follows:

suffix = 0
startingRoiCenter = np.array([0,0,-19.95]) 
for centerSuperiorOffset in np.arange(0,40,0.1):
    newRoiCenter = startingRoiCenter + np.array([0, 0, centerSuperiorOffset])
    roi.SetXYZ(newRoiCenter)
    take_one_screen_shot(suffix)
    suffix+=1
# Capture RGBA image
def take_one_screen_shot(suffix):
    renderWindow = view.renderWindow()
    renderWindow.SetAlphaBitPlanes(1)
    wti = vtk.vtkWindowToImageFilter()
    wti.SetInputBufferTypeToRGBA()
    wti.SetInput(renderWindow)
    writer = vtk.vtkPNGWriter()
    writer.SetFileName("c:/tmp/screen_shot"+str(suffix)+".png")
    writer.SetInputConnection(wti.GetOutputPort())
    writer.Write()

image

yeah,thanks for you,I did this slice thing along I-S range from -20 to +20 with step 0.1.
However I still got a quesiton maybe you can help me again, I will be very grateful. :grinning:
I set the startingRoiCenter as follows:
startingRoiCenter = np.array([0,0,-19.95])
-19.95 as a center and it gives both left range and right range 0.05,so the begining range is from -20 to -19.90.
image
run the code above and produced below:
image
I noticed the center is giving both sides 0.05mm,so it is 0.1mm between left and right.Is this number fixed?For example,say,I want to have a slice with 0.15mm not 0.1mm from -20 to +20 along I-S orientation,how to set up the iteration?
Thank you for your help again,it does help a lot!

1 Like

You’re welcome, I’m glad it was helpful!

Markups, including the ROI markup, has undergone a lot of revision recently, and the SetCenterPositionWorld() function is part of the updated versions (present in the current Slicer “preview” release). It looks like you are using an older version of Slicer where the ROI is from the “Annotations” module instead (this was the former default for ROIs). You are correct that the analogous function for Annotations ROIs is SetXYZ().

The thickness of the volume rendered region is not changed during the iteration in the code above, so the simplest way to change the thickness is to manually change it in the volume rendering module before you start (for example by entering -20 and -19.85 as the ends of the I-S range).

If you want to change this in python code, you can use the SetRadiusXYZ() function and change the Z radius to half your desired slice thickness. Both Annotation and Markups ROIs have SetRadiusXYZ() methods.

1 Like

Thanks Mike,
it really works and you do help me a lot! :+1:

1 Like