Camera calibration is a useful tool to allow a developer to undistort a camera using image properties via OpenCV. This not only increases accuracy, but also is a pre-ample before delving into stereo-vision with OpenCV.
OpenCV [conventionlly] uses a Chessboard pattern to calibrate a camera. Presenting a pattern which is regular to the camera will allow decisions regarding any radial distortions to be made.
***************There is a problem with cv.FindChessboardCorners in some distribution of OpenCV. The problems presents as a seg-fault when all corners of the chessboard are found. Install from Willowgrarage ***************
Find and Display Chessboard Corners
Create a 'capture' object from a webcam or file source can be done with the following lines of code. A webcam stream is initialised and frames checked for a chessboard using cv.FindChessboardCorners. The dims parameter represents the "shape" of the chessboard pattern. The video below shows an 8 by 5 chessboard, which is a measure of the internal corners of the chessboard pattern.
The values returned by cv.FindChessboardCorners are found and points. The parameter found is a value specifying if the chessboard was found. If this is not 0, the a pattern was detected. Calling the function cv.DrawChessboardCorners(f,dims,points,found), will then display the points, drawing then to an image. The points must be saved or passed to the next part of the process. cv.FindCornerSubPix() can optionally be called to increase the accuracy of the detected points.
Ok, so now we have a selection of patterns from this camera we can begin to calibrate the camera.
Calibration of the Camera
Firstly set up the matrices for collection of calibration patterns collected in the previous part of the process. The intrinsic "camera" matrix has the first two diagonal values set to 1 all others set to 0.
#Number of calibration patterns used
#Number of points in chessboard
num_pts=width_of_board * height_of_board
opts = cv.CreateMat(nimages * num_pts, 3, cv.CV_32FC1)
ipts = cv.CreateMat((points) * num_pts, 2, cv.CV_32FC1)
npts = cv.CreateMat(nimages, 1, cv.CV_32SC1)
Now, to clarify..... opts contains all of the "model points". One model point, for each chessboard point found in all of the images. Fill this matrix will the sequential pattern of the board. i.e. (x,y,z) where x=x_co-ordinate of model point, y=y_co-ordinate of model point, and z (normally set to 0) is the depth of the point. Put all of the model points into the ipts matrix, sequenctially with ipts[:] = x co-ordinate and ipts[:] is the y co-ordinate.
intrinsics = cv.CreateMat(3, 3, cv.CV_64FC1)
distortion = cv.CreateMat(4, 1, cv.CV_64FC1)
# focal lengths have 1/1 ratio
intrinsics[0,0] = 1.0
intrinsics[1,1] = 1.0
Secondly, do the calibration of the camera using the patterns. The calibration function cv.CalibrateCamera2, will recieve all of the previous parameters defined. It will store it's output into the intrinsics and distortion matrices. Two other matricies are included which return a rotation and translation vertor, but these aren't used here.
cv.CalibrateCamera2(opts, ipts, npts, size,intrinsics, distortion,
cv.CreateMat(points), 3, cv.CV_32FC1),cv.CreateMat(lenpoints), 3, cv.CV_32FC1),flags = 0)
Undistort and Remap
Lastly, cv.InitUndistortMap() is called it takes the new camera matrix and distortion matrix calculated from the image points. The two matrices, mapx and mapy, will hold the translations for the x and y components of the map. Finally cv.Remap will transpose the image using mapx and mapy, producing.... finally.... the undistorted image. The below example is overly distorted, to emphasise the changes, by bending the calibration pattern.
mapx = cv.CreateImage((mat_w,mat_h), cv.IPL_DEPTH_32F, 1)
mapy = cv.CreateImage((mat_w,mat_h), cv.IPL_DEPTH_32F, 1)
cv.InitUndistortMap(intrinsics, distortion, mapx, mapy)
r = cv.CloneImage(img)
cv.Remap(img, r, mapx, mapy)
Now, the good news. Once you're calibrated, you're ready to go. You can reuse the information collected during the calibration process, however the remap function must be called each time
Click here for the next part of the tutorial