Creating a screenshot by script

Dear all,

I have a question concerning creating a screenshot using ScreenCapture.
I found the relevant script on the repository on the website

viewNodeID = 'vtkMRMLSliceNodeRed'
import ScreenCapture
cap = ScreenCapture.ScreenCaptureLogic()
view = cap.viewFromNode(slicer.mrmlScene.GetNodeByID(viewNodeID))
cap.captureImageFromView(view,sceneSaveDirectory + "/segmentation export.png")

(sceneSaveDirectory is defined previously to this section of the script)

When I paste this script in the interactor it works as expected
However when I run this script as a part of a larger script (copy-pasted in the Python Interactor) it does not work.
No error is shown, the screenshot is just not generated.

I tried running a time.sleep(3) before and after this section, but this does not fix the problem.
If I paste each section of the script separately into the Python Interactor it does work, just not when I paste it as a whole.

Do you have a suggestion how to fix this?

Thanks in advance,
Justin

You can try slicer.app.processEvents() instead.

I find this quite strange. The script works for me in 4.11.20200930 and a recent preview as well on Windows. What Slicer version and operating system do you use?

Thanks for your reply!

@pieper: I tried switching the time.sleep(3) to slicer.app.processEvents() both before and after this script but that didn’t work. Was that a correct interpretation of your suggestion? (so that the script waits until it is finished processing, rather than just 3 seconds?)

@cpinter

I use Windows 10 and the Nightly slicer version of 2020-12-07
As said above, this part of the script on its own works fine, but not as a part of a bigger script:

I load a CT-scan from the dicom library
Then I use a script to rename the volume, create a segmentation. It then pauses so that I can manually segment everything and then the script continues to process the segmentations, save the scene and close. So basically I can process each scan by just pasting in the whole script.

I inserted the above script to be able to export a view of the segmentations, but it does not work as part of my bigger ‘workhorse’ script. (but does work when I just paste the lines above).

Hopefully that’s a better explanation of the situation.

In this case a self-contained test case would help a lot.

This is an example where it does not work

import time
import os

## Selecting and renaming the volume
masterVolumeNode=slicer.mrmlScene.GetFirstNodeByClass('vtkMRMLScalarVolumeNode')

# Get patient's Name and rename the volume
instUids=masterVolumeNode.GetAttribute('DICOM.instanceUIDs').split()
filename=slicer.dicomDatabase.fileForInstance(instUids[0])
patientName=slicer.dicomDatabase.fileValue(filename,'0010,0010')
masterVolumeNode.SetName(patientName + '_CWK') 

## part 1: creating segments
# Create segmentation
segmentationNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
segmentationNode.SetName('test segmentation')
segmentationNode.CreateDefaultDisplayNodes() # only needed for display
segmentationNode.SetReferenceImageGeometryParameterFromVolumeNode(masterVolumeNode)

# Create segments by thresholding
segmentsFromHounsfieldUnits = [
    ["A", -29, 150],
    ["B", -29, 150],
    ["C", -29, 150] ]
for segmentName, thresholdMin, thresholdMax in segmentsFromHounsfieldUnits:
    # Create segment
    addedSegmentID = segmentationNode.GetSegmentation().AddEmptySegment(segmentName)

# Delete temporary segment editor
segmentEditorWidget = None

# Select SegmentEditor
m = slicer.util.mainWindow()
m.moduleSelector().selectModule('SegmentEditor')
layoutManager = slicer.app.layoutManager()
layoutManager.setLayout(6)

# wait for segmentation
try:
    input("Now perform your segmentation and press ENTER. If you push ENTER the screen will look unresponsive for a few seconds.")
except EOFError: pass

slicer.app.processEvents()

# Create a new directory where the scene will be saved into
sceneSaveDirectory = "D:/Slicer/test/" + patientName
if not os.access(sceneSaveDirectory, os.F_OK):
  os.makedirs(sceneSaveDirectory)

slicer.app.processEvents()

viewNodeID = 'vtkMRMLSliceNodeRed'
import ScreenCapture
cap = ScreenCapture.ScreenCaptureLogic()
view = cap.viewFromNode(slicer.mrmlScene.GetNodeByID(viewNodeID))
cap.captureImageFromView(view,sceneSaveDirectory + "/segmentation export.png")

#time.sleep(3)
slicer.app.processEvents()

# Save the scene
if slicer.app.applicationLogic().SaveSceneToSlicerDataBundleDirectory(sceneSaveDirectory, None):
  logging.info("Scene saved to: {0}".format(sceneSaveDirectory))
else:
  logging.error("Scene saving failed")   

slicer.mrmlScene.Clear(0)
m.moduleSelector().selectModule('DICOM')

If I comment out (or remove) the last two lines and paste this after running the above script:

viewNodeID = 'vtkMRMLSliceNodeRed'
import ScreenCapture
cap = ScreenCapture.ScreenCaptureLogic()
view = cap.viewFromNode(slicer.mrmlScene.GetNodeByID(viewNodeID))
cap.captureImageFromView(view,sceneSaveDirectory + "/segmentation export.png")

then the exported screenshot does appear

I was able to spend a little time on this. Indeed, using your script the screenshot is not saved for some reason. I noticed that if the scene is not saved after capturing the screenshot, then the screenshot is saved alright, which is strange.

If the order of the commands does not matter, then I suggest simply saving the screenshot after saving the scene. However, I don’t have an explanation about the screenshot not being saved with your snippet without some more serious debugging.

Thanks for taking the time looking into this, I agree that changing the order of the script is the easiest way to go. For now the order of the commands do not matter, so I can work with it!

I’ve just taken another look and added a pause after saving the screenshot.
It appears that the screenshot is written, but it is then deleted by the ‘save scene’ part of the script.
That is SaveSceneToSlicerDataBundleDirectory()

When I look it up in the APIdocs I do not see a means to prevent overwriting the entire folder.
To me this is a problem, as I might want to add other segmentations to this folder later on.
Do you know if it is possible to save the files without overwriting the entire folder contents?

OK makes sense, so the scene saving function clears the folder.

What is your goal with the screenshot in the same folder? I’m asking because usually people save scenes in an MRB file, which is self-contained, much easier to manage because it’s a single file, and can be loaded in Slicer the same way as a MRML file that has a Data folder.

Thanks, my idea was that I want to create multiple 2D segmentations (so at different axial levels of the body)
I wanted to create a snapshot, so that I can preview each segmentation while not having to open the entire scene.

so the folder structure would be

Patient A
– segmentation 1 preview.png
– segmentation 2 preview.png
– scene.mrml
– “Data” folder containing all the data

Patient B
– segmentation 1 preview.png
– segmentation 2 preview.png
– scene.mrml
– “Data” folder containing all the data

I think I’ll work around this issue by saving all the screenshots in a separate folder (so not in the patient folder)

It is essential to clean the output folder before MRML scene bundle export because when MRB file is generated from the entire content of the folder. You do not want random files that pre-exist in that folder show up in your MRB file. Whatever you want to save into the MRB file, you can add it to the scene (for example, if you want to include an additional png file then you can load it into the scene as an image volume).

Yes, if you have a custom folder structure then you can rearrange Slicer’s default output any way you want.

I would strongly recommend to use a 3D viewer to review segmentations. Very often you don’t notice segmentation errors (or may think that a valid segmentation is incorrect), unless you review the segmentation along at least 3 orthogonal axes. Even if you export 3 image stacks, you will have trouble reviewing the data, because it is hard to find corresponding slice views for a specific 3D position.

Latest Slicer Preview Release is fully portable: you can just copy the application files to a USB stick along with the images extensions and provide a batch file that launches Slicer and iterates through all the images with a convenient user interface (for example, using CaseIterator extension or the DICOM browser).

If you want to avoid your users to run a desktop application then you can export segmentation as DICOM segmentation object and review along with the images in a DICOMweb browser in your web browser using Kheops or using your cloud services or your own server.

Thanks Andras for your explanation. If you explain it, it makes sense to keep the folder empty.
And it is a useful suggestion to add the export to the scene!

I only have one more question: For the scans I use controller.rotateSliceToBackground() and when I add a .png to the scene, the image is not shown in one view, but is also rotated, so I have to scroll to see the entire image.
And If I rotate the .png to be shown in one view, the scan is also rotated.

Is it possible that the .png is not rotated when I import it to the scene?

Thanks for your help so far! Justin