Module i2pp.core.utilities
Useful functions that are used in other modules.
Functions
def create_mesh_mask(discretization, image) ‑> numpy.ndarray-
Expand source code
def create_mesh_mask(discretization, image) -> np.ndarray: """Create a voxel-based mask of the discretization in the image's grid space. This function generates a 3D boolean mask that represents a discretized mesh within the grid space of a given image. The mask is computed by determining which voxels in the image grid are enclosed by the convex hulls of the mesh elements. Args: discretization: A discretized mesh object containing nodes, elements and surfaces. image: An image object that provides the grid space and pixel data. Returns: np.ndarray: A 3D boolean array where `True` indicates that the corresponding voxel is part of the mesh mask, and `False` otherwise. Notes: - The function uses convex hulls to approximate the spatial extent of each mesh element in the image grid space. - The convex hull computation requires at least three points. Elements with fewer than three nodes are skipped. - The mask is constructed iteratively by updating the relevant voxels for each mesh element. """ logging.info("Create mesh mask in image grid space") mask = np.zeros(image.pixel_data.shape[:3], dtype=bool) node_id_to_idx = {nid: i for i, nid in enumerate(discretization.nodes.ids)} nodes_grid = world_to_grid_coords( discretization.nodes.coords, image.orientation, image.position ) for ele in discretization.elements: node_idxs = np.fromiter( (node_id_to_idx[nid] for nid in ele.node_ids), dtype=int, count=len(ele.node_ids), ) ele_node_grid = nodes_grid[node_idxs] if ele_node_grid.shape[0] < 3: continue try: hull = ConvexHull(ele_node_grid) except Exception: continue z0, z1, y0, y1, x0, x1 = _grid_bbox_indices_for_points( ele_node_grid, image.grid_coords ) zz, yy, xx = np.meshgrid( image.grid_coords.slice[z0:z1], image.grid_coords.row[y0:y1], image.grid_coords.col[x0:x1], indexing="ij", ) grid_points = np.stack((zz.ravel(), yy.ravel(), xx.ravel()), axis=-1) A, b = hull.equations[:, :-1], hull.equations[:, -1] inside_mask = np.all(A @ grid_points.T + b[:, None] <= 0, axis=0) inside_mask = inside_mask.reshape(zz.shape) mask[z0:z1, y0:y1, x0:x1] |= inside_mask return maskCreate a voxel-based mask of the discretization in the image's grid space.
This function generates a 3D boolean mask that represents a discretized mesh within the grid space of a given image. The mask is computed by determining which voxels in the image grid are enclosed by the convex hulls of the mesh elements.
Args
discretization- A discretized mesh object containing nodes, elements and surfaces.
image- An image object that provides the grid space and pixel data.
Returns
np.ndarray- A 3D boolean array where
Trueindicates that the
corresponding voxel is part of the mesh mask, and
Falseotherwise.Notes
- The function uses convex hulls to approximate the spatial extent of each mesh element in the image grid space.
- The convex hull computation requires at least three points. Elements with fewer than three nodes are skipped.
- The mask is constructed iteratively by updating the relevant voxels for each mesh element.
def find_mins_maxs(points: numpy.ndarray, enlargement: float | None = 0) ‑> Tuple[numpy.ndarray, numpy.ndarray]-
Expand source code
def find_mins_maxs( points: np.ndarray, enlargement: Optional[float] = 0 ) -> Tuple[np.ndarray, np.ndarray]: """Computes the axis-aligned bounding box for a set of 3D points. This function calculates the minimum and maximum coordinate values along each axis (X, Y, Z) to determine the bounding box of the input points. An optional enlargement factor can be applied to expand the bounding box in all directions. Args: points (np.ndarray): A NumPy array of shape (N, 3) representing N points in 3D space. enlargement (Optional[float]): An optional value to expand the bounding box equally along all axes. Defaults to 0. Returns: Tuple[np.ndarray, np.ndarray]: Two NumPy arrays representing the minimum and maximum coordinates of the bounding box. The first array contains the minimum values [min_x, min_y, min_z], and the second array contains the maximum values [max_x, max_y, max_z]. """ min_coords = np.min(points, axis=0) - enlargement max_coords = np.max(points, axis=0) + enlargement return min_coords, max_coordsComputes the axis-aligned bounding box for a set of 3D points.
This function calculates the minimum and maximum coordinate values along each axis (X, Y, Z) to determine the bounding box of the input points. An optional enlargement factor can be applied to expand the bounding box in all directions.
Args
points:np.ndarray- A NumPy array of shape (N, 3) representing N points in 3D space.
enlargement:Optional[float]- An optional value to expand the bounding box equally along all axes. Defaults to 0.
Returns
Tuple[np.ndarray, np.ndarray]- Two NumPy arrays representing the minimum and maximum coordinates of the bounding box. The first array contains the minimum values [min_x, min_y, min_z], and the second array contains the maximum values [max_x, max_y, max_z].
def get_node_position_of_element(element_node_ids: numpy.ndarray, node_ids: numpy.ndarray) ‑> numpy.ndarray-
Expand source code
def get_node_position_of_element( element_node_ids: np.ndarray, node_ids: np.ndarray ) -> np.ndarray: """Retrieves the positions (indices) of element nodes in the global node list. This function maps each node ID in `element_node_ids` to its corresponding index in the `node_ids` array, allowing efficient lookup for finite element analysis. Arguments: element_node_ids (np.ndarray): An array of node IDs belonging to a specific element. node_ids (np.ndarray): An array of all node IDs in the Discretization. Returns: np.ndarray: An array of indices representing the positions of element nodes in `node_ids`. """ return np.searchsorted(node_ids, element_node_ids)Retrieves the positions (indices) of element nodes in the global node list.
This function maps each node ID in
element_node_idsto its corresponding index in thenode_idsarray, allowing efficient lookup for finite element analysis.Arguments
element_node_ids (np.ndarray): An array of node IDs belonging to a specific element. node_ids (np.ndarray): An array of all node IDs in the Discretization.
Returns
np.ndarray- An array of indices representing the positions of element
nodes in
node_ids.
def make_json_serializable(obj: Any) ‑> Any-
Expand source code
def make_json_serializable(obj: Any) -> Any: """Converts NumPy data types to standard Python types for JSON serialization. This function recursively processes NumPy arrays and scalar types to ensure compatibility with JSON serialization. Args: obj: The object to convert, which can be a NumPy array, scalar, or other data type. Returns: Any: The converted object, where NumPy arrays are converted to lists, and NumPy scalars are converted to standard Python types (int, float). """ if isinstance(obj, np.ndarray): return [make_json_serializable(item) for item in obj] elif isinstance( obj, ( np.int64, np.int32, np.int16, np.int8, np.uint64, np.uint32, np.uint16, np.uint8, ), ): return int(obj) elif isinstance(obj, (np.float64, np.float32, np.float16)): return float(obj) return objConverts NumPy data types to standard Python types for JSON serialization. This function recursively processes NumPy arrays and scalar types to ensure compatibility with JSON serialization.
Args
obj- The object to convert, which can be a NumPy array, scalar, or other data type.
Returns
Any- The converted object, where NumPy arrays are converted to lists,
and NumPy scalars are converted to standard Python types (int, float).
def normalize_values(data: numpy.ndarray, pixel_range: numpy.ndarray) ‑> numpy.ndarray-
Expand source code
def normalize_values(data: np.ndarray, pixel_range: np.ndarray) -> np.ndarray: """Normalizes data to a range between 0 and 1 based on the provided pixel range. This function shifts the data by subtracting the minimum pixel range value and then scales it by dividing it by the total range. The resulting values will be in the range [0, 1]. Arguments: data (np.ndarray): The array of data values to normalize. pixel_range (np.ndarray): A NumPy array containing the minimum and maximum pixel range values [min, max] used for normalization. Returns: np.ndarray: The normalized data with values scaled between 0 and 1. """ # Validate supplied pixel range range_diff = pixel_range[1] - pixel_range[0] if range_diff == 0: logging.error("Pixel range difference is zero.") elif range_diff < 0: logging.error("Pixel range is inverted (max < min).") # Normalize the data normalized_data = (data - pixel_range[0]) / range_diff return normalized_dataNormalizes data to a range between 0 and 1 based on the provided pixel range.
This function shifts the data by subtracting the minimum pixel range value and then scales it by dividing it by the total range. The resulting values will be in the range [0, 1].
Arguments
data (np.ndarray): The array of data values to normalize. pixel_range (np.ndarray): A NumPy array containing the minimum and maximum pixel range values [min, max] used for normalization.
Returns
np.ndarray- The normalized data with values scaled between 0 and 1.
def smooth_data(data: numpy.ndarray, smoothing_window: int, mask: numpy.ndarray) ‑> numpy.ndarray-
Expand source code
def smooth_data( data: np.ndarray, smoothing_window: int, mask: np.ndarray, ) -> np.ndarray: """Applies a smoothing filter to 3D image data by averaging pixel values. This function reduces noise or measurement errors in the image data by applying a smoothing filter. The filter calculates the average pixel value within a neighborhood defined by the `smoothing_window` parameter, which helps to smooth out irregularities in the data. If a mask is provided, performs masked smoothing by: - Zero data outside mask → data_masked - Smooth the zeroed data → smoothed_data (artificially darkened at edges) - Smooth the mask → smoothed_mask (tells you the "weight" of valid data) - Divide smoothed data by smoothed mask → This "undoes" the darkening by renormalizing - Only update pixels that were originally inside the mask → Preserves original data outside Args: data (np.ndarray): A 3D array containing the pixel data to be smoothed. smoothing_window (int): The size of the neighborhood (in points) used to compute the average. Larger values result in smoother data, but may reduce fine details. mask (np.ndarray): A boolean mask array of the same shape as `data`. If provided, smoothing is only applied within the masked region. Returns: np.ndarray: The smoothed image data as a 3D array, where each pixel's value has been replaced by the average of its neighbors within the defined smoothing window. """ logging.info("Smooth data!") if mask is None or not np.any(mask): logging.warning( "No mask provided for smoothing. " "Smoothing will be applied to entire data." ) return uniform_filter( data, size=smoothing_window, mode="nearest", axes=(0, 1, 2) ) orig_dtype = data.dtype data_f = data.astype(np.float32, copy=False) # Create a copy of the data where all values outside the mask are zero. data_masked = data_f.copy() data_masked[~mask, ...] = 0 # Apply a uniform filter to data_masked, which darkens values near edges. smoothed_data = uniform_filter( data_masked, size=smoothing_window, mode="nearest", axes=(0, 1, 2) ) # Apply the same filter to the mask to find the proportion of valid data. smoothed_mask = uniform_filter( mask.astype(np.float32), size=smoothing_window, mode="nearest", axes=(0, 1, 2), ) # Reshape smoothed_mask to match smoothed_data for broadcasting target_shape = list(smoothed_mask.shape) + [1] * ( smoothed_data.ndim - smoothed_mask.ndim ) smoothed_mask_exp = smoothed_mask.reshape(target_shape) valid_mask = (mask) & (smoothed_mask > 1e-8) result_f = data_f.copy() normalized = np.zeros_like(smoothed_data, dtype=np.float32) # Normalize smoothed data to correct darkening at the edges. np.divide( smoothed_data, smoothed_mask_exp, out=normalized, where=smoothed_mask_exp > 1e-8, ) # Update pixels inside the original mask with the smoothed values. result_f[valid_mask, ...] = normalized[valid_mask, ...] return result_f.astype(orig_dtype, copy=False)Applies a smoothing filter to 3D image data by averaging pixel values.
This function reduces noise or measurement errors in the image data by applying a smoothing filter. The filter calculates the average pixel value within a neighborhood defined by the
smoothing_windowparameter, which helps to smooth out irregularities in the data.If a mask is provided, performs masked smoothing by: - Zero data outside mask → data_masked - Smooth the zeroed data → smoothed_data (artificially darkened at edges) - Smooth the mask → smoothed_mask (tells you the "weight" of valid data) - Divide smoothed data by smoothed mask → This "undoes" the darkening by renormalizing - Only update pixels that were originally inside the mask → Preserves original data outside
Args
data:np.ndarray- A 3D array containing the pixel data to be smoothed.
smoothing_window:int- The size of the neighborhood (in points) used to compute the average. Larger values result in smoother data, but may reduce fine details.
mask:np.ndarray- A boolean mask array of the same shape as
data. If provided, smoothing is only applied within the masked region.
Returns
np.ndarray- The smoothed image data as a 3D array, where each pixel's
value has been replaced by the average of its neighbors within the defined smoothing window.
def world_to_grid_coords(points_world: numpy.ndarray,
orientation: numpy.ndarray,
position: numpy.ndarray) ‑> numpy.ndarray-
Expand source code
def world_to_grid_coords( points_world: np.ndarray, orientation: np.ndarray, position: np.ndarray ) -> np.ndarray: """Convert world coordinates to image grid coordinates using image orientation and origin. Args: points_world (np.ndarray): A NumPy array of shape (N, 3) representing N points in world coordinates. orientation (np.ndarray): A 3x3 matrix representing the orientation of the image grid in world space. position (np.ndarray): A 1D array of shape (3,) representing the origin of the image grid in world space. Returns: np.ndarray: A NumPy array of shape (N, 3) representing the points in image grid coordinates. """ return np.linalg.solve(orientation, (points_world - position).T).TConvert world coordinates to image grid coordinates using image orientation and origin.
Args
points_world:np.ndarray- A NumPy array of shape (N, 3) representing N points in world coordinates.
orientation:np.ndarray- A 3x3 matrix representing the orientation of the image grid in world space.
position:np.ndarray- A 1D array of shape (3,) representing the origin of the image grid in world space.
Returns
np.ndarray- A NumPy array of shape (N, 3) representing the points in image grid coordinates.