Giter Club home page Giter Club logo

slicerio's Introduction

Python package Upload Python Package PyPI version

slicerio

Python utilities for 3D Slicer interoperability.

The package contains utility functions for reading and writing segmentation files and convenience functions for using 3D Slicer via its web API. More functions will be added in the future.

Installation

Using pip:

pip install slicerio

Examples

Read segmentation and show some information about segments

import slicerio
import json

segmentation = slicerio.read_segmentation("path/to/Segmentation.seg.nrrd", skip_voxels=True)

number_of_segments = len(segmentation["segments"])
print(f"Number of segments: {number_of_segments}")

segment_names = slicerio.segment_names(segmentation)
print(f"Segment names: {', '.join(segment_names)}")

segment0 = slicerio.segment_from_name(segmentation, segment_names[0])
print("First segment info:\n" + json.dumps(segment0, sort_keys=False, indent=4))

Extract selected segments with chosen label values

Extract segments by terminology

Example for getting a 3D NRRD file that has label values assigned based on standard terminology codes. Terminology is a dict that must specify category and type codes and may optionally also specify typeModifier, anatomicRegion, and anatomicRegionModifier. Each code is specifed by a triplet of "coding scheme designator", "code value", "code meaning" in a list.

Coding scheme designator is typically SCT (SNOMED-CT) for clinical images. You can find codes in the SNOMED-CT browser. When code exists for "entire X" and "structure of X" then always use the "structure" code ("entire" code has a very strict meaning that is rarely applicable in practice).

Code meaning (third component of codes, such as "Anatomical Structure", "Ribs", "Right") is informational only, it can be used for troubleshooting or displayed to the user, but it is ignored in information processing (e.g., two codes match if their coding scheme designator and code value are the same even if code meaning is different).

import slicerio
import nrrd

input_filename = "path/to/Segmentation.seg.nrrd"
output_filename = "path/to/SegmentationExtracted.seg.nrrd"
segments_to_labels = [
   ({"category": ["SCT", "123037004", "Anatomical Structure"], "type": ["SCT", "113197003", "Ribs"]}, 1),
   ({"category": ["SCT", "123037004", "Anatomical Structure"], "type": ["SCT", "39607008", "Lung"], "typeModifier": ["SCT", "24028007", "Right"]}, 3)
   ]

segmentation = slicerio.read_segmentation(input_filename)
extracted_segmentation = slicerio.extract_segments(segmentation, segments_to_labels)
slicerio.write_segmentation(extracted_segmentation, output_filename)

Extract segments by name

It is strongly recommended to look up segments by standard terminology codes instead of segment name, as spelling errors and inconsistent use of names often causes mismatch.

import slicerio
import nrrd

input_filename = "path/to/Segmentation.seg.nrrd"
output_filename = "path/to/SegmentationExtracted.seg.nrrd"
segment_names_to_labels = [("ribs", 10), ("right lung", 12), ("left lung", 6)]

segmentation = slicerio.read_segmentation(input_filename)
extracted_segmentation = slicerio.extract_segments(segmentation, segment_names_to_labels)
slicerio.write_segmentation(extracted_segmentation, output_filename)

Create segmentation file from numpy array

# Create segmentation with two labels (1, 3)
voxels = np.zeros([100, 120, 150])
voxels[30:50, 20:60, 70:100] = 1
voxels[70:90, 80:110, 60:110] = 3

# Image geometry
spacing = [0.5, 0.5, 0.8]
origin = [10, 30, 15]

segmentation = {
   "voxels": voxels,
   "image": {
      "encoding": "gzip",
      "ijkToLPS": [[ spacing[0], 0., 0., origin[0]],
                   [ 0., spacing[1], 0., origin[1]],
                   [ 0., 0., spacing[2], origin[2]],
                   [ 0., 0., 0., 1. ]]
   },
   "segmentation": {
      "containedRepresentationNames": ["Binary labelmap", "Closed surface"],
      # "masterRepresentation": "Binary labelmap",
      # "referenceImageExtentOffset": [0, 0, 0],
   },
   "segments": [
      {
         "id": "Segment_1",
         "labelValue": 1,
         "layer": 0,
         "color": [0.9, 0.9, 0.6],
         "name": "ribs",
         "terminology": {
            "contextName": "Segmentation category and type - 3D Slicer General Anatomy list",
            "category": ["SCT", "123037004", "Anatomical Structure"],
            "type": ["SCT", "113197003", "Rib"] }
      },
      {
         "id": "Segment_2",
         "labelValue": 3,
         "layer": 0,
         "color": [0.9, 0.9, 0.6],
         "name": "spine",
         "status": "inprogress",
         "terminology": {
            "contextName": "Segmentation category and type - 3D Slicer General Anatomy list",
            "category": ["SCT", "123037004", "Anatomical Structure"],
            "type": ["SCT", "122494005", "Cervical spine"] }
      },
   ]
}

slicerio.write_segmentation(segmentation, "path/to/Segmentation.seg.nrrd")

View files in 3D Slicer

The server module allows using Slicer as a data viewer in any Python environment. All files are loaded into a single Slicer instance, which eliminates the wait time for application startup and also allows analyzing, comparing multiple data sets in one workspace. The feature is implemented by using 3D Slicer's built-in Web Server module, which offers data access via a REST API.

For example, an image file can be loaded with the command below. The command starts a new Slicer application instance with the web API enabled.

import os
import slicerio.server

# Load from remote URL
slicerio.server.file_load("https://github.com/rbumm/SlicerLungCTAnalyzer/releases/download/SampleData/LungCTAnalyzerChestCT.nrrd")

# Load from local file
# A Slicer application instance (with Web Server enabled) is automatically started, if it is not running already.
slicerio.server.file_load("path/to/SomeImage.nrrd", slicer_executable=f"{os.environ["LOCALAPPDATA"]}/NA-MIC/Slicer 5.2.0/Slicer.exe")

A segmentation file can be loaded by specifying the SegmentationFile file type:

nodeID = slicerio.server.file_load("path/to/Segmentation.seg.nrrd", "SegmentationFile")

If the loaded file is modified then it can be reloaded from the updated file:

slicerio.server.node_reload(id=nodeID)

Supported file types

  • image files (nrrd, nii.gz, ...): VolumeFile
  • segmentation file (.seg.nrrd, nrrd, nii.gz, ...): SegmentationFile
  • model file (.stl, .ply, .vtk, .vtp, .vtu, ...): ModelFile
  • markup file (.mrj.json): MarkupsFile
  • transform file (.tfm, .h5, .txt): TransformFile
  • spreadsheet file (.csv, .tsv): TableFile
  • text file (.txt, .json, ...): TextFile
  • sequence file (.mrb, .seq.nrrd): SequenceFile
  • Slicer scene file (.mrml, .mrb): SceneFile

Inspect data in 3D Slicer

Metadata of data sets loaded into the server can be obtained using node_properties function:

properties= slicerio.server.node_properties(name="MRHead")[0]
print(properties["ClassName"])
print(properties["ImageData"]["Extent"])

properties = slicerio.server.node_properties(id=segmentationId)[0]
segments = properties["Segmentation"]["Segments"]
for segmentId in segments:
   print(f"Segment name: {segments[segmentId]['Name']} - color: {segments[segmentId]['Color']}")

List of available nodes can be retrieved using node_names and node_idsfunctions:

# Retreve node names of all images
slicerio.server.node_names(class_name="vtkMRMLVolumeNode")

# Retrieve all node IDs
slicerio.server.node_ids(class_name="vtkMRMLVolumeNode")

Nodes can be removed from the workspace:

# Remove node by name
slicerio.server.node_remove(name="MRHead")

# Clear the whole scene
slicerio.server.node_remove()

Export files from 3D Slicer

Data sets created in Slicer (e.g., segmentations, landmark point sets), which can be retrieved by writing into file.

# Save the node identified by `MRHead` node name, uncompressed, into the specified file.
slicerio.server.file_save("c:/tmp/MRHeadSaved.nrrd", name="MRHead", properties={'useCompression': False})

slicerio's People

Contributors

johannesu avatar lassoan avatar vanossj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

slicerio's Issues

Make some segmentation fields optional

I would like to use the 3DSlicer nrrd segmentation schema by adding some tags to the nrrd header.

Slicer is perfectly happy to load .seg.nrrd files that only have Color, Name, LabelValue, ID, Layer. However slicerio won't load these files if they don't contain all the metadata like Extent, ColorAutoGenerated , etc.

What is the minimum required subset of segmentation fields and does it make sense to allow slicerio to ignore missing fields if they are not required by 3DSlicer.

`slicerio.server.node_remove` removes nodes other than specified

Running the below example reproduces the issue in the title.

import slicerio.server

path_to_mesh = "..."

mesh_id0, = slicerio.server.file_load(
    file_path=path_to_mesh,
    file_type='ModelFile'
)
print(mesh_id0)

mesh_id1, = slicerio.server.file_load(
    file_path=path_to_mesh,
    file_type='ModelFile'
)
print(mesh_id1)

result = slicerio.server.node_ids(class_name='vtkMRMLModelNode')
print(result)

result = slicerio.server.node_remove(id=mesh_id1)
print(result)

result = slicerio.server.node_ids(class_name='vtkMRMLModelNode')
print(result)
print(mesh_id0 in result)

Output:

vtkMRMLModelNode4
vtkMRMLModelNode5
['vtkMRMLModelNode1', 'vtkMRMLModelNode2', 'vtkMRMLModelNode3', 'vtkMRMLModelNode4', 'vtkMRMLModelNode5']
None
['vtkMRMLModelNode1', 'vtkMRMLModelNode2', 'vtkMRMLModelNode3']
False

Some details:
Slicer version: 5.1.0-2022-11-10 r31276 / 38d8d99
OS: Windows 10 64-bit

Util for creating a `.seg.nrrd` based on existing nrrd + segment names

Maybe that's something obvious but what I just needed was a simple tool to name the segments in nrrd.

It turned out that this works to create a valid seg.nrrd for Slicer:

from pathlib import Path
import nrrd

def add_segments(nrrd_data, nrrd_header, segment_names):
    assert len(segment_names) == nrrd_data.shape[0]
    for segment_index, segment_name in enumerate(segment_names):
        prefix = "Segment{0}_".format(segment_index)
        nrrd_header[prefix + "ID"]=str(segment_index)
        nrrd_header[prefix + "Name"]=segment_name
        nrrd_header[prefix + "ColorAutoGenerated"]=1
        nrrd_header[prefix + "LabelValue"]=1
        nrrd_header[prefix + "Layer"]=segment_index
    return nrrd_header

def add_segments_to_nrrd(nrrd_input_path, segment_names):
    nrrd_input_path = Path(nrrd_input_path)
    nrrd_output_path = nrrd_input_path.with_suffix(".seg.nrrd")
    
    nrrd_data, nrrd_header = nrrd.read(nrrd_input_path)
    new_header = add_segments(nrrd_data, nrrd_header, segment_names)
    with open(nrrd_output_path, "wb") as f:
        nrrd.write(f, nrrd_data, new_header)

Leaving it here for others finding the same need. Would this be something worth adding in this or extended way to slicerio ?

`file_load` support for organizing loaded object

Hello,

Is it possible to provide a parent item for an object loaded via file_load? For example, it would be useful to create a markup directory and load a markup (generated by an external program) into that directory. I see slicerio.server.file_load accepts a properties kwarg, but the only documented use case is providing name.

Thanks!

Support for sliceomatic

The sandbox extension provides support for reading sliceomati tag files
Any possibility of including .tag file support here

Support for loading or saving a `.mrml`

Is there a way to load and save the .mrml?

For example:

slicerio.server.start_server()
slicerio.server.mrml_load(...)  # this
slicerio.server.file_load(...)
slicerio.server.mrml_save(...)  # and this
slicerio.server.stop_server()

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.