VTK 图形基本操作进阶_点云配准技术(迭代最近点ICP算法)

摘要:
4VTK_ MODULE_INIT(vtkInteractionStyle);vtkProperty.h>vtkRenderWindowInteraction.h>22#包括<//坐标系交互23intmain()24{25vtkSmartPointer<reader=26vtkSmartPointer&lt,28reader->更新();
1.Iterative Closest Points算法
点云数据配准最经典的方法是迭代最近点算法(Iterative Closest Points,ICP)。ICP算法是一个迭代的过程,每次迭代中对于源数据点P找到目标点集Q中的最近点,然后给予最小二乘原理求解当前的变换矩阵T。通过不断迭代迭代直至收敛,即完成了点集的配准。

1.1 基本原理

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

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负责。
具体的示例代码如下所示:
  1 #include <vtkAutoInit.h>
  2 VTK_MODULE_INIT(vtkRenderingOpenGL);
  3 VTK_MODULE_INIT(vtkRenderingFreeType);
  4 VTK_MODULE_INIT(vtkInteractionStyle);
  5  
  6 #include <vtkSmartPointer.h>
  7 #include <vtkPolyDataReader.h>
  8 #include <vtkPolyData.h>
  9 #include <vtkTransform.h>
 10 #include <vtkTransformPolyDataFilter.h>
 11 #include <vtkVertexGlyphFilter.h>
 12 #include <vtkIterativeClosestPointTransform.h>
 13 #include <vtkLandmarkTransform.h>
 14 #include <vtkTransformPolyDataFilter.h>
 15 #include <vtkPolyDataMapper.h>
 16 #include <vtkActor.h>
 17 #include <vtkProperty.h>
 18 #include <vtkAxesActor.h>
 19 #include <vtkRenderer.h>
 20 #include <vtkRenderWindow.h>
 21 #include <vtkRenderWindowInteractor.h>
 22 #include <vtkOrientationMarkerWidget.h> //坐标系交互
 23 int main()
 24 {
 25     vtkSmartPointer <vtkPolyDataReader> reader =
 26         vtkSmartPointer<vtkPolyDataReader>::New();
 27     reader->SetFileName("fran_cut.vtk");
 28     reader->Update();
 29  
 30     //构造浮动数据点集
 31     vtkSmartPointer<vtkPolyData> orig = reader->GetOutput();
 32     vtkSmartPointer<vtkTransform> trans =
 33         vtkSmartPointer<vtkTransform>::New();
 34     trans->Translate(0.2, 0.1, 0.1);
 35     trans->RotateX(10);
 36  
 37     vtkSmartPointer<vtkTransformPolyDataFilter> transformFilter1 =
 38         vtkSmartPointer<vtkTransformPolyDataFilter>::New();
 39     transformFilter1->SetInputData(reader->GetOutput());
 40     transformFilter1->SetTransform(trans);
 41     transformFilter1->Update();
 42     /*********************************************************/
 43     //源数据 与 目标数据
 44     vtkSmartPointer<vtkPolyData> source =
 45         vtkSmartPointer<vtkPolyData>::New();
 46     source->SetPoints(orig->GetPoints());
 47  
 48     vtkSmartPointer<vtkPolyData> target =
 49         vtkSmartPointer<vtkPolyData>::New();
 50     target->SetPoints(transformFilter1->GetOutput()->GetPoints());
 51  
 52     vtkSmartPointer<vtkVertexGlyphFilter>  sourceGlyph =
 53         vtkSmartPointer<vtkVertexGlyphFilter>::New();
 54     sourceGlyph->SetInputData(source);
 55     sourceGlyph->Update();
 56  
 57     vtkSmartPointer<vtkVertexGlyphFilter>  targetGlyph =
 58         vtkSmartPointer<vtkVertexGlyphFilter>::New();
 59     targetGlyph->SetInputData(target);
 60     targetGlyph->Update();
 61  
 62     //进行ICP配准求变换矩阵
 63     vtkSmartPointer<vtkIterativeClosestPointTransform> icptrans =
 64         vtkSmartPointer<vtkIterativeClosestPointTransform>::New();
 65     icptrans->SetSource(sourceGlyph->GetOutput());
 66     icptrans->SetTarget(targetGlyph->GetOutput());
 67     icptrans->GetLandmarkTransform()->SetModeToRigidBody();
 68     icptrans->SetMaximumNumberOfIterations(50);
 69     icptrans->StartByMatchingCentroidsOn();
 70     icptrans->Modified();
 71     icptrans->Update();
 72  
 73     //配准矩阵调整源数据
 74     vtkSmartPointer<vtkTransformPolyDataFilter> solution =
 75         vtkSmartPointer<vtkTransformPolyDataFilter>::New();
 76     solution->SetInputData(sourceGlyph->GetOutput());
 77     solution->SetTransform(icptrans);
 78     solution->Update();
 79     /
 80     vtkSmartPointer<vtkPolyDataMapper> sourceMapper =
 81         vtkSmartPointer<vtkPolyDataMapper>::New();
 82     sourceMapper->SetInputConnection(sourceGlyph->GetOutputPort());
 83     vtkSmartPointer<vtkActor> sourceActor =
 84         vtkSmartPointer<vtkActor>::New();
 85     sourceActor->SetMapper(sourceMapper);
 86     sourceActor->GetProperty()->SetColor(1, 1, 0);
 87     sourceActor->GetProperty()->SetPointSize(2);
 88  
 89     vtkSmartPointer<vtkPolyDataMapper> targetMapper =
 90         vtkSmartPointer<vtkPolyDataMapper>::New();
 91     targetMapper->SetInputConnection(targetGlyph->GetOutputPort());
 92     vtkSmartPointer<vtkActor> targetActor =
 93         vtkSmartPointer<vtkActor>::New();
 94     targetActor->SetMapper(targetMapper);
 95     targetActor->GetProperty()->SetColor(0, 1, 0);
 96     targetActor->GetProperty()->SetPointSize(3);
 97  
 98     vtkSmartPointer<vtkPolyDataMapper> soluMapper =
 99         vtkSmartPointer<vtkPolyDataMapper>::New();
100     soluMapper->SetInputConnection(solution->GetOutputPort());
101     vtkSmartPointer<vtkActor> soluActor =
102         vtkSmartPointer<vtkActor>::New();
103     soluActor->SetMapper(soluMapper);
104     soluActor->GetProperty()->SetColor(1, 0, 0);
105     soluActor->GetProperty()->SetPointSize(2);
106     //设置坐标系
107     vtkSmartPointer<vtkAxesActor> axes =
108         vtkSmartPointer<vtkAxesActor>::New();
109     ///
110     vtkSmartPointer<vtkRenderer> render =
111         vtkSmartPointer<vtkRenderer>::New();
112     render->AddActor(sourceActor);
113     render->AddActor(targetActor);
114     render->AddActor(soluActor);
115     render->SetBackground(0, 0, 0);
116  
117     vtkSmartPointer<vtkRenderWindow> rw =
118         vtkSmartPointer<vtkRenderWindow>::New();
119     rw->AddRenderer(render);
120     rw->SetSize(480, 320);
121     rw->SetWindowName("Regisration by ICP");
122  
123     vtkSmartPointer<vtkRenderWindowInteractor> rwi =
124         vtkSmartPointer<vtkRenderWindowInteractor>::New();
125     rwi->SetRenderWindow(rw);
126     /****************************************************************/
127     //谨记:顺序不可以颠倒!!!
128     vtkSmartPointer<vtkOrientationMarkerWidget> widget =
129         vtkSmartPointer<vtkOrientationMarkerWidget>::New();
130     widget->SetOutlineColor(1, 1, 1);
131     widget->SetOrientationMarker(axes);
132     widget->SetInteractor(rwi); //加入鼠标交互  
133     widget->SetViewport(0.0, 0.0, 0.3, 0.3);  //设置显示位置  
134     widget->SetEnabled(1);
135     widget->InteractiveOn();//开启鼠标交互  
136     /****************************************************************/
137     render->ResetCamera();
138     rw->Render();
139     rwi->Initialize();
140     rwi->Start();
141  
142     return 0;
143 }
这个例子首先读取一个人脸模型。为了方便测试效果,这里对原始模型做了一个平移和旋转变换。
这里面有个细节应该正视:
vtkIterativeClosestPointTransform类中设置源点集和目标点集的函数为SetSource()和SetTarget(),其输入数据类型为VTKDataSet,所以集合点集必须进过一定的处理!这里使用vtkVertexGlyphFilter将读入模型和变换后的点集转换为相应的vtkPolyData数据。
1 vtkSmartPointer<vtkVertexGlyphFilter>  sourceGlyph =
2         vtkSmartPointer<vtkVertexGlyphFilter>::New();
3     sourceGlyph->SetInputData(source);
4     sourceGlyph->Update();
5  
6     vtkSmartPointer<vtkVertexGlyphFilter>  targetGlyph =
7         vtkSmartPointer<vtkVertexGlyphFilter>::New();
8     targetGlyph->SetInputData(target);
9     targetGlyph->Update();

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

 1 //进行ICP配准求变换矩阵
 2     vtkSmartPointer<vtkIterativeClosestPointTransform> icptrans =
 3         vtkSmartPointer<vtkIterativeClosestPointTransform>::New();
 4     icptrans->SetSource(sourceGlyph->GetOutput());
 5     icptrans->SetTarget(targetGlyph->GetOutput());
 6     icptrans->GetLandmarkTransform()->SetModeToRigidBody();
 7     icptrans->SetMaximumNumberOfIterations(50);
 8     icptrans->StartByMatchingCentroidsOn();
 9     icptrans->Modified();
10     icptrans->Update();
StartByMatchingCentroidsOn()该函数就是去偏移(中心归一/重心归一)。通过分别计算重心,然后平移,使得两点集中心重合。
配准后的结果如下图:
VTK 图形基本操作进阶_点云配准技术(迭代最近点ICP算法)第2张
黄色点集是浮动点集;绿色点集是金标准;红色点集是经过配准矩阵调整后的点集。
2.vtkOrientationMarkerWidget类设置坐标系心得
1     //谨记:顺序不可以颠倒!!!
2     vtkSmartPointer<vtkOrientationMarkerWidget> widget =
3         vtkSmartPointer<vtkOrientationMarkerWidget>::New();
4     widget->SetOutlineColor(1, 1, 1);
5     widget->SetOrientationMarker(axes);
6     widget->SetInteractor(rwi); //加入鼠标交互  
7     widget->SetViewport(0.0, 0.0, 0.3, 0.3);  //设置显示位置  
8     widget->SetEnabled(1);
9     widget->InteractiveOn();//开启鼠标交互
 

免责声明:文章转载自《VTK 图形基本操作进阶_点云配准技术(迭代最近点ICP算法)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Windows高效文件搜索工具/Everything/Listary/uToolsTeeChart 8.01 With Source在Delphi 7.0中的安装(转)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Hibernate环境搭建

1、导入jar包 hibernate.jar  antlr-2.7.6.jar  commons-collections-3.1.jar  jta-1.1.jar  dom4j-1.6.1.jar javassist-3.4.GA.jar  slf4j-api-1.5.2.jar  slf4j-log4j12-1.5.2.jar  假设使用C3P0数据源...

通过sqlplus执行sql脚本并生成log

如何在sqlplus中执行sql脚本 [1]登陆Sqlplus 请输入用户名 sys/admin@orcl as sysdba 成功后,可以执行导入 [2]执行 SQL>@ c:\sql\unitdata.sql  如果是在linux就执行start /tmp/shp/beijing_region.shp 你就可以看到提示执行的结果 如何生成sqlp...

开发maven项目,war包部署tomcat访问无效解决方法

 添加红框部门,移除tomcat插件 <!-- 移除嵌入式tomcat插件 --> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> &...

Qt QWidget控件透明动画实现

QWidget控件透明动画实现 要做子控件透明需要用到QGraphicsOpacityEffect类 使用例子如下代码: QGraphicsOpacityEffect * m_pGraphicsOpacityEffect1; QPropertyAnimation * m_pOpacityAnimation1; m_pGraphicsOpacityEf...

1.MyBatis所需要的配置文件

一、Mybatis介绍      MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库...

结构体数组(C++)

1.定义结构体数组 和定义结构体变量类似,定义结构体数组时只需声明其为数组即可。如: struct Student{ int num; char name[20]; char sex[5]; int age; float score; char addr[30]; }; Stude...