Module i2pp.core.image_readers.png_reader
Import PNG data and convert it into 3D data.
Classes
class PngReader (options: dict,
bounding_box: BoundingBox)-
Expand source code
class PngReader(ImageReader): """Handles reading and processing of PNG image data. This class extends `ImageReader` to process 2D PNG images and convert them into structured slices. It validates image metadata, loads PNG files from a folder, and processes them into `ImageData` objects. """ def _verify_image_metadata(self, image_metadata: dict) -> None: """Validates the format and dimensions of the image_metadata provided. This method checks that each required parameter in the dictionary image_metadata is present and has the correct shape or data type. It performs validation for: - pixel_spacing: Must be a 3x1 array. - image_position: Must be a 3x1 array. - row_orientation: Must be a 3x1 array or None. - column_orientation: Must be a 3x1 array or None. - slice_orientation: Must be a 3x1 array or None. Arguments: image_metadata (dict): Dictionary containing image_metadata with keys: 'pixel_spacing', 'image_position', 'row_orientation' and 'column_orientation','slice_orientation'. Raises: RuntimeError: If any of the parameters are missing, of incorrect data type, or have an incorrect shape. """ expected_shapes = { "pixel_spacing": (3,), "image_position": (3,), "row_orientation": (3,), "column_orientation": (3,), "slice_orientation": (3,), } for key, shape in expected_shapes.items(): value = image_metadata.get(key) if ( key == "row_orientation" or key == "column_orientation" or key == "slice_orientation" ) and value is None: continue if value is None: raise RuntimeError( f"Missing parameter '{key}' in image_metadata." ) if not isinstance(value, (list, tuple, np.ndarray)): raise RuntimeError( f"Parameter '{key}' has the wrong type. Expected: list, " "tuple, or np.ndarray." ) array_value = np.array(value) if array_value.shape != shape: raise RuntimeError( f"Parameter '{key}' has the wrong shape. Expected: " "{shape}, but got: {array_value.shape}." ) def _extract_number(self, path: Path) -> float: """Extracts the first number found in the filename (without extension). Arguments: path (Path): The file path to PNG-folder. Returns: float: The first number found in the filename. If no number is found, returns float('inf') to ensure non-numeric filenames are sorted last. """ match = re.search(r"\d+", path.stem) return int(match.group()) if match else float("inf") def load_image(self, folder_path: Path) -> list[np.ndarray]: """Loads and processes PNG image data from a specified directory. This function reads all PNG files in the given folder, verifies the format of the image_metadata dictionary, and converts the images to RGB format. The 2-dimensional PNG images together represent a 3D image. Arguments: folder_path (Path): The path to the folder containing the PNG image files. Returns: list[np.ndarray]: A list of RGB images as NumPy arrays, each representing a loaded image. Raises: RuntimeError: If the image_metadata does not meet the expected format or dimension. """ logging.info("Load image data!") self._verify_image_metadata(self.options["metadata"]) raw_png = [] for fname in sorted( folder_path.glob("*.png"), key=self._extract_number ): print(f"Loading image: {fname.name}") image_png = Image.open(fname) rgb_image = image_png.convert("RGB") raw_png.append(np.array(rgb_image)) return raw_png def convert_to_image_data(self, raw_pngs: list[np.ndarray]) -> ImageData: """Converts a list of 2D PNG images into a structured 3D volume. This method processes PNG images as individual slices, importing metadata such as pixel spacing, position, and orientation. It filters slices based on a specified bounding box, ensuring only relevant slices are included. Args: raw_pngs (list[np.ndarray]): A list of 2D NumPy arrays, each representing a slice in a 3D volume. Raises: RuntimeError: If PNGs are not in bounding box. Returns: ImageData: A structured representation containing 3D pixel data, grid coordinates, orientation, and metadata. """ image_metadata = self.options["metadata"] row_direction = np.array( image_metadata.get("row_direction") or [0, -1, 0] ) column_direction = np.array( image_metadata.get("column_direction") or [1, 0, 0] ) slice_direction = np.array( image_metadata.get("slice_direction") or [0, 0, 1] ) slice_orientation = self._get_slice_orientation( row_direction, column_direction ) spacing = np.array(image_metadata["pixel_spacing"]) start_coords = np.array(image_metadata["image_position"]) pixel_data_list = [] coords_in_crop = [] for i, png in enumerate(raw_pngs): coords_slice = start_coords + i * slice_direction * spacing[0] if slice_orientation.is_within_crop( coords_slice, self.bounding_box ): pixel_data_list.append(png) coords_in_crop.append(np.array(coords_slice)) else: continue if not pixel_data_list: raise RuntimeError( "No slice images found within the volume of the imported mesh." ) pixel_data = np.array(pixel_data_list) N_slice, N_row, N_col, _ = pixel_data.shape slice_coords = np.arange(N_slice) * spacing[0] row_coords = np.arange(N_row) * spacing[1] col_coords = np.arange(N_col) * spacing[2] return ImageData( pixel_data=pixel_data, grid_coords=GridCoords(slice_coords, row_coords, col_coords), orientation=np.column_stack( (slice_direction, row_direction, column_direction) ), position=coords_in_crop[0], pixel_type=PixelValueType.RGB, )Handles reading and processing of PNG image data.
This class extends
ImageReaderto process 2D PNG images and convert them into structured slices. It validates image metadata, loads PNG files from a folder, and processes them intoImageDataobjects.Init ImageReader.
Ancestors
- ImageReader
- abc.ABC
Methods
def convert_to_image_data(self, raw_pngs: list[numpy.ndarray]) ‑> ImageData-
Expand source code
def convert_to_image_data(self, raw_pngs: list[np.ndarray]) -> ImageData: """Converts a list of 2D PNG images into a structured 3D volume. This method processes PNG images as individual slices, importing metadata such as pixel spacing, position, and orientation. It filters slices based on a specified bounding box, ensuring only relevant slices are included. Args: raw_pngs (list[np.ndarray]): A list of 2D NumPy arrays, each representing a slice in a 3D volume. Raises: RuntimeError: If PNGs are not in bounding box. Returns: ImageData: A structured representation containing 3D pixel data, grid coordinates, orientation, and metadata. """ image_metadata = self.options["metadata"] row_direction = np.array( image_metadata.get("row_direction") or [0, -1, 0] ) column_direction = np.array( image_metadata.get("column_direction") or [1, 0, 0] ) slice_direction = np.array( image_metadata.get("slice_direction") or [0, 0, 1] ) slice_orientation = self._get_slice_orientation( row_direction, column_direction ) spacing = np.array(image_metadata["pixel_spacing"]) start_coords = np.array(image_metadata["image_position"]) pixel_data_list = [] coords_in_crop = [] for i, png in enumerate(raw_pngs): coords_slice = start_coords + i * slice_direction * spacing[0] if slice_orientation.is_within_crop( coords_slice, self.bounding_box ): pixel_data_list.append(png) coords_in_crop.append(np.array(coords_slice)) else: continue if not pixel_data_list: raise RuntimeError( "No slice images found within the volume of the imported mesh." ) pixel_data = np.array(pixel_data_list) N_slice, N_row, N_col, _ = pixel_data.shape slice_coords = np.arange(N_slice) * spacing[0] row_coords = np.arange(N_row) * spacing[1] col_coords = np.arange(N_col) * spacing[2] return ImageData( pixel_data=pixel_data, grid_coords=GridCoords(slice_coords, row_coords, col_coords), orientation=np.column_stack( (slice_direction, row_direction, column_direction) ), position=coords_in_crop[0], pixel_type=PixelValueType.RGB, )Converts a list of 2D PNG images into a structured 3D volume.
This method processes PNG images as individual slices, importing metadata such as pixel spacing, position, and orientation. It filters slices based on a specified bounding box, ensuring only relevant slices are included.
Args
raw_pngs:list[np.ndarray]- A list of 2D NumPy arrays, each representing a slice in a 3D volume.
Raises
RuntimeError- If PNGs are not in bounding box.
Returns
ImageData- A structured representation containing 3D pixel data, grid coordinates, orientation, and metadata.
def load_image(self, folder_path: pathlib.Path) ‑> list[numpy.ndarray]-
Expand source code
def load_image(self, folder_path: Path) -> list[np.ndarray]: """Loads and processes PNG image data from a specified directory. This function reads all PNG files in the given folder, verifies the format of the image_metadata dictionary, and converts the images to RGB format. The 2-dimensional PNG images together represent a 3D image. Arguments: folder_path (Path): The path to the folder containing the PNG image files. Returns: list[np.ndarray]: A list of RGB images as NumPy arrays, each representing a loaded image. Raises: RuntimeError: If the image_metadata does not meet the expected format or dimension. """ logging.info("Load image data!") self._verify_image_metadata(self.options["metadata"]) raw_png = [] for fname in sorted( folder_path.glob("*.png"), key=self._extract_number ): print(f"Loading image: {fname.name}") image_png = Image.open(fname) rgb_image = image_png.convert("RGB") raw_png.append(np.array(rgb_image)) return raw_pngLoads and processes PNG image data from a specified directory.
This function reads all PNG files in the given folder, verifies the format of the image_metadata dictionary, and converts the images to RGB format. The 2-dimensional PNG images together represent a 3D image.
Arguments
folder_path (Path): The path to the folder containing the PNG image files.
Returns
list[np.ndarray]- A list of RGB images as NumPy arrays, each representing a loaded image.
Raises
RuntimeError- If the image_metadata does not meet the expected format or dimension.