Source code for numpyradiomics.mod_shape

import numpy as np
from numpyradiomics.mod_shape_3d import shape_3d, shape_3d_units
from numpyradiomics.mod_shape_2d import shape_2d, shape_2d_units
from numpyradiomics.mod_ski_shape_3d import ski_shape_3d, ski_shape_3d_units


[docs] def shape(mask:np.ndarray, spacing=(1.0, 1.0, 1.0), extend=True): """ Compute shape descriptors for a binary mask in 2D or 3D. This wrapper automatically detects the dimensionality of the input mask and dispatches the calculation to the appropriate 2D or 3D sub-module. Parameters ---------- mask : np.ndarray A binary mask array where non-zero pixels represent the Region of Interest (ROI). - For 2D: Format (Y, X). - For 3D: Format (Z, Y, X). spacing : tuple of float, optional Pixel or voxel spacing in physical base units (e.g., mm). - For 2D: (y_spacing, x_spacing). - For 3D: (z_spacing, y_spacing, x_spacing). Defaults to unit spacing (1.0, 1.0, 1.0). extend : bool, optional If True (default), and the input is 3D, the returned dictionary includes additional shape metrics derived from `skimage.measure.regionprops` (e.g., Convex Hull Volume, Solidity, Moments of Inertia) that may not be part of the standard PyRadiomics set. Ignored for 2D inputs. Returns ------- dict: A dictionary containing shape feature names (keys) and their calculated values (float). For 2D data the following are returned: - **MeshSurface**: Area of the ROI defined by the mesh (marching squares). - **PixelSurface**: Area defined by the count of non-zero pixels. - **Perimeter**: Perimeter length of the ROI mesh. - **PerimeterSurfaceRatio**: Ratio of Perimeter to MeshSurface. - **Sphericity**: Measure of roundness (1.0 is a perfect circle). - **SphericalDisproportion**: Inverse of sphericity. - **MaximumDiameter**: Largest Euclidean distance between contour vertices. - **MajorAxisLength**: Principal axis length derived from image moments. - **MinorAxisLength**: Secondary axis length derived from image moments. - **Elongation**: Ratio of Minor to Major axis length. For 3D data the following are returned: - **MeshVolume**: Volume calculated from the surface mesh (Divergence theorem). - **VoxelVolume**: Volume calculated by counting voxels multiplied by voxel spacing. - **SurfaceArea**: Total area of the surface mesh. - **SurfaceVolumeRatio**: Ratio of Surface Area to Volume. - **Sphericity**: Measure of roundness (0 to 1), where 1 is a perfect sphere. - **Maximum3DDiameter**: Largest Euclidean distance between vertices on the convex hull. - **Maximum2DDiameterSlice**: Maximum diameter in the axial plane (X-Y). - **Maximum2DDiameterColumn**: Maximum diameter in the coronal plane (Z-X). - **Maximum2DDiameterRow**: Maximum diameter in the sagittal plane (Z-Y). - **MajorAxisLength**: Length of the largest principal axis (PCA). - **MinorAxisLength**: Length of the second largest principal axis (PCA). - **LeastAxisLength**: Length of the smallest principal axis (PCA). - **Elongation**: Ratio of major to minor axis components (sqrt(lambda_minor / lambda_major)). - **Flatness**: Ratio of major to least axis components (sqrt(lambda_least / lambda_major)). If `extend=True` (3D only), additional keys include: - **Solidity**: Ratio of region volume to convex hull volume. - **Extent**: Ratio of region volume to bounding box volume. - **MaximumDepth**: Radius of the largest inscribed sphere (Chebyshev radius). - **LongestCaliperDiameter**: Maximum Feret diameter. - **FractionalAnisotropyOfInertia**: Measures how elongated/flat the shape is (0 to 1). - **MomentsOfInertia**: First, Second, and Third moments along principal axes. Raises ------ ValueError If `mask` is not 2D or 3D. Examples -------- >>> from numpyradiomics import dro >>> # Create a synthetic 3D cube (e.g., 6x6x6 pixels centered in 10x10x10) >>> spacing = (1.0, 1.0, 1.0) >>> mask = dro.cuboid(radii_mm=(10.0, 5.0, 2.5), spacing=spacing) >>> # Calculate features >>> features = shape(mask, spacing, extend=True) >>> # Access standard and extended metrics >>> print(f"Volume: {features['VoxelVolume']}") Volume: 216.0 >>> print(f"Solidity: {features.get('Solidity', 'N/A')}") Solidity: 1.0 """ if np.ndim(mask) == 2: return shape_2d(mask, spacing) if np.ndim(mask) == 3: shape = shape_3d(mask, spacing) if extend: shape = shape | ski_shape_3d(mask, spacing) return shape
[docs] def shape_units(dim:int, voxel_unit='mm'): """Units of returned shape metrics Args: dim (int): nr of dimensions (2 or 3) voxel_unit (str, optional): unit of voxel length Returns: dict: features and their units """ if dim == 2: return shape_2d_units(voxel_unit) if dim == 3: return shape_3d_units(voxel_unit) | ski_shape_3d_units(voxel_unit)