Skip to main content

Volume Operations

Object Operations 📥 Download Script

Volume Operations Tutorial.

This tutorial demonstrates operations on 3D volume images including import, export, filtering, resampling, cropping, and transformation.

Prerequisites
  • Volvicon application must be running
  • Sample volume files for import examples

Listing Volumes​

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

Importing Volumes​

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

# Import single volume
# volume_name = VolumeOperations.import_3d_image_from_disk(r'C:\data\ct_scan.mha')
# print(f"Imported volume: {volume_name}")

# Import multiple volumes
# volume_names = VolumeOperations.import_3d_images_from_disk([
# r'C:\data\volume1.mha',
# r'C:\data\volume2.nii.gz'
# ])

Exporting Volumes​

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

# Export single volume to file
# VolumeOperations.export_volume_image_to_disk('Volume_1', r'C:\output\volume.mha')

# Export multiple volumes to a directory
# VolumeOperations.export_volume_images_to_disk(['Volume_1', 'Volume_2'], r'C:\output', 'mha')

Creating Blank Volumes​

# Create a blank volume with specified properties

# volume_name = VolumeOperations.create_blank_volume(
# "New_Volume",
# [256, 256, 128], # Dimensions [x, y, z] in voxels
# [0.5, 0.5, 1.0], # Spacing [x, y, z] in mm
# api.VoxelDataType.Unsigned8Bit
# )

Volume Properties​

if all_volumes:
vol = all_volumes[0]

# Get dimensions
dims = VolumeOperations.get_dimensions(vol)
print(f"Dimensions: {dims[0]} x {dims[1]} x {dims[2]} voxels")

# Get spacing
spacing = VolumeOperations.get_spacing(vol)
print(f"Spacing: {spacing[0]:.4f} x {spacing[1]:.4f} x {spacing[2]:.4f} mm")

# Get origin
origin = VolumeOperations.get_origin(vol)
print(f"Origin: ({origin[0]:.2f}, {origin[1]:.2f}, {origin[2]:.2f}) mm")

# Get scalar range
scalar_range = VolumeOperations.get_scalar_range(vol)
print(f"Intensity range: {scalar_range[0]} to {scalar_range[1]}")

# Get gray value at voxel index
# gray_values = VolumeOperations.get_gray_value_at_voxel(vol, [128, 128, 64])
# print(f"Gray value at [128,128,64]: {gray_values}")

# Get gray value at world coordinates
# gray_values = VolumeOperations.get_gray_value_at_world_coordinates(vol, [0.0, 0.0, 0.0])
# print(f"Gray value at origin: {gray_values}")

Spatial Transformations​

# if all_volumes:
# # Center volume at origin
# VolumeOperations.center_at_origin([all_volumes[0]])
#
# # Change spacing
# VolumeOperations.change_spacing([all_volumes[0]], [1.0, 1.0, 1.0])
#
# # Change origin
# VolumeOperations.change_origin([all_volumes[0]], [0.0, 0.0, 0.0])
#
# # Translate origin
# VolumeOperations.translate_origin([all_volumes[0]], [10.0, 10.0, 0.0])
#
# # Reorient (rotate around axis)
# VolumeOperations.reorient(
# [all_volumes[0]],
# 45.0, # angle (degrees)
# [0.0, 0.0, 1.0], # axis (vector)
# api.Interpolation.Linear, # volumeInterpolation
# api.Interpolation.Nearest # maskInterpolation
# )
#
# # Flip along axis
# VolumeOperations.flip(
# [all_volumes[0]],
# api.FlipAxis.X, # axis (api.FlipAxis.X, api.FlipAxis.Y, or api.FlipAxis.Z)
# False # aboutOrigin (bool)
# )
#
# # Swap (permute) two spatial axes
# # Useful when data is imported with axes in the wrong order
# VolumeOperations.swap_axes(
# [all_volumes[0]],
# api.SwapAxesPair.XY # pair: api.SwapAxesPair.XY, api.SwapAxesPair.XZ, or api.SwapAxesPair.YZ
# )
#
# # Swap multiple volumes at once
# # VolumeOperations.swap_axes(all_volumes, api.SwapAxesPair.YZ)

Cropping and Padding​

# if all_volumes:
# # Crop to bounds [xMin, xMax, yMin, yMax, zMin, zMax] in mm
# VolumeOperations.crop([all_volumes[0]], [-50.0, 50.0, -50.0, 50.0, -30.0, 30.0])
#
# # Pad project's image data to ensure surfaces/masks remain in bounds after mirroring
# # Get surface and mask names to consider
# all_surfaces = app.get_all_surface_names()
# all_masks = app.get_all_mask_names()
# VolumeOperations.pad(
# all_volumes[0], # volumeName (str)
# all_surfaces, # surfaceNames (list)
# all_masks, # 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)
# )
#
# # Shrink to bounds (bounds can extend outside original)
# VolumeOperations.shrink([all_volumes[0]], [-100.0, 100.0, -100.0, 100.0, -50.0, 50.0])
#
# # Add padding (in voxels)
# VolumeOperations.pad(
# [all_volumes[0]],
# [10, 10, 5], # Lower bounds padding [x, y, z]
# [10, 10, 5] # Upper bounds padding [x, y, z]
# )

Resampling​

# if all_volumes:
# VolumeOperations.resample(
# [all_volumes[0]],
# [512, 512, 256], # Target dimensions
# [0.5, 0.5, 0.5], # Target spacing in mm
# api.Interpolation.Linear, # volumeInterpolation
# api.Interpolation.Nearest # maskInterpolation
# )

Smoothing Filters​

# if all_volumes:
# # Gaussian filter
# VolumeOperations.gaussian_filter(
# [all_volumes[0]],
# 1.0, # stdDev (float)
# 3.0, # radiusFactor (float)
# True # threeDimensional (bool)
# )
#
# # Discrete Gaussian filter
# VolumeOperations.discrete_gaussian_filter(
# [all_volumes[0]],
# [1.0, 1.0, 1.0], # sigma (list of floats for X,Y,Z)
# False # useImageSpacing=False (bool: True=mm, False=pixels)
# )
#
# # Recursive Gaussian filter (efficient for large kernels)
# VolumeOperations.recursive_gaussian_filter(
# [all_volumes[0]],
# [1.0, 1.0, 1.0] # sigma (list of floats for X,Y,Z)
# )
#
# # Mean filter
# VolumeOperations.mean_filter([all_volumes[0]], [1, 1, 1]) # radius [x,y,z] in pixels (ints)
#
# # Median filter
# VolumeOperations.median_filter([all_volumes[0]], [1, 1, 1]) # radius [x,y,z] in pixels (ints)
#
# # Bilateral filter (edge-preserving)
# VolumeOperations.bilateral_filter(
# [all_volumes[0]],
# 2.0, # domainSigma (float)
# 50.0, # rangeSigma (float)
# 2 # kernelRadius (int)
# )
#
# # Binomial blur (fast approximation to Gaussian)
# VolumeOperations.binomial_blur_filter([all_volumes[0]], 5) # iterations=5 (int)

Edge-Preserving Smoothing​

# if all_volumes:
# # Curvature anisotropic diffusion
# VolumeOperations.curvature_anisotropic_diffusion_filter(
# [all_volumes[0]],
# 5, # iterations (int)
# 0.0625, # timeStep (float)
# 3.0 # conductance (float)
# )
#
# # Gradient anisotropic diffusion
# VolumeOperations.gradient_anisotropic_diffusion_filter(
# [all_volumes[0]],
# 5, # iterations (int)
# 0.0625, # timeStep (float)
# 3.0 # conductance (float)
# )
#
# # Curvature flow
# VolumeOperations.curvature_flow_filter(
# [all_volumes[0]],
# 10, # iterations (int)
# 0.0625 # timeStep (float)
# )
#
# # Min-max curvature flow
# VolumeOperations.min_max_curvature_flow_filter(
# [all_volumes[0]],
# 10, # iterations (int)
# 0.0625, # timeStep (float)
# 1 # stencilRadius (int)
# )
#
# # Patch-based denoising (non-local means)
# VolumeOperations.patch_based_denoising_filter(
# [all_volumes[0]],
# 4.0, # patchRadius (float)
# "Gaussian", # noiseModel (string)
# 1, # iterations (int)
# 0.1 # fidelityWeight (float)
# )

Edge Enhancement​

# if all_volumes:
# # Laplacian edge sharpening
# VolumeOperations.laplacian_edge_sharpening([all_volumes[0]])
#
# # Convolution-based edge sharpening
# VolumeOperations.convolution_based_edge_sharpening([all_volumes[0]])
#
# # Gradient magnitude
# VolumeOperations.gradient_magnitude_filter([all_volumes[0]])
#
# # Gradient magnitude with recursive Gaussian
# VolumeOperations.gradient_magnitude_recursive_gaussian_filter([all_volumes[0]], 1.0) # sigma (float)
#
# # Butterworth high-pass filter
# VolumeOperations.butterworth_high_pass_filter([all_volumes[0]], 0.1, 0.1) # xCutOff, yCutOff (float)

Intensity Transformations​

# if all_volumes:
# # Rescale intensity to range
# VolumeOperations.rescale_intensity_filter([all_volumes[0]], 0, 255) # minIntensity, maxIntensity (int)
#
# # Normalize (zero mean, unit variance)
# VolumeOperations.normalize_filter([all_volumes[0]])
#
# # Invert intensity
# VolumeOperations.invert_intensity([all_volumes[0]])
#
# # Sigmoid transformation
# VolumeOperations.sigmoid_filter(
# [all_volumes[0]],
# 0, # outputMin (int)
# 255, # outputMax (int)
# 100, # alpha (float: offset)
# 10 # beta (float: steepness)
# )

Morphological Operations​

# if all_volumes:
# # Available: Erode, Dilate, Open, Close
# VolumeOperations.morphological_operation(
# [all_volumes[0]],
# api.MorphologicalOperation.Dilate,
# [2, 2, 2] # Ball radius in pixels
# )

Distance Maps​

# if all_volumes:
# # Danielsson distance map (unsigned)
# VolumeOperations.danielsson_distance_map_filter([all_volumes[0]])
#
# # Signed Danielsson distance map
# VolumeOperations.signed_danielsson_distance_map_filter([all_volumes[0]])
#
# # Signed Maurer distance map
# VolumeOperations.signed_maurer_distance_map_filter([all_volumes[0]])

Regional Analysis​

# if all_volumes:
# # Extract regional minima
# VolumeOperations.regional_minima_filter([all_volumes[0]])
#
# # Extract regional maxima
# VolumeOperations.regional_maxima_filter([all_volumes[0]])

Combining Volumes​

# if len(all_volumes) >= 2:
# # Combine operations: Addition, Subtraction, Multiplication, Division, Mean, Minimum, Maximum
# result = VolumeOperations.combine(
# all_volumes[0],
# all_volumes[1],
# "", # Target name (not used when createNewObject=True)
# api.CombineOperation.Addition, # operation (Addition, Subtraction, Multiplication, Division, Mean, Minimum, Maximum)
# True # createNewObject (bool)
# )

Apply Mask to Volume​

# all_masks = app.get_all_mask_names()
# if all_volumes and all_masks:
# VolumeOperations.apply_mask_to_volume(
# all_volumes[0],
# all_masks[0],
# 0 # voxelIntensityValue=0 (int: value for voxels outside mask)
# )

Create Masks from Volumes​

# if all_volumes:
# # Direct copy to mask
# mask_names = VolumeOperations.create_mask_by_direct_copying([all_volumes[0]])
#
# # Create masks from pre-segmented volume
# mask_names = VolumeOperations.create_masks_by_segmentation(
# [all_volumes[0]],
# 1, # lowerThreshold (int)
# 255, # upperThreshold (int)
# True # createMultilabelMask=True (bool)
# )

Cone Beam CT Reconstruction​

# Reconstruct 3D volume from cone beam CT projection images using FDK algorithm

# # Helper function to create file paths with zero-padded numbering
# def create_file_paths(directory, prefix, start, end, num_zeros, extension='.tif'):
# file_paths = []
# for i in range(start, end + 1):
# padded_number = str(i).zfill(num_zeros)
# filename = f"{prefix}{padded_number}{extension}"
# full_path = os.path.join(directory, filename)
# file_paths.append(full_path)
# return file_paths
#
# projection_files = create_file_paths(
# directory="C:/Data/Projections",
# prefix="XYZ_Object",
# start=100001,
# end=100999,
# num_zeros=6
# )

# # Print the generated file paths (for verification)
# for path in projection_files:
# print(path)

# # Configure reconstruction settings
# cone_beam_reconstruction_settings = api.ConeBeamReconstructionSettings()
#
# # Inversion settings (optional, for correcting projection orientation)
# cone_beam_reconstruction_settings.invert_x = False # Mirror horizontally
# cone_beam_reconstruction_settings.invert_y = False # Mirror vertically
# cone_beam_reconstruction_settings.invert_z = False # Reverse projection order
# cone_beam_reconstruction_settings.invert_intensity = False # Invert intensity values
#
# # Geometry parameters (critical for accurate reconstruction)
# cone_beam_reconstruction_settings.source_to_detector_distance = 815.19 # Distance from X-ray source to detector (mm)
# cone_beam_reconstruction_settings.source_to_isocenter_distance = 153.48 # Distance from source to object center (mm)
# cone_beam_reconstruction_settings.detector_pixel_size_x = 0.4 # Detector pixel width (mm)
# cone_beam_reconstruction_settings.detector_pixel_size_y = 0.4 # Detector pixel height (mm)
# cone_beam_reconstruction_settings.xray_scan_start_angle = 0.0 # Starting scan angle (degrees)
# cone_beam_reconstruction_settings.xray_scan_total_angle = 360.0 # Total scan arc (degrees, 360° for full scan)
# cone_beam_reconstruction_settings.is_clockwise = False # Rotation direction (False = anti-clockwise)
#
# # Projection offset parameters (for detector misalignment correction)
# cone_beam_reconstruction_settings.projection_offset_x = 0.0 # Horizontal detector offset (mm)
# cone_beam_reconstruction_settings.projection_offset_y = 0.0 # Vertical detector offset (mm)
# cone_beam_reconstruction_settings.source_offset_x = 0.0 # Horizontal source offset (mm)
# cone_beam_reconstruction_settings.source_offset_y = 0.0 # Vertical source offset (mm)
# cone_beam_reconstruction_settings.out_of_plane_angle = 0.0 # Detector tilt around X axis (degrees)
# cone_beam_reconstruction_settings.in_plane_angle = 0.0 # Detector tilt around Z axis (degrees)
#
# # Displaced detector settings (for short scans)
# cone_beam_reconstruction_settings.enable_displaced_detector = False # Enable for off-center detector
# cone_beam_reconstruction_settings.angular_gap_threshold = 20.0 # Gap threshold for short scan detection (degrees)
#
# # Preprocessing parameters
# cone_beam_reconstruction_settings.enable_logarithm_conversion = True # Apply logarithmic transformation
#
# # Reconstruction filter parameters
# cone_beam_reconstruction_settings.truncation_correction = 1.0 # Truncation correction factor (>0 enables correction)
# cone_beam_reconstruction_settings.hann_cut_frequency = 0.0 # Hann filter frequency (0.0-1.5, higher=sharper)
#
# # Post-processing parameters
# cone_beam_reconstruction_settings.auto_invert_intensity = False # Auto-invert if contrast is reversed
# cone_beam_reconstruction_settings.auto_crop = True # Auto-crop to object bounds
# cone_beam_reconstruction_settings.crop_margin = 10 # Margin around cropped object (pixels)
#
# # Sharpening parameters (Unsharp Mask)
# cone_beam_reconstruction_settings.enable_sharpening = True # Enable USM sharpening
# cone_beam_reconstruction_settings.sharpening_iterations = 2 # Number of sharpening passes (1-5)
# cone_beam_reconstruction_settings.sharpening_radius = 3 # Sharpening radius (1-5 pixels)
# cone_beam_reconstruction_settings.sharpening_contrast = 50 # Sharpening amount (10-200 percent)
#
# # Example 1: Reconstruct from projection files on disk
# reconstructed_volume = VolumeOperations.reconstruct_volume_using_cone_beam_reconstruction(
# "", # volumeName: empty string when using files
# projection_files, # fileNames: list of projection file paths
# cone_beam_reconstruction_settings # reconstruction parameters
# )
# print(f"Reconstructed volume from files: {reconstructed_volume}")
#
# # Example 2: Reconstruct from active volume (3D stack of projections)
# # First load a 3D volume where each slice is a projection image
# # projection_stack = VolumeOperations.import_3d_image_from_disk(r'C:\data\projections_stack.mha')
# # reconstructed_volume = VolumeOperations.reconstruct_volume_using_cone_beam_reconstruction(
# # projection_stack, # volumeName: name of loaded projection stack
# # [], # fileNames: empty list when using volume
# # cone_beam_reconstruction_settings # reconstruction parameters
# # )
# # print(f"Reconstructed volume from stack: {reconstructed_volume}")

print("Volume operations tutorial completed successfully.")