Skip to content

Commit 1fcac2f

Browse files
authored
Add image rectification script (#21)
2 parents 8e102c3 + d83c74d commit 1fcac2f

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed

Diff for: scripts/rectify_image_from_distorted_image.py

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import os
2+
from pathlib import Path
3+
4+
import cv2
5+
import numpy as np
6+
import yaml
7+
8+
9+
class ImageRectifier:
10+
def __init__(self, calib_file_path):
11+
# Load the YAML file
12+
with open(calib_file_path, "r") as file:
13+
calib_data = yaml.safe_load(file)
14+
15+
# Restructure the YAML data
16+
camera_model = calib_data["sensor"]["camera_model"]
17+
cameras = calib_data["sensor"]["cameras"]
18+
19+
# Create the desired dictionary structure
20+
self.sensor_dict = {"camera_model": camera_model, "cameras": {camera["label"]: camera for camera in cameras}}
21+
self._init_rectification_maps()
22+
23+
def _init_rectification_maps(self):
24+
for cam_label, cam_data in self.sensor_dict["cameras"].items():
25+
balance = 0.0
26+
scale_factor = 1.0
27+
new_width = int(cam_data["image_width"] * scale_factor)
28+
new_height = int(cam_data["image_height"] * scale_factor)
29+
new_size = (new_width, new_height)
30+
31+
new_K = cv2.fisheye.estimateNewCameraMatrixForUndistortRectify(
32+
convert_to_intrinsic_matrix(np.array(cam_data["intrinsics"])),
33+
np.array(cam_data["extra_params"]),
34+
(new_width, new_height),
35+
np.eye(3),
36+
balance=balance,
37+
new_size=new_size,
38+
)
39+
print(f"{cam_label} has:")
40+
print(new_K)
41+
42+
self.sensor_dict["cameras"][cam_label]["map1"], self.sensor_dict["cameras"][cam_label]["map2"] = (
43+
cv2.fisheye.initUndistortRectifyMap(
44+
convert_to_intrinsic_matrix(np.array(cam_data["intrinsics"])),
45+
np.array(cam_data["extra_params"]),
46+
np.eye(3),
47+
new_K,
48+
new_size,
49+
cv2.CV_32FC1,
50+
)
51+
)
52+
self.sensor_dict["cameras"][cam_label]["rectified_width"] = new_width
53+
self.sensor_dict["cameras"][cam_label]["rectified_height"] = new_height
54+
55+
print(
56+
f"Camera {cam_label} rectified image size: Width={new_width}, Height={new_height}, please copy this value \n"
57+
)
58+
print("")
59+
60+
def process_image(self, image, output_path, cam_label):
61+
# Remap the image
62+
rectified = cv2.remap(
63+
image,
64+
self.sensor_dict["cameras"][cam_label]["map1"],
65+
self.sensor_dict["cameras"][cam_label]["map2"],
66+
interpolation=cv2.INTER_LINEAR,
67+
borderMode=cv2.BORDER_CONSTANT,
68+
)
69+
70+
# Save the rectified image
71+
os.makedirs(os.path.dirname(output_path), exist_ok=True)
72+
cv2.imwrite(str(output_path), rectified)
73+
74+
def process_directory(self, input_dir, output_dir):
75+
input_path = Path(input_dir)
76+
output_path = Path(output_dir)
77+
output_path.mkdir(parents=True, exist_ok=True)
78+
for cam_label, cam_data in self.sensor_dict["cameras"].items():
79+
path_parts = cam_data["topic"].split("/", 1)
80+
img_subfolder = path_parts[0] + path_parts[1].replace("/", "_")
81+
for img_path in input_path.rglob("*"):
82+
if img_path.parent == img_subfolder:
83+
image = cv2.imread(str(img_path))
84+
if image is None:
85+
print(f"Failed to read image: {img_path}")
86+
continue
87+
rel_path = img_path.relative_to(input_path)
88+
output_file = output_path / rel_path
89+
self.process_image(image, output_file, cam_label)
90+
# print(f"Processed: {img_path.name}")
91+
92+
93+
def convert_to_intrinsic_matrix(intrinsics):
94+
if len(intrinsics) != 4:
95+
raise ValueError("Intrinsics must be a list or array with exactly 4 elements: [fx, fy, cx, cy].")
96+
fx, fy, cx, cy = intrinsics
97+
intrinsic_matrix = np.array([[fx, 0, cx], [0, fy, cy], [0, 0, 1]], dtype=np.float64)
98+
return intrinsic_matrix
99+
100+
101+
if __name__ == "__main__":
102+
"""
103+
Args:
104+
- calib_file_path: base calibration file
105+
- distorted_image_input_path: distorted images folder directory
106+
- rectified_image_output_path: output folder name to save undistorted rectified images
107+
"""
108+
109+
calib_file_path = "./config/sensor.yaml"
110+
distorted_image_input_path = "/data/2024-03-12-keble-college-01/images"
111+
rectified_image_output_path = "/data/2024-03-12-keble-college-01/test/images_rectified"
112+
113+
# Initialize rectifier
114+
rectifier = ImageRectifier(calib_file_path)
115+
rectifier.process_directory(distorted_image_input_path, rectified_image_output_path)
116+
print("Please copy and paste output rectified intrinsics")

0 commit comments

Comments
 (0)