1+ from copy import deepcopy
2+ import cv2
3+ import numpy as np
4+ from math import sqrt
5+
6+ class CustomCalibrator ():
7+ def __init__ (self , points_3d ):
8+ self .points_2d = []
9+ self .img = None
10+ self .clean_img = None
11+ self .video = '/dev/video2'
12+ self .points_3d = points_3d
13+
14+ def take_points (self , event , x , y , flags , param ):
15+ if event == cv2 .EVENT_LBUTTONDOWN :
16+ self .points_2d .append ((x , y ))
17+ self .img = cv2 .circle (self .img , (x , y ), 3 , (0 , 0 , 255 ), 3 )
18+
19+ def find_homography (self ):
20+ """Find homography matrix"""
21+ # check point's length
22+ if len (self .points_2d ) != 6 :
23+ raise Exception ("Not enough points!" )
24+
25+ self .points_2d = np .array (self .points_2d , dtype = np .float32 )
26+ self .points_3d = np .array (self .points_3d , dtype = np .float32 )
27+
28+ # Make A, B matrix
29+ A = None
30+ B = None
31+ for i in range (len (self .points_2d )):
32+ rx , ry , rz = self .points_3d [i ]
33+ u , v = self .points_2d [i ]
34+ A_matrix = np .array ([[rx , ry , rz , 1 , 0 , 0 , 0 , 0 , - u * rx , - u * ry , - u * rz ],
35+ [0 , 0 , 0 , 0 , rx , ry , rz , 1 , - v * rx , - v * ry , - v * rz ]],
36+ dtype = np .float32 )
37+ B_matrix = np .array ([[u , v ]], dtype = np .float32 )
38+ if i == 0 :
39+ A = A_matrix .copy ()
40+ B = B_matrix .copy ()
41+ else :
42+ A = np .vstack ((A , A_matrix ))
43+ B = np .hstack ((B , B_matrix ))
44+
45+ # Before pseudo inverse, Check det(A.T @ A)
46+ det = np .linalg .det ((A .T @ A ))
47+ if det == 0 :
48+ # raise Exception("(A.T @ A) is Singular Matrix!!!")
49+ print (A .T @ A )
50+
51+ # Do pseudo inverse
52+ V = np .linalg .inv (A .T @ A ) @ A .T @ B .T
53+ V = V .T
54+ return V
55+
56+ def calibrate_homography (self , V ):
57+ # decompose matrix items
58+ h11 , h12 , h13 , h14 , h21 , h22 , h23 , h24 , h31 , h32 , h33 = V [0 ].tolist () # squeeze를 쓴다면 더 세련될 것.
59+ print (h11 , h12 , h13 , h14 , h21 , h22 , h23 , h24 , h31 , h32 , h33 )
60+ # 1) calculate tz
61+ tz = 1.0 / sqrt (h31 ** 2 + h32 ** 2 + h33 ** 2 )
62+ print (tz )
63+ # 2) calculate R3
64+ R3 = tz * np .array ([[h31 , h32 , h33 ]])
65+ # 3) calculate u0
66+ u0 = tz * np .array ([[h11 , h12 , h13 ]]) @ R3 .T
67+ # 4) calculate v0
68+ v0 = tz * np .array ([[h21 , h22 , h23 ]]) @ R3 .T
69+ # 5) calculate fx
70+ fx = tz * np .array ([[h11 , h12 , h13 ]]) - u0 * R3
71+ fx = np .linalg .norm (fx )
72+ # 6) calculate fy
73+ fy = tz * np .array ([[h21 , h22 , h23 ]]) - v0 * R3
74+ fy = np .linalg .norm (fy )
75+ # 7) ca;culate R1
76+ R1 = tz / fx * np .array ([[h11 , h12 , h13 ]]) - u0 / fx * R3
77+ # 8) calculate R2
78+ R2 = tz / fy * np .array ([[h21 , h22 , h23 ]]) - v0 / fy * R3
79+ # 9) calculate tx
80+ tx = tz / fx * (h14 - u0 )
81+ # 10) calculate ty
82+ ty = tz / fy * (h24 - v0 )
83+
84+ # Return Homography, Intrinsic and Extrinsic matrix
85+ R = np .vstack ((np .vstack ((R1 , R2 )), R3 ))
86+ t = np .array ([[tx , ty , tz ]], dtype = object )
87+ H1 = np .array ([[h11 , h12 , h13 , h14 ]])
88+ H2 = np .array ([[h21 , h22 , h23 , h24 ]])
89+ H3 = np .array ([[h31 , h32 , h33 , 1 ]])
90+ homography_matrix = np .vstack ((np .vstack ((H1 , H2 )), H3 ))
91+ extrinsic_matrix = np .hstack ((R , t .T ))
92+ intrinsic_matrix = np .array ([[fx , 0 , u0 ], [0 , fy , v0 ], [0 , 0 , 1 ]], dtype = np .float32 )
93+ # test_R = R.T
94+ # test_T = -R.T @ t
95+ return homography_matrix , intrinsic_matrix , extrinsic_matrix # , test_R, test_T
96+
97+ def reprojection (self , homography_matrix ):
98+ # Get Reprojected points
99+ reprojected_points = []
100+ for point in self .points_3d :
101+ point = np .append (point , 1 )
102+ reprojected_point = homography_matrix @ point .T
103+ reprojected_point [0 ] /= reprojected_point [2 ]
104+ reprojected_point [1 ] /= reprojected_point [2 ]
105+ reprojected_points .append (reprojected_point .T [:2 ])
106+
107+
108+ # Show 2d proj positions
109+ show_points = np .array (reprojected_points , dtype = np .int32 )
110+ for reproj in show_points :
111+ self .img = cv2 .circle (self .img , tuple (reproj ), 3 , (0 , 255 , 0 ), 3 )
112+ cv2 .imwrite (f"saved_proj_img.png" , self .img )
113+
114+ # Calc Error
115+ point_2d = self .points_2d .astype (np .float32 )
116+ err = np .linalg .norm (point_2d - reprojected_points )
117+ print ("points_2d = " , point_2d )
118+ print ("reproj 2d = " , reprojected_points )
119+ return err
120+
121+ def pipeline (self , ** mode ):
122+ cap = cv2 .VideoCapture (self .video )
123+ cv2 .namedWindow ('img' )
124+ cv2 .setMouseCallback ('img' , self .take_points )
125+ n_save = 0
126+ FIX_IMAGE = False
127+ print (mode )
128+ # get points_2d
129+ if mode ['mode' ] == 'using_video' :
130+ GET_CLEAN_IMG = True
131+ while True :
132+ if not FIX_IMAGE :
133+ ret , self .img = cap .read ()
134+ if ret != None and GET_CLEAN_IMG :
135+ self .clean_img = deepcopy (self .img )
136+ GET_CLEAN_IMG = False
137+
138+ # Add info to IMG
139+ h , w , _ = self .img .shape
140+
141+ # View Image
142+ cv2 .imshow ("img" , self .img )
143+
144+ # key event
145+ key = cv2 .waitKey (1 )
146+ if key == ord ('q' ):
147+ break
148+ elif key == ord ('s' ):
149+ FIX_IMAGE = True
150+ elif key == ord ('e' ):
151+ n_save += 1
152+ cv2 .imwrite (f"saved_img_{ n_save } .png" , self .img )
153+ cap .release ()
154+
155+ elif mode ['mode' ] == 'image_cal' :
156+ self .img = cv2 .imread (mode ['img' ])
157+ self .clean_img = deepcopy (self .img )
158+ while True :
159+ h , w , _ = self .img .shape
160+
161+ # View Image
162+ cv2 .imshow ("img" , self .img )
163+
164+ # key event
165+ key = cv2 .waitKey (1 )
166+ if key == ord ('q' ):
167+ break
168+ elif key == ord ('s' ):
169+ FIX_IMAGE = True
170+ elif key == ord ('e' ):
171+ n_save += 1
172+ cv2 .imwrite (f"saved_img_{ n_save } .png" , self .img )
173+ cap .release ()
174+
175+ elif mode ['mode' ] == 'done_image' :
176+ self .points_2d = [(402 , 324 ), (434 , 300 ), (451 ,113 ), (414 , 124 ), (270 ,172 ), (208 , 315 )]
177+
178+ # find homography items
179+ V = self .find_homography ()
180+
181+ # From homography, find intrinsic and extrinsic
182+ homography , intrinsic , extrinsic = self .calibrate_homography (V ) # , test_R, test_T
183+ print ("homography matrix\n " , homography )
184+ print ("intrinsic matrix\n " , intrinsic )
185+ print ("extrinsic matrix, and shape\n " , extrinsic .shape , extrinsic )
186+
187+ # reproject points to image
188+ err = self .reprojection (homography )
189+
190+ # Show projected image
191+ if mode ['mode' ] != 'done_image' :
192+ self .img = cv2 .putText (self .img , f"err = { err :.3f} " , (20 , 30 ), cv2 .FONT_HERSHEY_SIMPLEX , 1 , (0 , 255 , 0 ), 2 , cv2 .LINE_AA )
193+ cv2 .imshow ("img" , self .img )
194+ cv2 .waitKey (0 )
195+
196+ return homography , intrinsic , extrinsic , self .clean_img
197+
198+ def main ():
199+ # cm 단위로 했음. m단위로 바꾸기.
200+ points_3d = [(3 ,0 ,0 ), (5 ,0 ,0 ), (5 ,0 ,6 ), (3 ,0 ,6 ), (0 ,2 ,5 ), (0 ,5 ,0 )]
201+ cc = CustomCalibrator (points_3d = points_3d )
202+ cc .pipeline ()
203+
204+ if __name__ == "__main__" :
205+ main ()
0 commit comments