Source code for medical_image.data.png_image
import os
import numpy as np
import torch
from PIL import Image as PILImage
from medical_image.data.image import Image
from medical_image.utils.ErrorHandler import ErrorMessages
[docs]
class PNGImage(Image):
"""PNG image backed by *Pillow*.
Supports lazy loading: the constructor validates the file extension;
pixel data is read only when :meth:`load` is called.
Attributes:
_pil_image (Optional[PIL.Image.Image]): The Pillow image object
(``None`` until :meth:`load` is called).
"""
[docs]
def __init__(self, file_path: str):
"""Initialise a PNG image.
Args:
file_path: Path to a ``.png`` file.
Raises:
ValueError: If *file_path* does not have a ``.png`` extension.
"""
super().__init__(file_path)
ext = os.path.splitext(self.file_path)[1].lower()
if ext not in [".png"]:
raise ErrorMessages.unsupported_file_type(ext)
self._pil_image = None
[docs]
def load(self) -> None:
"""Open the PNG file via Pillow and populate ``pixel_data`` as a float tensor."""
self._pil_image = PILImage.open(self.file_path)
img = np.array(self._pil_image)
self.pixel_data = torch.from_numpy(img).float()
self._post_load()
[docs]
def save(self) -> None:
"""Write pixel data to ``{name}_modified.png`` as uint8.
Raises:
DicomDataNotLoadedError: If ``pixel_data`` is ``None``.
"""
if self.pixel_data is None:
raise ErrorMessages.dicom_data_not_loaded()
img_np = self.pixel_data.detach().cpu().numpy()
if img_np.dtype != np.uint8:
img_np = np.clip(img_np, 0, 255).astype(np.uint8)
filename, _ = os.path.splitext(self.file_path)
out_path = filename + "_modified.png"
PILImage.fromarray(img_np).save(out_path)