Skip to main content

Mask Operations

Object Operations 📥 Download Script

Mask Operations Tutorial.

This tutorial demonstrates operations on segmentation masks including creation, thresholding, region growing, morphological operations, boolean operations, filtering, and conversion to surfaces.

Prerequisites
  • Volvicon application must be running
  • A volume loaded in the project for segmentation operations

Listing Masks​

all_masks = app.get_all_mask_names()
all_volumes = app.get_all_volume_names()
print(f"Available masks: {all_masks}")
print(f"Available volumes: {all_volumes}")

Importing and Exporting Masks​

# Supported formats: mha, mhd, nii, nii.gz, vti

# Import a single mask file
# mask_name = mask_operations.import_3d_mask_image_from_disk(r'C:\data\segmentation.mha')

# Import multiple mask files
# mask_names = mask_operations.import_3d_mask_images_from_disk([
# r'C:\data\mask1.mha',
# r'C:\data\mask2.nii.gz'
# ])

# Export a single mask to file
# mask_operations.export_mask_image_to_disk('Mask_1', r'C:\output\mask.mha')

# Export multiple masks to a directory.
# Returns True only when every listed mask was written, so the result is worth checking.
# if not mask_operations.export_mask_images_to_disk(['Mask_1', 'Mask_2'], r'C:\output', 'mha'):
# print("One or more masks could not be exported.")

Creating Masks​

# Create a blank mask with auto-assigned color
# new_mask = mask_operations.create_mask("New_Mask", True) # autoColor=True (bool: if True, assign a color automatically)

# Create a blank mask with specific RGB color (values 0.0-1.0)
# new_mask = mask_operations.create_mask("Red_Mask", False, [1.0, 0.0, 0.0]) # autoColor=False, rgb=[r,g,b] (0.0-1.0 floats)

Threshold Segmentation​

# Create mask by selecting voxels within intensity range

# if all_volumes:
# params = api.ThresholdParams()
# params.lower_threshold = 200 # Minimum intensity
# params.upper_threshold = 3000 # Maximum intensity
# params.fill_cavities = False
# params.filter_regions = False
#
# mask_name = mask_operations.threshold(all_volumes[0], params)
# print(f"Created threshold mask: {mask_name}")

# Threshold with region filtering
# params = api.ThresholdParams()
# params.lower_threshold = 200
# params.upper_threshold = 3000
# params.filter_regions = True
# params.keep_largest = True # Keep only largest region
# mask_name = mask_operations.threshold(all_volumes[0], params)

Region Growing Segmentation​

# Grow from seed points within an existing mask

# if all_volumes and all_masks:
# params = api.RegionGrowSegmentationParams()
# params.source_mask_name = all_masks[0]
# params.seeds = [[100.0, 100.0, 50.0]] # World coordinates
# params.multiple_layer = True # Apply to all slices (3D)
# params.fully_connected = False # 6-connectivity
#
# mask_name = mask_operations.region_grow(all_volumes[0], params)

Dynamic Region Growing​

# Connectivity-based (grows based on neighborhood statistics)
# if all_volumes:
# params = api.DynamicRegionGrowConnectivitySegmentationParams()
# params.seeds = [[100.0, 100.0, 50.0]]
# params.multiplier = 2.5
# params.number_of_iterations = 4
# params.initial_neighborhood_radius = 1
# params.fill_cavities = True
# params.multiple_layer = True
#
# mask_name = mask_operations.dynamic_region_grow_connectivity(all_volumes[0], params)

# Threshold-based (grows based on intensity range)
# if all_volumes:
# params = api.DynamicRegionGrowThresholdSegmentationParams()
# params.seeds = [[100.0, 100.0, 50.0]]
# params.lower_threshold = 100
# params.upper_threshold = 500
# params.fully_connected = False
# params.fill_cavities = True
# params.multiple_layer = True
#
# mask_name = mask_operations.dynamic_region_grow_threshold(all_volumes[0], params)

Morphological Operations​

# Available operations: Erode, Dilate, Open, Close

# if all_masks:
# # Dilate (expand mask)
# mask_operations.morphological_operation(
# [all_masks[0]],
# api.MorphologicalOperationType.Dilate,
# [2, 2, 2] # Ball radius in pixels [x, y, z]
# )
#
# # Erode (shrink mask)
# mask_operations.morphological_operation(
# [all_masks[0]],
# api.MorphologicalOperationType.Erode,
# [2, 2, 2]
# )
#
# # Open (erode then dilate - removes small protrusions)
# mask_operations.morphological_operation(
# [all_masks[0]],
# api.MorphologicalOperationType.Open,
# [2, 2, 2]
# )
#
# # Close (dilate then erode - fills small holes)
# mask_operations.morphological_operation(
# [all_masks[0]],
# api.MorphologicalOperationType.Close,
# [2, 2, 2]
# )

Boolean Operations​

# Combine masks using Union, Intersection, or Difference

# if len(all_masks) >= 2:
# # Union - combine both masks
# result = mask_operations.boolean(
# all_masks[0],
# [all_masks[1]],
# "", # Target name (not used when createNewMask=True)
# api.BooleanOperation.Union,
# True # createNewMask=True (bool: create a new mask instead of overwriting target)
# )
#
# # Intersection - keep only overlapping regions
# result = mask_operations.boolean(
# all_masks[0],
# [all_masks[1]],
# "",
# api.BooleanOperation.Intersection,
# True # createNewMask=True
# )
#
# # Difference - subtract second from first
# result = mask_operations.boolean(
# all_masks[0],
# [all_masks[1]],
# "",
# api.BooleanOperation.Difference,
# True # createNewMask=True
# )

Region Filtering​

# Keep largest regions or remove small regions

# if all_masks:
# # Keep only the 3 largest regions
# mask_operations.keep_largest_regions([all_masks[0]], 3) # numberOfLargestRegions=3 (int)
#
# # Filter by size (remove regions smaller than 100 voxels)
# mask_operations.filter_regions(
# [all_masks[0]],
# False, # keepLargestRegions=False (bool)
# 0, # numberOfLargestRegions=0 (int)
# 100 # minimumRegionSizeInVoxels=100 (int)
# )
#
# # Count regions in mask
# count = mask_operations.count_regions_in_mask(all_masks[0])
# print(f"Number of regions: {count}")

Cavity and Hole Filling​

# if all_masks:
# # Fill internal cavities
# mask_operations.cavity_fill([all_masks[0]], False) # fullyConnected=False (bool: 6 vs 26 connectivity)
#
# # Fill holes using iterative voting
# mask_operations.fill_holes([all_masks[0]], 5) # numberOfIterations=5 (int)

Grow and Shrink Masks​

# if all_masks:
# # Grow mask by 2mm
# mask_operations.grow_masks([all_masks[0]], 2.0) # marginSizeMM=2.0 (float in mm)
#
# # Shrink mask by 1mm
# mask_operations.shrink_masks([all_masks[0]], 1.0) # marginSizeMM=1.0 (float in mm)

Hollow Masks​

# if all_masks:
# # Create hollow shell inside surface
# mask_operations.hollow_masks(
# [all_masks[0]],
# api.HollowShellMode.InsideSurface,
# 2.0 # shellThicknessMM=2.0 (float in mm)
# )

Smoothing Operations​

# if all_masks:
# # Discrete Gaussian smoothing
# mask_operations.smooth_discrete_gaussian_filter([all_masks[0]], 1.0, 1.0, 1.0) # sigmaX, sigmaY, sigmaZ (float)
#
# # Median smoothing
# mask_operations.smooth_median_filter([all_masks[0]], 1, 1, 1) # radiusX, radiusY, radiusZ (int, pixels)
#
# # Mean smoothing
# mask_operations.smooth_mean_filter([all_masks[0]], 1, 1, 1) # radiusX, radiusY, radiusZ (int, pixels)

Multi-Label Masks​

# Check if mask is multi-label
# if all_masks:
# is_multilabel = mask_operations.is_multilabel_mask(all_masks[0])
# print(f"Is multi-label: {is_multilabel}")
#
# if is_multilabel:
# # Get label information
# labels = mask_operations.get_mask_labels(all_masks[0])
# for item in labels:
# label : api.MaskLabelInfo = item
# print(f"Label {label.value}: {label.annotation}")
#
# # Get label values
# label_values = mask_operations.get_mask_label_values(all_masks[0])
# print(f"Label values: {label_values}")

# Split multi-label mask into individual masks
# if all_masks:
# individual_masks = mask_operations.split_multi_label_mask(
# all_volumes[0],
# all_masks[0],
# False, # separateOnlyLargestLabels=False (bool)
# 0, # numLargestLabels=0 (int)
# False # removeActiveMask=False (bool)
# )

# Merge multiple masks into multi-label mask
# if len(all_masks) >= 2:
# merged = mask_operations.merge_into_multi_label_mask(
# all_masks[:3],
# "",
# True, # createNewMask=True (bool)
# False # removeInputMasks=False (bool)
# )

Convert Mask to Surface​

# if all_masks:
# params = api.MaskToSurfaceParams()
# params.resolution_reduction = False
# params.smoothing = True
# params.smooth_iterations = 10
# params.smooth_factor = 0.5
# params.triangle_reduction = True
# params.triangle_reduction_percent = 50
#
# surface_names = mask_operations.convert_to_surface_objects([all_masks[0]], params)
# print(f"Created surfaces: {surface_names}")

Other Operations​

# if all_masks:
# # Read binary mask voxel data for direct inspection.
# mask_data : api.MaskUint8 = mask_operations.get_mask_uint8(all_masks[0])
# print(f"Mask data dimensions: {mask_data.dimensions}")
#
# # Read label-preserving mask voxel data for multi-label masks.
# mask_label_data : api.MaskUint16 = mask_operations.get_mask_uint16(all_masks[0])
# print(f"Mask label data dimensions: {mask_label_data.dimensions}")
#
# # Get basic mesh information for the generated mask 3D preview.
# basic_surface_mesh_info : api.BasicSurfaceMeshInfo = mask_operations.get_basic_surface_mesh_info(all_masks[0])
# print(f"Preview vertices: {basic_surface_mesh_info.vertex_count}")
#
# # Invert mask
# mask_operations.invert_masks([all_masks[0]])
#
# # Binarize mask
# mask_operations.binarize([all_masks[0]])
#
# # Flip mask along axis (0=X, 1=Y, 2=Z)
# mask_operations.flip([all_masks[0]], 0, False) # axis=0 (0=X,1=Y,2=Z), aboutOrigin=False (bool)
#
# # Mirror mask across a custom plane
# mask_operations.mirror_to_plane(
# [all_masks[0]], # maskNames (list)
# [0.0, 0.0, 0.0], # planeOrigin [x, y, z] in mm
# [1.0, 0.0, 0.0] # planeNormal [x, y, z] (unit vector)
# )
#
# # Clear mask content
# mask_operations.clear_masks([all_masks[0]])
#
# # Crop mask to bounds [xMin, xMax, yMin, yMax, zMin, zMax]
# mask_operations.crop_mask(all_masks[0], [-50, 50, -50, 50, -30, 30])

print("Mask operations tutorial completed successfully.")