点击上方“3D视觉工坊”,选择“星标”

干货第一时间送达

理解好图像的点云拼接,需要从相机的模型说起。理解相机的成像原理之后,便可更为深刻的理解图像的点云如何拼接在一起。

首先说下相机的概念与原理。

相机概念与原理

相机的作用:将三维世界中的坐标点(单位为米)映射到二维图像平面(单位为像素)。

通常我们见到的相机都是针孔相机,但是不是简单的 针孔,还有透镜的畸变存在,所以在做图像处理时要进行畸变校正。

由于畸变的存在,我们在使用相机之前都要进行相机标定

目的就是求出内参对于简单的应用求出径向畸变切向畸变就够了。

所谓的外参就是相机的位姿。

有了外参、内参、图像、深度信息 便可以把图像中的点,转到世界坐标系下,并带有RGB的颜色。就形成了所谓的点云。

相关的公式也很好理解 ,就是通过相似三角形的原理。

注意其中fx 、fy、 cx 、cy也就是相机的内参了。在相机出厂时会给出。

理解了相机的概念与原理,来编辑代码实现下。

Code实现

全部代码

先放上全部代码,再进行分段讲解

#include <iostream>
#include <fstream>
using namespace std;
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <Eigen/Geometry>
#include <boost/format.hpp> // for formating strings
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/visualization/pcl_visualizer.h>int main( int argc, char** argv )
{vector<cv::Mat> colorImgs,depthImgs;//彩色图和深度图vector<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 );}//相机内参double cx=325.5;double cy=253.5;double fx=518.0;double fy=519.0;double depthScale = 1000.0;cout<<"正在将图像转换为点云..."<<endl;//定义点云使用的格式  用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];//获得 对应的posefor(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() ];//赋值点云对应RGB颜色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 );}}}pointCloud->is_dense = false; cout<<"点云共有"<<pointCloud->size()<<"个点."<<endl;pcl::io::savePCDFileBinary("map.pcd", *pointCloud );//保存点云文件return 0;
}

代码讲解

    vector<cv::Mat> colorImgs,depthImgs;//彩色图和深度图vector<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 );}

读取对应路径下的彩色图像、深度图像、相机的位姿。

上面说了,有了这三个量再加上相机内参,就可以得到点云

     //相机内参double cx=325.5;double cy=253.5;double fx=518.0;double fy=519.0;double depthScale = 1000.0;

传说中的相机内参

    //定义点云使用的格式  用XYZRGBtypedef pcl::PointXYZRGB PointT;typedef pcl::PointCloud<PointT> PointCloud;//新建一个点云PointCloud::Ptr pointCloud( new PointCloud );

声明pcl的格式创建一个点云,然后开始for循环处理每一张图片和对应的深度图片与相机位姿。

        cv::Mat color = colorImgs[i];//获得 要处理的彩色图像cv::Mat depth = depthImgs[i];//获得 要处理的深度图像Eigen::Isometry3d T = poses[i];//获得 对应的pose

得到要处理的彩色图像深度图像对应的pose,然后for循环处理每一个像素。

unsigned int d = depth.ptr<unsigned short>(v)[u];//深度值

得到深度信息

                Eigen::Vector3d point;//相机坐标系下的点//计算相机坐标系下的点的坐标point[2] = double(d)/depthScale;point[0] = (u-cx)*point[2]/fx;point[1] = (v-cy)*point[2]/fy;

通过:

这个公式得到相机坐标系下的 x,y,z

  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() ];//赋值点云对应RGB颜色p.g = color.data[ v*color.step+u*color.channels()+1 ];p.r = color.data[ v*color.step+u*color.channels()+2 ];

赋值点云的坐标与颜色。

 pcl::io::savePCDFileBinary("map.pcd", *pointCloud );//保存点云文件

保存成点云文件。

本文仅做学术分享,如有侵权,请联系删文。

下载1

在「3D视觉工坊」公众号后台回复:3D视觉即可下载 3D视觉相关资料干货,涉及相机标定、三维重建、立体视觉、SLAM、深度学习、点云后处理、多视图几何等方向。

下载2

在「3D视觉工坊」公众号后台回复:3D视觉github资源汇总即可下载包括结构光、标定源码、缺陷检测源码、深度估计与深度补全源码、点云处理相关源码、立体匹配源码、单目、双目3D检测、基于点云的3D检测、6D姿态估计源码汇总等。

下载3

在「3D视觉工坊」公众号后台回复:相机标定即可下载独家相机标定学习课件与视频网址;后台回复:立体匹配即可下载独家立体匹配学习课件与视频网址。

重磅!3DCVer-学术论文写作投稿 交流群已成立

扫码添加小助手微信,可申请加入3D视觉工坊-学术论文写作与投稿 微信交流群,旨在交流顶会、顶刊、SCI、EI等写作与投稿事宜。

同时也可申请加入我们的细分方向交流群,目前主要有3D视觉CV&深度学习SLAM三维重建点云后处理自动驾驶、多传感器融合、CV入门、三维测量、VR/AR、3D人脸识别、医疗影像、缺陷检测、行人重识别、目标跟踪、视觉产品落地、视觉竞赛、车牌识别、硬件选型、学术交流、求职交流、ORB-SLAM系列源码交流、深度估计等微信群。

一定要备注:研究方向+学校/公司+昵称,例如:”3D视觉 + 上海交大 + 静静“。请按照格式备注,可快速被通过且邀请进群。原创投稿也请联系。

▲长按加微信群或投稿

▲长按关注公众号

3D视觉从入门到精通知识星球:针对3D视觉领域的视频课程(三维重建系列、三维点云系列、结构光系列、手眼标定、相机标定、orb-slam3等视频课程)、知识点汇总、入门进阶学习路线、最新paper分享、疑问解答五个方面进行深耕,更有各类大厂的算法工程人员进行技术指导。与此同时,星球将联合知名企业发布3D视觉相关算法开发岗位以及项目对接信息,打造成集技术与就业为一体的铁杆粉丝聚集区,近2000星球成员为创造更好的AI世界共同进步,知识星球入口:

学习3D视觉核心技术,扫描查看介绍,3天内无条件退款

圈里有高质量教程资料、可答疑解惑、助你高效解决问题

觉得有用,麻烦给个赞和在看~  

图像的点云拼接-原理讲解与代码实现相关推荐

  1. C#冒泡排序原理讲解及代码块

    C#冒泡排序原理讲解及代码块 一.冒泡排序理论 (1)基本概念由于在排序过程中总是小数往前放,大数往后放,相当于气泡往上升,所以称作冒泡排序.冒泡排序的时间复杂度为O(n*n). (2)逻辑分析依次比 ...

  2. C++版本计算n阶乘末尾0的个数原理讲解及代码实现

    C++版本计算n阶乘末尾0的个数原理讲解及代码实现 /*! * Copyright (c) 2020,ZYF. * All Rights Reserved. * * \file Factorial.c ...

  3. 图像融合之泊松融合,原理讲解及C++代码实现(特别适合新手)

    本篇文章主要为讲解图像处理的泊松融合的原理及实现. 泊松融合原理来源于这篇文章:<Poisson Image Editing> 本人为图像处理的小白,在机缘巧合下,看到了泊松融合的图像处理 ...

  4. Wemos D1 Arduino超声波测距原理讲解加代码、超声波驱动SG90电机(垃圾桶感应开关)

    先介绍一下超声波模块: 总共有四个端子,分别为VCC.GND.Trig.Echo. VCC一般接5V电压:GND接地 Trig为输出接口,Echo为输入接口 Trig和Echo与超声波测距原理有关, ...

  5. python主成分分析代码_PCA主成分分析 原理讲解 python代码实现

    1. 用途: 通俗来说: 考察一个人的智力情况,就直接看数学成绩就行(存在:数学.语文.英语成绩) .就是找出一个最主要的特征,然后进行分析. 数据压缩 (Data Compression) ,将高维 ...

  6. linux进程池 自动增长,linux下C 线程池的原理讲解和代码实现(能自行伸缩扩展线程数)...

    什么线程池,为什么要使用线程池?下面是一个比喻. 阶段一.一个医院,每天面对成千上万的病人,处理方式是:来一个病人找来一个医生处理,处理完了医生也走了.当看病时间较短的时候,医生来去的时间,显得尤为费 ...

  7. Rotary Position Embedding (RoPE, 旋转式位置编码) | 原理讲解+torch代码实现

  8. 『OPEN3D』1.3 RGB-D image与点云拼接 python篇

    目录 1 open3d中的IMAGE 1.1 open3d中的image 1.2 open3d中的rgbd image 2 open3d.camera 3 RGBD图像的点云拼接 1 open3d中的 ...

  9. php 模板 原理,php模板原理讲解

    php模板原理讲解 复制代码 代码如下: $data = array( 'title'=>'ilsea', 'list'=>array( 'hello', 'world' ) ); inc ...

最新文章

  1. Java8新特性Stream
  2. redhat7.4启动telnet和SSH服务,修改IP地址
  3. URP中的2D Light光照在移动端不生效的问题
  4. 智能机器人建房子后房价走势_深圳建二手房价引导制度,学习长沙意图明显,距离稳准狠差点意思...
  5. 实操代码带你理解CSS中的常用选择器(你值得掌握!)
  6. CSS 总结我对3D效果的一些误解
  7. ftp 命令访问 ftp服务器
  8. Tomcat(4)-集群
  9. 计算机等级考试考几级才能成为数据库工程师?
  10. python | plt.scatter画圆形或者方框——没有填充色、边框颜色设置
  11. Zstandard(Zstd)新型压缩算法代替Zlib 简单使用教程
  12. Mysql数据库基础知识(五)之:视图、变量、存储过程、函数、流程控制结构
  13. 【刷题-剑指 Offer】 54. 二叉搜索树的第k大节点
  14. Zbrush学习笔记之利用法线贴图制作伪高模
  15. 04.千淘万漉虽辛苦,吹尽黄沙始到金——SQL Server大数据群集初探
  16. 搞懂Python切片中start、end、steps正负的区别
  17. PLC笔记(二)西门子STEP 7编程软件安装
  18. [转]北京好吃地点大全 2004年5月版
  19. 中国APP系统软件开发公司十大排名榜
  20. NLP自然语言处理学习笔记(四)(转自咕泡AI)

热门文章

  1. Javase;jdk的安装调试;基础语法和变量以及基础数据类型
  2. 以实助实的京东物流,给实体经济带来了什么?
  3. 《大话数据结构》1、2数据结构、算法
  4. 宇宙可能是一台计算机,科学说明一切:宇宙是一个计算机模拟?
  5. Ngrok 内网穿透神器
  6. python win32print txt_Python Win32Print打印机状态混乱
  7. win10无法打开匿名级安全令牌_Printer Spoofer:在Windows 10及Server 2019上滥用Impersonation权限...
  8. kido机器人用流量吗_当4G遇到儿童手表 乐视Kido Watch评测
  9. Web大学生网页作业成品——美食餐饮网站设计与实现(HTML+CSS+JavaScript)
  10. python入门基础——综合实践(学生信息管理系统的构建)