前言
又咸鱼了一年,先废话一下——接触3D视觉应该有一年了,虽然划水的时间占多,但耳濡目染杂七杂八还是学到了不少,虽然2D图像处理都还没学透彻,但还是被推着继续在3D这根“更高维”的钢丝上前进。过程中见识了三维重建、物体识别、轨迹规划……各个点相互之间又有着千丝万缕的联系,想要只掌握并精通一个点是不可能的,必然要多管齐下,这也是我感觉到“学海无涯”的一点 Σ(っ °Д °;)っ 。趁着寒假又来假装好好学习一波,“弯道超车”势在必行!
开始正经:As we know,vtk(Visualization Toolkit)是一个用于可视化的开源库,三维计算机图形、图像处理及可视化是VTK主要的应用方向。而点云处理库PCL也是采用了vtk做的可视化,譬如常用的工具pcl_viewer,可视化交互等都是基于vtk向上包装实现的,趁着最近利用Qt开发交互软件,来对可视化部分进行一个总结。
很多三维成像的(如solidworks、catia等建模软件的交互)也可以此类推。
(ps,没系统学过vtk,只是对pcl中常见的一些操作进行演示)
(测试时是在vs+qt平台下,调用pcl库,可视化窗口qvtkwidget进行调试)
VTK中成像原理
直接上图,图是别的地方搬的,–侵权删
我们可以将整个成像体系理解为图中的样子,也就是在三维空间中,拿着一个相机朝着一个方向去拍照片,最后拍到的照片就是你在可视化窗口看到的东西,利用到的其实就是这个投影成像原理,将三维中的物体利用映射关系,映射到二维平面上,从而用户才能够看到。
这个系统中有几个重要的参数:
- 焦点位置 focal ——在三维空间中一个点;
- 相机位置 pos ——相机在三维空间中所处的点;
- 朝上方向 view up——相机朝上的一个向量;
- 裁剪平面 clip plane——相当于设定了一块可视区域,区域外的不可见;
- 视角 fovy——看图好理解一点;
这里解释的比较口语化,pos指代相机在空间的位置,可以明确知道 focal 和 pos 都是空间中的一个点,那么pos --> focal ,这个向量,方向代表相机的朝向,大小代表焦距(我认为应该理解为相机离焦点的距离),而view up本身是指代一个向量。
基于世界坐标系,有了一个空间点,两个向量,那么就可以将相机理解为一个空间三维坐标系,按照右手规则,相机朝向为 z 轴,view up为 x 轴, pos 为坐标系原点。 看不懂不要急,后面还会继续解释~~
直接去翻看pcl的官方文档,可以很快查到一个类叫做camera:
Camera class holds a set of camera parameters together with the window pos/size
那么我们列出这个类的参数:
参数 | 解释 |
---|---|
double focal [3] | Focal point or lookAt |
double pos [3] | Position of the camera. |
double view [3] | Up vector of the camera |
double clip [2] | Clipping planes depths. |
double fovy | Field of view angle in y direction (radians). |
double window_size [2] | 窗口大小 |
double window_pos [2] | 窗口位置 |
可以看到这里的前5个参数和前面的原理图参数一一对应!!!,也就是说你最后看到的二维图像是什么样的可以完全取决于camera这个参数。下一节直接代码结合测试来演示这几个参数的效果。
相机默认参数
截取部分关键代码:
viewer.reset(new pcl::visualization::PCLVisualizer("viewer", false));
viewer->addPointCloud(cloud, "cloud"); //添加一个空的点云
// 设置qvtk窗口
ui.qvtkWidget->SetRenderWindow(viewer->getRenderWindow());
viewer->setupInteractor(ui.qvtkWidget->GetInteractor(), ui.qvtkWidget->GetRenderWindow());
//获取当前相机参数
viewer->getCameraParameters(original_camera);
以此来获取pcl中vtk的相机默认参数:
参数 | 值 |
---|---|
double focal [3] | 0,0,0 |
double pos [3] | 0,0,1 |
double view [3] | 0,1,0 |
double clip [2] | 0.01 ,1000.01 |
double fovy | 0.5235 (大概是30° |
double window_size [2] | 100,30 |
double window_pos [2] | 0,0 |
这里先主要看前三个参数,需要大家结合三维空间来想象,画个大概的草图哈:
导入三维点云(bunny兔)
三维点云我们知道是分布在三维空间中的一系列点,每个点的值为三维参考坐标系的坐标值。通常在建模的时候也会指代一个全局坐标系作为参考,那么最后建出的模型或是转换出来的点云也会以这个坐标系为参考,此处将其叫为模型(刚体)坐标系,(模型点云相对于模型坐标系固定不变)
不做改变,viewer中直接导入三维点云后,重点: 模型坐标系会与世界坐标系重合,也可以理解为将点云(值不变)直接放在了这个世界坐标系中,如下图的图左,其中坐标系为模型(世界)坐标系(默认规定rgb对应xyz——红色-x轴,绿色-y轴,蓝色-z轴)相对于点云的位置。结合上一节,相机的默认位置,拍出的图片为下图右。
注意:viewer->resetCamera() 会重置camera的focal点为三维点云的中心点
在交互窗口 按R键效果同上
点云的坐标值是不会发生改变的,除非用transformPointCloud函数变换点云。这里变的只是相机的参数值
可视化交互机制
对于一个可视化的交互界面,通常能够做到 平移、缩放、旋转,那么这些操作与该成像系统是怎样一个关系呢?
平移:
参数 | double focal [3] | double pos [3] | double view [3] |
---|---|---|---|
值(前) | 0,0,0 | 0,0,1 | 0,1,0 |
值(后) | -0.16,0.24,0 | -0.16,0.24,1 | 0,1,0 |
从图上也可以看到,兔子映射出来的图像发生了略微的变化,并不是兔子点云发生了平移,而是相机进行了平移,得到了不一样的视图而已。
发挥想象———你将相机朝世界坐标系的x负方向和y轴正方向进行平移,拍照后得到了第二幅图像。
缩放:
参数 | double focal [3] | double pos [3] | double view [3] |
---|---|---|---|
值(前) | 0,0,0 | 0,0,1 | 0,1,0 |
值(后) | 0,0,0 | 0,0,3.13 | 0,1,0 |
可以看到其实就是将相机的位置拿远了,其他的值都没改变,很好理解,人离远一点看到的东西就小一点, focal也没变,意味着还是盯着这一点在看。
旋转:
参数 | double focal [3] | double pos [3] | double view [3] |
---|---|---|---|
值(前) | 0,0,0 | 0,0,1 | 0,1,0 |
值(后) | 0,0,0 | 0.99,0.10,-0.06 | -0.10,0.99,-0.05 |
旋转视图后,首先可以看到focal焦点没变,pos->focal 这个向量的大小没变,但是方向改变了,且该向量还是和view up这个向量垂直的,表明这个操作实则是将 相机坐标系绕着焦点进行旋转 。
其他相机参数
上一节三个参数都是可视化软件所共有的。
接下来对pcl中的Camera类的剩余四个参数一一介绍。
clip 裁剪平面:
clip[2] ,默认值为{0.01,1000.01}, 前后裁剪平面垂直于视线方向,这两个值分别代表裁剪平面离相机的距离(延视线方向裁剪平面距相机位置的距离)。最终裁剪平面之间(红色区域)可视,其他的都会被隐藏。
reset相机或者按R键,会自动将值改为点云离相机的最近和最远的距离,也就是裁剪平面会容纳所有点云点。
fovy:
这个可以类比为正常镜头和广角镜头,看到的东西更广了,相同尺寸的相片得到的东西也就更小一点,这里其实涉及到的相机成像的原理(三角成像),同样的物体,通过相机将其映射到一张固定尺寸的二维图像上面。这里就不多解释了(懂的都懂/doge)
window_size :
字面意思,窗口大小,看起来和相机没关系,实则确实没关系,指代的可视化窗口的大小而已,因为和用户看到的二维界面相关,pcl也就一并放在camera类里面了。
通过调节窗口大小,或者人为设定值,都能够改变其值。(看图就懂
window_pos
和qvtkwidget在主窗口中的布局有关。基本用不上。
结束
对 pcl vtk中的 camera 做一个系统的介绍,如有错误,多多指正。
参考博客: