Coverage for src/susi/reduc/spc_registration/spc_registration.py: 0%
53 statements
« prev ^ index » next coverage.py v7.5.0, created at 2025-06-13 14:15 +0000
« prev ^ index » next coverage.py v7.5.0, created at 2025-06-13 14:15 +0000
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3"""
4Provides registration of SP1 and SP2 cameras
6@author: iglesias
7"""
9import numpy as np
10import logging
11from skimage.transform import warp
13log = logging.getLogger("SUSI")
16class SPCRegistration:
17 """
18 TODO: Test!!
19 Simultaneously Fits and apply the following transformations to image2 to mnimize difference wrt image1:
20 - estimateAffinePartial2D
21 """
23 def __init__(self):
24 self.offset = None
25 self.rot = None
26 self.scale = None
27 self.scale2 = None
28 self.result = None
29 self.out_roi = None
31 def transform(self, image, offset=(0, 0), rot=(0, 0), scale=((1, 0), (1, 0))):
32 """
33 Image transform using the given parameters (see nl_transform).
34 """
35 self.offset = offset
36 self.rot = rot
37 self.scale = scale
39 self.result = self.apply_trasnf(image)
40 self.result = self.keep_valid_rectangular_roi()
41 return self.result
43 def apply_trasnf(self, image):
44 transform = SPCRegistration.nl_transform
45 transformed_img = warp(
46 image,
47 inverse_map=transform,
48 map_args={"offset": self.offset, "rot": self.rot, "scale": self.scale},
49 mode="constant",
50 cval=np.NAN,
51 )
52 return transformed_img
54 def keep_valid_rectangular_roi(self):
55 roi = [None, None]
56 is_nan = np.isnan(self.result)
57 y = ~np.all(is_nan, axis=1)
58 x = ~np.all(is_nan, axis=0)
59 roi[0] = slice(np.min(np.where(y)), np.max(np.where(y)))
60 roi[1] = slice(np.min(np.where(x)), np.max(np.where(x)))
61 is_nan = is_nan[y, :][:, x]
62 y = np.all(~is_nan, axis=1)
63 roi[0] = slice(roi[0].start + np.min(np.where(y)), roi[0].start + np.max(np.where(y)))
64 self.out_roi = roi[0], roi[1]
65 return self.result[roi[0], roi[1]]
67 @staticmethod
68 def nl_transform(coords, offset=(0, 0), rot=(0, 0), scale=((1, 0), (1, 0))):
69 """
70 Non-linear transformation of the input coordinates using the given parameters. The transformation is
71 the combination of (in this order):
72 - offset: 2D translation
73 - rot: 2D rotation
74 - scale: 2D non-linear scaling. We use a 2nd order polynomial per dimension:
75 x' = scale[0][0] * x + scale[0][1] * x**2
76 """
77 x, y = coords.T
78 # offset
79 if offset != (0, 0):
80 x = x + offset[0]
81 y = y + offset[1]
83 # rotation
84 if rot != (0, 0):
85 x_rot = x * np.cos(rot[0]) - y * np.sin(rot[0])
86 y_rot = x * np.sin(rot[0]) + y * np.cos(rot[0])
87 else:
88 x_rot = x
89 y_rot = y
91 # scaling
92 if scale != ((1, 0), (1, 0)):
93 x_rot = x_rot * scale[0][0] + scale[0][1] * x_rot**2
94 y_rot = y_rot * scale[1][0] + scale[1][1] * y_rot**2
95 else:
96 x_rot = x_rot
97 y_rot = y_rot
99 return np.column_stack((x, y))