Source code for giant.image_processing.utilities.image_validation_mixin
import numpy as np
from numpy.typing import NDArray, DTypeLike
[docs]
class ImageValidationMixin:
"""
A mixin class that provides functionality for validating and converting image data types.
This mixin is designed to be used with image processing classes that require
specific data types for their input images. It ensures that the input image
is of an allowed data type before processing, and converts it if necessary.
Attributes:
allowed_dtypes (list[DTypeLike]): A list of allowed data types for the image.
This must be implemented by subclasses.
Methods:
_validate_and_convert_image: Checks if an image is of an allowed dtype
and converts it if not.
"""
allowed_dtypes: list[DTypeLike]
"""
A list of dtypes allowed by the detector.
This must be implemented by subclasses.
"""
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
if not self.allowed_dtypes:
raise NotImplementedError(f'allowed_dtypes must be specified as a class attribute for {self.__class__.__name__}')
def _validate_and_convert_image(self, image: NDArray) -> NDArray:
"""
Checks if an image is of an allowed dtype and converts it if not.
:param image: the image to validate
:returns: The image as the rigth dtype
"""
if image.dtype in self.allowed_dtypes:
return image
target_dtype = self.allowed_dtypes[0]
# Check if the target data type is an integer type
if np.issubdtype(target_dtype, np.integer):
# Get the maximum possible value for the target integer type
target_max = np.iinfo(target_dtype).max
target_min = np.iinfo(target_dtype).min
# Use float64 for precision during the scaling calculation
scaled_image = image.astype(np.float64).copy()
source_max = scaled_image.max()
scale = 1
if target_min < 0:
source_min = scaled_image.min()
abs_max = np.argmax([abs(source_min), abs(source_max)])
if abs_max == 0 and target_min != 0:
scale = target_min/source_min
elif target_max != 0:
scale = target_max/source_max
if scale < 0:
raise ValueError('Something is wrong')
else:
if source_max > 0:
scale = target_max/source_max
# Safely scale the image to the target's full range
scaled_image *= scale
# Round to the nearest integer and cast to the final type
return np.round(scaled_image).astype(target_dtype)
else:
# For floating point targets, a direct cast is sufficient
return image.astype(target_dtype)