OpenCV 2.3 camera calibration

hokiebird picture hokiebird · Apr 5, 2012 · Viewed 10.7k times · Source

I'm trying to use OpenCV 2.3 python bindings to calibrate a camera. I've used the data below in matlab and the calibration worked, but I can't seem to get it to work in OpenCV. The camera matrix I setup as an initial guess is very close to the answer calculated from the matlab toolbox.

import cv2
import numpy as np

obj_points = [[-9.7,3.0,4.5],[-11.1,0.5,3.1],[-8.5,0.9,2.4],[-5.8,4.4,2.7],[-4.8,1.5,0.2],[-6.7,-1.6,-0.4],[-8.7,-3.3,-0.6],[-4.3,-1.2,-2.4],[-12.4,-2.3,0.9],    [-14.1,-3.8,-0.6],[-18.9,2.9,2.9],[-14.6,2.3,4.6],[-16.0,0.8,3.0],[-18.9,-0.1,0.3],    [-16.3,-1.7,0.5],[-18.6,-2.7,-2.2]]
img_points = [[993.0,623.0],[942.0,705.0],[1023.0,720.0],[1116.0,645.0],[1136.0,764.0],[1071.0,847.0],[1003.0,885.0],[1142.0,887.0],[886.0,816.0],[827.0,883.0],[710.0,636.0],[837.0,621.0],[789.0,688.0],[699.0,759.0],[768.0,800.0],[697.0,873.0]]

obj_points = np.array(obj_points)
img_points = np.array(img_points)

w = 1680
h = 1050
size = (w,h)

camera_matrix = np.zeros((3, 3))
camera_matrix[0,0]= 2200.0
camera_matrix[1,1]= 2200.0
camera_matrix[2,2]=1.0
camera_matrix[2,0]=750.0
camera_matrix[2,1]=750.0 

dist_coefs = np.zeros(4)
results = cv2.calibrateCamera(obj_points, img_points,size, 
    camera_matrix, dist_coefs)

Answer

mathematical.coffee picture mathematical.coffee · Apr 5, 2012

First off, your camera matrix is wrong. If you read the documentation, it should look like:

fx  0 cx
 0 fy cy
 0  0  1

If you look at yours, you've got it the wrong way round:

fx  0  0
 0 fy  0
cx cy  1

So first, set camera_matrix to camera_matrix.T (or change how you construct camera_matrix. Remember that camera_matrix[i,j] is row i, column j).

camera_matrix = camera_matrix.T

Next, I ran your code and I see that "can't seem to get it to work" means the following error (by the way - always say what you mean by "can't seem to get it to work" in your questions - if it's an error, post the error. If it runs but gives you weirdo numbers, say so):

OpenCV Error: Assertion failed (ni >= 0) in collectCalibrationData, file /home/cha66i/Downloads/OpenCV-2.3.1/modules/calib3d/src/calibration.cpp, line 3161
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
cv2.error: /home/cha66i/Downloads/OpenCV-2.3.1/modules/calib3d/src/calibration.cpp:3161: error: (-215) ni >= 0 in function collectCalibrationData

I then read the documentation (very useful by the way) and noticed that obj_points and img_points have to be vectors of vectors, because it is possible to feed in sets of object/image points for multiple images of the same chessboard(/calibration points).

Hence:

cv2.calibrateCamera([obj_points], [img_points],size, camera_matrix, dist_coefs)

What? I still get the same error?!

Then, I had a look at the OpenCV python2 samples (in the folder OpenCV-2.x.x/samples/python2), and noticed a calibration.py showing me how to use the calibration functions (never underestimate the samples, they're often better than the documentation!).

I tried to run calibration.py but it doesn't run because it doesn't supply the camera_matrix and distCoeffs arguments, which are necessary. So I modified it to feed in a dummy camera_matrix and distCoeffs, and hey, it works!

The only difference I can see between my obj_points/img_points and theirs, is that theirs has dtype=float32, while mine doesn't.

So, I change my obj_points and img_points to also have dtype float32 (the python2 interface to OpenCV is funny like that; often functions don't work when matrices don't have a dtype):

obj_points = obj_points.astype('float32')
img_points = img_points.astype('float32')

Then I try again:

>>> cv2.calibrateCamera([obj_points], [img_points],size, camera_matrix, dist_coefs)
OpenCV Error: Bad argument 
(For non-planar calibration rigs the initial intrinsic matrix must be specified) 
in cvCalibrateCamera2, file ....

What?! A different error at least. But I did supply an initial intrinsic matrix!

So I go back to the documentation, and notice the flags parameter:

flags – Different flags that may be zero or a combination of the following values:

CV_CALIB_USE_INTRINSIC_GUESS cameraMatrix contains valid initial values of fx, fy, cx, cy that are optimized further

...

Aha, so I have to tell the function explicitly to use the initial guesses I provided:

cv2.calibrateCamera([obj_points], [img_points],size, camera_matrix.T, dist_coefs,
                    flags=cv2.CALIB_USE_INTRINSIC_GUESS)

Hurrah! It works!

(Moral of the story - read the OpenCV documentation carefully, and use the newest version (i.e. on opencv.itseez.com) if you're using the Python cv2 interface. Also, consult the examples in the samples/python2 directory to supplement the documentation. With these two things you should be able to work out most problems.)