1.Iterative Closest Points算法

点云数据配准最经典的方法是迭代最近点算法(Iterative Closest Points,ICP)。ICP算法是一个迭代的过程,每次迭代中对于源数据点P找到目标点集Q中的最近点,然后给予最小二乘原理求解当前的变换矩阵T。通过不断迭代迭代直至收敛,即完成了点集的配准。

1.1 基本原理

ICP算法是大多数点云配准算法的心, 它是一个点对刚性算法。基本思想是: 假设两个点集 P和 X近似对齐, 对 P上的每个点,假设 X上的最近点与之对齐。采用最近点搜索, 在 X上找出 P上各个点对应的最近点, 构成集合 Y, 然后计算一个新的 P到 Y的刚体变换。重复上述过程直到配准收敛。

1.2 算法步骤(四元数配准)

假设给两个三维点集 X1 和 X2,ICP方法的配准步骤如下:
第一步,计算X2中的每一个点在X1 点集中的对应近点;
第二步,求得使上述对应点对平均距离最小的刚体变换,求得平移参数和旋转参数;
第三步,对X2使用上一步求得的平移和旋转参数,得到新的变换点集;
第四步, 如果新的变换点集与参考点集满足两点集的平均距离小于某一给定阈值,则停止迭代计算,否则新的变换点集作为新的X2继续迭代,直到达到目标函数的要求。

1.3 最近点对查找

对应点的计算是整个配准过程中耗费时间最长的步骤,查找最近点,利用 k-d tree提高查找速度 K-d tree 法建立点的拓扑关系是基于二叉树的坐标轴分割,构造 k-d tree 的过程就是按照二叉树法则生成,首先按 X 轴寻找分割线,即计算所有点的x值的平均值,以最接近这个平均值的点的x值将空间分成两部分,然后在分成的子空间中按 Y 轴寻找分割线,将其各分成两部分,分割好的子空间在按X轴分割……依此类推,最后直到分割的区域内只有一个点。这样的分割过程就对应于一个二叉树,二叉树的分节点就对应一条分割线,而二叉树的每个叶子节点就对应一个点。这样点的拓扑关系就建立了。

2.VTK中实现ICP算法实验

vtk中已经封装了最基本的ICP算法,由类vtkIterativeClosestPointTransform负责。
具体的示例代码如下所示:
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL);
VTK_MODULE_INIT(vtkRenderingFreeType);
VTK_MODULE_INIT(vtkInteractionStyle);#include <vtkSmartPointer.h>
#include <vtkPolyDataReader.h>
#include <vtkPolyData.h>
#include <vtkTransform.h>
#include <vtkTransformPolyDataFilter.h>
#include <vtkVertexGlyphFilter.h>
#include <vtkIterativeClosestPointTransform.h>
#include <vtkLandmarkTransform.h>
#include <vtkTransformPolyDataFilter.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkProperty.h>
#include <vtkAxesActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkOrientationMarkerWidget.h> //坐标系交互
int main()
{vtkSmartPointer <vtkPolyDataReader> reader =vtkSmartPointer<vtkPolyDataReader>::New();reader->SetFileName("fran_cut.vtk");reader->Update();//构造浮动数据点集vtkSmartPointer<vtkPolyData> orig = reader->GetOutput();vtkSmartPointer<vtkTransform> trans =vtkSmartPointer<vtkTransform>::New();trans->Translate(0.2, 0.1, 0.1);trans->RotateX(10);vtkSmartPointer<vtkTransformPolyDataFilter> transformFilter1 =vtkSmartPointer<vtkTransformPolyDataFilter>::New();transformFilter1->SetInputData(reader->GetOutput());transformFilter1->SetTransform(trans);transformFilter1->Update();/*********************************************************///源数据 与 目标数据vtkSmartPointer<vtkPolyData> source =vtkSmartPointer<vtkPolyData>::New();source->SetPoints(orig->GetPoints());vtkSmartPointer<vtkPolyData> target =vtkSmartPointer<vtkPolyData>::New();target->SetPoints(transformFilter1->GetOutput()->GetPoints());vtkSmartPointer<vtkVertexGlyphFilter>  sourceGlyph =vtkSmartPointer<vtkVertexGlyphFilter>::New();sourceGlyph->SetInputData(source);sourceGlyph->Update();vtkSmartPointer<vtkVertexGlyphFilter>  targetGlyph =vtkSmartPointer<vtkVertexGlyphFilter>::New();targetGlyph->SetInputData(target);targetGlyph->Update();//进行ICP配准求变换矩阵vtkSmartPointer<vtkIterativeClosestPointTransform> icptrans =vtkSmartPointer<vtkIterativeClosestPointTransform>::New();icptrans->SetSource(sourceGlyph->GetOutput());icptrans->SetTarget(targetGlyph->GetOutput());icptrans->GetLandmarkTransform()->SetModeToRigidBody();icptrans->SetMaximumNumberOfIterations(50);icptrans->StartByMatchingCentroidsOn();icptrans->Modified();icptrans->Update();//配准矩阵调整源数据vtkSmartPointer<vtkTransformPolyDataFilter> solution =vtkSmartPointer<vtkTransformPolyDataFilter>::New();solution->SetInputData(sourceGlyph->GetOutput());solution->SetTransform(icptrans);solution->Update();/vtkSmartPointer<vtkPolyDataMapper> sourceMapper =vtkSmartPointer<vtkPolyDataMapper>::New();sourceMapper->SetInputConnection(sourceGlyph->GetOutputPort());vtkSmartPointer<vtkActor> sourceActor =vtkSmartPointer<vtkActor>::New();sourceActor->SetMapper(sourceMapper);sourceActor->GetProperty()->SetColor(1, 1, 0);sourceActor->GetProperty()->SetPointSize(2);vtkSmartPointer<vtkPolyDataMapper> targetMapper =vtkSmartPointer<vtkPolyDataMapper>::New();targetMapper->SetInputConnection(targetGlyph->GetOutputPort());vtkSmartPointer<vtkActor> targetActor =vtkSmartPointer<vtkActor>::New();targetActor->SetMapper(targetMapper);targetActor->GetProperty()->SetColor(0, 1, 0);targetActor->GetProperty()->SetPointSize(3);vtkSmartPointer<vtkPolyDataMapper> soluMapper =vtkSmartPointer<vtkPolyDataMapper>::New();soluMapper->SetInputConnection(solution->GetOutputPort());vtkSmartPointer<vtkActor> soluActor =vtkSmartPointer<vtkActor>::New();soluActor->SetMapper(soluMapper);soluActor->GetProperty()->SetColor(1, 0, 0);soluActor->GetProperty()->SetPointSize(2);//设置坐标系vtkSmartPointer<vtkAxesActor> axes =vtkSmartPointer<vtkAxesActor>::New();///vtkSmartPointer<vtkRenderer> render =vtkSmartPointer<vtkRenderer>::New();render->AddActor(sourceActor);render->AddActor(targetActor);render->AddActor(soluActor);render->SetBackground(0, 0, 0);vtkSmartPointer<vtkRenderWindow> rw =vtkSmartPointer<vtkRenderWindow>::New();rw->AddRenderer(render);rw->SetSize(480, 320);rw->SetWindowName("Regisration by ICP");vtkSmartPointer<vtkRenderWindowInteractor> rwi =vtkSmartPointer<vtkRenderWindowInteractor>::New();rwi->SetRenderWindow(rw);/****************************************************************///谨记:顺序不可以颠倒!!!vtkSmartPointer<vtkOrientationMarkerWidget> widget =vtkSmartPointer<vtkOrientationMarkerWidget>::New();widget->SetOutlineColor(1, 1, 1);widget->SetOrientationMarker(axes);widget->SetInteractor(rwi); //加入鼠标交互  widget->SetViewport(0.0, 0.0, 0.3, 0.3);  //设置显示位置  widget->SetEnabled(1);widget->InteractiveOn();//开启鼠标交互  /****************************************************************/render->ResetCamera();rw->Render();rwi->Initialize();rwi->Start();return 0;
}

这个例子首先读取一个人脸模型。为了方便测试效果,这里对原始模型做了一个平移和旋转变换。

这里面有个细节应该正视:
vtkIterativeClosestPointTransform类中设置源点集和目标点集的函数为SetSource()和SetTarget(),其输入数据类型为VTKDataSet,所以集合点集必须进过一定的处理!这里使用vtkVertexGlyphFilter将读入模型和变换后的点集转换为相应的vtkPolyData数据。
vtkSmartPointer<vtkVertexGlyphFilter>  sourceGlyph =vtkSmartPointer<vtkVertexGlyphFilter>::New();sourceGlyph->SetInputData(source);sourceGlyph->Update();vtkSmartPointer<vtkVertexGlyphFilter>  targetGlyph =vtkSmartPointer<vtkVertexGlyphFilter>::New();targetGlyph->SetInputData(target);targetGlyph->Update();

vtkLandmarkTransform类有点不同,输入数据类型就是vtkPoints类型

//进行ICP配准求变换矩阵vtkSmartPointer<vtkIterativeClosestPointTransform> icptrans =vtkSmartPointer<vtkIterativeClosestPointTransform>::New();icptrans->SetSource(sourceGlyph->GetOutput());icptrans->SetTarget(targetGlyph->GetOutput());icptrans->GetLandmarkTransform()->SetModeToRigidBody();icptrans->SetMaximumNumberOfIterations(50);icptrans->StartByMatchingCentroidsOn();icptrans->Modified();icptrans->Update();

StartByMatchingCentroidsOn()该函数就是去偏移(中心归一/重心归一)。通过分别计算重心,然后平移,使得两点集中心重合。

配准后的结果如下图:

黄色点集是浮动点集;绿色点集是金标准;红色点集是经过配准矩阵调整后的点集。

2.vtkOrientationMarkerWidget类设置坐标系心得

    //谨记:顺序不可以颠倒!!!vtkSmartPointer<vtkOrientationMarkerWidget> widget =vtkSmartPointer<vtkOrientationMarkerWidget>::New();widget->SetOutlineColor(1, 1, 1);widget->SetOrientationMarker(axes);widget->SetInteractor(rwi); //加入鼠标交互  widget->SetViewport(0.0, 0.0, 0.3, 0.3);  //设置显示位置  widget->SetEnabled(1);widget->InteractiveOn();//开启鼠标交互  

3.参看资料

1.《C++ primer》
2.《The VTK User’s Guide – 11thEdition》
3.  张晓东, 罗火灵. VTK图形图像开发进阶[M]. 机械工业出版社, 2015.

VTK修炼之道58:图形基本操作进阶_点云配准技术(迭代最近点ICP算法)相关推荐

  1. VTK修炼之道57:图形基本操作进阶_点云配准技术(LandMark标记点算法和坐标系显示方法)

    1.点云配准 在计算机逆向工程中,通过三维扫描等实物数字化技术可以获取各种点云数据.但是受到测量环境和设备的影响,再一次测量的情况下,难以获取实物整体的点云数据,因此需要多次从不同角度进行测量.但不同 ...

  2. VTK修炼之道46:图形基本操作进阶_三角网格体积、表面积、测地距离、包围盒

    1.基本图形操作意义 图形处理,比如图形平滑.多分辨率分析.特征提取等都离不开一些基本的图形操作.掌握这些基本的图形操作有助于理解和深入学习图形处理和分析方法. VTK中提供了多种图形的基本操作,其中 ...

  3. VTK修炼之道63:纹理映射体绘制_二维纹理映射

    1.纹理映射体绘制 基于软件实现的光线投影体绘制算法计算量非常大,不利于进行实时渲染.因此,目前体绘制经常使用图形硬件利用纹理映射来加速. 其主要原理是将三维体数据作为纹理装载入硬件缓存中,利用硬件来 ...

  4. VTK修炼之道59:图形基本操作进阶_纹理映射

    1.纹理映射 纹理映射是将纹理空间中的纹理像素映射到屏幕空间中的像素的过程.纹理生成过程实质上是将所定义的纹理映射为某种三维物体表面的属性,并参与后续的光照计算.在三维图形中,纹理映射运用的十分广泛, ...

  5. VTK修炼之道56:图形基本操作进阶_表面重建技术(三维点云曲面重建)

    1.点云重建 虽然Delaunay三角剖分算法可以实现网格曲面重建,但是其应用主要在二维剖分,在三维空间网格生成中遇到了问题.因为在三维点云曲面重建中,Delaunay条件不在满足,不仅基于最大最小角 ...

  6. VTK修炼之道55:图形基本操作进阶_表面重建技术(等值面提取)

    1.等值面提取 等值面(线)提取是一种常用的可视化技术,常应用于医学.地质.气象等领域.例如,在医学图像处理中,由于CT.MRI等图像分辨率越来越高,虽然体绘制技术可以清晰地对数据内部结构进行可视化, ...

  7. VTK修炼之道54:图形基本操作进阶_表面重建技术(三角剖分)

    1.表面重建 通过三维扫描仪所获取的实际物体的空间点云数据仅仅表示物体的几何形状,而无法表达其内部的拓扑结构.拓扑结构对于实际图形处理以及可视化具有更重要的意义.因此,这就需要利用表面重建技术奖点云数 ...

  8. VTK修炼之道53:图形基本操作进阶_多分辨率策略(模型细化的三种方法)

    1.模型细化 vtk中实现网格细化的累有vtkLinearSubdivisionFilter.vtkLoopsubdivisionFilter.vtkButterflySubdivisionFilte ...

  9. VTK修炼之道52:图形基本操作进阶_多分辨率策略(模型抽取的三种方法)

    1.多分辨率处理策略 模型抽取(Decimation)和细化(Subdivision)是两个相反的操作,是三角形网格模型多分辨处理中的两个重要操作.使用这两个操作可以在保持模型拓扑结构的同时,得到不同 ...

最新文章

  1. eclipse项目转android studio详解
  2. php返回json的结果
  3. html中el表达式遍历list,EL表达式在JS中取出来打印[object HTMLDivElement]的问题
  4. echart 数据点可以加链接吗_地理可视化就这么简单、酷炫,蚂蚁金服AntV 空间数据可视化引擎 L72.0发布...
  5. 端到端加密优缺点_基于Filecoin的去中心化文件保存和加密分享平台
  6. 吃货注意接收,精美美食图片壁纸来喽
  7. 软件工程的极端所有权
  8. 苹果手机录屏软件_手机录屏软件哪个好 好用的手机录屏软件推荐
  9. 系统分析师(4)-系统分析师考试大纲
  10. fw313r手机登录_迅捷(FAST)fw313r路由器手机设置教程 | 192路由网
  11. Win7停服,引发国产操作系统“蝴蝶效应”
  12. 什么是静电?什么是ESD?ESD分为几种形式?有哪些测试标准?
  13. JAVA算法:三角形周长(JAVA版本算法)
  14. 360手机卫士企业版下载
  15. 各类暴力事件频发,究竟是为何?
  16. 【解题报告】2015ACM/ICPC亚洲区上海站
  17. python怎么去掉视频字幕_python实现去除下载电影和电视剧文件名中的多余字符的方法...
  18. 51nod 1479 小Y的数论题
  19. 【高等数学】三.一元函数积分学
  20. 推荐五款浏览器实用插件,总有几个是你需要的

热门文章

  1. 假日教程-ZStack映像檔系列(TurnkeyLinux Observium)
  2. 【133】常见问题解答
  3. 代码评析与重构——求完数问题
  4. POJ-1789 Truck History 最小生成树
  5. ActionT和FuncT委托
  6. leetcode之Tow Sum两数之和的三种思路
  7. <DependencyManagement>记录
  8. 关于Django中,实现序列化的几种不同方法
  9. 卡常神器——register 与 快速读入输出
  10. POJ:3579-Median(二分+尺取寻找中位数)