• Open Source Computer Vision Library

摄像头标定

Wikipedia,自由的百科全书

Revision as of 11:10 2009年1月18日; view current revision
←Older revision | Newer revision→

目录

标定原理介绍

标定步骤

OPENCV没有提供完整的示例,自己整理了一下,贴出来记录。

  1. 首先自制一张标定图片,用A4纸打印出来,设定距离,再设定标定棋盘的格子数目,如8×6,以下是我做的图片8×8

  1. 然后利用cvFindChessboardCorners找到棋盘在摄像头中的2D位置,这里cvFindChessboardCorners不太稳定,有时不能工作,也许需要图像增强处理。
  2. 计算实际的距离,应该是3D的距离。我设定为21.6毫米,既在A4纸上为两厘米。
  3. 再用cvCalibrateCamera2计算内参,
  4. 最后用cvUndistort2纠正图像的变形。

结果如下:

调用命令行和参数介绍

可以使用\OpenCV\samples\c目录下的calibration.cpp这个程序,程序的输入支持USB摄像机,avi文件或者图片

  • 使用说明

输入图片命令

// example command line (for copy-n-paste):
// calibration -w 6 -h 8 -s 2 -n 10 -o camera.yml -op -oe [<list_of_views.txt>]

txt文件格式

/* The list of views may look as following (discard the starting and ending ------ separators):
-------------------
view000.png
view001.png
#view002.png
view003.png
view010.png
one_extra_view.jpg
-------------------

that is, the file will contain 6 lines, view002.png will not be used for calibration,
other ones will be (those, in which the chessboard pattern will be found)

输入为摄像机或者avi文件时

        "When the live video from camera is used as input, the following hot-keys may be used:\n"
            "  <ESC>, 'q' - quit the program\n"
            "  'g' - start capturing images\n"
            "  'u' - switch undistortion on/off\n";

输入参数说明

           "Usage: calibration\n"
" -w <board_width> # the number of inner corners per one of board dimension\n"
" -h <board_height> # the number of inner corners per another board dimension\n"
" [-n <number_of_frames>] # the number of frames to use for calibration\n"
" # (if not specified, it will be set to the number\n"
" # of board views actually available)\n"
" [-d <delay>] # a minimum delay in ms between subsequent attempts to capture a next view\n"
" # (used only for video capturing)\n"
" [-s <square_size>] # square size in some user-defined units (1 by default)\n"
" [-o <out_camera_params>] # the output filename for intrinsic [and extrinsic] parameters\n"
" [-op] # write detected feature points\n"
" [-oe] # write extrinsic parameters\n"
" [-zt] # assume zero tangential distortion\n"
" [-a <aspect_ratio>] # fix aspect ratio (fx/fy)\n"
" [-p] # fix the principal point at the center\n"
" [-v] # flip the captured images around the horizontal axis\n"
" [input_data] # input data, one of the following:\n"
" # - text file with a list of the images of the board\n"
" # - name of video file with a video of the board\n"
" # if input_data not specified, a live view from the camera is used\n"
  • 经多次使用发现,不指定 -p参数时计算的结果误差较大,主要表现在对u0,v0的估计误差较大,因此建议使用时加上-p参数

代码

具体的函数使用,请参考Cv照相机定标和三维重建#照相机定标

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// OpenCV
#include <cxcore.h>
#include <cv.h>
#include <highgui.h>
#include <cvaux.h>
 
 
void InitCorners3D(CvMat *Corners3D, CvSize ChessBoardSize, int Nimages, float SquareSize);
void makeChessBoard();
int myFindChessboardCorners( const void* image, CvSize pattern_size,
                             CvPoint2D32f* corners, int* corner_count=NULL,
                             int flags=CV_CALIB_CB_ADAPTIVE_THRESH );
 
 
inline int drawCorssMark(IplImage *dst,CvPoint pt)
/*************************************************
  Function:        main_loop
  Description:     绘制一个十字标记					
  Calls:          
  Called By:      
  Input:           RGB image,  pt               
  Output:         
  Return:         
  Others:          需要检查坐标是否越界 to do list
*************************************************/
{
 
	const int cross_len = 4;
	CvPoint pt1,pt2,pt3,pt4;
	pt1.x = pt.x;
	pt1.y = pt.y - cross_len;
	pt2.x = pt.x;
	pt2.y = pt.y + cross_len;
	pt3.x = pt.x - cross_len;
	pt3.y = pt.y;
	pt4.x = pt.x + cross_len;
	pt4.y = pt.y;
 
	cvLine(dst,pt1,pt2,CV_RGB(0,255,0),2,CV_AA, 0 );	
	cvLine(dst,pt3,pt4,CV_RGB(0,255,0),2,CV_AA, 0 );
 
	return 0;
}
 
/* declarations for OpenCV */
IplImage                 *current_frame_rgb,grid;
IplImage                 *current_frame_gray;
IplImage                 *chessBoard_Img;
 
int                       Thresholdness = 120;
 
int image_width = 320;
int image_height = 240;
 
bool verbose = false;
 
const int ChessBoardSize_w = 7;
const int ChessBoardSize_h = 7;
// Calibration stuff
bool			calibration_done = false;
const CvSize 	ChessBoardSize = cvSize(ChessBoardSize_w,ChessBoardSize_h);
//float 			SquareWidth = 21.6f; //实际距离 毫米单位 在A4纸上为两厘米
float 			SquareWidth = 17; //投影实际距离 毫米单位  200
 
const   int NPoints = ChessBoardSize_w*ChessBoardSize_h;
const   int NImages = 20; //Number of images to collect 
 
CvPoint2D32f corners[NPoints*NImages];
int corner_count[NImages] = {0};
int captured_frames = 0;
 
CvMat *intrinsics;
CvMat *distortion_coeff;
CvMat *rotation_vectors;
CvMat *translation_vectors;
CvMat *object_points;
CvMat *point_counts;
CvMat *image_points;
int find_corners_result =0 ;
 
 
void on_mouse( int event, int x, int y, int flags, void* param )
{
 
    if( event == CV_EVENT_LBUTTONDOWN )
    {
		//calibration_done = true; 
    }
}
 
 
int main(int argc, char *argv[])
{
 
 
  CvFont font;
  cvInitFont( &font, CV_FONT_VECTOR0,5, 5, 0, 7, 8);
 
  intrinsics 		= cvCreateMat(3,3,CV_32FC1);
  distortion_coeff 	= cvCreateMat(1,4,CV_32FC1);
  rotation_vectors 	= cvCreateMat(NImages,3,CV_32FC1);
  translation_vectors 	= cvCreateMat(NImages,3,CV_32FC1);
 
  point_counts 		= cvCreateMat(NImages,1,CV_32SC1);
 
  object_points 	= cvCreateMat(NImages*NPoints,3,CV_32FC1);
  image_points 		= cvCreateMat(NImages*NPoints,2,CV_32FC1);
 
 
  // Function to fill in the real-world points of the checkerboard
  InitCorners3D(object_points, ChessBoardSize, NImages, SquareWidth);
 
 
  CvCapture* capture = 0;
 
 
  if( argc == 1 || (argc == 2 && strlen(argv[1]) == 1 && isdigit(argv[1][0])))
	  capture = cvCaptureFromCAM( argc == 2 ? argv[1][0] - '0' : 0 );
  else if( argc == 2 )
	  capture = cvCaptureFromAVI( argv[1] );
 
  if( !capture )
  {
	  fprintf(stderr,"Could not initialize capturing...\n");
	  return -1;
  }
 
 
  // Initialize all of the IplImage structures
  current_frame_rgb = cvCreateImage(cvSize(image_width, image_height), IPL_DEPTH_8U, 3);
 
  IplImage *current_frame_rgb2 = cvCreateImage(cvSize(image_width, image_height), IPL_DEPTH_8U, 3);
  current_frame_gray = cvCreateImage(cvSize(image_width, image_height), IPL_DEPTH_8U, 1);
 
  chessBoard_Img   = cvCreateImage(cvSize(image_width, image_height), IPL_DEPTH_8U, 3);  
  current_frame_rgb2->origin = chessBoard_Img->origin  = current_frame_gray->origin = current_frame_rgb->origin = 1;
 
  makeChessBoard();
 
  cvNamedWindow( "result", 0);
  cvNamedWindow( "Window 0", 0);
  cvNamedWindow( "grid", 0);
  cvMoveWindow( "grid", 100,100);
  cvSetMouseCallback( "Window 0", on_mouse, 0 );  
  cvCreateTrackbar("Thresholdness","Window 0",&Thresholdness, 255,0);
 
  while (!calibration_done)
  {
 
	while (captured_frames < NImages)
    {
	  current_frame_rgb = cvQueryFrame( capture );
	  //current_frame_rgb = cvLoadImage( "c:\\BoardStereoL3.jpg" );
	  //cvCopy(chessBoard_Img,current_frame_rgb);
 
	  if( !current_frame_rgb )
		  break;
 
	  cvCopy(current_frame_rgb,current_frame_rgb2);
	  cvCvtColor(current_frame_rgb, current_frame_gray, CV_BGR2GRAY);
	  //cvThreshold(current_frame_gray,current_frame_gray,Thresholdness,255,CV_THRESH_BINARY);
	  //cvThreshold(current_frame_gray,current_frame_gray,150,255,CV_THRESH_BINARY_INV);
 
/*
	int pos = 1;
	IplConvKernel* element = 0;
	const int element_shape = CV_SHAPE_ELLIPSE;
	element = cvCreateStructuringElementEx( pos*2+1, pos*2+1, pos, pos, element_shape, 0 );
	cvDilate(current_frame_gray,current_frame_gray,element,1);
	cvErode(current_frame_gray,current_frame_gray,element,1);
	cvReleaseStructuringElement(&element);
*/
 
	find_corners_result = cvFindChessboardCorners(current_frame_gray,
                                          ChessBoardSize,
                                          &corners[captured_frames*NPoints],
                                          &corner_count[captured_frames],
                                          0);
 
 
 
	cvDrawChessboardCorners(current_frame_rgb2, ChessBoardSize, &corners[captured_frames*NPoints], NPoints, find_corners_result);
 
 
	cvShowImage("Window 0",current_frame_rgb2);
	cvShowImage("grid",chessBoard_Img);
 
	if(find_corners_result==1)
	{
		cvWaitKey(2000);
		cvSaveImage("c:\\hardyinCV.jpg",current_frame_rgb2);
		captured_frames++;
	}
	//cvShowImage("result",current_frame_gray);
 
	intrinsics->data.fl[0] = 256.8093262;   //fx		
	intrinsics->data.fl[2] = 160.2826538;   //cx
	intrinsics->data.fl[4] = 254.7511139;   //fy
	intrinsics->data.fl[5] = 127.6264572;   //cy
 
	intrinsics->data.fl[1] = 0;   
	intrinsics->data.fl[3] = 0;   
	intrinsics->data.fl[6] = 0;   
	intrinsics->data.fl[7] = 0;   
	intrinsics->data.fl[8] = 1;   	
 
	distortion_coeff->data.fl[0] = -0.193740;  //k1
	distortion_coeff->data.fl[1] = -0.378588;  //k2
	distortion_coeff->data.fl[2] = 0.028980;   //p1
	distortion_coeff->data.fl[3] = 0.008136;   //p2
 
	cvWaitKey(40);
	find_corners_result = 0;
    }   
	//if (find_corners_result !=0)
	{
 
		printf("\n");
 
		cvSetData( image_points, corners, sizeof(CvPoint2D32f));
		cvSetData( point_counts, &corner_count, sizeof(int));
 
 
		cvCalibrateCamera2( object_points,
			image_points,
			point_counts,
			cvSize(image_width,image_height),
			intrinsics,
			distortion_coeff,
			rotation_vectors,
			translation_vectors,
			0);
 
 
		// [fx 0 cx; 0 fy cy; 0 0 1].
		cvUndistort2(current_frame_rgb,current_frame_rgb,intrinsics,distortion_coeff);
		cvShowImage("result",current_frame_rgb);
 
 
		float intr[3][3] = {0.0};
		float dist[4] = {0.0};
		float tranv[3] = {0.0};
		float rotv[3] = {0.0};
 
		for ( int i = 0; i < 3; i++)
		{
			for ( int j = 0; j < 3; j++)
			{
				intr[i][j] = ((float*)(intrinsics->data.ptr + intrinsics->step*i))[j];
			}
			dist[i] = ((float*)(distortion_coeff->data.ptr))[i];
			tranv[i] = ((float*)(translation_vectors->data.ptr))[i];
			rotv[i] = ((float*)(rotation_vectors->data.ptr))[i];
		}
		dist[3] = ((float*)(distortion_coeff->data.ptr))[3];
 
		printf("-----------------------------------------\n");
		printf("INTRINSIC MATRIX: \n");
		printf("[ %6.4f %6.4f %6.4f ] \n", intr[0][0], intr[0][1], intr[0][2]);
		printf("[ %6.4f %6.4f %6.4f ] \n", intr[1][0], intr[1][1], intr[1][2]);
		printf("[ %6.4f %6.4f %6.4f ] \n", intr[2][0], intr[2][1], intr[2][2]);
		printf("-----------------------------------------\n");
		printf("DISTORTION VECTOR: \n");
		printf("[ %6.4f %6.4f %6.4f %6.4f ] \n", dist[0], dist[1], dist[2], dist[3]);
		printf("-----------------------------------------\n");
		printf("ROTATION VECTOR: \n");
		printf("[ %6.4f %6.4f %6.4f ] \n", rotv[0], rotv[1], rotv[2]);
		printf("TRANSLATION VECTOR: \n");
		printf("[ %6.4f %6.4f %6.4f ] \n", tranv[0], tranv[1], tranv[2]);
		printf("-----------------------------------------\n");
 
		cvWaitKey(0);
 
		calibration_done = true;      
	}
 
  }
 
  exit(0);
  cvDestroyAllWindows();
}
 
void InitCorners3D(CvMat *Corners3D, CvSize ChessBoardSize, int NImages, float SquareSize)
{
  int CurrentImage = 0;
  int CurrentRow = 0;
  int CurrentColumn = 0;
  int NPoints = ChessBoardSize.height*ChessBoardSize.width;
  float * temppoints = new float[NImages*NPoints*3];
 
  // for now, assuming we're row-scanning
  for (CurrentImage = 0 ; CurrentImage < NImages ; CurrentImage++)
  {
    for (CurrentRow = 0; CurrentRow < ChessBoardSize.height; CurrentRow++)
    {
      for (CurrentColumn = 0; CurrentColumn < ChessBoardSize.width; CurrentColumn++)
      {
		  temppoints[(CurrentImage*NPoints*3)+(CurrentRow*ChessBoardSize.width + CurrentColumn)*3]=(float)CurrentRow*SquareSize;
		  temppoints[(CurrentImage*NPoints*3)+(CurrentRow*ChessBoardSize.width + CurrentColumn)*3+1]=(float)CurrentColumn*SquareSize;
		  temppoints[(CurrentImage*NPoints*3)+(CurrentRow*ChessBoardSize.width + CurrentColumn)*3+2]=0.f;
      }
    }
  }
  (*Corners3D) = cvMat(NImages*NPoints,3,CV_32FC1, temppoints);
}
 
int myFindChessboardCorners( const void* image, CvSize pattern_size,
                             CvPoint2D32f* corners, int* corner_count,
                             int flags )
 
{
 
 
	IplImage* eig = cvCreateImage( cvGetSize(image), 32, 1 );
	IplImage* temp = cvCreateImage( cvGetSize(image), 32, 1 );
	double quality = 0.01;
	double min_distance = 5;
	int win_size =10;
 
	int count = pattern_size.width * pattern_size.height;
	cvGoodFeaturesToTrack( image, eig, temp, corners, &count,
		quality, min_distance, 0, 3, 0, 0.04 );
	cvFindCornerSubPix( image, corners, count,
		cvSize(win_size,win_size), cvSize(-1,-1),
		cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03));
 
	cvReleaseImage( &eig );
	cvReleaseImage( &temp );
 
	return 1;
}
 
void makeChessBoard()
{
 
  CvScalar e; 
  e.val[0] =255;
  e.val[1] =255;
  e.val[2] =255;
  cvSet(chessBoard_Img,e,0);
  for(int i = 0;i<ChessBoardSize.width+1;i++)
	  for(int j = 0;j<ChessBoardSize.height+1;j++)
	  {
		  int w =(image_width)/2/(ChessBoardSize.width);
		  int h = w; //(image_height)/2/(ChessBoardSize.height);
 
		  int ii = i+1;
		  int iii = ii+1;
		  int jj =j+1;
		  int jjj =jj+1;
		  int s_x = image_width/6;		  
 
		if((i+j)%2==1)
		   cvRectangle( chessBoard_Img, cvPoint(w*i+s_x,h*j+s_x),cvPoint(w*ii-1+s_x,h*jj-1+s_x), CV_RGB(0,0,0),CV_FILLED, 8, 0 );
	  }
}

参考资料

  1. piao在论坛里面发布的帖子
  2. matlab标定工具箱
  3. C++标定软件,开源,基于Opencv
Views
Personal tools