Camera Calibration using OpenCV

Here is a simple test by which you can look into the parameters of your camera and correct any distortions.

The entire program is as is from the O'Reilly book [link].


The method used is based on pin-hole camera model, correcting intrinsic deviations and also distortions like radial and tangential.


Some of the links which you might be interested in:
Wikipedia     [link]
Zhengyou Zhang: A Flexible New Technique for Camera Calibration    [link]

Jean-Yves Bouguet : (Matlab)    [link]

Download the source code along with the calibration patterns here:

#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <stdlib.h>

int n_boards = 0;  //Number of snapshots of the chessboard
int frame_step;   //Frames to be skipped
int board_w;   //Enclosed corners horizontally on the chessboard
int board_h;   //Enclosed corners vertically on the chessboard
int main() 
{
 CvCapture* capture;

 printf("Enter the numbers of spanspots = ");
 scanf("%d",&n_boards);
 
 printf("\rEnter the numbers of frames to skip = ");
 scanf("%d",&frame_step);

 board_w  = 9;
 board_h  = 6;
  
 int board_total  = board_w * board_h;          //Total enclosed corners on the board
 CvSize board_sz = cvSize( board_w, board_h );

 capture = cvCreateCameraCapture( 0 );
 if(!capture) 
 { 
  printf("\nCouldn't open the camera\n"); 
  return -1;
 }

 cvNamedWindow( "Snapshot" );
 cvNamedWindow( "Raw Video");

 //Allocate storage for the parameters according to total number of 
        //corners and number of snapshots
 CvMat* image_points      = cvCreateMat(n_boards*board_total,2,CV_32FC1);
 CvMat* object_points     = cvCreateMat(n_boards*board_total,3,CV_32FC1);
 CvMat* point_counts      = cvCreateMat(n_boards,1,CV_32SC1);
 CvMat* intrinsic_matrix  = cvCreateMat(3,3,CV_32FC1);
 CvMat* distortion_coeffs = cvCreateMat(4,1,CV_32FC1);

 //Note:
 //Intrinsic Matrix - 3x3 Lens Distorstion Matrix - 4x1
 // [fx 0 cx]              [k1 k2 p1 p2   k3(optional)]
 // [0 fy cy]
 // [0  0  1]


 CvPoint2D32f* corners = new CvPoint2D32f[ board_total ];
 int corner_count;
 int successes = 0;
 int step, frame = 0;

 IplImage *image = cvQueryFrame( capture );
 IplImage *gray_image = cvCreateImage(cvGetSize(image),8,1);    //subpixel

 //Loop while successful captures equals total snapshots
 //Successful captures implies when all the enclosed corners 
        //are detected from a snapshot

 while(successes < n_boards)     
 {
  if((frame++ % frame_step) == 0)                             
                //Skip frames
  {
   //Find chessboard corners:
   int found = cvFindChessboardCorners(image, 
                                     board_sz, corners,&corner_count,
                               CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_FILTER_QUADS );

   cvCvtColor(image, gray_image, CV_BGR2GRAY);           
                        
                        //Get Subpixel accuracy on those corners
   cvFindCornerSubPix(gray_image, corners, corner_count, 
                                             cvSize(11,11),cvSize(-1,-1), 
                               cvTermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 30, 0.1 ));

   cvDrawChessboardCorners(image, board_sz, corners, corner_count, found);   
                        //Draw it
   
   // If we got a good board, add it to our data
   if(corner_count == board_total) 
   {
    cvShowImage( "Snapshot", image );            
                                //show in color if we did collect the image
    step = successes*board_total;
    for( int i=step, j=0; j<board_total; ++i,++j ) {
    CV_MAT_ELEM(*image_points, float,i,0) = corners[j].x;
    CV_MAT_ELEM(*image_points, float,i,1) = corners[j].y;
    CV_MAT_ELEM(*object_points,float,i,0) = (float) j/board_w;
    CV_MAT_ELEM(*object_points,float,i,1) = (float) (j%board_w);
    CV_MAT_ELEM(*object_points,float,i,2) = 0.0f;
   }
   CV_MAT_ELEM(*point_counts, int,successes,0) = board_total;    
   successes++;
   printf("\r%d successful Snapshots out of %d 
                                        collected.",successes,n_boards);
   }
   else
   cvShowImage( "Snapshot", gray_image );          
                        //Show Gray if we didn't collect the image
  } 
  
  //Handle pause/unpause and ESC
  int c = cvWaitKey(15);
  if(c == 'p')
  {  
   c = 0;
   while(c != 'p' && c != 27)
   {
    c = cvWaitKey(250);
   }
  }
  if(c == 27)
   return 0;

  image = cvQueryFrame( capture );        
                //Get next image
  cvShowImage("Raw Video", image);
 } 

 //End WHILE loop with enough successful captures

 cvDestroyWindow("Snapshot");

 printf("\n\n *** Calbrating the camera now...\n");
 
 //Allocate matrices according to successful number of captures
 CvMat* object_points2  = cvCreateMat(successes*board_total,3,CV_32FC1);
 CvMat* image_points2   = cvCreateMat(successes*board_total,2,CV_32FC1);
 CvMat* point_counts2   = cvCreateMat(successes,1,CV_32SC1);

 //Tranfer the points to matrices
 for(int i = 0; i<successes*board_total; ++i)
 {
      CV_MAT_ELEM( *image_points2, float, i, 0) = CV_MAT_ELEM( *image_points, float, i, 0);
      CV_MAT_ELEM( *image_points2, float,i,1)   = CV_MAT_ELEM( *image_points, float, i, 1);
      CV_MAT_ELEM(*object_points2, float, i, 0) = CV_MAT_ELEM( *object_points, float, i, 0) ;
      CV_MAT_ELEM( *object_points2, float, i, 1)= CV_MAT_ELEM( *object_points, float, i, 1) ;
      CV_MAT_ELEM( *object_points2, float, i, 2)= CV_MAT_ELEM( *object_points, float, i, 2) ;
 } 
 
 for(int i=0; i<successes; ++i)
 { 
      CV_MAT_ELEM( *point_counts2, int, i, 0)= CV_MAT_ELEM( *point_counts, int, i, 0);   
             //These are all the same number
 }
 cvReleaseMat(&object_points);
 cvReleaseMat(ℑ_points);
 cvReleaseMat(&point_counts);

 // Initialize the intrinsic matrix with both the two focal lengths in a ratio of 1.0

 CV_MAT_ELEM( *intrinsic_matrix, float, 0, 0 ) = 1.0f;
 CV_MAT_ELEM( *intrinsic_matrix, float, 1, 1 ) = 1.0f;

 //Calibrate the camera
 //______________________________________________________
 
 cvCalibrateCamera2(object_points2, image_points2, point_counts2,  
                           cvGetSize( image ), 
                           intrinsic_matrix, distortion_coeffs, NULL, NULL,0 );
                                           
//CV_CALIB_FIX_ASPECT_RATIO
 //______________________________________________________

 //Save values to file
 printf(" *** Calibration Done!\n\n");
 printf("Storing Intrinsics.xml and Distortions.xml files...\n");
 cvSave("Intrinsics.xml",intrinsic_matrix);
 cvSave("Distortion.xml",distortion_coeffs);
 printf("Files saved.\n\n");

 printf("Starting corrected display....");

 //Sample: load the matrices from the file
 CvMat *intrinsic = (CvMat*)cvLoad("Intrinsics.xml");
 CvMat *distortion = (CvMat*)cvLoad("Distortion.xml");

 // Build the undistort map used for all subsequent frames.

 IplImage* mapx = cvCreateImage( cvGetSize(image), IPL_DEPTH_32F, 1 );
 IplImage* mapy = cvCreateImage( cvGetSize(image), IPL_DEPTH_32F, 1 );
 cvInitUndistortMap(intrinsic,distortion,mapx,mapy);

 // Run the camera to the screen, showing the raw and the undistorted image.

 cvNamedWindow( "Undistort" );
 while(image) 
 {
  IplImage *t = cvCloneImage(image);
  cvShowImage( "Raw Video", image );  // Show raw image
  cvRemap( t, image, mapx, mapy );  // Undistort image
  cvReleaseImage(&t);
  cvShowImage("Undistort", image);  // Show corrected image

  //Handle pause/unpause and ESC
  int c = cvWaitKey(15);
  if(c == 'p')
  { 
   c = 0;
   while(c != 'p' && c != 27)
   {
    c = cvWaitKey(250);
   }
  }
  if(c == 27)
   break;

  image = cvQueryFrame( capture );
 } 
 
 return 0;
}
Pattern [Size A4, 3508x2480px, PNG]:












Images:

27 comments:

  1. nicely done!
    thank you for sharing..
    keep it up!

    ReplyDelete
  2. Hi, I dont know how to make these programs, can you send me the compiled binaries as a favour, please?
    Best,
    Colin

    ReplyDelete
  3. OK, I compiled it, now how do I use it? I take photos of the calibration target, then what?

    ReplyDelete
  4. Hi geezer,

    Run the code and the camera starts to take snapshots (..say 12).. Now you have to hold the calibration pattern infront of the camera in various positions until it registers 12 VALID snapshots (ie all 54 points should be recognized in each shot). Then it calibrates accordingly and displays the corrected image in a new window. (Code also saves the distortion parameters in the .xml file)

    ReplyDelete
  5. It starts up my webcam and shows two windows with video in. I want to calibrate my Android phone camera. Perhaps Droidcam will work?
    If you make an Android app for camera calibration, you will be famous!

    Colin

    ReplyDelete
  6. Hi Colin,
    I'm still learning Android programming as of now, i'll see what i can do. :)

    ReplyDelete
  7. Hi Bharath,
    I tried running the above code in open cv and ran in to a lot of errors. Is this code specific to any version of opencv? i am using opencv version 1.1. Should i use an update version to get this code running? Or is there any settings that i have to modify in my project to get it running?

    ReplyDelete
  8. Hi Richards,

    No.. code works on all OpenCV versions..

    The program was compiled in debug mode. Create a new project and add only the code to it and compile a fresh.

    ReplyDelete
  9. Hi Bharath,
    I'm past the stage of calibration for now, I used one of the samples for that, it gave me an intrinsics.yml and an extrinsics.yml. I also made a program that finds the features points in consecutive frames, and calculates the z value for each of those points, but i assumed focus x baseline = 1000 for that(which gave me decent values but not that nice). Now that I've calibrated my stereo setup i need to find the actual f and b from the yml files, can you help me here?

    Thanks
    Ravish

    ReplyDelete
  10. You just copy and pasted this from the OpenCV book...

    ReplyDelete
  11. Hi Bharath,
    I'm a very beginner of computer vision system. I've just received new project from my professor which it related to camera calibration. I've just found this site talking and sharing camera calibration by using openCV library. I am trying to compile and run your code but it was not succeeded. I got some errors as below :
    1 error LNK2019: unresolved external symbol _cvCalibrateCamera2 referenced in function _main
    2 error LNK2019: unresolved external symbol _cvDrawChessboardCorners referenced in function _main
    3 error LNK2019: unresolved external symbol _cvFindChessboardCorners referenced in function _main
    4 fatal error LNK1120: 3 unresolved externals

    Can you help me to solve this problem ?
    I really appreciate anyone who solve this problem.

    Thank you.
    From Alonso

    ReplyDelete
  12. In that code format can any one tell give me code for stereoscopy. I mean to calibrate more than one cameras and make 3d.

    Regards
    Ishwar lal

    ReplyDelete
  13. Alonso: if you are using visual studio 2010, you are missing a lib file I suspect. In your linker >> input >> additional dependencies, click on the dropdown arrow and then do edit.. make sure you have opencv_calib3d231d.lib defined in there (the full path if you dare).

    ReplyDelete
  14. Thanks for the post, by the way there are some missing release functions for a proper exit, always release what you got. Add the following lines to the end of the function before return statement:
    /////////////////////////////////
    // Release the capture device & windows
    cvDestroyWindow( "Undistort" );
    cvDestroyWindow( "Raw Video" ); cvReleaseCapture( &capture );
    //////////////////////////////////

    ReplyDelete
  15. I have a little problem. I think that everything goes good until remapping. Program returns xml files (they look fine) and it crush at a moment of remapping( when I remove this line program doesn't crush). Any ideas why this can be happening?

    ReplyDelete
  16. i want to know how to reconstruct a 3d using openCV.Can you give me a tutorials for this.
    i am doing a project that is a 3d face recognition system.but i am a beginner to do this.
    please help me.

    ReplyDelete
  17. Hi, Can this code be used if im using 2 cameras forming a stereoscopic system?

    ReplyDelete
  18. This comment has been removed by the author.

    ReplyDelete
  19. Hm, with current OpenCV version 2.4.3 this code does not work, there is nearly no difference between rwa and undistorted image. Any ideas what could be wrong?

    ReplyDelete
  20. hi how would i deal with partial occlusions thanks

    ReplyDelete
  21. This comment has been removed by the author.

    ReplyDelete
  22. Bharath thanx again , you have done grate work , i only have one question for you...! Any ideas why my final undistort image shows up in random shape or a plain black screen each time??

    ReplyDelete
  23. hey there,
    I had a project based on this calibration. I have a very silly question... what does the entry number of frames to skip mean ?
    thanks,
    venky

    ReplyDelete
  24. I'm using opencv 2.4.5 and VS2010. so after I convert your source code, all code is red lineup, why......what can I do

    ReplyDelete
  25. Hi, would you tell me what's ℑ_points for? That ℑ character can't compile! i'm sorry if it was a stupid question

    ReplyDelete
  26. I think there is mistake in line 92 and 93:
    CV_MAT_ELEM(*object_points,float,i,0) = (float) j/board_w;
    CV_MAT_ELEM(*object_points,float,i,1) = (float) (j%board_w);
    "float" cast should not be there, otherwise, it will cause the object points skewed.

    ReplyDelete
  27. @FRAUD: You must provide more samples to the program for it to calibrate. I assume the martix is empty as it has failed to calibrate and hence the blank/invalid image.
    @VENKY: "Number of frames to skip" - will skip so many frames from camera until it captures a frame for chessboard identification. This skipped frame will give you time to change the
    orientation of the Chessboard pattern
    @HUY PHAN: Where is this character ? I assume you are using Translator. Better not use it while copying the code.
    @SAIPOL HADI HASIM and @DIDA : I'll report back...

    ReplyDelete