Source code for imgreg.models.angleselect.params

"""
Module implementing the parameter classes for the angle selector Model

Author: Fabian A. Preiss
"""
from enum import Enum
from typing import Dict, Optional, cast

import numpy as np
from skimage.registration import phase_cross_correlation
from skimage.transform import AffineTransform

from imgreg.models.angleselect.enums import AngleSelectParams
from imgreg.util.methods import ImageMethods
from imgreg.util.params import ImageParameter, Parameter


[docs]class ImageParam(ImageParameter): """ Image. Attributes ---------- value : numpy.ndarray """ def __init__(self, image: Optional[np.ndarray] = None): super().__init__(AngleSelectParams.IMG, np.ndarray) if image is not None: self.value = image self.title = "Image"
[docs]class RefImageParam(ImageParameter): """ Reference image. Attributes ---------- value : numpy.ndarray """ def __init__(self, image: Optional[np.ndarray] = None): super().__init__(AngleSelectParams.REF_IMG, np.ndarray) if image is not None: self.value = image self.title = "Reference Image"
[docs]class AngleAParam(Parameter): """ Candidate Angle A. Attributes ---------- value : float """ def __init__(self, angle_a: float): super().__init__(AngleSelectParams.ANGLE_A, float) self.value = angle_a
[docs]class AngleBParam(Parameter): """ Candidate Angle B. Attributes ---------- value : float """ def __init__(self, angle_b: float): super().__init__(AngleSelectParams.ANGLE_B, float) self.value = angle_b
[docs]class UpsamplingParam(Parameter): """ Upsampling factor. 1 => no upsampling, 20 => precision to 1/20 of a pixel. Attributes ---------- value : int """ def __init__(self, upsampling: int): super().__init__(AngleSelectParams.UPSAMPLING, int) self.value = upsampling
# derived properties
[docs]class RotAParam(ImageParameter): """ Image Rotated by Angle A. Attributes ---------- value : numpy.ndarray """ def __init__(self, parent_parameters, recovered_rot_img=None): super().__init__( AngleSelectParams.ROT_A_IMG, RotAParam._value_function_handle, parent_parameters=parent_parameters, ) self.title = "Image Rotated A" if recovered_rot_img is not None: self.value = recovered_rot_img @staticmethod def _value_function_handle(params: Dict[Enum, Parameter]) -> np.ndarray: return ImageMethods.compute_rts( params[AngleSelectParams.IMG].value, angle=params[AngleSelectParams.ANGLE_A].value, scale=1, translation=(0, 0), inverse=True, preserve_range=True, )
[docs]class RotBParam(ImageParameter): """ Image Rotated by Angle B. Attributes ---------- value : numpy.ndarray """ def __init__(self, parent_parameters, recovered_rot_img=None): super().__init__( AngleSelectParams.ROT_B_IMG, RotBParam._value_function_handle, parent_parameters=parent_parameters, ) self.title = "Image Rotated B" if recovered_rot_img is not None: self.value = recovered_rot_img @staticmethod def _value_function_handle(params: Dict[Enum, Parameter]) -> np.ndarray: return ImageMethods.compute_rts( params[AngleSelectParams.IMG].value, angle=params[AngleSelectParams.ANGLE_B].value, scale=1, translation=(0, 0), inverse=True, preserve_range=True, )
[docs]class TranslationAParam(Parameter): """ Recovered x,y translation vector and error for A. Attributes ---------- value : numpy.ndarray """ def __init__(self, parent_parameters, recovered_translation=None): super().__init__( AngleSelectParams.TRANSLATION_A, TranslationAParam._value_function_handle, parent_parameters=parent_parameters, ) if recovered_translation is not None: self.value = recovered_translation @staticmethod def _value_function_handle(params: Dict[Enum, Parameter]) -> np.ndarray: shifts, error, _ = phase_cross_correlation( params[AngleSelectParams.REF_IMG].value, params[AngleSelectParams.ROT_A_IMG].value, upsample_factor=params[AngleSelectParams.UPSAMPLING].value, ) tform = AffineTransform( scale=1, rotation=np.deg2rad(-params[AngleSelectParams.ANGLE_A].value), ) shifts = tform.params[:2, :2] @ shifts[::-1] return np.array([*shifts, error])
[docs]class TranslationBParam(Parameter): """ Recovered x,y translation vector and error for B. Attributes ---------- value : numpy.ndarray """ def __init__(self, parent_parameters, recovered_translation=None): super().__init__( AngleSelectParams.TRANSLATION_B, TranslationBParam._value_function_handle, parent_parameters=parent_parameters, ) if recovered_translation is not None: self.value = recovered_translation @staticmethod def _value_function_handle(params: Dict[Enum, Parameter]) -> np.ndarray: shifts, error, _ = phase_cross_correlation( params[AngleSelectParams.REF_IMG].value, params[AngleSelectParams.ROT_B_IMG].value, upsample_factor=params[AngleSelectParams.UPSAMPLING].value, ) tform = AffineTransform( scale=1, rotation=np.deg2rad(-params[AngleSelectParams.ANGLE_B].value), ) shifts = tform.params[:2, :2] @ shifts[::-1] return np.array([*shifts, error])
[docs]class RotTrAParam(ImageParameter): """ Rotation, and translation recovered image A. Attributes ---------- value : numpy.ndarray """ def __init__(self, parent_parameters, recovered_rot_tr_img=None): super().__init__( AngleSelectParams.ROT_TR_A_IMG, RotTrAParam._value_function_handle, parent_parameters=parent_parameters, ) self.title = "Recovered Image" if recovered_rot_tr_img is not None: self.value = recovered_rot_tr_img @staticmethod def _value_function_handle(params: Dict[Enum, Parameter]) -> np.ndarray: return ImageMethods.compute_rts( params[AngleSelectParams.IMG].value, angle=params[AngleSelectParams.ANGLE_A].value, scale=1, translation=params[AngleSelectParams.TRANSLATION_A].value[:-1], inverse=True, preserve_range=True, )
[docs]class RotTrBParam(ImageParameter): """ Rotation, and translation recovered image B. Attributes ---------- value : numpy.ndarray """ def __init__(self, parent_parameters, recovered_rot_tr_img=None): super().__init__( AngleSelectParams.ROT_TR_B_IMG, RotTrBParam._value_function_handle, parent_parameters=parent_parameters, ) self.title = "Recovered Image" if recovered_rot_tr_img is not None: self.value = recovered_rot_tr_img @staticmethod def _value_function_handle(params: Dict[Enum, Parameter]) -> np.ndarray: return ImageMethods.compute_rts( params[AngleSelectParams.IMG].value, angle=params[AngleSelectParams.ANGLE_B].value, scale=1, translation=params[AngleSelectParams.TRANSLATION_B].value[:-1], inverse=True, preserve_range=True, )
[docs]class SelectorParam(Parameter): """ True, if A matches better than B Attributes ---------- value : bool """ def __init__(self, parent_parameters, selection=None): super().__init__( AngleSelectParams.SELECTOR, SelectorParam._value_function_handle, parent_parameters=parent_parameters, ) if selection is not None: self.value = selection @staticmethod def _value_function_handle(params: Dict[Enum, Parameter]) -> bool: return ImageMethods.norm_rel_l2( params[AngleSelectParams.ROT_TR_A_IMG].value, params[AngleSelectParams.REF_IMG].value, ) <= ImageMethods.norm_rel_l2( params[AngleSelectParams.ROT_TR_B_IMG].value, params[AngleSelectParams.REF_IMG].value, )
[docs]class RecoveredRotationParam(Parameter): """ Recovered rotation angle and error between the modified and reference image. Notes ----- The errors are a lower estimate under ideal assumptions and can be much larger depending on the data. Attributes ---------- value : numpy.ndarray """ def __init__(self, parent_parameters, recovered_rotation=None): super().__init__( AngleSelectParams.RECOVERED_ROTATION, RecoveredRotationParam._value_function_handle, parent_parameters=parent_parameters, ) if recovered_rotation is not None: self.value = recovered_rotation @staticmethod def _value_function_handle(params: Dict[Enum, Parameter]) -> float: return cast( float, params[AngleSelectParams.ANGLE_A].value if params[AngleSelectParams.SELECTOR].value else params[AngleSelectParams.ANGLE_B].value, )
[docs]class RecoveredRotParam(ImageParameter): """ Rotation recovered image. Attributes ---------- value : numpy.ndarray """ def __init__(self, parent_parameters, recovered_rot_img=None): super().__init__( AngleSelectParams.RECOVERED_ROT_IMG, RecoveredRotParam._value_function_handle, parent_parameters=parent_parameters, ) if recovered_rot_img is not None: self.value = recovered_rot_img @staticmethod def _value_function_handle(params: Dict[Enum, Parameter]) -> np.ndarray: return cast( np.ndarray, params[AngleSelectParams.ROT_A_IMG].value if params[AngleSelectParams.SELECTOR].value else params[AngleSelectParams.ROT_B_IMG].value, )
[docs]class RecoveredTranslationParam(Parameter): """ Recovered x,y translation vector and error. Attributes ---------- value : numpy.ndarray """ def __init__(self, parent_parameters, recovered_translation=None): super().__init__( AngleSelectParams.RECOVERED_TRANSLATION, RecoveredTranslationParam._value_function_handle, parent_parameters=parent_parameters, ) if recovered_translation is not None: self.value = recovered_translation @staticmethod def _value_function_handle(params: Dict[Enum, Parameter]) -> np.ndarray: return cast( np.ndarray, params[AngleSelectParams.TRANSLATION_A].value if params[AngleSelectParams.SELECTOR].value else params[AngleSelectParams.TRANSLATION_B].value, )
[docs]class RecoveredRotTrParam(ImageParameter): """ Rotation, and translation recovered image. Attributes ---------- value : numpy.ndarray """ def __init__(self, parent_parameters, recovered_rot_tr_img=None): super().__init__( AngleSelectParams.RECOVERED_ROT_TR_IMG, RecoveredRotTrParam._value_function_handle, parent_parameters=parent_parameters, ) if recovered_rot_tr_img is not None: self.value = recovered_rot_tr_img @staticmethod def _value_function_handle(params: Dict[Enum, Parameter]) -> np.ndarray: return cast( np.ndarray, params[AngleSelectParams.ROT_TR_A_IMG].value if params[AngleSelectParams.SELECTOR].value else params[AngleSelectParams.ROT_TR_B_IMG].value, )