摄像机标定(附源码)

来源:互联网 发布:百度seo关键词优化 编辑:程序博客网 时间:2024/05/29 11:37

今天介绍图和标定camera。首先是用到的opencv函数。后面附上源码。


FindChessboardCorners 寻找棋盘图的内角点位置

 

int cvFindChessboardCorners( const void*image, CvSize pattern_size,

CvPoint2D32f* corners,int* corner_count=NULL,

int flags=CV_CALIB_CB_ADAPTIVE_THRESH );

 

image

输入的棋盘图,必须是8位的灰度或者彩色图像。

pattern_size

棋盘图中每行和每列角点的个数。

corners

检测到的角点

corner_count

输出,角点的个数。如果不是NULL,函数将检测到的角点的个数存储于此变量。

flags

各种操作标志,可以是0或者下面值的组合:

CV_CALIB_CB_ADAPTIVE_THRESH - 使用自适应阈值(通过平均图像亮度计算得到)将图像转换为黑白图,而不是一个固定的阈值。

CV_CALIB_CB_NORMALIZE_IMAGE - 在利用固定阈值或者自适应的阈值进行二值化之前,先使用cvNormalizeHist来均衡化图像亮度。

CV_CALIB_CB_FILTER_QUADS - 使用其他的准则(如轮廓面积,周长,方形形状)来去除在轮廓检测阶段检测到的错误方块。

函数cvFindChessboardCorners试图确定输入图像是否是棋盘模式,并确定角点的位置。如果所有角点都被检测到切它们都被以一定顺序排布(一行一行地,每行从左到右),函数返回非零值,否则在函数不能发现所有角点或者记录它们地情况下,函数返回0。例如一个正常地棋盘图右8x8个方块和7x7个内角点,内角点是黑色方块相互联通地位置。这个函数检测到地坐标只是一个大约地值,如果要精确地确定它们的位置,可以使用函数cvFindCornerSubPix。

 

 


FindCornerSubPix 寻找棋盘图的内角点位置

精确角点位置

void cvFindCornerSubPix( const CvArr* image, CvPoint2D32f* corners,

                        int count, CvSize win, CvSize zero_zone,

                         CvTermCriteria criteria );

image

输入图像.

corners

输入角点的初始坐标,也存储精确的输出坐标

count

角点数目

win

搜索窗口的一半尺寸。如果win=(5,5) 那么使用 5*2+1 × 5*2+1 = 11 × 11 大小的搜索窗口

zero_zone

死区的一半尺寸,死区为不对搜索区的中央位置做求和运算的区域。它是用来避免自相关矩阵出现的某些可能的奇异性。当值为 (-1,-1) 表示没有死区。

criteria

求角点的迭代过程的终止条件。即角点位置的确定,要么迭代数大于某个设定值,或者是精确度达到某个设定值。 criteria 可以是最大迭代数目,或者是设定的精确度,也可以是它们的组合。

函数cvFindCornerSubPix通过迭代来发现具有子象素精度的角点位置,或如图所示的放射鞍点(radial saddle points)。

子象素级角点定位的实现是基于对向量正交性的观测而实现的,即从中央点q到其邻域点p 的向量和p点处的图像梯度正交(服从图像和测量噪声)。考虑以下的表达式:

εi=DIpiT·(q-pi)

其中,DIpi表示在q的一个邻域点pi处的图像梯度,q的值通过最小化εi得到。通过将εi设为0,可以建立系统方程如下:

sumi(DIpi·DIpiT)·q - sumi(DIpi·DIpiT·pi) = 0

其中q的邻域(搜索窗)中的梯度被累加。调用第一个梯度参数G和第二个梯度参数b,得到:

q=G-1·b

该算法将搜索窗的中心设为新的中心q,然后迭代,直到找到低于某个阈值点的中心位置。

 


DrawChessBoardCorners 绘制检测到的棋盘角点

 

void cvDrawChessboardCorners( CvArr* image,CvSize pattern_size,CvPoint2D32f*corners,

int count,int pattern_was_found );

 

image

结果图像,必须是8位彩色图像。

pattern_size

每行和每列地内角点数目。

corners

检测到地角点数组。

count

角点数目。

pattern_was_found

指示完整地棋盘被发现(≠0)还是没有发现(=0)。可以传输cvFindChessboardCorners函数的返回值。

当棋盘没有完全检测出时,函数cvDrawChessboardCorners以红色圆圈绘制检测到的棋盘角点;如果整个棋盘都检测到,则用直线连接所有的角点。

 


CalibrateCamera2利用定标来计算摄像机的内参数和外参数

void cvCalibrateCamera2 ( const CvMat* object_points,

               constCvMat* image_points,const CvMat* point_counts,

               CvSizeimage_size, CvMat* intrinsic_matrix,

               CvMat*distortion_coeffs, CvMat* rotation_vectors=NULL,

               CvMat*translation_vectors=NULL, int flags=0 );

 

object_points

定标点的世界坐标,为3xN或者Nx3的矩阵,这里N是所有视图中点的总数。

image_points

定标点的图像坐标,为2xN或者Nx2的矩阵,这里N是所有视图中点的总数。

point_counts

向量,指定不同视图里点的数目,1xM或者Mx1向量,M是视图数目。

image_size

图像大小,只用在初始化内参数时。

intrinsic_matrix

输出内参矩阵(A) ,如果指定CV_CALIB_USE_INTRINSIC_GUESS和(或)CV_CALIB_FIX_ASPECT_RATION,fx、 fy、 cx和cy部分或者全部必须被初始化。

distortion_coeffs

输出大小为4x1或者1x4的向量,里面为形变参数[k1, k2, p1, p2]。

rotation_vectors

输出大小为3xM或者Mx3的矩阵,里面为旋转向量(旋转矩阵的紧凑表示方式,具体参考函数cvRodrigues2)

translation_vectors

输出大小为3xM或Mx3的矩阵,里面为平移向量。

flags

不同的标志,可以是0,或者下面值的组合:

·  CV_CALIB_USE_INTRINSIC_GUESS- 内参数矩阵包含fx,fy,cx和cy的初始值。否则,(cx,cy)被初始化到图像中心(这儿用到图像大小),焦距用最小平方差方式计算得到。注意,如果内部参数已知,没有必要使用这个函数,使用cvFindExtrinsicCameraParams2则可。

·  CV_CALIB_FIX_PRINCIPAL_POINT- 主点在全局优化过程中不变,一直在中心位置或者在其他指定的位置(当CV_CALIB_USE_INTRINSIC_GUESS设置的时候)。

·  CV_CALIB_FIX_ASPECT_RATIO- 优化过程中认为fx和fy中只有一个独立变量,保持比例fx/fy不变,fx/fy的值跟内参数矩阵初始化时的值一样。在这种情况下, (fx, fy)的实际初始值或者从输入内存矩阵中读取(当CV_CALIB_USE_INTRINSIC_GUESS被指定时),或者采用估计值(后者情况中fx和fy可能被设置为任意值,只有比值被使用)。

·  CV_CALIB_ZERO_TANGENT_DIST– 切向形变参数(p1, p2)被设置为0,其值在优化过程中保持为0。

函数cvCalibrateCamera2从每个视图中估计相机的内参数和外参数。3维物体上的点和它们对应的在每个视图的2维投影必须被指定。这些可以通过使用一个已知几何形状切具有容易检测的特征点的物体来实现。这样的一个物体被称作定标设备或者定标模式,OpenCV有内建的把棋盘当作定标设备方法(参考cvFindChessboardCorners)。目前,传入初始化的内参数(当CV_CALIB_USE_INTRINSIC_GUESS被设置时)只支持平面定标设备(物体点的Z坐标必须时全0或者全1)。不过3维定标设备依然可以用在提供初始内参数矩阵情况。在内参数和外参数矩阵的初始值都计算出之后,它们会被优化用来减小反投影误差(图像上的实际坐标跟cvProjectPoints2计算出的图像坐标的差的平方和)。

 


Undistort2校正图像因相机镜头引起的变形

void cvUndistort2( const CvArr* src, CvArr* dst,

                  const CvMat* intrinsic_matrix,

                  const CvMat* distortion_coeffs );

 

src

原始图像(已经变形的图像)。

dst

结果图像(已经校正的图像)。

intrinsic_matrix

相机内参数矩阵,格式为 。

distortion_coeffs

四个变形系数组成的向量,大小为4x1或者1x4,格式为[k1,k2,p1,p2]。

函数cvUndistort2对图像进行变换来抵消径向和切向镜头变形。相机参数和变形参数可以通过函数cvCalibrateCamera2取得。使用本节开始时提到的公式,对每个输出图像像素计算其在输入图像中的位置,然后输出图像的像素值通过双线性插值来计算。如果图像得分辨率跟定标时用得图像分辨率不一样,fx、fy、cx和cy需要相应调整,因为形变并没有变化。

 


FindExtrinsicCameraParams2计算指定视图的摄像机外参数

void cvFindExtrinsicCameraParams2 ( const CvMat*object_points,

                                   const CvMat*image_points,

                                   const CvMat*intrinsic_matrix,

                                   const CvMat*distortion_coeffs,

                                   CvMat*rotation_vector,

                                   CvMat*translation_vector );

object_points

定标点的坐标,为3xN或者Nx3的矩阵,这里N是视图中的个数。

image_points

定标点在图像内的坐标,为2xN或者Nx2的矩阵,这里N是视图中的个数。

intrinsic_matrix

内参矩阵(A) 。

distortion_coeffs

大小为4x1或者1x4的向量,里面为形变参数[k1,k2,p1,p2]。如果是NULL,所有的形变系数都为0。

rotation_vector

输出大小为3x1或者1x3的矩阵,里面为旋转向量(旋转矩阵的紧凑表示方式,具体参考函数cvRodrigues2)。

translation_vector

大小为3x1或1x3的矩阵,里面为平移向量。

函数cvFindExtrinsicCameraParams2使用已知的内参数和某个视图的外参数来估计相机的外参数。3维物体上的点坐标和相应的2维投影必须被指定。这个函数也可以用来最小化反投影误差。

 

 


GoodFeaturesToTrack 确定图像的强角点

 

void cvGoodFeaturesToTrack( const CvArr*image, CvArr* eig_image, CvArr* temp_image,

CvPoint2D32f* corners, int* corner_count,

double quality_level, double min_distance,

const CvArr* mask=NULL );

image

输入图像,8-位或浮点32-比特,单通道

eig_image

临时浮点32-位图像,尺寸与输入图像一致

temp_image

另外一个临时图像,格式与尺寸与 eig_image 一致

corners

输出参数,检测到的角点

corner_count

输出参数,检测到的角点数目

quality_level

最大最小特征值的乘法因子。定义可接受图像角点的最小质量因子。

min_distance

限制因子。得到的角点的最小距离。使用 Euclidian 距离

mask

ROI:感兴趣区域。函数在ROI中计算角点,如果 mask 为 NULL,则选择整个图像。

函数cvGoodFeaturesToTrack 在图像中寻找具有大特征值的角点。该函数,首先用cvCornerMinEigenVal 计算输入图像的每一个象素点的最小特征值,并将结果存储到变量 eig_image 中。然后进行非最大值抑制(仅保留3x3邻域中的局部最大值)。下一步将最小特征值小于 quality_level?max(eig_image(x,y)) 排除掉。最后,函数确保所有发现的角点之间具有足够的距离,(最强的角点第一个保留,然后检查新的角点与已有角点之间的距离大于 min_distance )。


<pre name="code" class="cpp">#include <cv.h>#include <highgui.h>#include <iostream>using namespace std;void PrintMat(CvMat*);void FputMat(FILE *,CvMat *);int main(int argc ,char * argv[]){/*读入图像*/CvSize image_size;int n_board=4;//图像数目int sn_board=0;//成功找到角点的图像数目int board_w=6;int board_h=9;int board_n=board_h*board_w;//每幅图像的角点数CvSize patter_size=cvSize(board_w,board_h);//每幅图像的角点数CvPoint2D32f * corners=new CvPoint2D32f[board_n];//一幅图像的角点数组CvMat * object_points= cvCreateMat(board_n*n_board,3,CV_32FC1);CvMat * image_points=cvCreateMat(board_n*n_board,2,CV_32FC1);CvMat * point_counts=cvCreateMat(n_board,1,CV_32SC1);for(int i=1;i<=n_board;i++){/*读入图像*/char path[100]="E:\\TestCase\\CalibrationTest\\picture\\picture";char num[10];itoa(i,num,10);strcat(num,".jpg");IplImage *SourceImg=cvLoadImage(strcat(path,num),CV_LOAD_IMAGE_COLOR);image_size=cvGetSize(SourceImg);//图像的大小IplImage *SourceImgGray=cvCreateImage(image_size,IPL_DEPTH_8U,1);cvCvtColor(SourceImg,SourceImgGray,CV_BGR2GRAY);cvNamedWindow("MyCalib",CV_WINDOW_AUTOSIZE);cvShowImage("MyCalib",SourceImg);cvWaitKey(NULL);/*提取角点精确到亚像素*/int corner_count;if(0==cvFindChessboardCorners(SourceImgGray,patter_size,corners,&corner_count,CV_CALIB_CB_ADAPTIVE_THRESH|CV_CALIB_CB_FILTER_QUADS)){ cout<<"......无法找出第"<<i<<"幅图的角点"<<endl;cvWaitKey(NULL);cvReleaseImage(&SourceImgGray);cvReleaseImage(&SourceImg);continue;//return -1;}else{// find specific locations of corners---yvonne cvFindCornerSubPix(SourceImgGray,corners,corner_count,cvSize(11,11),cvSize(-1,-1),cvTermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER,30,0.1));//CvSize win 和matlab标定工具箱类似cvDrawChessboardCorners(SourceImg,patter_size,corners,corner_count,1);cvShowImage("MyCalib",SourceImg);cvSaveImage("E:\\TestCase\\CalibrationTest\\picture\\picture.jpg",SourceImg);} cvWaitKey(0);for (int j=0;j<board_n;j++){CV_MAT_ELEM(*image_points,float,sn_board*board_n+j,0)=corners[j].x;CV_MAT_ELEM(*image_points,float,sn_board*board_n+j,1)=corners[j].y;CV_MAT_ELEM(*object_points,float,sn_board*board_n+j,0)=float(j/board_w);CV_MAT_ELEM(*object_points,float,sn_board*board_n+j,1)=float(j%board_w);CV_MAT_ELEM(*object_points,float,sn_board*board_n+j,2)=0.0f;CV_MAT_ELEM(*point_counts,int,sn_board,0)=board_n;}sn_board++;cvReleaseImage(&SourceImgGray);cvReleaseImage(&SourceImg);cout<<"......成功找出第"<<i<<"幅图的角点"<<endl;}cout<<"......一共成功获得"<<sn_board<<"幅图像的角点"<<"......无法获得"<<n_board-sn_board<<"幅图像的角点"<<endl;//重新赋值CvMat * object_points0= cvCreateMat(board_n*sn_board,3,CV_32FC1);CvMat * image_points0=cvCreateMat(board_n*sn_board,2,CV_32FC1);CvMat * point_counts0=cvCreateMat(sn_board,1,CV_32SC1);for (int i=0;i<sn_board*board_n;i++){CV_MAT_ELEM(*image_points0,float,i,0)=CV_MAT_ELEM(*image_points,float,i,0);CV_MAT_ELEM(*image_points0,float,i,1)=CV_MAT_ELEM(*image_points,float,i,1);CV_MAT_ELEM(*object_points0,float,i,0)=CV_MAT_ELEM(*object_points,float,i,0);CV_MAT_ELEM(*object_points0,float,i,1)=CV_MAT_ELEM(*object_points,float,i,1);CV_MAT_ELEM(*object_points0,float,i,2)=0.0f; //z=0}for (int i=0;i<sn_board;i++){CV_MAT_ELEM(*point_counts0,int,i,0)=CV_MAT_ELEM(*point_counts,int,i,0);}cvReleaseMat(&object_points);cvReleaseMat(&point_counts);cvReleaseMat(&image_points); /*摄像机标定并求得内部参数*/CvMat * camera_matrix=cvCreateMat(3,3,CV_32FC1);CvMat * distortion_coeffs=cvCreateMat(1,4,CV_32FC1);CvMat * rotation_vectors=cvCreateMat(sn_board,3,CV_32FC1);CvMat * translation_vectors=cvCreateMat(sn_board,3,CV_32FC1);int flags=0;cvCalibrateCamera2(object_points0,image_points0,point_counts0,image_size,camera_matrix//,distortion_coeffs,NULL,NULL,flags);,distortion_coeffs,rotation_vectors,translation_vectors,0);//输出结果printf("/*****摄像机内部参数*****/\n");PrintMat(camera_matrix);printf("/*****畸变参数k1,k2,p1,p2*****/\n");PrintMat(distortion_coeffs);cvWaitKey(0);//保存数据printf("/*****保存内部参数与畸变参数*****/\n");cvSave("camera_matrix1111.xml",camera_matrix);cvSave("distortion_coeffs.xml",distortion_coeffs);FILE *fp=NULL;if ((fp=fopen("E:\\TestCase\\CalibrationTest\\camera_matrix.txt","w"))==NULL){printf("无法打开文件!");exit(1);}FputMat(fp,camera_matrix);fclose(fp);if ((fp=fopen("E:\\TestCase\\CalibrationTest\\distortion_coeffs.txt","w"))==NULL){printf("无法打开文件!");exit(1);}FputMat(fp,distortion_coeffs);fclose(fp);//误差分析CvMat * object_points2=cvCreateMat(board_n,3,CV_32FC1);CvMat * image_points2=cvCreateMat(board_n,2,CV_32FC1);CvMat * rotation_vectors2=cvCreateMat(1,3,CV_32FC1);CvMat * translation_vectors2=cvCreateMat(1,3,CV_32FC1);CvMat * Err=cvCreateMat(sn_board*board_n,2,CV_32FC1);for(int k=0;k<sn_board;k++){for (int i=0;i<board_n;i++)//取一幅图的数据{CV_MAT_ELEM(*object_points2,float,i,0)=CV_MAT_ELEM(*object_points0,float,k*board_n+i,0);CV_MAT_ELEM(*object_points2,float,i,1)=CV_MAT_ELEM(*object_points0,float,k*board_n+i,1);CV_MAT_ELEM(*object_points2,float,i,2)=0.0f;}for (int i=0;i<3;i++){CV_MAT_ELEM(*rotation_vectors2,float,0,i)=CV_MAT_ELEM(*rotation_vectors,float,k,i);CV_MAT_ELEM(*translation_vectors2,float,0,i)=CV_MAT_ELEM(*translation_vectors,float,k,i);}cvProjectPoints2(object_points2,rotation_vectors2,translation_vectors2,camera_matrix,distortion_coeffs,image_points2);for (int i=0;i<board_n;i++){CV_MAT_ELEM(*Err,float,k*board_n+i,0)=CV_MAT_ELEM(*image_points0,float,k*board_n+i,0)-CV_MAT_ELEM(*image_points2,float,i,0);CV_MAT_ELEM(*Err,float,k*board_n+i,1)=CV_MAT_ELEM(*image_points0,float,k*board_n+i,1)-CV_MAT_ELEM(*image_points2,float,i,1);}}//PrintMat(Err);CvMat * Err_abs=cvCreateMat(board_n*sn_board,2,CV_32FC1);cvAbs(Err,Err_abs);CvScalar Dmean;CvScalar Ddev;cvAvgSdv(Err_abs,&Dmean,&Ddev);printf("/*****反投影误差分析*****/\n");cout<<"......绝对值误差矩阵的平均值:"<<endl;cout<<Dmean.val[0]<<endl;cout<<"......绝对值误差矩阵的标准差:"<<endl;cout<<Ddev.val[0]<<endl;cout<<"......保存误差矩阵"<<endl;cvSave("err.xml",Err);if ((fp=fopen("E:\\TestCase\\CalibrationTest\\err.txt","w"))==NULL){printf("无法打开文件!");exit(1);}FputMat(fp,Err);fclose(fp);cvReleaseMat (&object_points2);cvReleaseMat (&image_points2);cvReleaseMat (&rotation_vectors2);cvReleaseMat (&translation_vectors2);cvReleaseMat (&Err);cvReleaseMat (&Err_abs);//释放总变量cvWaitKey(NULL);cvReleaseMat(&object_points0);cvReleaseMat(&point_counts0);cvReleaseMat(&image_points0);cvReleaseMat(&camera_matrix);cvReleaseMat(&distortion_coeffs);cvReleaseMat(&rotation_vectors);cvReleaseMat(&translation_vectors);delete [] corners;cvDestroyWindow("MyCalib");}void PrintMat(CvMat* arry){for (int i=0;i<arry->rows;i++){for (int j=0;j<arry->cols;j++){printf("%f",CV_MAT_ELEM(*arry,float,i,j));if(j<arry->cols-1)printf(",");}printf("\n");}}void FputMat(FILE * fp,CvMat * arry){for (int i=0;i<arry->rows;i++){for (int j=0;j<arry->cols;j++){fprintf(fp,"%f",CV_MAT_ELEM(*arry,float,i,j));if(j<arry->cols-1)fprintf(fp,",");}fprintf(fp,"\n");}}


结果:



0 0
原创粉丝点击