Zayd Vanderson
Jan 03, 2025

Unlock the Potential of 3D Slicer on Windows: Batch Processing Imaging Data with Sonador

3D Slicer is a powerful medical imaging tool, excelling as both an interactive interface and a batch processing platform. While batch workflows run seamlessly on Linux, Windows introduces challenges like redirected logging. This article provides a PowerShell wrapper for Slicer.exe and script structure which can be used to run batch processing scripts on Windows with the same behavior as Linux or Mac OS X.

In medical imaging, the ability to efficiently process and analyze large volumes of data is an essential need. 3D Slicer is an open-source desktop program that enables clinicians and researchers to visualize, segment, and analyze complex imaging data from a large variety of sources. It excels both as an interface for visualization and a robust batch processing tool.

One of Slicer's standout capabilities is its ability to create DICOM-SEG files (DICOM-SEG is a standards based way to describe image masks), enabling broad compatibility and seamless integration within the open-source imaging ecosystem.

Sonador IO Extension for Slicer 0.1
3D Slicer is a scientific imaging platform which supports visualization, segmentation, registration, and advanced analysis of biomedical data.

Sonador is a cloud platform which provides secure storage, large scale processing, and collaboration capabilities which extends the reach of imaging tools beyond the desktop. When used together with Slicer, Sonador enables a number of advanced workflows for advanced analysis and simulation, 3D-printed patient specific instruments, and robotically executed interventions.

To harness the full potential of both Slicer and Sonador, the ability to exchange data is essential. Designed as a set of microservices which enable client programs to retrieve imaging meta and binary data, Sonador provides a set of libraries which make it easy to integrate with embedded systems, cloud solutions, and desktop applications (including 3D Slicer itself). As a cloud compatible system, Sonador is able to store and manage large medical datasets beyond what can be stored or processed in a desktop environment.

The Sonador project provides an integration to enable the exchange of data between Sonador and Slicer. Taking advantage of Slicer's Python API, the integration allows for data to be retrieved from the cloud, processed using Slicer's advanced features, and for the results to be uploaded back to Sonador's medical imaging database.

Slicer's batch processing interface, along with the Sonador extension, can be combined to automate repetitive operations; enabling efficient handling of large datasets without manual intervention. This helps to streamline tasks such image segmentation, registration, and data conversion.

Despite the powerful capabilities of 3D Slicer and the advantages of batch processing, users on the Windows platform encounter challenges with headless processing. Unlike on Linux and other Unix like environments (where Slicer's command line and batch interface runs without issue), users on Windows may encounter problems with logging being redirected and face limitations in passing arguments to the Slicer.exe CLI application. These issues can hinder the development, execution, and troubleshooting of automated scripts.

Slicer provides a near-universal toolkit for working with complex data. It is able to read and write data from a huge cross-section of tools and can create DICOM-SEG files which can be read by SimpleITK, OHIF, VTK, and nearly every library in the open source imaging ecosystem. Almost no other tool has such broad compatibility.

Slicer Challenges on Windows

The root of the issue on Windows is how Slicer is built for the platform. On Windows, 3D Slicer's application launcher (Slicer.exe) is built as a Windows GUI application rather than a console application. This design choice is intentional, as it prevents the automatic opening of an additional console window during startup (which can be disruptive in a graphical desktop environment).

The main application launcher, Slicer.exe, acts as a wrapper responsible for initializing the environment (such as setting up paths and configurations) and launching the main application binary: SlicerApp-real.exe. SlicerApp-real.exe is the "real" application that executes the parsing of options and core functionality.

While this design achieves a cleaner startup experience for users interacting with Slicer through its graphical interface, it introduces significant challenges for batch processing and automation. Since Slicer.exe is a GUI application, it does not produce standard console output (stdout ) or error output (stderr). Unfortunately, this behavior extends to the launched application (SlicerApp-real.exe), as its output is similarly suppressed during execution unless explicitly captured. This behavior contrasts sharply Slicer on Linux, where the application operates as a console application, making stdout and stderr available.

Addressing this challenge requires specialized handling of the output streams on Windows to ensure they can be captured into the session log.

Creating a Custom Launcher: Slicer-App.ps1

It is possible to work around these limitations by creating a custom launcher script. The example below (written in PowerShell) can be added to a user's path to capture command-line arguments and launch Slicer.exe with explicit redirection to the console.

The script:

  1. Creates a custom output function Slicer-Output that can direct the application logs to the console. The function provides functionality similar to the PowerScript built-in tee function (and works similarly to the console redirection example in Slicer's "Tips and Tricks" documentation), but does not require a file input.
  2. Dynamically locates Slicer.exe if it exists in the user's path, and provides a configurable environment variable (SLICER_PATH) which can be used to specify the location of the binary if it cannot be located.
  3. Captures and redirects stdout and stderr to a single output stream, ensuring all messages are visible in the console.
# PowerShell commandlet which can be used to emulate "bash-like" behavior
# when running Slicer commands from the CLI in Windows.
# 1. Provides a method for directing application output to the console
# 2. Able to dynamically locate Slicer.exe (if it's part of the path)
#    and provides an environment variable to specify the path if it is not.
# 3. Handles direction of stdout and stderr to a single output stream to make
#    logging more streamlined.


function Slicer-Output {
    param (
        [Parameter(ValueFromPipeline = $true)]
        $InputObject
    )
    process {
        Write-Host $InputObject
    }
}


# Function to locate the Slicer executable
function Get-SlicerPath {
    # Check if the environment variable SLICER_PATH is set
    $slicerPathEnv = [Environment]::GetEnvironmentVariable("SLICER_PATH")
    if ($slicerPathEnv -and (Test-Path $slicerPathEnv)) {
        return $slicerPathEnv
    }

    # Check if Slicer is in the global PATH
    $slicerCommand = Get-Command Slicer.exe -ErrorAction SilentlyContinue
    if ($slicerCommand) {
        return $slicerCommand.Source
    }

    # If not found, return null
    return $null
}


# Get the Slicer executable path
$SlicerPath = Get-SlicerPath


# Validate that Slicer was found
if (-not $SlicerPath) {
    Write-Error "Error: Could not locate the Slicer executable.
    Ensure that:
    1. The SLICER_PATH environment variable is set to the full path of Slicer.exe, OR
    2. Slicer is available in the global PATH.
    "
    exit 1
}


# Collect and pass all arguments as an array
$ArgsList = $args

# Run Slicer in headless mode, passing all arguments, and redirect output to stdout via
# custom output method from above
& Slicer $ArgsList 2>&1 | Slicer-Output

To use the script, save the contents of the listings above to a file within the path of the user as Slicer-App.ps1.

Running Python Code and Scripts in Slicer

Headless Slicer commands can be executed by providing a Python script or a Python code snippet to Slicer-App.ps1 to be executed (along with the desired options). For example:

Slicer-App.ps1 --no-main-window --python-code "print('Hello world!'); exit();"

The --no-main-window option prevents Slicer from opening an application window, and the --python-code option provides a Python snippet for Slicer to execute. exit() is called explicitly in the snippet to prevent the application from hanging.

It is possible to provide Slicer with a Python script to execute using the --python-script option:

Slicer-App.ps1 --no-main-window --python-script ./test.py

Example Python Script for Slicer

3D Slicer is primarily implemented as an interface for visualization and interactively executing workflows. For that reason, it is sometimes necessary to bridge the world of headless batch processing and GUI conventions. The following script example demonstrates three components essential for headless scripts in Slicer:

  1. Retrieving Connection Credentials to enable cloud communication. The script uses helper methods from the Sonador IO extension for Slicer to retrieve credentials configured via the Slicer UI and stored in the system keyring.
  2. Capturing logging events and exporting those to the system console.
  3. Error handling and script structure. The script follows a structured approach to handle errors using try/except/finally blocks, ensuring cleanup and exit.
import slicer, logging, sonador, traceback

# Sonador libraries for retrieving connection credentials and to configure logging
from sonadorqt.base import fetch_sonador_credentials, fetch_sonador_connection
from sonador_ext.scripts import configure_script_logging

# Configure logging to stdout for the script
configure_script_logging(level='info')
logger = logging.getLogger(__name__)


try:
    
    # Retrieve Sonador credentials
    _conn = fetch_sonador_connection()
    logger.info('Hello Sonador batch processing! Sonador server:\n%s' % (
        _conn.url
    ))

except Exception as err:

    # Log any exceptions along with a traceback
    logger.error('Unable to complete batch processing because of an error. Error: "%s"\n%s' % (
        err, traceback.format_exc(),
    ))

finally:

    # Explicit exit the application after all data has been processed. The Slicer 
    # process will hang without an explicit exit, which is undesirable for batch scripts.
    exit()

Scripts based on the template above are intended to be executed using Slicer-App.ps1, and can be passed via the --python-script option. Example:

Slicer-App.ps1 --no-main-window --python-script ./hello-slicer.py
Directing Application Events to the System Console

The capture and export of logging events within 3D Slicer scripts is an important piece of functionality for tracking script progress and troubleshooting issues. This is particularly true given that Slicer workflows may span numerous modules, libraries, and integrations.

By default, Slicer's logging capabilities help to aggregates outputs from C++ components, third-party libraries, and Python scripted components into an internal console which are displayed in its own GUI controls. While this is effective for interactive usage, however, it creates challenges for headless batch processing where the GUI is inaccessible.

To ensure that all application output is available in the system log, the example script above uses the configure_script_logging helper method from the Sonador IO extension. This method can be used to augment the internal Slicer logging with output to the system console (via a logging.StreamHandler instance) or by providing other Python logging instances to output the application log to a file or external storage. Refer to the Python logging documentation for additional detail about "log handlers."

If no handler instances are provided to configure_script_logging, it will create a logging.StreamHandler instance and associate with sys.stdout. The listing below shows how a file handler could also be included.

# Logging and system packages to create handlers
import sys, logging

# Script
from sonador_ext.scripts import configure_script_logging

# Output file for the script
outfile_path = '/path/to/file'

# Create stream and file handlers for script handling
syslog_stream_handler = logging.StreamHandler(sys.log)
outfile_handler = logging.FileHandler(outfile_path)

# Configure script logging with both stream and file handlers
configure_script_logging(level='info', handlers=[
    syslog_stream_handler, outfile_handler
])

Enhancing Batch Processing in 3D Slicer for Windows Users

Batch processing in 3D Slicer on Windows introduces unique challenges, particularly due to the platform’s GUI-focused design and limitations in logging output when running headless. This article provided two key solutions to bridge this gap for Windows users: a PowerShell-based custom launcher to redirect logs to the system console and a Python script template designed for structured batch workflows.

The custom launcher ensures complete visibility of application logs, addressing Windows-specific limitations, while the Python template provides a foundation for creating scripts with robust error handling, enhanced logging with configure_script_logging, and integration with Slicer and Sonador's systems for managing credentials. By addressing these obstacles, Windows users will be able to fully leverage 3D Slicer’s capabilities in automated, headless workflows, bringing its batch processing potential in line with the seamless experience available on Linux and Mac platforms.

Zayd Vanderson Jan 03, 2025
More Articles by Zayd Vanderson

Loading

Unable to find related content

Comments

Loading
Unable to retrieve data due to an error
Retry
No results found
Back to All Comments