Source code for medical_image.data.dicom_image
import os
from typing import Optional, Union
import numpy as np
import pydicom
import torch
from medical_image.data.image import Image
from medical_image.utils.ErrorHandler import ErrorMessages
from medical_image.utils.image_utils import TensorConverter
[docs]
class DicomImage(Image):
"""DICOM image backed by *pydicom*.
Supports lazy loading: the constructor stores the file path and
validates the extension; pixel data is read only when :meth:`load`
is called.
Attributes:
dicom_data (Optional[pydicom.Dataset]): The parsed DICOM dataset
(``None`` until :meth:`load` is called).
"""
[docs]
def __init__(
self,
file_path: Optional[str] = None,
array: Optional[Union[np.ndarray, torch.Tensor]] = None,
width: Optional[int] = None,
height: Optional[int] = None,
source_image: Optional[Image] = None,
):
"""Initialise a DICOM image.
Args:
file_path: Path to a ``.dcm`` file.
array: Pre-existing pixel data (numpy or tensor).
width: Explicit width hint.
height: Explicit height hint.
source_image: Another Image to clone from.
Raises:
ValueError: If *file_path* does not have a ``.dcm`` extension.
"""
super().__init__(
file_path=file_path,
array=array,
width=width,
height=height,
source_image=source_image,
)
if file_path:
ext = os.path.splitext(self.file_path)[1].lower()
if ext != ".dcm":
raise ErrorMessages.unsupported_file_type(ext)
self.dicom_data = None
[docs]
def load(self) -> None:
"""Read the DICOM file and populate ``pixel_data``, ``width``, and ``height``."""
self.dicom_data = pydicom.dcmread(self.file_path)
self.pixel_data = torch.tensor(self.dicom_data.pixel_array)
self._width = self.dicom_data.Columns
self._height = self.dicom_data.Rows
self._post_load()
[docs]
def save(self) -> None:
"""Write modified pixel data back to ``{name}_modified.dcm``.
Raises:
ValueError: If ``dicom_data`` has not been loaded.
"""
if self.dicom_data is None:
raise ErrorMessages.dicom_data_not_loaded()
filename, extension = os.path.splitext(self.file_path)
self.dicom_data.set_pixel_data(
TensorConverter.to_numpy(self), "MONOCHROME2", 16
)
self.dicom_data.save_as(filename + "_modified.dcm")
def __repr__(self) -> str:
"""Return a one-line summary of the DICOM image."""
status = "loaded" if self.pixel_data is not None else "unloaded"
return (
f"DicomImage(path='{self.file_path}', "
f"{self.width}x{self.height}, {status})"
)