4PCS点云配准算法的C++实现如下:
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/common/common.h>
#include <pcl/common/distances.h>
#include <pcl/common/transforms.h>
#include <pcl/search/kdtree.h>
struct Points4
{
pcl::PointXYZ p1;
pcl::PointXYZ p2;
pcl::PointXYZ p3;
pcl::PointXYZ p4;
};
int compute_LCP(const pcl::PointCloud<pcl::PointXYZ>& cloud, pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree, float radius)
{
std::vector<int>index(1);
std::vector<float>distance(1);
int count = 0;
for (size_t i = 0; i < cloud.size(); i++)
{
kdtree->nearestKSearch(cloud.points[i], 1, index, distance);
if (distance[0] < radius)
count = count + 1;
}
return count;
}
int main(int argc, char** argv)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr source_cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr target_cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr pcs_cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile("bunny1.pcd", *source_cloud);
pcl::io::loadPCDFile("bunny2.pcd", *target_cloud);
pcl::PointXYZ min_pt, max_pt;
pcl::getMinMax3D(*source_cloud, min_pt, max_pt);
float d_max = pcl::euclideanDistance(min_pt, max_pt);
//srand(time(0));
int iters = 100;
float s_max = 0;
float f = 0.5;
float ff = 0.1;
float delta = 0.0001;
int index1 = -1, index2 = -1, index3 = -1, index4 = -1;
for (size_t i = 0; i < iters; i++)
{
int n1 = rand() % source_cloud->size(), n2 = rand() % source_cloud->size(), n3 = rand() % source_cloud->size();
pcl::PointXYZ p1 = source_cloud->points[n1], p2 = source_cloud->points[n2], p3 = source_cloud->points[n3];
Eigen::Vector3f a(p1.x - p2.x, p1.y - p2.y, p1.z - p2.z);
Eigen::Vector3f b(p1.x - p3.x, p1.y - p3.y, p1.z - p3.z);
float s = 0.5 * sqrt(pow(a.y() * b.z() - b.y() * a.z(), 2) + pow(a.x() * b.z() - b.x() * a.z(), 2) + pow(a.x() * b.y() - b.x() * a.y(), 2));
if (s > s_max && pcl::euclideanDistance(source_cloud->points[n1], source_cloud->points[n2]) < f* d_max
&& pcl::euclideanDistance(source_cloud->points[n1], source_cloud->points[n3]) < f * d_max)
{
index1 = n1;
index2 = n2;
index3 = n3;
}
}
if (index1 == -1 || index2 == -1 || index3 == -1)
{
std::cout << "find three points error!" << std::endl;
return -1;
}
pcl::PointXYZ p1 = source_cloud->points[index1], p2 = source_cloud->points[index2], p3 = source_cloud->points[index3];
float A = (p2.y - p1.y) * (p3.z - p1.z) - (p2.z - p1.z) * (p3.y - p1.y);
float B = (p2.z - p1.z) * (p3.x - p1.x) - (p2.x - p1.x) * (p3.z - p1.z);
float C = (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
float D = -(A * p1.x + B * p1.y + C * p1.z);
for (size_t i = 0; i < source_cloud->size(); i++)
{
pcl::PointXYZ p = source_cloud->points[i];
float d = fabs(A * p.x + B * p.y + C * p.z + D) / sqrt(A * A + B * B + C * C);
bool flag = (pcl::euclideanDistance(p, p1) < f * d_max && pcl::euclideanDistance(p, p2) < f * d_max && pcl::euclideanDistance(p, p3) < f * d_max
&& pcl::euclideanDistance(p, p1) > ff * d_max && pcl::euclideanDistance(p, p2) > ff * d_max && pcl::euclideanDistance(p, p3) > ff * d_max);
if (d < delta * d_max && flag)
{
index4 = i;
}
}
if (index4 == -1)
{
std::cout << "find fouth point error!" << std::endl;
return -1;
}
pcl::PointXYZ p4 = source_cloud->points[index4];
pcl::PointCloud<pcl::PointXYZ>::Ptr four_points(new pcl::PointCloud<pcl::PointXYZ>);
four_points->push_back(p1);
four_points->push_back(p2);
four_points->push_back(p3);
four_points->push_back(p4);
pcl::io::savePCDFile("four_points.pcd", *four_points);
Eigen::VectorXf line_a(6), line_b(6);
line_a << p1.x, p1.y, p1.z, p1.x - p2.x, p1.y - p2.y, p1.z - p2.z;
line_b << p3.x, p3.y, p3.z, p3.x - p4.x, p3.y - p4.y, p3.z - p4.z;
Eigen::Vector4f pt1_seg, pt2_seg;
pcl::lineToLineSegment(line_a, line_b, pt1_seg, pt2_seg);
pcl::PointXYZ p5((pt1_seg[0]+ pt2_seg[0])/2, (pt1_seg[1] + pt2_seg[1]) / 2, (pt1_seg[2] + pt2_seg[2]) / 2);
float d1 = pcl::euclideanDistance(p1, p2); //d1=|b1-b2|
float d2 = pcl::euclideanDistance(p3, p4); //d2=|b3-b4|
float r1 = pcl::euclideanDistance(p1, p5) / d1; //r1=|b1-e| / |b1-b2|
float r2 = pcl::euclideanDistance(p3, p5) / d2; //r2=|b3-e| / |b3-b4|
std::cout << d1 << " " << d2 << " " << r1 << " " << r2 << std::endl;
std::vector<std::pair<pcl::PointXYZ, pcl::PointXYZ>> R1, R2;
for (size_t i = 0; i < target_cloud->size(); i++)
{
pcl::PointXYZ pt1 = target_cloud->points[i];
for (size_t j = i + 1; j < target_cloud->size(); j++)
{
pcl::PointXYZ pt2 = target_cloud->points[j];
if (pcl::euclideanDistance(pt1, pt2) > d1 * (1 - delta) && pcl::euclideanDistance(pt1, pt2) < d1 * (1 + delta))
{
R1.push_back(std::pair<pcl::PointXYZ, pcl::PointXYZ>(pt1, pt2));
}
else if (pcl::euclideanDistance(pt1, pt2) > d2 * (1 - delta) && pcl::euclideanDistance(pt1, pt2) < d2 * (1 + delta))
{
R2.push_back(std::pair<pcl::PointXYZ, pcl::PointXYZ>(pt1, pt2));
}
}
}
std::cout << R1.size() << " " << R2.size() << std::endl;
std::vector<std::pair<float, std::vector<pcl::PointXYZ>>> Map1, Map2;
pcl::PointCloud<pcl::PointXYZ>::Ptr pts1(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr pts2(new pcl::PointCloud<pcl::PointXYZ>);
for (auto r : R1)
{
pcl::PointXYZ p1, p2; p3;
p1 = r.first;
p2 = r.second;
p3 = pcl::PointXYZ(p1.x + r1 * (p2.x - p1.x), p1.y + r1 * (p2.y - p1.y), p1.z + r1 * (p2.z - p1.z));
Map1.push_back(std::pair<float, std::vector<pcl::PointXYZ>>(r1, { p1, p2, p3 }));
pts1->push_back(p3);
}
for (auto r : R2)
{
pcl::PointXYZ p1, p2; p3;
p1 = r.first;
p2 = r.second;
p3 = pcl::PointXYZ(p1.x + r2 * (p2.x - p1.x), p1.y + r2 * (p2.y - p1.y), p1.z + r2 * (p2.z - p1.z));
Map2.push_back(std::pair<float, std::vector<pcl::PointXYZ>>(r2, { p1, p2, p3 }));
pts2->push_back(p3);
}
pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree(new pcl::search::KdTree<pcl::PointXYZ>);
kdtree->setInputCloud(pts1);
std::vector<Points4> Uvec;
for (size_t i = 0; i < pts2->size(); i++)
{
std::vector<int> indices;
std::vector<float> distance;
if (kdtree->radiusSearch(pts2->points[i], 0.001 * d_max, indices, distance) > 0)
{
for (int indice: indices)
{
Points4 points4;
points4.p1 = Map1[indice].second[0];
points4.p2 = Map1[indice].second[1];
points4.p3 = Map2[i].second[0];
points4.p4 = Map2[i].second[1];
Uvec.push_back(points4);
//points4.p1 = Map1[indice].second[1];
//points4.p2 = Map1[indice].second[0];
//points4.p3 = Map2[i].second[0];
//points4.p4 = Map2[i].second[1];
//Uvec.push_back(points4);
//points4.p1 = Map1[indice].second[0];
//points4.p2 = Map1[indice].second[1];
//points4.p3 = Map2[i].second[1];
//points4.p4 = Map2[i].second[0];
//Uvec.push_back(points4);
//points4.p1 = Map1[indice].second[1];
//points4.p2 = Map1[indice].second[0];
//points4.p3 = Map2[i].second[1];
//points4.p4 = Map2[i].second[0];
//Uvec.push_back(points4);
}
}
}
std::cout << Uvec.size() << std::endl;
int max_count = 0;
Eigen::Matrix4f transformation;
kdtree->setInputCloud(target_cloud);
for (int i = 0; i < Uvec.size(); i++)
{
//if (i % 1000 == 0 && i> 0)
//std::cout << i << std::endl;
pcl::PointCloud<pcl::PointXYZ>::Ptr temp(new pcl::PointCloud<pcl::PointXYZ>);
temp->resize(4);
temp->points[0] = Uvec[i].p1;
temp->points[1] = Uvec[i].p2;
temp->points[2] = Uvec[i].p3;
temp->points[3] = Uvec[i].p4;
Eigen::Vector4f pts1_centroid, pts2_centroid;
pcl::compute3DCentroid(*four_points, pts1_centroid);
pcl::compute3DCentroid(*temp, pts2_centroid);
Eigen::MatrixXf pts1_cloud, pts2_cloud;
pcl::demeanPointCloud(*four_points, pts1_centroid, pts1_cloud);
pcl::demeanPointCloud(*temp, pts2_centroid, pts2_cloud);
Eigen::MatrixXf W = (pts1_cloud * pts2_cloud.transpose()).topLeftCorner(3, 3);
Eigen::JacobiSVD<Eigen::MatrixXf> svd(W, Eigen::ComputeFullU | Eigen::ComputeFullV);
Eigen::Matrix3f V = svd.matrixV(), U = svd.matrixU();
if (U.determinant() * V.determinant() < 0)
{
for (int x = 0; x < 3; ++x)
V(x, 2) *= -1;
}
Eigen::Matrix3f R = V * U.transpose();
Eigen::Vector3f T = pts2_centroid.head(3) - R * pts1_centroid.head(3);
Eigen::Matrix4f H;
H << R, T, 0, 0, 0, 1;
//std::cout << H << std::endl;
pcl::transformPointCloud(*source_cloud, *pcs_cloud, H);
int count = compute_LCP(*pcs_cloud, kdtree, 0.0001 * d_max);
if (count > max_count)
{
std::cout << count << std::endl;
std::cout << H << std::endl;
max_count = count;
transformation = H;
}
}
pcl::transformPointCloud(*source_cloud, *pcs_cloud, transformation);
pcl::io::savePCDFile("result.pcd", *pcs_cloud);
return 0;
}
算法的流程基本上和原理能对得上,但是实现过程中发现该算法结果不太稳定。可能实现有些问题吧,希望有懂的大神指出来(逃~)
参考:
3D点云配准算法-4PCS(4点全等集配准算法)
点云配准–4PCS原理与应用