@windmelon
2018-12-20T08:23:19.000000Z
字数 3600
阅读 2505
智能机器人实验
了解深度图像处理的一般工作流程,掌握OpenCV,Eigen,PCL等第三方库的使用
尝试自行搭建双目视觉系统,实现对视图中各物体的深度测量。
Ubuntu 16.04 虚拟机
C++
OpenCV 3
color
depth
CMakeLists.txt
joinMap.cpp
pose.txt
Color和depth文件夹下分别放置了5张彩色和深度图,取自同一个RGB-D摄像机,按序号配对;
CMakeList.txt为编译配置文件,joinMap.cpp为点云拼接主程序,pose.txt为相机参数。
矩阵的定义非常简单,Eigen里有模板类Matrix和用该模板定义好的矩阵和向量
Matrix< typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime >
#数据类型为float的4X4矩阵
typedef Matrix< float, 4, 4 > Matrix4f;
#数据类型为float的3X1列向量
typedef Matrix< float, 3, 1 > Vector3f;
#数据类型为int的1X2行向量
typedef Matrix< int, 1, 2 > RowVector2i;
#数据类型为double的自定义大小的矩阵
typedef Matrix< double, Dynamic, Dynamic > MatrixXd;
使用方法如下
#include <iostream>#include <Eigen/Dense>using Eigen::MatrixXd;int main(){MatrixXd m(2,2);m(0,0) = 3;m(1,0) = 2.5;m(0,1) = -1;m(1,1) = m(1,0) + m(0,1);std::cout << m << std::endl;}
实现矩阵加减乘法也很简单,因为Matrix类已经重载过这些操作符,直接使用即可
MatrixXd m(2,2);m(0,0) = 3;m(1,0) = 2.5;m(0,1) = -1;m(1,1) = m(1,0) + m(0,1);std::cout << "this is matrix m:\n" << m << std::endl;MatrixXd n(2,2);n(0,0) = 1;n(1,0) = 2;n(0,1) = 3;n(1,1) = 4;std::cout << "this is matrix n:\n" << n << std::endl;std::cout << "this is the result of m+n:\n" << m+n << std::endl;std::cout << "this is the result of m*n:\n" << m*n << std::endl;
this is matrix m:
3 -1
2.5 1.5
this is matrix n:
1 3
2 4
this is the result of m+n:
4 2
4.5 5.5
this is the result of m*n:
1 5
5.5 13.5
实现矩阵转置也很方便
std::cout << "this is the result of m^T:\n" << m.transpose() << std::endl;
this is the result of m^T:
3 2.5
-1 1.5
vector<cv::Mat> colorImgs, depthImgs; // 彩色图和深度图vector<Eigen::Isometry3d, Eigen::aligned_allocator<Eigen::Isometry3d>> poses; // 相机位姿ifstream fin("./pose.txt");if (!fin){cerr<<"请在有pose.txt的目录下运行此程序"<<endl;return 1;}for ( int i=0; i<5; i++ ){boost::format fmt( "./%s/%d.%s" ); //图像文件格式colorImgs.push_back( cv::imread( (fmt%"color"%(i+1)%"png").str() ));depthImgs.push_back( cv::imread( (fmt%"depth"%(i+1)%"pgm").str(), -1 )); // 使用-1读取原始图像double data[7] = {0};for ( auto& d:data )fin>>d;Eigen::Quaterniond q( data[6], data[3], data[4], data[5] );Eigen::Isometry3d T(q);T.pretranslate( Eigen::Vector3d( data[0], data[1], data[2] ));poses.push_back( T );}
首先读入两组图片,一组是颜色图,一组是深色图
相机位姿数据记录的形式是平移向量加旋转四元数
[x; y; z; qx; qy; qz; qw];
每一对深度图和颜色图都有对应的相机位姿数据,也读入进来并转化为旋转矩阵,存入数组中
// 定义点云使用的格式:这里用的是XYZRGBtypedef pcl::PointXYZRGB PointT;typedef pcl::PointCloud<PointT> PointCloud;// 新建一个点云PointCloud::Ptr pointCloud( new PointCloud );for ( int i=0; i<5; i++ ){cout<<"转换图像中: "<<i+1<<endl;cv::Mat color = colorImgs[i];cv::Mat depth = depthImgs[i];Eigen::Isometry3d T = poses[i];for ( int v=0; v<color.rows; v++ )for ( int u=0; u<color.cols; u++ ){unsigned int d = depth.ptr<unsigned short> ( v )[u]; // 深度值if ( d==0 ) continue; // 为0表示没有测量到Eigen::Vector3d point;point[2] = double(d)/depthScale;point[0] = (u-cx)*point[2]/fx;point[1] = (v-cy)*point[2]/fy;Eigen::Vector3d pointWorld = T*point;PointT p ;p.x = pointWorld[0];p.y = pointWorld[1];p.z = pointWorld[2];p.b = color.data[ v*color.step+u*color.channels() ];p.g = color.data[ v*color.step+u*color.channels()+1 ];p.r = color.data[ v*color.step+u*color.channels()+2 ];pointCloud->points.push_back( p );}}
对于每张图的每个像素,生成一个点云数据
点云的计算流程大致为:
对于每个有深度的像素点,将该点的坐标由相机坐标系转换到世界坐标系
并将颜色图中该像素点对应的颜色赋给该点云
运行程序,结果如图

双目测距原理:通过两个摄像头同时成像,可以确定物体在从相机光心到归一化平面连线上的具体位置

双目测距步骤:
1. 双目摄像机标定
2. 双目校正
3. 双目匹配(计算视差)
4. 计算深度
关键代码:
Mat alineImgL,alineImgR;ImageRectify imgRectify;imgRectify.run(&leftCam,&rightCam,&alineImgL,&alineImgR);cout<<"Start disparity!"<<endl;StereoDepth calcDepth;calcDepth.depthSGM(alineImgL,alineImgR);
内参和外参
内参:相机的固定参数,用于进行相机坐标系到成像坐标系的转换。包括相机焦距,像素大小等
外参:用于描述相机与世界坐标系的关系,包括旋转矩阵和平移向量
外参的矩阵表示:四元数和平移向量组成旋转矩阵和平移向量的4X4矩阵
内参和外参
内参:即每个相机的内参
外参:每个相机的外参,相机间的基线,右摄像头相对于左摄像头的旋转矩阵和平移向量
本征矩阵:包含空间中摆布两个相机的扭转R和平移信息T,描述了摆布相机间的位姿关系;但不包罗相机自己的任何信息;