I have been trying to replicate what initUndistortRectifyMap does for my application. For some reason, when I do manual undistortion, I get different result than using initUndistortRectifyMap.
I feel I am following the steps to the letter, but somehow I get different outcome. Any insights will be greatly appreciated
Here is the undistorted image applying manually the undistortion equations (don’t mind the white lines in the picture, those are missing pixels that I didn’t interpolate for code simplicity). The forum doesn’t allow me to show two images in the same post but initUndistortRectifyMap-undistorted image looks more pincushiony for my camera parameters.
Here’s the code I used to repro the image above
import numpy as np
import cv2 as cv
# Generating an image of 2160x3840 pixels
# I am using the Charuco board generator that comes with OpenCV,
# (it can be any arbitrary image of 2160x3840 pixels)
num_squares_x = 32
num_squares_y = 18
square_width_m = 0.05
marker_width_m = 0.04
dictionary = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_6X6_1000)
board = cv.aruco.CharucoBoard(
(num_squares_x, num_squares_y),
square_width_m, marker_width_m, dictionary
)
board.setLegacyPattern(True)
img = board.generateImage((num_squares_x*120, num_squares_y*120))
h, w = img.shape[:2]
# Camera intrinsic parameters
f = 2931.87670606
cx = 1923.04908337
cy = 1070.12953259
# Distortion parameters (radial distortion only for my application)
k1 = 0.12027473
k2 = -0.49076343
k3 = 0.66055698
p1 = 0.00000000
p2 = 0.00000000
# my camera has same fx and fy
K = np.array([
[ f, 0.0, cx],
[0.0, f, cy],
[0.0, 0.0, 1.0]], dtype=np.float32)
dist = np.array([[k1, k2, p1, p2, k3]], dtype=np.float32)
# Method 1: using initUndistortRectifyMap (barrel)
map1, map2 = cv.initUndistortRectifyMap(K, dist, None, K, img.shape[:2][::-1], cv.CV_32FC1)
undistorted_imgv1 = cv.remap(img, map1, map2, cv.INTER_LINEAR)
# Method2 : doing manual undistortion (pincushion)
# (skiping pixel interpolation for simplicity)
undistorted_imgv2 = np.ones((h, w, 3), dtype=np.uint8) * 255
# Generating all the undistorted u',v' pixel coordinates from
# the original u,v pixel coordinates, using the camera intrinsics
# for the image with dimensions (h, w)
uv = np.meshgrid(range(0, w), range(0, h))
uu = uv[0].flatten()
vv = uv[1].flatten()
for i in range(len(uu)):
u = uu[i]
v = vv[i]
xp = (u - cx) / f
yp = (v - cy) / f
r2 = xp**2 + yp**2
r4 = r2**2
r6 = r2**3
gamma = 1 + k1*r2+ k2*r4+ k3*r6
xpp = xp * gamma + 2*p1*xp*yp + p2*(r2 + 2*xp**2)
ypp = yp * gamma + p1*(r2+ 2*yp**2) + 2*p2*xp*yp
new_uv=np.dot(K, np.array([xpp, ypp, 1]).reshape(3,1)).flatten()
up = new_uv[0] / new_uv[2]
vp = new_uv[1] / new_uv[2]
# Check if the new pixel coordinates are within the bounds of the image
if up < 0 or up >= w or vp < 0 or vp >= h:
continue
undistorted_imgv2[int(vp), int(up)] = img[int(v), int(u)]
cv.imwrite("original_image.png", img)
cv.imwrite("undistorted_image_initUndistortRectifyMap.png", undistorted_imgv1)
cv.imwrite("undistorted_manual.png", undistorted_imgv2)