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:
nicely done!
ReplyDeletethank you for sharing..
keep it up!
Hi, I dont know how to make these programs, can you send me the compiled binaries as a favour, please?
ReplyDeleteBest,
Colin
OK, I compiled it, now how do I use it? I take photos of the calibration target, then what?
ReplyDeleteHi geezer,
ReplyDeleteRun 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)
It starts up my webcam and shows two windows with video in. I want to calibrate my Android phone camera. Perhaps Droidcam will work?
ReplyDeleteIf you make an Android app for camera calibration, you will be famous!
Colin
Hi Colin,
ReplyDeleteI'm still learning Android programming as of now, i'll see what i can do. :)
Hi Bharath,
ReplyDeleteI 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?
Hi Richards,
ReplyDeleteNo.. 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.
Hi Bharath,
ReplyDeleteI'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
You just copy and pasted this from the OpenCV book...
ReplyDeleteHi Bharath,
ReplyDeleteI'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
In that code format can any one tell give me code for stereoscopy. I mean to calibrate more than one cameras and make 3d.
ReplyDeleteRegards
Ishwar lal
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).
ReplyDeleteThanks 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:
ReplyDelete/////////////////////////////////
// Release the capture device & windows
cvDestroyWindow( "Undistort" );
cvDestroyWindow( "Raw Video" ); cvReleaseCapture( &capture );
//////////////////////////////////
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?
ReplyDeletei want to know how to reconstruct a 3d using openCV.Can you give me a tutorials for this.
ReplyDeletei am doing a project that is a 3d face recognition system.but i am a beginner to do this.
please help me.
Hi, Can this code be used if im using 2 cameras forming a stereoscopic system?
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteHm, 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?
ReplyDeletehi how would i deal with partial occlusions thanks
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteBharath 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??
ReplyDeletehey there,
ReplyDeleteI 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
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
ReplyDeleteHi, would you tell me what's ℑ_points for? That ℑ character can't compile! i'm sorry if it was a stupid question
ReplyDeleteI think there is mistake in line 92 and 93:
ReplyDeleteCV_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.
@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.
ReplyDelete@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...