Writing Algorithms
Custom Algorithms can be used both in the Annotation or the Experiments tabs. An algorithm is a function to be defined in a Python script, with the following signature:
def algorithm(
image: imfusion.SharedImageSet,
labels: imfusion.SharedImageSet,
descriptor: imfusion.labels.DataDescriptor
) -> imfusion.SharedImageSet | None
where image
is the currently loaded image, labels
the label map of the currently active layer and descriptor
the imfusion.labels.DataDescriptor
of the currently loaded dataset.
The function is expected to return an imfusion.SharedImageSet
with the same shape of labels
, encoded as unsigned bytes.
If it returns None, the label map is considered to be unchanged (i.e. the “Latest Edit Time” is not changed and no undo step is generated).
Note
For more information about the data structures used in the ImFusion SDK, please refer to the documentation of the imfusion Python module itself.
Example 1: Clear labels
This example removes all labels from a label map, similar to the Erase action.
import imfusion as imf
import numpy as np
def algorithm(image, labels):
# Convert ImFusion SharedImageSet to standard Numpy array with the following shape
# (num_frames, slices, height, width, channels)
image_array = np.array(image)
labels_array = np.array(labels)
# Create zero labels
labels_array = 0 * labels_array
# Create a set of labels from the numpy array
new_labels = imf.SharedImageSet(labels_array)
return new_labels
Label Map structure
The above example works because 0 is always the background label. However, more practical scripts will also involve setting labels other than the background. therefore, it is important to understand how label maps are structured.
A label map in ImFusion Labels is stored as an 8-bit image. Each pixel in the label map contains an index that identifies a label. Those indices are defined in the project.
For example a project could define two labels: one for “Bone” with an index of 1 and one for “Soft Tissue” with an index of 2. Inside the label map, every pixel with a value of 1 is therefore related to the “Bone” label, while every pixel with a value of 2 to the “Soft Tissue” label.
Because each pixel can only contain one index, labels cannot overlap.
That is a pixel cannot be both “Bone” and “Soft Tissue” in the above example.
Overlapping structures can be realized by using different layers.
Each layer has a separate label map.
The algorithm
function only receives the currently active layer, but other layers can be accessed through the descriptor
.
The descriptor
can also be used to retrieve the label indices as defined by the project.
This is demonstrated with the next example.
Example 2: Threshold the image
The following example thresholds the input image and uses the result as the new label map. This example can also be seen as a blueprint for scripts with a more sophisticated method than thresholding for initializing label maps.
import imfusion as imf
import numpy as np
def algorithm(image, labels, descriptor):
# Create empty container for output label map
# by cloning only the meta-data but not the pixel data
new_labels = labels.clone(withData=False)
# Get the indices for the labels that should be changed.
# This assumes that the layer has at least one label defined.
# Alternatively, the label name could also be used, e.g.
# forground_label = active_layer.annotations["Bone"].value
active_layer = descriptor.labelmap_layers.active
forground_label = active_layer.annotations[1].value
# imf.SharedImageSet can contain multiple images.
# For demo purposes, this example works on the individual frames.
for image_frame, labels_frame in zip(image, labels):
# Get current image and turn into grayscale
image_array = np.array(image_frame)
image_grayscale = np.mean(image_array, -1)
mean_intensity = np.mean(image_grayscale[:])
# Create label map with 0 everywhere
new_labels_array = np.zeros(labels_frame.shape, dtype=np.ubyte)
# Set the new labels values based on a threshold
new_labels_array[image_grayscale > mean_intensity] = forground_label
# Add label map to the container
new_labels.add(imf.SharedImage(new_labels_array))
return new_labels
Example 3: Set a tag
This example sets a tag without modifing the label map. It assumes that the project defines a true/false tag with a name of “Healthy”.
def algorithm(image, labels, descriptor):
descriptor.tags["Healthy"] = True
return None
For further questions, please get in touch with us via the ImFusion Support Forum.
Note
To run an algorithm on all datasets see Python API.