Module i2pp.core.interpolators.interpolator_all_voxel

Interpolates pixel values from image-data to mesh-data.

Classes

class InterpolatorAllVoxel
Expand source code
class InterpolatorAllVoxel(Interpolator):
    """Subclass of Interpolator for mapping 3D image data to finite element
    mesh elements.

    This class extends Interpolator and specializes in assigning pixel values
    from 3D image data to finite element mesh elements by computing the mean
    of all voxels contained within each element. This approach is used when
    `interpolation_method` is set to "allvoxels".
    """

    def _search_bounding_box(
        self, grid_coords: GridCoords, element_grid_coords: np.ndarray
    ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
        """Searches for the indices within the grid that correspond to the
        bounding box of an element.

        This function computes the bounding box of the given element in grid
        coordinates and finds the indices in the grid that correspond to the
        minimum and maximum bounds along each axis (slice, row, and column).
        It uses the `np.searchsorted` method to efficiently find the
        appropriate index ranges that enclose the element's bounding box.

        The bounding box is optionally enlarged by a specified amount (default
        is 0) to ensure proper inclusion of all relevant voxels.

        Arguments:
            grid_coords (GridCoords): The grid coordinates containing the full
                range of slices, rows, and columns.
            element_grid_coords (np.ndarray): The coordinates of the element
                in the grid, used to compute the bounding box.

        Returns:
            (Tuple[np.ndarray, np.ndarray]):
                - slice_indices: An array of indices for slices within the
                    bounding box.
                - row_indices: An array of indices for rows within the
                    bounding box.
                - col_indices: An array of indices for columns within the
                    bounding box.
        """

        mins, maxs = find_mins_maxs(points=element_grid_coords, enlargement=0)

        slice_min_idx, slice_max_idx = np.searchsorted(
            grid_coords.slice, [mins[0], maxs[0]], side="left"
        ), np.searchsorted(grid_coords.slice, [mins[0], maxs[0]], side="right")
        row_min_idx, row_max_idx = np.searchsorted(
            grid_coords.row, [mins[1], maxs[1]], side="left"
        ), np.searchsorted(grid_coords.row, [mins[1], maxs[1]], side="right")
        col_min_idx, col_max_idx = np.searchsorted(
            grid_coords.col, [mins[2], maxs[2]], side="left"
        ), np.searchsorted(grid_coords.col, [mins[2], maxs[2]], side="right")

        slice_indices = np.arange(slice_min_idx[0], slice_max_idx[1])
        row_indices = np.arange(row_min_idx[0], row_max_idx[1])
        col_indices = np.arange(col_min_idx[0], col_max_idx[1])

        return slice_indices, row_indices, col_indices

    def _is_inside_element(self, point: np.ndarray, hull: ConvexHull):
        """Checks if a point is inside a convex element.

        This function determines whether a given point is inside a convex hull
        defined by the `ConvexHull` object. It does this by evaluating the
        inequalities that define the convex region, using the hull's equations.
        If the point satisfies all of these inequalities, it is considered to
        be inside the element.

        Arguments:
            point (np.ndarray): A 3D point in space, represented as a NumPy
                array.
            hull (ConvexHull): A `ConvexHull` object that defines the
                boundaries of the element.

        Returns:
            bool: True if the point is inside the convex element, False
                otherwise.
        """

        A = hull.equations[:, :-1]
        b = hull.equations[:, -1]

        return np.all(A @ point + b <= 0)

    def _get_data_of_element(
        self, element_node_grid_coords: np.ndarray, image_data: ImageData
    ) -> np.ndarray:
        """Computes the representative pixel value for a given element based on
        its nodes in grid coordinates.

        This function identifies voxels within the element by checking whether
        their grid coordinates fall inside the convex hull formed by the
        element's node coordinates. It then extracts the corresponding pixel
        values and returns their mean.

        If no voxels are found, it estimates the pixel value via interpolation
        at the element's center. If the center of the element is outside the
        grid, it returns `np.nan`.

        Arguments:
            element_node_grid_coords (np.ndarray): The grid coordinates of
                the element's nodes.
            image_data (ImageData): Image data containing voxel coordinates
                and pixel values.

        Returns:
            np.ndarray: The mean pixel value of all voxels inside the element.
                If no voxels are found but at least one node is inside the
                grid, an interpolated value is returned.
                If all nodes are outside the grid, returns `np.nan`.
        """

        data = []

        slice_indices, row_indices, col_indices = self._search_bounding_box(
            image_data.grid_coords, element_node_grid_coords
        )

        hull = ConvexHull(element_node_grid_coords)

        for i in slice_indices:
            for j in row_indices:
                for k in col_indices:
                    grid_coord = np.array(
                        [
                            image_data.grid_coords.slice[i],
                            image_data.grid_coords.row[j],
                            image_data.grid_coords.col[k],
                        ]
                    )

                    if self._is_inside_element(grid_coord, hull):

                        data.append(image_data.pixel_data[i, j, k])

        if data:
            return np.mean(data, axis=0)
        else:
            self.backup_interpolation += 1

            element_center = np.mean(element_node_grid_coords, axis=0)

            return self.interpolate_image_values_to_points(
                element_center, image_data
            )[0]

    def compute_element_data(
        self, dis: Discretization, image_data: ImageData
    ) -> list[Element]:
        """Converts FEM node coordinates to grid coordinates and computes the
        mean pixel value for each element.

        This function first converts the world coordinates of the FEM nodes
        into grid coordinates. It then checks which voxels are located within
        each element by using the grid coordinates of the nodes. Finally, it
        calculates the mean value of all voxels inside the element and assigns
        this mean value to the element's data.

        Arguments:
            dis (Discretization): The Discretization object containing FEM
                elements and node coordinates.
            image_data (ImageData): A structured representation containing 3D
                pixel data, grid coordinates, orientation, and metadata.

        Returns:
            list[Element]: A list of FEM elements with their pixel values
                assigned.
        """

        node_grid_coords = self.world_to_grid_coords(
            dis.nodes.coords, image_data.orientation, image_data.position
        )

        node_positions = np.array(
            [
                get_node_position_of_element(ele.node_ids, dis.nodes.ids)
                for ele in dis.elements
            ]
        )

        for i, ele in tqdm(
            enumerate(dis.elements),
            total=len(dis.elements),
            desc="Element values",
        ):

            element_node_grid_coords = node_grid_coords[node_positions[i]]

            ele.data = self._get_data_of_element(
                element_node_grid_coords, image_data
            )

            if np.all(np.isnan(ele.data)):
                self.nan_elements += 1

        self._log_interpolation_warnings()

        return dis.elements

Subclass of Interpolator for mapping 3D image data to finite element mesh elements.

This class extends Interpolator and specializes in assigning pixel values from 3D image data to finite element mesh elements by computing the mean of all voxels contained within each element. This approach is used when interpolation_method is set to "allvoxels".

Initialize the Interpolator.

Ancestors

Methods

def compute_element_data(self,
dis: Discretization,
image_data: ImageData) ‑> list[Element]
Expand source code
def compute_element_data(
    self, dis: Discretization, image_data: ImageData
) -> list[Element]:
    """Converts FEM node coordinates to grid coordinates and computes the
    mean pixel value for each element.

    This function first converts the world coordinates of the FEM nodes
    into grid coordinates. It then checks which voxels are located within
    each element by using the grid coordinates of the nodes. Finally, it
    calculates the mean value of all voxels inside the element and assigns
    this mean value to the element's data.

    Arguments:
        dis (Discretization): The Discretization object containing FEM
            elements and node coordinates.
        image_data (ImageData): A structured representation containing 3D
            pixel data, grid coordinates, orientation, and metadata.

    Returns:
        list[Element]: A list of FEM elements with their pixel values
            assigned.
    """

    node_grid_coords = self.world_to_grid_coords(
        dis.nodes.coords, image_data.orientation, image_data.position
    )

    node_positions = np.array(
        [
            get_node_position_of_element(ele.node_ids, dis.nodes.ids)
            for ele in dis.elements
        ]
    )

    for i, ele in tqdm(
        enumerate(dis.elements),
        total=len(dis.elements),
        desc="Element values",
    ):

        element_node_grid_coords = node_grid_coords[node_positions[i]]

        ele.data = self._get_data_of_element(
            element_node_grid_coords, image_data
        )

        if np.all(np.isnan(ele.data)):
            self.nan_elements += 1

    self._log_interpolation_warnings()

    return dis.elements

Converts FEM node coordinates to grid coordinates and computes the mean pixel value for each element.

This function first converts the world coordinates of the FEM nodes into grid coordinates. It then checks which voxels are located within each element by using the grid coordinates of the nodes. Finally, it calculates the mean value of all voxels inside the element and assigns this mean value to the element's data.

Arguments

dis (Discretization): The Discretization object containing FEM elements and node coordinates. image_data (ImageData): A structured representation containing 3D pixel data, grid coordinates, orientation, and metadata.

Returns

list[Element]
A list of FEM elements with their pixel values assigned.

Inherited members