66import numpy as np
77from numba import njit
88
9- from openptv_python .vec_utils import vec_set
10-
119
1210@njit
13- def rotation_matrix (phi : float , omega : float , kappa : float ) -> np .ndarray :
14- """Calculate the necessary trigonometric functions to rotate the Dmatrix of Exterior ext_par."""
15- cp = np .cos (phi )
16- sp = np .sin (phi )
11+ def rotation_matrix (ext : np .ndarray ) -> None :
12+ """Calculate the necessary trigonometric functions to rotate the Dmatrix of Exterior ext_par.
13+
14+ Rotation is performed by multiplication of three rotation matrices,
15+ rotation around X axis is performed first, then around Y axis, and finally around Z axis.
16+ rotation around X axis does not change values along X axis, i.e. X_omega = X
17+
18+ Maas, H.G., Gruen, A. & Papantoniou, D. Particle tracking velocimetry in
19+ three-dimensional flows. Experiments in Fluids 15, 133–146 (1993).
20+ https://doi.org/10.1007/BF00190953
21+
22+ """
23+ omega , phi , kappa = ext [0 ]['omega' ], ext [0 ]['phi' ], ext [0 ]['kappa' ]
24+
25+
1726 co = np .cos (omega )
1827 so = np .sin (omega )
28+
29+ cp = np .cos (phi )
30+ sp = np .sin (phi )
31+
1932 ck = np .cos (kappa )
2033 sk = np .sin (kappa )
2134
22- dm = np .zeros ((3 , 3 ), dtype = np .float64 )
35+ # dm = np.zeros((3, 3), dtype=np.float64)
36+ dm = ext [0 ]['dm' ] # shortcut to the dm field of the first element of the array
37+
2338 dm [0 , 0 ] = cp * ck
2439 dm [0 , 1 ] = - cp * sk
2540 dm [0 , 2 ] = sp
@@ -29,59 +44,39 @@ def rotation_matrix(phi: float, omega: float, kappa: float) -> np.ndarray:
2944 dm [2 , 0 ] = so * sk - co * sp * ck
3045 dm [2 , 1 ] = so * ck + co * sp * sk
3146 dm [2 , 2 ] = co * cp
32- return dm
3347
48+ # print(dm)
3449
35- class Exterior :
36- """Exterior orientation data structure."""
50+ return None
3751
38- def __init__ (self , x0 = 0.0 , y0 = 0.0 , z0 = 0.0 , omega = 0.0 , phi = 0.0 , kappa = 0.0 , dm = None ):
39- self .x0 = x0
40- self .y0 = y0
41- self .z0 = z0
42- self .omega = omega
43- self .phi = phi
44- self .kappa = kappa
45- self .dm = dm if dm is not None else np .identity (3 , dtype = np .float64 )
52+ exterior_dtype = np .dtype ([
53+ ('x0' , np .float64 ),
54+ ('y0' , np .float64 ),
55+ ('z0' , np .float64 ),
56+ ('omega' , np .float64 ),
57+ ('phi' , np .float64 ),
58+ ('kappa' , np .float64 ),
59+ ('dm' , np .float64 , (3 , 3 ))
60+ ])
61+ Exterior = np .zeros (1 , dtype = exterior_dtype ).view (np .recarray ) # initialize memory
62+ rotation_matrix (Exterior ) # rotation should be a unit matrix
63+ assert np .allclose (np .eye (3 ), Exterior [0 ]['dm' ])
4664
47- def update_rotation_matrix (self ) -> None :
48- """Create rotation matrix using three angles of the camera."""
49- self .dm = rotation_matrix (self .phi , self .omega , self .kappa )
50-
51- def set_rotation_matrix (self , dm : np .ndarray ) -> None :
52- """Set the rotation matrix of the camera."""
53- self .dm = dm
54-
55- def set_pos (self , pos : np .ndarray ) -> None :
56- """Set the position of the camera."""
57- pos = np .array (pos , dtype = np .float64 )
58-
59- if pos .shape != (3 ,):
60- raise ValueError (
61- "Illegal array argument "
62- + str (pos )
63- + " for x, y, z. Expected array/list of 3 numbers"
64- )
65- self .x0 , self .y0 , self .z0 = pos
6665
67- def set_angles (self , angles : np .ndarray ) -> None :
68- """Set the angles of the camera."""
69- self .omega , self .phi , self .kappa = angles
66+ # rotation_matrix(exterior) # inplace update of the rotation matrix
7067
71- # adjust rotation matrix
72- self .update_rotation_matrix ()
7368
74- def increment_attribute (self , attr_name , increment_value ):
75- """Update the value of an attribute by increment_value."""
76- if hasattr (self , attr_name ):
77- setattr (self , attr_name , getattr (
78- self , attr_name ) + increment_value )
69+ # def increment_attribute(self, attr_name, increment_value):
70+ # """Update the value of an attribute by increment_value."""
71+ # if hasattr(self, attr_name):
72+ # setattr(self, attr_name, getattr(
73+ # self, attr_name) + increment_value)
7974
80- def __repr__ (self ) -> str :
81- """Return a string representation of the Exterior object."""
82- output = f"Exterior: x0={ self . x0 } , y0={ self . y0 } , z0={ self . z0 } \n "
83- output += f"omega={ self . omega } , phi={ self . phi } , kappa={ self . kappa } \n "
84- return output
75+ # def __repr__(self) -> str:
76+ # """Return a string representation of the Exterior object."""
77+ # output = f"Exterior: x0={self['x0'] }, y0={self['y0'] }, z0={self['z0'] }\n"
78+ # output += f"omega={self[' omega'] }, phi={self[' phi'] }, kappa={self[' kappa'] }\n"
79+ # return output
8580
8681
8782class Interior :
@@ -100,10 +95,6 @@ def set_back_focal_distance(self, cc: float) -> None:
10095 """Set the back focal distance of the camera."""
10196 self .cc = cc
10297
103- def default_glass_vec () -> np .ndarray :
104- """Return default glass vector."""
105- return vec_set (0.0 , 0.0 , 1.0 )
106-
10798class ap_52 :
10899 """Additional parameters for distortion correction."""
109100
@@ -149,7 +140,7 @@ class Calibration:
149140
150141 def __init__ (self , ext_par = None , int_par = None , glass_par = None , added_par = None , mmlut = None ):
151142 if ext_par is None :
152- ext_par = Exterior ()
143+ ext_par = Exterior . copy ()
153144 if int_par is None :
154145 int_par = Interior ()
155146 if glass_par is None :
@@ -165,6 +156,7 @@ def __init__(self, ext_par=None, int_par=None, glass_par=None, added_par=None, m
165156 self .added_par = added_par
166157 self .mmlut = mmlut
167158
159+
168160 @classmethod
169161 def from_file (cls , ori_file : str , add_file : str ):
170162 """
@@ -197,7 +189,7 @@ def from_file(cls, ori_file: str, add_file: str):
197189 # skip line
198190 fp .readline ()
199191
200- # read 3 lines and set a rotation matrix
192+ # read 3 lines of rotation matrix, but recalculate it from angles
201193 _ = [[float (x ) for x in fp .readline ().split ()] for _ in range (3 )]
202194
203195 # I skip reading rotation matrix as it's set up by set_angles.
@@ -254,23 +246,42 @@ def write(self, ori_file: str, addpar_file: str):
254246 if not success :
255247 raise IOError ("Failed to write ori file" )
256248
249+
250+ def increment_attribute (self , attr_name , increment_value ):
251+ """Update the value of an attribute by increment_value."""
252+ if hasattr (self , attr_name ):
253+ setattr (self , attr_name , getattr (
254+ self , attr_name ) + increment_value )
255+
256+ def update_rotation_matrix (self ) -> None :
257+ """Update the rotation matrix of the exterior orientation."""
258+ rotation_matrix (self .ext_par )
259+
257260 def set_rotation_matrix (self , dm : np .ndarray ) -> None :
258261 """Set exterior rotation matrix."""
259262 if dm .shape != (3 , 3 ):
260263 raise ValueError ("Illegal argument for exterior rotation matrix" )
261- self .ext_par . set_rotation_matrix ( dm )
264+ self .ext_par [ 0 ][ 'dm' ] = dm
262265
263- def set_pos (self , x_y_z_np : np .ndarray ) -> None :
266+ def set_pos (self , pos : np .ndarray ) -> None :
264267 """
265268 Set exterior position.
266269
267270 Parameter: x_y_z_np - numpy array of 3 elements for x, y, z.
268271 """
269- self . ext_par . set_pos ( x_y_z_np )
272+ pos = np . array ( pos , dtype = np . float64 )
270273
271- def get_pos (self ):
274+ if pos .shape != (3 ,):
275+ raise ValueError (
276+ "Illegal array argument "
277+ + str (pos )
278+ + " for x, y, z. Expected array/list of 3 numbers"
279+ )
280+ self .ext_par ['x0' ], self .ext_par ['y0' ], self .ext_par ['z0' ] = pos
281+
282+ def get_pos (self ) -> np .ndarray :
272283 """Return array of 3 elements representing exterior's x, y, z."""
273- return np .r_ [self .ext_par . x0 , self .ext_par . y0 , self .ext_par . z0 ]
284+ return np .r_ [self .ext_par [ 'x0' ] , self .ext_par [ 'y0' ] , self .ext_par [ 'z0' ] ]
274285
275286 def set_angles (self , o_p_k_np : np .ndarray ) -> None :
276287 """
@@ -285,15 +296,16 @@ def set_angles(self, o_p_k_np: np.ndarray) -> None:
285296 f"Illegal array argument { o_p_k_np } for "
286297 "omega, phi, kappa. Expected array or list of 3 float"
287298 )
288- self .ext_par .set_angles (o_p_k_np )
299+ self .ext_par ['omega' ], self .ext_par ['phi' ], self .ext_par ['kappa' ] = o_p_k_np
300+ self .update_rotation_matrix ()
289301
290- def get_angles (self ):
302+ def get_angles (self ) -> np . ndarray :
291303 """Return an array of 3 elements representing omega, phi, kappa."""
292- return np .r_ [self .ext_par . omega , self .ext_par . phi , self .ext_par . kappa ]
304+ return np .r_ [self .ext_par [ ' omega' ] , self .ext_par [ ' phi' ] , self .ext_par [ ' kappa' ] ]
293305
294- def get_rotation_matrix (self ):
306+ def get_rotation_matrix (self ) -> np . ndarray :
295307 """Return a 3x3 numpy array that represents Exterior's rotation matrix."""
296- return self .ext_par . dm
308+ return self .ext_par [ 'dm' ]. copy ()
297309
298310 def set_primary_point (self , prim_point_pos : np .ndarray ) -> None :
299311 """
@@ -418,7 +430,7 @@ def copy(self, new_copy):
418430
419431
420432def write_ori (
421- ext_par : Exterior ,
433+ ext_par : np . ndarray ,
422434 int_par : Interior ,
423435 glass : np .ndarray ,
424436 added_par : ap_52 ,
@@ -429,9 +441,9 @@ def write_ori(
429441 success = False
430442
431443 with open (filename , "w" , encoding = "utf-8" ) as fp :
432- fp .write (f"{ ext_par . x0 :.8f} { ext_par . y0 :.8f} { ext_par . z0 :.8f} \n " )
433- fp .write (f"{ ext_par . omega :.8f} { ext_par . phi :.8f} { ext_par . kappa :.8f} \n \n " )
434- for row in ext_par . dm :
444+ fp .write (f"{ ext_par [ 0 ][ 'x0' ] :.8f} { ext_par [ 0 ][ 'y0' ] :.8f} { ext_par [ 0 ][ 'z0' ] :.8f} \n " )
445+ fp .write (f"{ ext_par [ 0 ][ ' omega' ] :.8f} { ext_par [ 0 ][ ' phi' ] :.8f} { ext_par [ 0 ][ ' kappa' ] :.8f} \n \n " )
446+ for row in ext_par [ 0 ][ 'dm' ] :
435447 fp .write (f"{ row [0 ]:.7f} { row [1 ]:.7f} { row [2 ]:.7f} \n " )
436448 fp .write (f"\n { int_par .xh :.4f} { int_par .yh :.4f} \n { int_par .cc :.4f} \n " )
437449 fp .write (
@@ -470,16 +482,16 @@ def read_ori(ori_file: str, add_file: str) -> Calibration:
470482 return ret
471483
472484
473- def compare_exterior (e1 : Exterior , e2 : Exterior ) -> bool :
485+ def compare_exterior (e1 : np . ndarray , e2 : np . ndarray ) -> bool :
474486 """Compare exterior orientation parameters."""
475487 return (
476- np .allclose (e1 . dm , e2 . dm , atol = 1e-6 )
477- and (e1 . x0 == e2 . x0 )
478- and (e1 . y0 == e2 . y0 )
479- and (e1 . z0 == e2 . z0 )
480- and (e1 . omega == e2 . omega )
481- and (e1 . phi == e2 . phi )
482- and (e1 . kappa == e2 . kappa )
488+ np .allclose (e1 [ 'dm' ] , e2 [ 'dm' ] , atol = 1e-6 )
489+ and (e1 [ 'x0' ] == e2 [ 'x0' ] )
490+ and (e1 [ 'y0' ] == e2 [ 'y0' ] )
491+ and (e1 [ 'z0' ] == e2 [ 'z0' ] )
492+ and (e1 [ ' omega' ] == e2 [ ' omega' ] )
493+ and (e1 [ ' phi' ] == e2 [ ' phi' ] )
494+ and (e1 [ ' kappa' ] == e2 [ ' kappa' ] )
483495 )
484496
485497
0 commit comments