坐标系:
世界坐标系:Xw,Yw,Zw;
摄像机坐标系:Xc,Yc,Zc;
图像坐标系:[x,y] ,[u,v] (包含图像物理坐标系和图像像素坐标系)
这就是由世界坐标系映射到像素坐标系的齐次变换:
其中K为摄像机的内参矩阵,R为旋转变换,t为平移变换。
这其实就构成了摄像机标定的理论基础。
下面来介绍一种经典的方法张正友平面标定发:
基本原理:
这里假定模板平面在世界坐标系Z=0的平面上,K为内参矩阵,M=[X,Y,1],为模版平面上的其次坐标系,m=[u,v,1]为模板平面映射到图像平面的坐标系;
根据旋转矩阵的性质有:
这可得
opencv实现:
#include<iostream>
#include<opencv2\opencv.hpp>
#include<vector>
#include<stdio.h>
using namespace cv;
using namespace std;
void show(char *windowname,Mat &img)
{
namedWindow(windowname,1);
imshow(windowname,img);
waitKey(0);
}
void printmat(Mat &img)
{
int row=img.rows;
int col=img.cols;
int i=0;
int j=0;
for(i=0;i<row;i++){
for(j=0;j<col;j++){
cout<<img.ptr<double>(i)[j]<<" ";
}
cout<<endl;
}
}
void main()
{
Mat src,src_gray;//图像矩阵
Mat cameraMatrix(3,3,CV_32FC1);
Mat distCoeffs;
vector<Mat> rvecs;
vector<Mat> tvecs;
char filename[50];
vector <Point2f> point_buf;
vector <Point2f> test_point;
vector <Point3f> object_buf;
vector <vector <Point2f> > coner_point;
vector <vector <Point3f> > object_point;
int real_size=27.70; //标定板实际的尺寸为27.70mm
Size bord_size(6,8);
bool flag;
int mat_size=0;
int success=0;
Point3f temp_object;
double img_err;
int i=0;
int j=0;
int w=0;
int h=0;
cout<<" begin search for the corners\n";
for(i=1;i<15;i++) {
sprintf(filename,"src//%d.jpg",i);
src=imread(filename,1);
flag=findChessboardCorners(src,bord_size,point_buf,CALIB_CB_ADAPTIVE_THRESH);
if(flag) {
success++;
cvtColor(src,src_gray,CV_RGB2GRAY);
cornerSubPix(src_gray, point_buf, Size(11, 11), Size(-1, -1),
TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));
coner_point.push_back(point_buf);
drawChessboardCorners(src,bord_size,point_buf,flag);
show(filename,src);
}
}
cout<<"Searching the corners is ended\n";
cout<<"begin calculate the object point\n";
for(h=0;h<bord_size.height;h++){
for(w=0;w<bord_size.width;w++){
temp_object.x=(float)h*real_size;
temp_object.y=(float)w*real_size;
temp_object.z=0.0f;
object_buf.push_back(temp_object);
}
}
for(i=0;i<success;i++) {
object_point.push_back(object_buf);
}
cout<<"calculate the object ended\n";
cout<<"start the calibrateCamera\n";
cameraMatrix.ptr<double>(0)[0]=1.0;
cameraMatrix.ptr<double>(1)[1]=1.0;
calibrateCamera(object_point,coner_point,src.size(),
cameraMatrix,distCoeffs,rvecs,tvecs );
cout<<"ended the calibrateCamera\n";
cout<<"摄像机的内参矩阵为:"<<endl;
printmat(cameraMatrix);
cout<<endl;
cout<<"摄像机的畸变系数为:"<<endl;
printmat(distCoeffs);
cout<<endl;
cout<<"test the calibrateCamera\n";
for(i=0;i<success;i++){
projectPoints(object_point[i],rvecs[i],tvecs[i],cameraMatrix,distCoeffs,test_point);
img_err=norm(test_point,coner_point[i]);
cout<<"图像"<<i+1<<":总体误差"<<img_err<<endl;
cout<<"图像"<<i+1<<":各点的平均误差"<<img_err/(bord_size.height*bord_size.width)<<endl;
}
for(i=0;i<success;i++){
Mat r_mat(3,3,CV_32FC1);
Rodrigues(rvecs[i],r_mat);
cout<<"图像"<<i+1<<":旋转矩阵为"<<endl;
printmat(r_mat);
cout<<endl;
cout<<"图像"<<i+1<<":平移矩阵为"<<endl;
printmat(tvecs[i]);
cout<<endl;
}
}