-
Notifications
You must be signed in to change notification settings - Fork 0
/
radial_distortion_correction.py
102 lines (86 loc) · 3.3 KB
/
radial_distortion_correction.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import cv2
import numpy as np
import math
# Global variables to store points and image
points = []
image = None
# Mouse callback function to capture points
def click_event(event, x, y, flags, param):
global points, image
if event == cv2.EVENT_LBUTTONDOWN:
points.append((x, y))
if len(points) <= 3:
cv2.circle(image, (x, y), 5, (0, 255, 0), -1)
cv2.imshow("Image", image)
# Function to apply the radial distortion
def apply_radial_distortion(point, center, w):
if w == 0 or point == center:
return point
ru = np.linalg.norm(np.array(point) - np.array(center))
rd = np.arctan(2 * ru * np.tan(w / 2)) / w
distorted = (np.array(point) - np.array(center)) * rd / ru + np.array(center)
return distorted
# Function to correct the radial distortion
def correct_radial_distortion(point, center, w):
if w == 0 or point == center:
return point
rd = np.linalg.norm(np.array(point) - np.array(center))
ru = np.tan(rd * w) / (2 * np.tan(w / 2))
corrected = (np.array(point) - np.array(center)) * ru / rd + np.array(center)
return corrected
# Objective function to minimize
def objective_function(w, points, center):
p1 = correct_radial_distortion(points[0], center, w)
p2 = correct_radial_distortion(points[1], center, w)
p3 = correct_radial_distortion(points[2], center, w)
v1 = (np.array(p2) - np.array(p1)) / np.linalg.norm(np.array(p2) - np.array(p1))
v2 = (np.array(p3) - np.array(p1)) / np.linalg.norm(np.array(p3) - np.array(p1))
loss = np.linalg.norm(np.cross(v1, v2))
return loss
# Gradient Descent implementation
def gradient_descent(points, center, learning_rate=1e-6, tolerance=1e-12, max_iter=1000):
w = 0
eps = 1e-7
for iter in range(max_iter):
w_eps = w + eps
grad = (objective_function(w_eps, points, center) - objective_function(w, points, center)) / eps
new_w = w - learning_rate * grad
delta = abs(new_w - w)
if delta < tolerance:
break
w = new_w
loss = objective_function(w, points, center)
print(f"[{iter}] loss = {loss}, w = {w}")
return w
# Main function
if __name__ == "__main__":
# Read the image
image = cv2.imread("sample_radial.png")
if image is None:
print("Image not found!")
exit(-1)
cv2.namedWindow("Image", cv2.WINDOW_AUTOSIZE)
cv2.setMouseCallback("Image", click_event)
print("Click on three points in the image")
cv2.imshow("Image", image)
cv2.waitKey(0)
if len(points) < 3:
print("Not enough points selected!")
exit(-1)
# Perform gradient descent to find k1 and k2
height, width = image.shape[:2]
center = (width / 2, height / 2)
w = gradient_descent(points, center)
print(f"Found radial distortion coefficients: w = {w}")
# Apply distortion correction to the image
new_image = np.zeros_like(image)
for y in range(height):
for x in range(width):
distorted_point = apply_radial_distortion((x, y), center, w)
xd = int(round(distorted_point[0]))
yd = int(round(distorted_point[1]))
if 0 <= xd < width and 0 <= yd < height:
new_image[y, x] = image[yd, xd]
cv2.imshow("Corrected Image", new_image)
cv2.waitKey(0)
cv2.destroyAllWindows()