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.

hej, im acutally having the same opinion as you. Im doing my Masterthesis acutally and try to recalculate the cameraCalibration Error. The documentation calculates it wrong.