OpenCV: 攝像機標定原理
#include "stdafx.h"
#include "cv.h"
#include "highgui.h"
#include <string>
#include <iostream>
using namespace std;
int main()
{
int cube_length=7;
CvCapture* capture;
capture=cvCreateCameraCapture(0);
if(capture==0){
printf("無法捕獲攝像頭設備!\n\n");
return 0;
}else{
printf("捕獲攝像頭設備成功!!\n\n");
}
IplImage* frame;
cvNamedWindow("攝像機幀截取窗口",1); //cvNamedWindow()函數用於在屏幕上創建一個窗口,將被顯示的圖像包含於該窗口中。函數的第一個參數指定了該窗口的窗口標題,如果要使用HighGUI庫所提供的其他函數與該窗口進行交互時,我們將通過該參數值引用這個窗口。
printf("按“C”鍵截取當前幀並保存為標定圖片...\n按“Q”鍵退出截取幀過程...\n\n");
int number_image=1;
char *str1;
str1=".jpg";
char filename[20]="";
while(true)
{
frame=cvQueryFrame(capture);// 從攝像頭或者文件中抓取並返回一幀
if(!frame)
break;
cvShowImage("攝像機幀截取窗口",frame); //圖像顯示
if(cvWaitKey(10)=='c'){
sprintf_s (filename,"%d.jpg",number_image); // int sprintf_s( char *buffer, size_t sizeOfBuffer, const char *format [, argument] ... );
這個函數的主要作用是將若干個argument按照format格式存到buffer中
cvSaveImage(filename,frame);//保存
cout<<"成功獲取當前幀,並以文件名"<<filename<<"保存...\n\n";
printf("按“C”鍵截取當前幀並保存為標定圖片...\n按“Q”鍵退出截取幀過程...\n\n");
number_image++;
}else if(cvWaitKey(10)=='q'){
printf("截取圖像幀過程完成...\n\n");
cout<<"共成功截取"<<--number_image<<"幀圖像!!\n\n";
break;
}
}
cvReleaseImage(&frame); //釋放圖像
cvDestroyWindow("攝像機幀截取窗口");
IplImage * show;
cvNamedWindow("RePlay",1);
int a=1;
int number_image_copy=number_image;
CvSize board_size=cvSize(7,7); // Cvsizes:OpenCV的基本數據類型之一。表示矩陣框大小,以像素為精度。與CvPoint結構類似,但數據成員是integer類型的width和height。
//cvSize是
int board_width=board_size.width;
int board_height=board_size.height;
int total_per_image=board_width*board_height;
CvPoint2D32f * image_points_buf = new CvPoint2D32f[total_per_image];
CvMat * image_points=cvCreateMat(number_image*total_per_image,2,CV_32FC1);//圖像坐標系
CvMat * object_points=cvCreateMat(number_image*total_per_image,3,CV_32FC1);//世界坐標系
CvMat * point_counts=cvCreateMat(number_image,1,CV_32SC1);//
CvMat * intrinsic_matrix=cvCreateMat(3,3,CV_32FC1);//
CvMat * distortion_coeffs=cvCreateMat(5,1,CV_32FC1);
int count;
int found;
int step;
int successes=0;
while(a<=number_image_copy){
sprintf_s (filename,"%d.jpg",a);
show=cvLoadImage(filename,-1);
found=cvFindChessboardCorners(show,board_size,image_points_buf,&count,
CV_CALIB_CB_ADAPTIVE_THRESH|CV_CALIB_CB_FILTER_QUADS);
if(found==0){
cout<<"第"<<a<<"幀圖片無法找到棋盤格所有角點!\n\n";
cvNamedWindow("RePlay",1);
cvShowImage("RePlay",show);
cvWaitKey(0);
}else{
cout<<"第"<<a<<"幀圖像成功獲得"<<count<<"個角點...\n";
cvNamedWindow("RePlay",1);
IplImage * gray_image= cvCreateImage(cvGetSize(show),8,1); //創建頭並分配數據IplImage* cvCreateImage( CvSize size, int depth, int channels ); depth 圖像元素的位深度
cvCvtColor(show,gray_image,CV_BGR2GRAY); // cvCvtColor(...),是Opencv裡的顏色空間轉換函數,可以實現rgb顏色向HSV,HSI等顏色空間的轉換,也可以轉換為灰度圖像。
cout<<"獲取源圖像灰度圖過程完成...\n";
cvFindCornerSubPix(gray_image,image_points_buf,count,cvSize(11,11),cvSize(-1,-1),由於非常接近P的像素產生了很小的特征值,所以這個自相關矩陣並不總是可逆的。為了解決這個問題,一般可以簡單地剔除離P點非常近的像素。輸入參數:ero_zone定義了一個禁區(與win相似,但通常比win小),這個區域在方程組以及自相關矩陣中不被考慮。如果不需要這樣一個禁區,則zero_zone應設置為cvSize(-1,-1)0
cvTermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER,30,0.1));
cout<<"灰度圖亞像素化過程完成...\n";
cvDrawChessboardCorners(show,board_size,image_points_buf,count,found);
cout<<"在源圖像上繪制角點過程完成...\n\n";
cvShowImage("RePlay",show);
cvWaitKey(0);
}
if(total_per_image==count){
step=successes*total_per_image;
for(int i=step,j=0;j<total_per_image;++i,++j){
CV_MAT_ELEM(*image_points,float,i,0)=image_points_buf[j].x; // opencv中用來訪問矩陣每個元素的宏,這個宏只對單通道矩陣有效,多通道CV_MAT_ELEM( matrix, elemtype, row, col )參數 matrix:要訪問的矩陣 elemtype:矩陣元素的類型 row:所要訪問元素的行數 col:所要訪問元素的列數
CV_MAT_ELEM(*image_points,float,i,1)=image_points_buf[j].y;// 求完每個角點橫縱坐標值都存在image_point_buf裡
CV_MAT_ELEM(*object_points,float,i,0)=(float)(j/cube_length);
CV_MAT_ELEM(*object_points,float,i,1)=(float)(j%cube_length);
CV_MAT_ELEM(*object_points,float,i,2)=0.0f;
}
CV_MAT_ELEM(*point_counts,int,successes,0)=total_per_image;
successes++;
}
a++;
}
cvReleaseImage(&show);
cvDestroyWindow("RePlay");
cout<<"*********************************************\n";
cout<<number_image<<"幀圖片中,標定成功的圖片為"<<successes<<"幀...\n";
cout<<number_image<<"幀圖片中,標定失敗的圖片為"<<number_image-successes<<"幀...\n\n";
cout<<"*********************************************\n\n";
cout<<"按任意鍵開始計算攝像機內參數...\n\n";
CvCapture* capture1;
capture1=cvCreateCameraCapture(0);
IplImage * show_colie;
show_colie=cvQueryFrame(capture1);
CvMat * object_points2=cvCreateMat(successes*total_per_image,3,CV_32FC1); // OpenCV 中重要的矩陣變換函數,使用方法為cvMat* cvCreateMat ( int rows, int cols, int type ); 這裡type可以是任何預定義類型,預定義類型的結構如下:CV_<bit_depth> (S|U|F)C<number_of_channels>。
CvMat * image_points2=cvCreateMat(successes*total_per_image,2,CV_32FC1);
CvMat * point_counts2=cvCreateMat(successes,1,CV_32SC1);
for(int i=0;i<successes*total_per_image;++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);
}
cvReleaseMat(&object_points);
cvReleaseMat(&image_points);
cvReleaseMat(&point_counts);
CV_MAT_ELEM(*intrinsic_matrix,float,0,0)=1.0f;
CV_MAT_ELEM(*intrinsic_matrix,float,1,1)=1.0f;
cvCalibrateCamera2(object_points2,image_points2,point_counts2,cvGetSize(show_colie),
intrinsic_matrix,distortion_coeffs,NULL,NULL,0);
cout<<"攝像機內參數矩陣為:\n";
cout<<CV_MAT_ELEM(*intrinsic_matrix,float,0,0)<<" "<<CV_MAT_ELEM(*intrinsic_matrix,float,0,1)
<<" "<<CV_MAT_ELEM(*intrinsic_matrix,float,0,2)
<<"\n\n";
cout<<CV_MAT_ELEM(*intrinsic_matrix,float,1,0)<<" "<<CV_MAT_ELEM(*intrinsic_matrix,float,1,1)
<<" "<<CV_MAT_ELEM(*intrinsic_matrix,float,1,2)
<<"\n\n";
cout<<CV_MAT_ELEM(*intrinsic_matrix,float,2,0)<<" "<<CV_MAT_ELEM(*intrinsic_matrix,float,2,1)
<<" "<<CV_MAT_ELEM(*intrinsic_matrix,float,2,2)
<<"\n\n";
cout<<"畸變系數矩陣為:\n";
cout<<CV_MAT_ELEM(*distortion_coeffs,float,0,0)<<" "<<CV_MAT_ELEM(*distortion_coeffs,float,1,0)
<<" "<<CV_MAT_ELEM(*distortion_coeffs,float,2,0)
<<" "<<CV_MAT_ELEM(*distortion_coeffs,float,3,0)
<<" "<<CV_MAT_ELEM(*distortion_coeffs,float,4,0)
<<"\n\n";
cvSave("Intrinsics.xml",intrinsic_matrix);
cvSave("Distortion.xml",distortion_coeffs);
cout<<"攝像機矩陣、畸變系數向量已經分別存儲在名為Intrinsics.xml、Distortion.xml文檔中\n\n";
CvMat * intrinsic=(CvMat *)cvLoad("Intrinsics.xml");
CvMat * distortion=(CvMat *)cvLoad("Distortion.xml");
IplImage * mapx=cvCreateImage(cvGetSize(show_colie),IPL_DEPTH_32F,1);
IplImage * mapy=cvCreateImage(cvGetSize(show_colie),IPL_DEPTH_32F,1);
cvInitUndistortMap(intrinsic,distortion,mapx,mapy);
cvNamedWindow("原始圖像",1);
cvNamedWindow("非畸變圖像",1);
cout<<"按‘E’鍵退出顯示...\n\n";
while(show_colie){
IplImage * clone=cvCloneImage(show_colie);
cvShowImage("原始圖像",show_colie);
cvRemap(clone,show_colie,mapx,mapy);
cvReleaseImage(&clone);
cvShowImage("非畸變圖像",show_colie);
if(cvWaitKey(10)=='e'){
break;
}
show_colie=cvQueryFrame(capture1);
}
return 0;
}
各標定步驟實現方法
1 計算標靶平面與圖像平面之間的映射矩陣
計算標靶平面與圖像平面之間的映射矩陣,計算映射矩陣時不考慮攝像機的成像模型,只是根據平面標靶坐標點和對應的圖像坐標點的數據,利用最小二乘方法計算得到[ [ix] ]
.2 求解攝像機參數矩陣
由計算得到的標靶平面和圖像平面的映射矩陣得到與攝像機內部參數相關的基本方程關系,求解方程得到攝像機內部參數,考慮鏡頭的畸變模型,將上述解方程獲
得的內部參數作為初值,進行非線性優化搜索,從而計算出所有參數的准確值 [[x] ]
.3 求解左右兩攝像機之間的相對位置關系
設雙目視覺系統左右攝像機的外部參數分別為Rl, Tl,與Rr, Tr,,即Rl, Tl表示左攝像機與世界坐標系的相對位置,Rr, Tr表示右攝像機與世界坐標系的相對位置 [[xi] ]。因此,對於空間任意一點,如果在世界坐標系、左攝像機坐標系和右攝像機坐標系中的坐標分別為Xw,, Xl , Xr,則有:Xl=RlXw+Tl ;Xr=RrXw+Tr .因此,兩台攝像機之間的相對幾何關系可以由下式表示R=RrRl-1 ;T=Tr- RrRl-1Tl
在實際標定過程中,由標定靶對兩台攝像機同時進行攝像標定,以分別獲得兩台攝像機的內、外參數,從而不僅可以標定出攝像機的內部參數,還可以同時標定出雙目視覺系統的結構參數 [xii] 。由單攝像機標定過程可以知道,標定靶每變換一個位置就可以得到一組攝像機外參數:Rr,Tr,與Rl, Tl,因此,由公式R=RrRl-1 ;T=Tr- RrRl-1Tl,可以得到一組結構參數R和T
--------------------------------------分割線 --------------------------------------
Ubuntu Linux下安裝OpenCV2.4.1所需包 http://www.linuxidc.com/Linux/2012-08/68184.htm
Ubuntu 12.04 安裝 OpenCV2.4.2 http://www.linuxidc.com/Linux/2012-09/70158.htm
CentOS下OpenCV無法讀取視頻文件 http://www.linuxidc.com/Linux/2011-07/39295.htm
Ubuntu 12.04下安裝OpenCV 2.4.5總結 http://www.linuxidc.com/Linux/2013-06/86704.htm
Ubuntu 10.04中安裝OpenCv2.1九步曲 http://www.linuxidc.com/Linux/2010-09/28678.htm
基於QT和OpenCV的人臉識別系統 http://www.linuxidc.com/Linux/2011-11/47806.htm
--------------------------------------分割線 --------------------------------------
OpenCV的詳細介紹:請點這裡
OpenCV的下載地址:請點這裡