False Computation of Reprojection Error in Python Camera Calibration Tutorial

Hello,

i wanted to ask, whether there is an error in the python camera calibration tutorial:
https://docs.opencv.org/5.x/dc/dbb/tutorial_py_calibration.html

in the very bottom of the webpage the re-projection error is computed in the following way:

mean_error = 0
for i in range(len(objpoints)):
    imgpoints2, _ = cv.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
    error = cv.norm(imgpoints[i], imgpoints2, cv.NORM_L2)/len(imgpoints2)
    mean_error += error
print( "total error: {}".format(mean_error/len(objpoints)) )

I am specifically wondering if the use of the L2 norm is appropriate in this case.
I have prepared a small minimal example where a=imgpoints[i] (the found chess tile corners) and b = imgpoints2 (the projected imagepoints). the columns of a and b represent the x and y coordinates for 3 total corners:

import numpy
import cv2

a = np.array([[1.1,1.2],
              [0.3,1.4],
              [1.5,0.6]])

b = np.array([[1,1],
              [0,1],
              [1,0]]).astype(float)

opencv_error = cv2.norm(a, b, cv2.NORM_L2)/3
opencv_numpy_error = np.sqrt(np.sum(np.square(a - b)))/3 #the same implemented in numpy
#sqrt(0.1^2 + 0.2^2 + 0.3^2 + 0.4^2 + 0.5^2 + 0.6^2)/3

mean_error_alternative = np.sum(np.sqrt(np.sum(np.square(a-b),axis = 1)))/3 # this should be the correct computation imo
#(sqrt(0.1^2 + 0.2^2) + sqrt(0.3^2 + 0.4^2) + sqrt(0.5^2 + 0.6^2))/3

print(opencv_error)
print(opencv_numpy_error)
print(mean_error_alternative)

My issue is that the l2 norm error (“opencv_error”) does not actually take into account the 2D differences per corner (sqrt(deltax^2 + deltay^2)), but sums up all individual squared differences. Or is there something I am missing?

Best

ViaAppia

1 Like

The L2 norm should compute the Euclidean distance - are you saying that it does not? Can you include the results of your program (what gets printed for opencv_error, opencv_numpy_error, mean_error_alternative?)

Dear Steven,
thank you for your reply!

The printed results of my excectuted code are:
opencv_error: 0.31797973380564853
opencv_numpy_error: 0.31797973380564853
mean_error_alternative: 0.5015439217802148

It is computing the Euclidian distance, but it is handling it as if it is computing the error for a 6D problem (sqrt((1.1-1)^2 + (1.2-1)^2 + (0.3-0)^2 + (1.4-1)^2 + (1.5-1)^2 + (0.6-0)^2)/3) and is making no difference between x and y values.
However, in the example I posted we have only three corners and the re-projection error is defined as the 2D difference between a found point (e.g. x,y = (1.1,1.2)) and projected point (e.g. x,y = (1,1)) which would be sqrt((1.1-1)^2 + (1.2-1)^2) for the first point, which imo has to be repeated for all three point pairs and then averaged.

np.linalg.norm with axis=… argument? then you get a vector of distances.

Yes, with np.linalg.norm I can substitute part of my code:
from:

  1. np.sum(np.sqrt(np.sum(np.square(a-b),axis = 1)))/3
    to:
  2. np.sum(np.linalg.norm(a-b, axis = 1))/3
    which both yield 0.5015439217802148 as the error.

However the way, the error is computed on OpenCV: Camera Calibration
still differs from this approach and yields a different error (0.31797973380564853), which in my opinion makes little sense.