在OpenGL中理解摄像机标定
来源:互联网 发布:淘宝经营地址怎么填写 编辑:程序博客网 时间:2024/05/23 02:05
摄像机是3D世界和2D图像之间的一种映射。
利用齐次坐标表示3D空间坐标
x=PX (1−1)
如果已知足够数量的x与X一一对应的点,便可以计算出矩阵
为了更进一步了解
考虑空间点到一张平面上的中心投影,假设投影中心位于欧式坐标系的原点,而平面
(X,Y,Z)T−>(fX/Z,fY/Z)T (1−2)
这是从三维欧氏空间到二维欧氏空间的一个映射。并且假定图像平面的坐标原点在主点上,(摄像机中心到图像平面的垂线称为摄像机的主轴,而主轴与图像平面的交点称为主点,如上图中点p)。一般情况下,存在主点偏置。
(X,Y,Z)T−>(fX/Z+px,fY/Z+py)T (1−3)
其中,
参考图5.2,公式(1-3)用齐次坐标可以表示成:
Z⎡⎣⎢xy1⎤⎦⎥=⎡⎣⎢f000f0pxpy1000⎤⎦⎥⎡⎣⎢⎢⎢XYZ1⎤⎦⎥⎥⎥ (1−4)
需要强调的是,坐标
如果
Xcam=[R0T−RC˜1]⎡⎣⎢⎢⎢XYZ1⎤⎦⎥⎥⎥=[R0T−RC˜1]X (1−5)
将公式(1-4)与公式(1-5)合并,
x=KR[I |−C˜]X (1−6) .
其中,
K=⎡⎣⎢f000f0pxpy1⎤⎦⎥ (1−7)
根据(1-1)式,摄像机矩阵可分解为:
P=K[R|t] ,t=−RC˜ (1−8)
上面推导的针孔摄像机模型假设图像坐标在两个轴向上有等尺度的欧氏坐标。在CCD摄像机模型中,像素不可能为正方形,如果图像坐标以像素来测量,则需要在每个方向上引入非等量的尺度因子。具体地说,如果在x,y方向上图像坐标单位距离的像素数分别是
K=⎡⎣⎢fx000fy0x0y01⎤⎦⎥ (1−9)
其中,
至此,摄像机的映射已介绍完毕,需要提醒一点的是,在上图5.2中,建立的图像坐标y轴方向是朝上的,其与实际的图像坐标系y轴是反向的。
在OpenCV中,给出了摄像机标定的方法,通过示例,可以准确地计算出矩阵
关于OpenCV相机的标定看这里。
关于OpenGL的Transformation 看这里。
OpenCV标定程序是sources\samples\cpp目录下的calibration.cpp,使用的图像数据是 left01.jpg ~ left14.jpg ,共13张图片。
如图,识别到的棋盘点的顺序如图编号所示,由于点的坐标是以图片左上角为原点,所以我将坐标原点修改为图像的左下角,此时,图像坐标系与上述图5.2中的保持一致。在calibration.cpp中代码修改如下:
...//line 487if( mode == CAPTURING && found && (!capture.isOpened() || clock() - prevTimestamp > delay*1e-3*CLOCKS_PER_SEC) ) { vector<Point2f> points; for (int i = 0; i < pointbuf.size(); i++) { Point2f p = pointbuf[i]; points.push_back(Point2f(p.x, imageSize.height - p.y)); } imagePoints.push_back(points);//存储更新坐标系后的点 prevTimestamp = clock(); blink = capture.isOpened(); }...
同样,在定义棋盘点的空间坐标时,选取的坐标系与图5.2保持一致,从而设定点46为坐标系原点,点1的三维坐标即为(0,5,0),点9的坐标为(8,5,0)(squareSize = 1)。在calibration.cpp中修改代码如下:
...//line 114case CHESSBOARD: case CIRCLES_GRID: for( int i = 0; i < boardSize.height; i++ ) for( int j = 0; j < boardSize.width; j++ ) corners.push_back(Point3f(float(j*squareSize), float((boardSize.height - 1 - i)*squareSize), 0)); break;...
命令运行标定程序:
E:\OpenCV\Sample\camCalib\bin\bin>cameraCalib.exe -w 9 -h 6 -o camera.yml -oe imagelist.xml
生成的camera.yml包括摄像机的内参数以及在不同视角下的旋转和位移。旋转通过单一的旋转角
此时,因为角-轴表示密切关联于四元数表示。依据轴和角,四元数可以给出为正规化四元数 Q :
Q=(xi+yj+zk)sin(θ/2)+cos(θ/2)
然后,根据四元数与欧拉角的关系,计算出绕Z轴、Y轴、X轴的旋转角度
由公式(1-8),不难求出摄像机的中心坐标,
代码如下:
...int i = cameraId;Mat camPos = tvecs[i]; //获取第i个相机的位移向量Mat camRot = rvecs[i]; //获取第i个相机的旋转向量Mat rotation;cv::Rodrigues(camRot, rotation); Mat cameraPos = -rotation.inv()*camPos; // C = -R^-1*tstd::cout << "Cam Position: " << cameraPos << std::endl;float w, x, y, z;w = cos(norm(camRot) / 2);x = sin(norm(camRot) / 2)*camRot.ptr<double>(0)[0] / norm(camRot);y = sin(norm(camRot) / 2)*camRot.ptr<double>(1)[0] / norm(camRot);z = sin(norm(camRot) / 2)*camRot.ptr<double>(2)[0] / norm(camRot);std::cout << w << " " << x << " " << y << " " << z << std::endl;//四元素转欧拉角cameraAngle[0] = atan2(2 * (w*x + z*y), 1 - 2 * (y*y + x*x));cameraAngle[1] = asin(2 * (w*y - x*z));cameraAngle[2] = atan2(2 * (w*z + y*x), 1 - 2 * (z*z + y*y));//欧拉角反推旋转矩阵,进行验证Matrix3 Rx, Ry, Rz;Rx.set(1, 0, 0, 0, cos(cameraAngle[0]), -sin(cameraAngle[0]), 0, sin(cameraAngle[0]), cos(cameraAngle[0]));Ry.set(cos(cameraAngle[1]), 0, sin(cameraAngle[1]), 0, 1, 0, -sin(cameraAngle[1]), 0, cos(cameraAngle[1]));Rz.set(cos(cameraAngle[2]), -sin(cameraAngle[2]), 0, sin(cameraAngle[2]), cos(cameraAngle[2]), 0, 0, 0, 1);Matrix3 R = Rz*Ry*Rx;std::cout << rotation << std::endl;std::cout << R << std::endl << std::endl;...
利用计算得到的摄像机位置与姿态,处理如下:
...glPushMatrix();glTranslatef(cameraPos.at<double>(0, 0), cameraPos.at<double>(1, 0), -cameraPos.at<double>(2, 0)); //OpenGL的Z轴与相机自身Z轴是反向的。glRotatef(cameraAngle[0] * 180 / 3.1415926, 1, 0, 0);glRotatef(cameraAngle[1] * 180 / 3.1415926, 0, 1, 0);glRotatef(cameraAngle[2] * 180 / 3.1415926, 0, 0, 1);drawCamera();drawFrustum(FOV_Y, 4.0/3.0, 1, 20);glPopMatrix();...
为了将摄像机参数文件camera.yml 的相关数据读入程序,提供了如下方法:
void readCamerasParams(const string filename, std::vector<Mat>& rvecs, std::vector<Mat>& tvecs){ FileStorage fs(filename, FileStorage::READ); int nframes; fs["nframes"] >> nframes; Mat bigmat; fs["extrinsic_parameters"] >> bigmat; rvecs.resize(bigmat.rows); tvecs.resize(bigmat.rows); for (int i = 0; i < bigmat.rows; i++ ) { Mat r = bigmat(Range(i, i + 1), Range(0, 3)); Mat t = bigmat(Range(i, i + 1), Range(3, 6)); rvecs[i] = r.t(); tvecs[i] = t.t(); } for (int i = 0; i < rvecs.size();i++) { std::cout << rvecs[i] << " ," << tvecs[i] << std::endl; }}
结果展示:
0号相机_left01.jpg:
5号相机_left06.jpg:
全部相机:
- 在OpenGL中理解摄像机标定
- 摄像机标定到底是在干什么?
- 摄像机标定
- 摄像机标定
- 摄像机标定
- 摄像机标定
- 摄像机标定
- 摄像机标定
- 摄像机标定
- 摄像机标定
- 摄像机标定
- 摄像机标定
- 摄像机标定
- 摄像机标定
- 摄像机标定
- 摄像机标定
- 摄像机标定
- 摄像机标定
- 图片效果
- 第一周
- Netty解惑
- csdn的第一篇2017年5月11日12:27:29
- oracle的定时任务demo
- 在OpenGL中理解摄像机标定
- 你身边的人,决定了你的人生高度
- Brew安装mysql时,最后ln -sfv /usr/local/opt/mysql/*.plist ~/Library/LaunchAgents这句什么意思
- 1017. A除以B (20) PAT
- spring mvc学习笔记《一》
- java多线程学习之一——线程的状态、上下文切换和线程监控
- Mybatis配置之<environments>配置元素详述
- 安装 dpdk-ans 时的编译问题:librte_ans.a(ans_init.o): unrecognized relocation (0x2a) in section `.text'
- android 加密解密一