当前位置:   article > 正文

《视觉slam十四讲》ch5相机与图像学习笔记(3)——实践部分 RGB-D相机代码解释及相关函数介绍_poses.push_back

poses.push_back

        在这篇博客中,主要介绍《视觉SLAM十四讲》第五讲的实践部分——RGB-D代码详解。关于imageBasics的代码可见我另一篇博客:

《视觉slam十四讲》ch5学习笔记(1)—— 实践部分imageBasics代码讲解_sticker_阮的博客-CSDN博客_视觉slam十四讲代码

        关于双目视觉的代码可见我另一篇博客:

《视觉slam十四讲》ch5 学习笔记(2)——实践部分双目视觉代码讲解_sticker_阮的博客-CSDN博客

1.代码详解

        代码框架:输入RGB-D图像——读取每个像素点的深度值——转化成真实世界下的深度——计算图像三维坐标——形成3D点云。

        代码流程图如下:

 源码加上详细的注释如下:

  1. #include <iostream>
  2. #include <fstream>
  3. #include <opencv2/opencv.hpp>
  4. #include <boost/format.hpp> // for formating strings
  5. #include <pangolin/pangolin.h>
  6. #include <sophus/se3.hpp>
  7. #include<chrono>
  8. using namespace std;
  9. typedef vector<Sophus::SE3d, Eigen::aligned_allocator<Sophus::SE3d>> TrajectoryType;
  10. typedef Eigen::Matrix<double, 6, 1> Vector6d;
  11. // 在pangolin中画图,已写好,无需调整
  12. void showPointCloud(
  13. const vector<Vector6d, Eigen::aligned_allocator<Vector6d>> &pointcloud);
  14. int main(int argc, char **argv) {
  15. vector<cv::Mat> colorImgs, depthImgs; // 彩色图和深度图
  16. TrajectoryType poses; // 相机位姿
  17. ifstream fin("/home/rxz/slambook2/ch5/rgbd/pose.txt"); //ifstream默认以输入方式打开文件,文件内容为5张图片的相机外参位姿,即平移向量加旋转四元数[x,y,z,qx,qy,qz,qw]
  18. if (!fin) {
  19. cerr << "请在有pose.txt的目录下运行此程序" << endl;
  20. return 1;
  21. }
  22. chrono::steady_clock::time_point t1=chrono::steady_clock::now();
  23. for (int i = 0; i < 5; i++) {
  24. //创建格式化字符串对象
  25. boost::format fmt("../%s/%d.%s"); //图像文件格式
  26. colorImgs.push_back(cv::imread((fmt % "color" % (i + 1) % "png").str()));
  27. depthImgs.push_back(cv::imread((fmt % "depth" % (i + 1) % "pgm").str(), -1)); // 使用-1读取原始图像
  28. double data[7] = {0};
  29. for (auto &d:data)//基于范围的for循环,auto表示自动类型推导
  30. fin >> d; //之前是采用输入的方式获得pose.txt文件,在这里将之前获取到的pose文件中的相机位姿输入给d
  31. Sophus::SE3d pose(Eigen::Quaterniond(data[6], data[3], data[4], data[5]), //四元数
  32. Eigen::Vector3d(data[0], data[1], data[2])); //平移向量 三维的
  33. poses.push_back(pose);
  34. }
  35. // 计算点云并拼接
  36. // 相机内参
  37. double cx = 325.5;
  38. double cy = 253.5;
  39. double fx = 518.0;
  40. double fy = 519.0;
  41. double depthScale = 1000.0;
  42. vector<Vector6d, Eigen::aligned_allocator<Vector6d>> pointcloud;
  43. pointcloud.reserve(1000000); //重新调整pointcloud容器大小,使其能容纳1000000个元素
  44. for (int i = 0; i < 5; i++) {
  45. cout << "转换图像中: " << i + 1 << endl;
  46. cv::Mat color = colorImgs[i];
  47. cv::Mat depth = depthImgs[i];
  48. Sophus::SE3d T = poses[i];
  49. for (int v = 0; v < color.rows; v++)
  50. for (int u = 0; u < color.cols; u++) {
  51. unsigned int d = depth.ptr<unsigned short>(v)[u]; // 读取第v行第u列元素的深度值
  52. if (d == 0) continue; // 为0表示没有测量到,continue表示跳出本次循环,继续下次循环
  53. Eigen::Vector3d point;
  54. point[2] = double(d) / depthScale;//真实世界深度图
  55. point[0] = (u - cx) * point[2] / fx; //转化成真实世界坐标 相机坐标转化为三维坐标
  56. point[1] = (v - cy) * point[2] / fy;
  57. Eigen::Vector3d pointWorld = T * point; //将相机位姿与像素点3D坐标相乘
  58. Vector6d p;//前三维表示点云的位置,后三维表示点云的颜色
  59. p.head<3>() = pointWorld;//head<n>()函数是对于Eigen库中的向量类型而言的,表示提取前n个元素
  60. //opencv中图像的data数组表示把其颜色信息按行优先的方式展成的一维数组!
  61. //color.step等价于color.cols
  62. //color.channels()表示图像的通道数
  63. p[5] = color.data[v * color.step + u * color.channels()]; // blue
  64. p[4] = color.data[v * color.step + u * color.channels() + 1]; // green
  65. p[3] = color.data[v * color.step + u * color.channels() + 2]; // red
  66. pointcloud.push_back(p);
  67. }
  68. }
  69. cout << "点云共有" << pointcloud.size() << "个点." << endl;
  70. showPointCloud(pointcloud);
  71. chrono::steady_clock::time_point t2=chrono::steady_clock::now();
  72. chrono::duration<double>time_used=chrono::duration_cast<chrono::duration<double>>(t2-t1);
  73. cout<<"used time by this project: "<<time_used.count()<<"second!"<<endl;
  74. return 0;
  75. }
  76. void showPointCloud(const vector<Vector6d, Eigen::aligned_allocator<Vector6d>> &pointcloud) {
  77. if (pointcloud.empty()) {
  78. cerr << "Point cloud is empty!" << endl;
  79. return;
  80. }
  81. pangolin::CreateWindowAndBind("Point Cloud Viewer", 1024, 768);
  82. glEnable(GL_DEPTH_TEST);
  83. glEnable(GL_BLEND);
  84. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  85. //ProjectionMatrix()中各参数依次为图像宽度=1024、图像高度=768、fx=500、fy=500、cx=512、cy=389、最近距离=0.1和最远距离=1000
  86. //ModelViewLookAt()中各参数为相机位置,被观察点位置和相机哪个轴朝上
  87. //比如,ModelViewLookAt(0, -0.1, -1.8, 0, 0, 0, 0.0, -1.0, 0.0)表示相机在(0, -0.1, -1.8)位置处观看视点(0, 0, 0),并设置相机XYZ轴正方向为(0,-1,0),即右上前
  88. //创建交互视图,显示上一帧图像内容
  89. pangolin::OpenGlRenderState s_cam(
  90. pangolin::ProjectionMatrix(1024, 768, 500, 500, 512, 389, 0.1, 1000),
  91. pangolin::ModelViewLookAt(0, -0.1, -1.8, 0, 0, 0, 0.0, -1.0, 0.0)
  92. );
  93. pangolin::View &d_cam = pangolin::CreateDisplay()
  94. .SetBounds(0.0, 1.0, pangolin::Attach::Pix(175), 1.0, -1024.0f / 768.0f)
  95. .SetHandler(new pangolin::Handler3D(s_cam));
  96. //SetBounds()内的前4个参数分别表示交互视图的大小,均为相对值,范围在0.0至1.0之间
  97. //第1个参数表示bottom,即为视图最下面在整个窗口中的位置
  98. //第2个参数为top,即为视图最上面在整个窗口中的位置
  99. //第3个参数为left,即视图最左边在整个窗口中的位置
  100. //第4个参数为right,即为视图最右边在整个窗口中的位置
  101. //第5个参数为aspect,表示横纵比
  102. while (pangolin::ShouldQuit() == false) {//如果Pangolin窗口没关闭
  103. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清空颜色和深度缓存,使得前后帧不会互相干扰
  104. d_cam.Activate(s_cam); //激活相机
  105. glClearColor(1.0f, 1.0f, 1.0f, 1.0f);//设置背景颜色为白色
  106. glPointSize(2);
  107. glBegin(GL_POINTS);//绘制点云
  108. for (auto &p: pointcloud) {
  109. glColor3d(p[3] / 255.0, p[4] / 255.0, p[5] / 255.0);//设置颜色信息
  110. glVertex3d(p[0], p[1], p[2]);//设置位置信息
  111. }
  112. glEnd();
  113. pangolin::FinishFrame();//按照上面的设置执行渲染
  114. usleep(5000); // sleep 5 ms
  115. }
  116. return;
  117. }

2.相关函数解释:

(1)boost::forma

        c++语言中传统的格式数是c语言的sprintf函数,缺点是不安全,在stl中引入了stringstream, 它虽然解决了安全性的问题,但是没有sprintf函数直观。boost::format是用来替代sprintf,格式化字符串的,既具有安全性,又具有直观性。

作用:批量存储文件路径

       在本代码中是以以下形式出现的:

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));

        其中,先按照第一句代码规定图像的文件格式,其中最开始的 ../ 表示图像存储的相对路径,后面的 %s,%d表示类型。

        随后第二句的意思为:按照第一句规定的格式,读取图像,其中“color”为存储5张彩色图的文件夹(即路径),之后的i+1表示第几张图片,后面的"png"表示图片的后缀名。总的而言,就是把“color”文件中的5张彩色图依次存放在colorImgs这个容器内。

        第三句代码含义同第二句。

(2). return 0 return 1 区别

return 0 代表程序正常退出,return 1代表程序异常退出

(3).cerr指令

       cerr为流对象,为标准错误流,常用于错误语句的输出。具体含义见下表:

全局流对象名称缓存
cout标准输出流带缓存
cin标准输入流带缓存
clog标准日志流带缓存
cerr标准错误流无缓存

(4).auto

for (auto &d:data)  //其中data为一个容器,效果是利用d遍历并获得data容器中的每一个值

fin >> d;

        使用auto,不用声明变量类型,直接复制数组元素类型。
:for(auto iter:vec)不改变迭代对象的值,for(auto &iter:vec)可以改变迭代对象的值
两者都可以获取到迭代容器中的值,但是使用auto iter时不会对容器对象造成改变,而使用auto &iter,对于iter的任何修改将直接修改容器内对应的值。
上述两句代码的意思:总而言之,就是从pose.txt文件中读取相机位姿,并输入存储在data[]容器中。

3.效果图:

参考链接;视觉SLAM十四讲CH5代码解析及课后习题详解_长沙有肥鱼的博客-CSDN博客_slam十四讲代码

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/102962
推荐阅读
相关标签
  

闽ICP备14008679号