ICP算法——迭代最近邻算法及应用

转载自: http://blog.youkuaiyun.com/xiaowei_cqu/article/details/8470376


基本原理

假定已给两个数据集P、Q, ,给出两个点集的空间变换f使他们能进行空间匹配。这里的问题是,f为一未知函数,而且两点集中的点数不一定相同。解决这个问题使用的最多的方法是迭代最近点法(Iterative Closest Points Algorithm)。

基本思想是:根据某种几何特性对数据进行匹配,并设这些匹配点为假想的对应点,然后根据这种对应关系求解运动参数。再利用这些运动参数对数据进行变换。并利用同一几何特征,确定新的对应关系,重复上述过程。


迭代最近点法目标函数

三维空间中两个3D点,  ,他们的欧式距离表示为:

三维点云匹配问题的目的是找到P和Q变化的矩阵R和T,对于  ,利用最小二乘法求解最优解使:

最小时的R和T。

数据预处理

实验中采集了五个面的点如下所示:
由于第一组(第一排第1个)和第三组(第一排第三个)采集均为模型正面点云,所以选用一和三做后续的实验。
首先利用Geomagic Studio中删除点的工具,去除原始数据中的一些隔离的噪点,效果如下:


平行移动和旋转的分离

先对平移向量T进行初始的估算,具体方法是分别得到点集P和Q的中心:
 
 
分别将点集P和Q平移至中心点处:

则上述最优化目标函数可以转化为:
 
最优化问题分解为:
  1. 求使E最小的 
  2. 求使 
平移中心点的 具体代码为:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //计算点云P的中心点mean  
  2. void CalculateMeanPoint3D(vector<Point3D> &P, Point3D &mean)  
  3. {  
  4.     vector<Point3D>::iterator it;  
  5.     mean.x = 0;  
  6.     mean.y = 0;  
  7.     mean.z = 0;  
  8.     for(it=P.begin(); it!=P.end(); it++){  
  9.         mean.x += it->x;  
  10.         mean.y += it->y;  
  11.         mean.z += it->z;  
  12.     }  
  13.     mean.x = mean.x/P.size();  
  14.     mean.y = mean.y/P.size();  
  15.     mean.z = mean.z/P.size();  
  16. }  
初始平移效果如下:


利用控制点求初始旋转矩阵

在确定对应关系时,所使用的几何特征是空间中位置最近的点。这里,我们甚至不需要两个点集中的所有点。可以指用从某一点集中选取一部分点,一般称这些点为 控制点(Control Points)。这时,配准问题转化为:
这里,pi,qi为最近匹配点。
在Geomagic Studio中利用三个点就可以进行两个模型的“手动注册”(感觉这里翻译的不好,Registration,应该为“手动匹配”)。


我们将手动选择的三个点导出,作为实验初始的控制点:


对于第i对点,计算点对的矩阵 Ai:
 
 , 的转置矩阵。
(*这里在査老师的课上给了一个错误的矩阵变换公式)
对于每一对矩阵Ai,计算矩阵B: 
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. double B[16];  
  2.         for(int i=0;i<16;i++)  
  3.             B[i]=0;  
  4.         for(itp=P.begin(),itq=Q.begin();itp!=P.end();itp++,itq++ ){  
  5.             double divpq[3]={itp->x,itp->y,itp->z};  
  6.             double addpq[3]={itp->x,itp->y,itp->z};  
  7.             double q[3]={itq->x,itq->y,itq->z};  
  8.             MatrixDiv(divpq,q,3,1);  
  9.             MatrixAdd(addpq,q,3,1);  
  10.             double A[16];  
  11.             for(int i=0;i<16;i++)  
  12.                 A[i]=0;  
  13.             for(int i=0;i<3;i++){  
  14.                 A[i+1]=divpq[i];  
  15.                 A[i*4+4]=divpq[i];  
  16.                 A[i+13]=addpq[i];  
  17.             }  
  18.             double AT[16],AMul[16];  
  19.             MatrixTran(A,AT,4,4);  
  20.             MatrixMul(A,AT,AMul,4,4,4,4);  
  21.             MatrixAdd(B,AMul,4,4);  
  22.         }  

原最优化问题可以转为求B的最小特征值和特征向量,具体代码:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //使用奇异值分解计算B的特征值和特征向量  
  2.         double eigen, qr[4];  
  3.         MatrixEigen(B, &eigen, qr, 4);  

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //计算n阶正定阵m的特征值分解:eigen为特征值,q为特征向量  
  2. void MatrixEigen(double *m, double *eigen, double *q, int n)  
  3. {  
  4.     double *vec, *eig;  
  5.     vec = new double[n*n];  
  6.     eig = new double[n];  
  7.     CvMat _m = cvMat(n, n, CV_64F, m);  
  8.     CvMat _vec = cvMat(n, n, CV_64F, vec);  
  9.     CvMat _eig = cvMat(n, 1, CV_64F, eig);  
  10.     //使用OpenCV开源库中的矩阵操作求解矩阵特征值和特征向量  
  11.     cvEigenVV(&_m, &_vec, &_eig);  
  12.     *eigen = eig[0];  
  13.     for(int i=0; i<n; i++)  
  14.         q[i] = vec[i];  
  15.     delete[] vec;  
  16.     delete[] eig;  
  17. }  
  18.   
  19. //计算旋转矩阵  
  20. void CalculateRotation(double *q, double *R)  
  21. {  
  22.     R[0] = q[0]*q[0] + q[1]*q[1] - q[2]*q[2] - q[3]*q[3];  
  23.     R[1] = 2.0 * (q[1]*q[2] - q[0]*q[3]);  
  24.     R[2] = 2.0 * (q[1]*q[3] + q[0]*q[2]);  
  25.     R[3] = 2.0 * (q[1]*q[2] + q[0]*q[3]);  
  26.     R[4] = q[0]*q[0] - q[1]*q[1] + q[2]*q[2] - q[3]*q[3];  
  27.     R[5] = 2.0 * (q[2]*q[3] - q[0]*q[1]);  
  28.     R[6] = 2.0 * (q[1]*q[3] - q[0]*q[2]);  
  29.     R[7] = 2.0 * (q[2]*q[3] + q[0]*q[1]);  
  30.     R[8] = q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3];  
  31. }  

平移矩阵计算

2.4中可以得到选择矩阵的4元数表示,由于在 "平行移动和旋转的分离"中,我们将最优问题分解为:
  1. 求使E最小的 
  2. 求使 
因此还需要通过中心点计算平移矩阵。
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //通过特征向量计算旋转矩阵R1和平移矩阵T1  
  2.         CalculateRotation(qr, R1);  
  3.         double mean_Q[3]={_mean_Q.x,_mean_Q.y,_mean_Q.z};  
  4.         double mean_P[3]={_mean_P.x,_mean_P.y,_mean_P.z};  
  5.         double meanT[3]={0,0,0};  
  6.         int nt=0;  
  7.         for(itp=P.begin(),itq=Q.begin();itp!=P.end();itp++,itq++ ){  
  8.             double tmpP[3]={itp->x,itp->y,itp->z};  
  9.             double tmpQ[3]={itq->x,itq->y,itq->z};  
  10.             double tmpMul[3];  
  11.             MatrixMul(R1, mean_P, tmpMul, 3, 3, 3, 1);  
  12.             MatrixDiv(tmpQ,tmpMul,3,1);  
  13.             MatrixAdd(meanT,tmpQ,3,1);  
  14.             nt++;  
  15.         }  
  16.         for(int i=0; i<3; i++)  
  17.             T1[i] = meanT[i]/(double)(nt);  

一次旋转计算得到的矩阵如下:

效果在Geomagic Studio中显示如图:

迭代最近点

在初始匹配之后,所点集P`中所有点做平移变化,在比较点集合P`和Q`的匹配度,(或迭代次数)作为算法终止的条件。
具体为对点集P中每个点,找Q中离他最近的点作为对应点。在某一步利用前一步得到的 ,求使下述函数最小的
 
这里, 
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //计算误差和d  
  2.         d = 0.0;  
  3.         if(round==1){  
  4.             FindClosestPointSet(data,P,Q);  
  5.         }  
  6.         int pcount=0;  
  7.         for(itp = P.begin(),itq=Q.begin();itp!=P.end(); itp++, itq++){  
  8.             double sum = (itp->x - itq->x)*(itp->x - itq->x) + (itp->y - itq->y)*(itp->y - itq->y)   
  9.                 + (itp->z - itq->z)*(itp->z - itq->z);  
  10.             d += sum;  
  11.             pcount++;  
  12.         }  
  13.         d=d/(double)(pcount);  

循环结束后能得到较好的匹配效果:

封装后的效果图:


<think>嗯,用户问的是广义普氏算法最近邻迭代算法的区别。我需要先理解这两个算法各自是什么,然后找出它们的区别。 首先,广义普氏算法,可能是指广义的Procrustes分析,用来对齐形状,比如在图像处理或统计形状分析中。普氏分析主要是通过缩放、旋转和平移来最小化点集之间的差异,属于无监督对齐方法。参考里提到的普氏分析法是迭代找标准形状,用最小二乘法优化仿射变换参数[^2][^3]。这可能涉及到多组点集对齐到一个平均形状上,每次迭代调整参数,直到收敛。 然后是最近邻迭代算法(Iterative Closest Point, ICP)。ICP主要用于三维点云配准,分为两步:最近邻匹配和变换估计。通过不断寻找最近点对应,然后计算最优刚体变换,直到误差最小。这个过程是迭代的,每次找到对应点再优化变换参数。 接下来要比较它们的区别。应用场景方面,广义普氏可能更多用于形状统计或生物学中对齐样本,而ICP用于点云配准,比如三维重建。数学原理上,普氏分析可能用最小二乘法优化缩放、旋转和平移参数,而ICP每次迭代先找对应点再优化刚体变换。对应关系处理方面,普氏不需要显式对应,假设点顺序已知;ICP需要动态建立对应关系,通过最近邻搜索。收敛方式可能也有差异,普氏通过多组对齐到平均形状,而ICP是逐步逼近最优变换。 还要注意用户提到的参考资料,引用2和3提到了普氏分析的最小二乘和参数优化,而引用1是关于粒子群优化的,可能不相关。因此需要集中参考2和3的信息,确保回答准确。 可能需要详细说明每个算法的步骤,然后对比它们的处理方式、数学优化目标、对应关系建立等。最后总结区别,帮助用户明确两者的不同应用和原理。</think>### 广义普氏算法 vs 最近邻迭代算法ICP)区别详解 #### 1. **核心目标与定义** - **广义普氏算法(Generalized Procrustes Analysis, GPA)** 旨在通过**仿射变换**(缩放、旋转、平移)将多组形状对齐到统一坐标系,最小化所有形状与平均形状的差异。其数学表达式为: $$\arg\min_{s, R, T} \| s R p^T + T - q^T \|_F$$ 其中$s$为缩放因子,$R$为旋转矩阵,$T$为平移向量,$p$和$q$为待对齐的点集。 - **最近邻迭代算法(Iterative Closest Point, ICP)** 主要用于**刚体配准**(仅旋转、平移),通过迭代寻找最近邻点对应关系并优化变换参数,最终最小化两点集的距离。其目标函数为: $$\arg\min_{R, T} \sum_{i=1}^n \| R p_i + T - q_j \|^2$$ 其中$q_j$是$p_i$的最近邻点。 --- #### 2. **关键区别** | **维度** | **广义普氏算法** | **最近邻迭代算法ICP)** | |-------------------|-----------------------------------------------|--------------------------------------------| | **应用场景** | 多形状对齐(如生物形态分析、统计形状建模) | 单对点云配准(如三维重建、机器人定位) | | **数学优化目标** | 最小化所有形状与平均形状的仿射差异(含缩放) | 最小化两点集刚体变换后的欧氏距离 | | **对应关系建立** | 假设点集间已知对应顺序(无需动态匹配) | 依赖动态最近邻搜索建立对应关系(可能引入误差) | | **迭代内容** | 迭代更新平均形状与仿射参数 | 交替优化对应关系与刚体变换参数 | | **参数自由度** | 包含缩放因子(更灵活) | 仅旋转和平移(更严格) | --- #### 3. **流程对比** **广义普氏算法流程** 1. 计算所有形状的初始平均形状 2. 对每个形状独立优化仿射参数($s, R, T$) 3. 更新平均形状 4. 重复步骤2-3至收敛。 **ICP算法流程** 1. 初始化粗略对齐 2. 对每个点寻找目标点集中的最近邻 3. 计算最优刚体变换($R, T$) 4. 应用变换并更新误差 5. 重复步骤2-4至收敛。 --- #### 4. **优缺点对比** - **广义普氏算法** - ✅ 支持多组对齐,适用于形状统计分析 - ✅ 包含缩放因子,适应不同尺度 - ❌ 依赖点集间的已知对应关系 - **ICP算法** - ✅ 动态建立对应关系,适应部分重叠点云 - ✅ 计算效率高,适合实时应用 - ❌ 对初始位置敏感,可能陷入局部最优 --- #### 5. **典型应用场景** - **广义普氏算法**:人脸关键点对齐、生物骨骼形态比较 - **ICP算法**:三维扫描模型拼接、自动驾驶中的点云匹配 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值