-
-
Notifications
You must be signed in to change notification settings - Fork 56.2k
undistortPoints function implementation is not working the near 180 degree points #26174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Hi, I am interested in working on this issue! I have experience in image processing and calibration with OpenCV. |
When I tried to transform some image coordinates within the area of image of a camera to world coordinates using undistortPoints and then back to image coordinates using projectPoints, I could get coordinates very close to the ones before transformation (undistortPoints) in most cases but could not get a close coordinates for the ones far from the image center. Here, I used undistortPoints and projectPoints both in https://github.com/opencv/opencv/blob/4.x/modules/calib3d/src/fisheye.cpp. I tried to find the cause and reached the conclusion that it is due to the implementation of undistortPoints, because when I implemented locally an alternative function for undistortPoints (in Python) which meets the following policies, I could get coordinates very close to the ones before transformation even in cases where the image points are far from the image center. Policies:
Because I don't have the skill to implement the above in C++, I would be very grateful if someone skilled enough implemented considering the above. Actually, in my local implementation, I tried initializing theta with the following value which lead to a quicker convergence. I believe it will be beneficial if the initialization described above can be chosen as an option. Personally, I think this initialization could also serve as a default initialization. I'll be grateful if this is considered when implementing. |
Hi there I briefly wanted to chime in. I fixed the issue with #27199. The routine in Therefore: Only the solution thetas need to be between 0 and pi/2. Also here is a sanity check, which undistorts and redistorts a regular image grid: import cv2
import numpy as np
import matplotlib.pyplot as plt
h, w = 384, 576
xv, yv = np.meshgrid(np.arange(w), np.arange(h))
distorted_points = np.vstack([xv.ravel(), yv.ravel()]).T.astype("float32") # (N,2)
intrinsics = np.array([2.0268e+02, 2.0289e+02, 2.8999e+02, 1.9243e+02, 5.6743e-02, -4.8614e-03, -1.1769e-04, -2.9011e-04])
K = np.zeros((3,3))
K[0,0] = intrinsics[0]
K[1,1] = intrinsics[1]
K[0,2] = intrinsics[2]
K[1,2] = intrinsics[3]
K[2,2] = 1
undistorted_points = cv2.fisheye.undistortPoints(
distorted=distorted_points[:, np.newaxis, :],
K=K,
D=intrinsics[4:],
R=None,
P=np.eye(3)
)
plt.scatter(undistorted_points[:,0,0], undistorted_points[:,0,1])
plt.axis("equal")
plt.show()
redistorted_points = cv2.fisheye.distortPoints(
undistorted=undistorted_points,
K=np.eye(3),
D=intrinsics[4:],
)
plt.scatter(redistorted_points[:,0,0], redistorted_points[:,0,1], color='red')
plt.axis("equal")
plt.show() Before Fix (=> Clamped edges) : Note: Yes, the undistortion really stretches out the pixels. The furthest point is undistorted to (-121.6, -80.6). We can calculate the angle in degrees as follows: print(np.degrees(np.arctan(np.linalg.norm(undistorted_points[0])))) This prints 89.60795, which corresponds to a FOV to 179.215 degrees. Values which are not in the valid range are set to (-1000000.0, -1000000.0) as before. EDIT: Note for @gitcheol. I don't think the incident angle is sqrt(atan2(theta_d**2+1)/theta_d)). I think we can only solve for the incident angle by using the root finder. Usually atan(r) = theta only holds for the undistorted (rectified) points. But feel free to correct me. Hope this helps. |
@Tomoyuki-Ohtsuki I also tried implementing your strategy but found only little difference in terms of average iterations. Do you have some more examples I could try? |
Thank you so much for fixing the problem! k1 = 0.07394581288099289 somehow, I get negative values for inclination of theta_d, which is calculated by 1 + 3 * k0_theta2 + 5 * k1_theta4 + 7 * k2_theta6 + 9 * k3_theta8 (found in https://github.com/opencv/opencv/blob/4.x/modules/calib3d/src/fisheye.cpp). The followings are plots of theta_d and inclination of theta_d (theta_d') and, weirdly, looking at the plots of theta_d, it doesn't look like it has a negative inclination. Due to the fact that inclination is calculated as a negative value, in many cases, when I executed the calculation, it went through an infinite loop, resulting in reaching the maximum iteration. I post an example for the case where theta_d = 1.5653739337616015 and the initial value of theta is theta_d. theta before update: 1.5653739337616015 theta before update: 1.5707963267948966 theta before update: 1.5707963267948966 repetition of the above 4 lines continues theta after update = theta before update - theta_fix This infinite loop does not happen when I use the initial value that I proposed. I wonder why I get negative value of inclination which lead to non-convergence of Newton method. It's possible that this is true only in the case of python but I haven't verified. I, actually, analyzed a case of another camera (distortion) where k1 = 0.09150316566228867 In this case, for example, the numbers of iteration were as follows for theta_d = 2.0245863572631397 . 546 for initial theta = theta_d The improvement is much smaller compared to the first camera, but I still believe it is worth implementing because I assume it achieves quicker convergence for a major part of existing fisheye cameras. |
@Tomoyuki-Ohtsuki How did you calculate that the derivative / theta_fix is negative? I plotted both functions and they should never produce something negative unless theta_d is negative(? I think). Can you show me how you logged it the values. Also I currently don't understand why it there is clipping inside the loop? Could you clarify. Any kind of code snippet would also be great such that I can properly debug it 👍 EDIT: Here's a plot of f(x) and the derivative with your first example: ![]() So I currently cannot see how for 1.57 theta_fix could be negative EDIT2: Heres a sympy snippet with the calculation: from sympy import is_monotonic, symbols, Interval, pi, diff
theta = symbols('theta')
k1 = 0.07394581288099289
k2 = 0.05758174881339073
k3 = -0.02578509785234928
k4 = 0.004517474211752415
theta_d = 1.5653739337616015
theta_curr = theta_d
f_theta = theta + k1 * theta ** 3 + k2 * theta ** 5 + k3 * theta ** 7 + k4 * theta ** 9 - theta_d
f_diff = diff(f_theta)
theta_fix = f_theta.subs('theta', theta_d) / f_diff.subs('theta', theta_d)
print(f"before update : {theta_curr}")
theta_curr = theta_curr - theta_fix
print(f"theta_fix : {theta_fix}")
print(f"after update : {theta_curr}") Prints:
(Note that the solution for this theta_d lies at 1.302) |
@mokrueger
D contains distortion coefficients. |
Uh oh!
There was an error while loading. Please reload this page.
opencv/modules/calib3d/src/fisheye.cpp
Line 439 in b2e118e
when i use this function with fisheye image, there are some error near the 180 degrees. so i checked the codes of original source.
in opencv source code, theta_d means the distance from origin to xy plane. but according to the annotation, the theta_d means the incidence of points. this parts of code may occur error. we need to check the codes.
i think the incidence angle of this should be the sqrt(atan2(theta_d**2+1)/theta_d)) and need some other way exceptions for out of 180 degrees.
I am open to work on this error, please feel free to comment me back if any are interested in fixing this problem.
The text was updated successfully, but these errors were encountered: