Architecture¶
Medical Image Standard is designed around five core principles:
Abstraction-first — Unified interfaces hide format-specific complexity.
Lazy loading — No I/O until explicitly requested.
Stateless processing — Processing operations are pure functions on tensors.
Algorithm composition — Pipelines built by composing simple operations.
Automatic device inference — GPU usage requires no special handling.
Package Structure¶
medical_image/
├── data/ # Image abstractions, patches, annotations
├── process/ # Stateless operations (filters, threshold, morphology)
├── algorithms/ # Multi-step pipelines (FEBDS, FCM, DeepSeg, ...)
├── datasets/ # PyTorch Dataset subclasses (INbreast, CBIS-DDSM)
└── utils/ # Device management, export, logging, errors
Data Flow¶
A typical workflow proceeds through these layers:
Input Processing Output
───── ────────── ──────
DicomImage.load() ──> Filters / Threshold ──> output.pixel_data
| | |
v v v
image.pixel_data Algorithm.apply() output.annotations
(torch.Tensor) (composed pipeline) (List[Annotation])
Design Patterns¶
Strategy Pattern (Algorithms)¶
The Algorithm base class defines the interface. Concrete algorithms are interchangeable strategies:
# Any Algorithm can be used interchangeably
algo = FebdsAlgorithm(method="dog")
algo = KMeansAlgorithm(k=3)
algo = DeepSegmentationAlgorithm.from_pretrained("unetpp_bce_dice_32_inbreast")
# Same calling convention
algo(image=image, output=output)
Template Method (Algorithm.__call__)¶
The base class __call__ wraps apply() with optional mixed-precision autocast. Subclasses only implement apply():
class Algorithm(ABC):
def __call__(self, image, output):
if self.precision != Precision.FULL and self.device != "cpu":
with torch.cuda.amp.autocast(dtype=self.precision.value):
self.apply(image, output)
else:
self.apply(image, output)
return output
Lambda Composition (FEBDS)¶
Complex algorithms define processing steps as lambda functions in __init__, executed sequentially in apply(). This allows swapping individual steps without subclassing:
class FebdsAlgorithm(Algorithm):
def __init__(self, method="dog"):
self.dog = lambda img, out: Filters.difference_of_gaussian(
image=img, output=out, low_sigma=1.7, high_sigma=2.0
)
self.otsu = lambda img, out: Threshold.otsu_threshold(
image=img, output=out
)
Adapter Pattern (Image Subclasses)¶
Each image format (DICOM, PNG, in-memory) adapts its native I/O library to the common Image interface. Users interact with the same API regardless of the underlying format.
Factory Methods¶
The Image class provides factory constructors: from_file(), from_array(), from_image(), empty(). These select the right subclass or create appropriate instances without exposing construction details.
Error Handling¶
The framework defines a custom exception hierarchy rooted at AppError:
FileNotFoundAppError— file path does not existInvalidPixelDataError— pixel data is None or invalidUnsupportedFileTypeError— wrong file extension for image typeDicomDataNotLoadedError— operation on unloaded DICOMEmptyDatasetError— dataset has no samples
The @requires_loaded decorator on processing methods raises DicomDataNotLoadedError automatically when pixel_data is None.