Skip to content

Commit cc71576

Browse files
committed
Add DLT_calib and epipolar line
1 parent 68174d3 commit cc71576

6 files changed

Lines changed: 357 additions & 0 deletions

File tree

scripts/DLT_calib.py

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
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()

scripts/DLT_calib_done.png

388 KB
Loading

scripts/DLT_calib_undo.png

387 KB
Loading

scripts/DLT_epipolar_line.py

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
from logging import raiseExceptions
2+
import cv2
3+
import numpy as np
4+
from copy import deepcopy
5+
from math import sqrt
6+
from Homework_02 import CustomCalibrator
7+
8+
def screw(vec1x3):
9+
return np.array([[0, -vec1x3[2], vec1x3[1]], [vec1x3[2], 0, -vec1x3[0]], [-vec1x3[1], vec1x3[0], 0]])
10+
11+
def inv_intrinsic(intri):
12+
fx, cx, fy, cy = intri[0, 0], intri[0, 2], intri[1, 1], intri[1, 2]
13+
return np.array([[1./fx, 0., -cx/fx], [0, 1./fy, -cy/fy], [0., 0., 1.]])
14+
15+
class CustomEpipolarFinder():
16+
def __init__(self, s1_datas, s2_datas):
17+
"""
18+
Get scene_1, scene_2 intrinsic, extrinsic datas, original img. Make Camera Matrix Inverse for each scene.
19+
"""
20+
self.points_2d = []
21+
if len(s1_datas) == 3:
22+
s1_Intrinsic = s1_datas[0]
23+
s1_Extrinsic = s1_datas[1]
24+
self.s1_img = s1_datas[2]
25+
self.clean_img_1 = deepcopy(self.s1_img)
26+
else:
27+
raise Exception("Short datas. May be no images?")
28+
29+
if len(s2_datas) == 3:
30+
s2_Intrinsic = s2_datas[0]
31+
s2_Extrinsic = s2_datas[1]
32+
self.s2_img = s2_datas[2]
33+
self.clean_img_2 = deepcopy(self.s2_img)
34+
else:
35+
raise Exception("Short datas. May be no images?")
36+
37+
# Check Intrinsic Similarity and Get Inv Intrinsic mat
38+
self.s1_inv_intrinsic = inv_intrinsic(s1_Intrinsic)
39+
self.s2_inv_intrinsic = inv_intrinsic(s2_Intrinsic)
40+
41+
# Get rotation and translation inform
42+
c1_M_r = np.vstack((s1_Extrinsic, np.array([[0.0, 0.0, 0.0, 1.0]]))).astype(float)
43+
c2_M_r = np.vstack((s2_Extrinsic, np.array([[0.0, 0.0, 0.0, 1.0]]))).astype(float)
44+
c1_M_c2 = c1_M_r @ np.linalg.inv(c2_M_r)
45+
self.rot_2to1 = c1_M_c2[:3, :3]
46+
trans_2to1 = c1_M_c2[:3, 3]
47+
self.trans_2to1 = screw(trans_2to1) # 3x3 screw sym matrix
48+
49+
def take_points(self, event, x, y, flags, param):
50+
"""
51+
L = Take interesting point on img!
52+
R = Delete point before chosen.
53+
"""
54+
if event == cv2.EVENT_LBUTTONDOWN:
55+
print("left")
56+
self.points_2d.append((x, y))
57+
cv2.circle(self.s1_img, (x, y), 3, (0, 0, 255), 3)
58+
59+
elif event == cv2.EVENT_RBUTTONDOWN:
60+
print("right")
61+
self.point_2d[0], self.point_2d[1] = 0, 0
62+
self.points_2d.pop()
63+
cv2.circle(self.s1_img, (x, y), 3, (0, 0, 255), 3)
64+
65+
def get_epipolarline(self):
66+
"""
67+
Find Epipolar line.
68+
"""
69+
# Make point 2d shape (u, v, 1)
70+
self.points_2d = np.array([(u, v, 1) for (u, v) in self.points_2d])
71+
72+
# Calculate Fundamental Matrix
73+
Fundamental = self.s1_inv_intrinsic.T @ self.trans_2to1.T @ self.rot_2to1 @ self.s2_inv_intrinsic
74+
75+
# Calculate coefficient of point_2d
76+
coeffs = np.array([p2d @ Fundamental for p2d in self.points_2d])
77+
78+
# Draw Line on Img
79+
x_range = np.linspace(0, self.s2_img.shape[1] - 1, 50)
80+
ys_range = []
81+
for coeff in coeffs:
82+
y_range = -coeff[0] / coeff[1] * x_range - coeff[2] / coeff[1]
83+
ys_range.append(y_range)
84+
85+
# 1) Filter Outlier
86+
good_results = []
87+
for y_range in ys_range:
88+
good_result = []
89+
for i, y in enumerate(y_range):
90+
if y < 0 or y > self.s1_img.shape[0]:
91+
continue
92+
else:
93+
good_result.append((x_range[i], y))
94+
good_results.append(good_result)
95+
96+
# 2) Use left and right value of results and draw it.
97+
for good_result in good_results:
98+
# get data from it
99+
left, right = good_result[0], good_result[-1]
100+
# Make it tuple
101+
left, right = (int(left[0]), int(left[1])), (int(right[0]), int(right[1]))
102+
cv2.line(self.s2_img, left, right, (0, 255, 0), 3)
103+
104+
return coeffs
105+
106+
def pipeline(self):
107+
"""
108+
Get images and check point which I want to find epipolar line.
109+
"""
110+
# From s1_img, pick interesting 2d point.
111+
cv2.namedWindow('img')
112+
cv2.setMouseCallback('img', self.take_points)
113+
while True:
114+
cv2.imshow("img", self.s1_img)
115+
if cv2.waitKey(0) == ord('q'):
116+
break
117+
# Find epipolar line on s2_img
118+
coeffs = self.get_epipolarline()
119+
# Check Correspondence
120+
print(f"coeffs of epipolar line = \n{coeffs}")
121+
# Show Img
122+
img = np.hstack((self.s1_img, self.s2_img))
123+
cv2.imshow("end", img)
124+
cv2.waitKey(0)
125+
# Save Img
126+
cv2.imwrite("good_epipolar_img.png", img)
127+
128+
return
129+
130+
131+
def main():
132+
points_3d = [(1,0,0), (3,0,1.9), (5,0,0), (0,1, 1.9), (0, 3, 0), (0, 5, 1.9)]
133+
mode1 = {'mode': 'image_cal', 'img': 'img1.png'}
134+
mode2 = {'mode': 'image_cal', 'img': 'img2.png'}
135+
136+
cc1 = CustomCalibrator(points_3d=points_3d)
137+
cc2 = CustomCalibrator(points_3d=points_3d)
138+
139+
140+
_, s1_Intrinsic, s1_Extrinsic, s1_img = cc1.pipeline(**mode1)
141+
s1_datas = (s1_Intrinsic, s1_Extrinsic, s1_img)
142+
if s1_datas != None: print("s1 success!")
143+
144+
_, s2_Intrinsic, s2_Extrinsic, s2_img = cc2.pipeline(**mode2)
145+
s2_datas = (s2_Intrinsic, s2_Extrinsic, s2_img)
146+
if s2_datas != None: print("s2 success!")
147+
148+
cef = CustomEpipolarFinder(s1_datas=s1_datas, s2_datas=s2_datas)
149+
cef.pipeline()
150+
151+
if __name__ == "__main__":
152+
main()

scripts/img1.png

218 KB
Loading

scripts/img2.png

230 KB
Loading

0 commit comments

Comments
 (0)