一、软件环境
win10、pcl1.11.0、vs2019。
二、代码介绍
刚学习完平面拟合,想自己试试根据算法写个球拟合的代码,另外不用更改pcl库的文件,放一个文件里,后续这样想加点约束条件来改进试试,其实就是RANSAC算法,利用mt19937来确定四个随机点,四点确定一个圆判断内外点。本人萌新,写的有些粗糙,请见谅。
三、算法介绍
算法参考链接:根据空间4点坐标求球心坐标 - 百度文库 (baidu.com)
四、详细代码
#include <pcl/io/pcd_io.h>
#include <pcl/sample_consensus/ransac.h>
#include <pcl/sample_consensus/sac_model_sphere.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/point_types.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <boost/thread/thread.hpp>
#include <pcl/features/normal_3d_omp.h>
#include <pcl/io/io.h>
#include <pcl/PolygonMesh.h>
#include <pcl/point_cloud.h>
#include <pcl/io/vtk_lib_io.h>
#include<pcl/features/normal_3d.h>
#include<pcl/features/principal_curvatures.h>
#include <vector>
#include <iostream>
#include <random>
using namespace std;
using namespace pcl;
int user_data;
void
viewerPsycho(pcl::visualization::PCLVisualizer& viewer)
{
static unsigned count = 0;
std::stringstream ss;
ss << "Once per viewer loop: " << count++;
viewer.removeShape("text", 0);
viewer.addText(ss.str(), 200, 300, "text", 0);
//FIXME: possible race condition here:
user_data++;
}
bool isPointDuplicate(const pcl::PointXYZ& point, const std::vector<pcl::PointXYZ>& selectedPoints)
{
for (const auto& selectedPoint : selectedPoints)
{
if (point.x == selectedPoint.x && point.y == selectedPoint.y && point.z == selectedPoint.z)
{
return true;
}
}
return false;
}
int main()
{
pcl::PointCloud<pcl::PointNormal>::Ptr cloud_inputsb(new pcl::PointCloud<pcl::PointNormal>);
if (pcl::io::loadPCDFile("D://code//Project5//normal_with_cur.pcd", *cloud_inputsb) < 0)
{
PCL_ERROR("点云读取失败!\n");
return -1;
}
cout << "加载点云点数:" << cloud_inputsb->points.size() << endl;
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_input(new pcl::PointCloud<pcl::PointXYZ>);
if (pcl::io::loadPCDFile("D://code//Project5//1.pcd", *cloud_input) < 0)
{
PCL_ERROR("点云读取失败!\n");
return -1;
}
cout << "加载点云点数:" << cloud_input->points.size() << endl;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dist(0, cloud_input->size() - 1);
double jieshou_x = 0;
double jieshou_y = 0;
double jieshou_z = 0;
double banjing = 0;
double zuijia_x = 0;
double zuijia_y = 0;
double zuijia_z = 0;
double zuijia_banjing = 0;
double zuijia_neidianlv;
double distance = 0;
double neidianlv_indices = 0;
double neidianlv_index = 0;
int diedaicishu = 10000;
double x_centre = 0;
double y_centre = 0;
double z_centre = 0;
for (int i = 0; i < diedaicishu; i++)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr neidianji(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr waidianji(new pcl::PointCloud<pcl::PointXYZ>);
neidianji->resize(cloud_input->size());
waidianji->resize(cloud_input->size());
double neidianjishu = 0;
double waidianjishu = 0;
std::vector<pcl::PointXYZ> selectedPoints;
while (selectedPoints.size() < 4)
{
int index = dist(gen);
pcl::PointXYZ selectedPoint = cloud_input->points[index];
if (!isPointDuplicate(selectedPoint, selectedPoints))
{
selectedPoints.push_back(selectedPoint);
std::cout << "Selected point: (" << selectedPoint.x << ", " << selectedPoint.y << ", " << selectedPoint.z << ")" << std::endl;
}
}
double p1x = selectedPoints[0].x;
double p1y = selectedPoints[0].y;
double p1z = selectedPoints[0].z;
double p2x = selectedPoints[1].x;
double p2y = selectedPoints[1].y;
double p2z = selectedPoints[1].z;
double p3x = selectedPoints[2].x;
double p3y = selectedPoints[2].y;
double p3z = selectedPoints[2].z;
double p4x = selectedPoints[3].x;
double p4y = selectedPoints[3].y;
double p4z = selectedPoints[3].z;
double a = p1x - p2x, b = p1y - p2y, c = p1z - p2z;
double a1 = p3x - p4x, b1 = p3y - p4y, c1 = p3z - p4z;
double a2 = p2x - p3x, b2 = p2y - p3y, c2 = p2z - p3z;
double A = p1x * p1x - p2x * p2x;
double B = p1y * p1y - p2y * p2y;
double C = p1z * p1z - p2z * p2z;
double A1 = p3x * p3x - p4x * p4x;
double B1 = p3y * p3y - p4y * p4y;
double C1 = p3z * p3z - p4z * p4z;
double A2 = p2x * p2x - p3x * p3x;
double B2 = p2y * p2y - p3y * p3y;
double C2 = p2z * p2z - p3z * p3z;
double P = (A + B + C) / 2;
double Q = (A1 + B1 + C1) / 2;
double R = (A2 + B2 + C2) / 2;
double D = a * b1 * c2 + a2 * b * c1 + c * a1 * b2 - (a2 * b1 * c + a1 * b * c2 + a * b2 * c1);
double Dx = P * b1 * c2 + b * c1 * R + c * Q * b2 - (c * b1 * R + P * c1 * b2 + Q * b * c2);
double Dy = a * Q * c2 + P * c1 * a2 + c * a1 * R - (c * Q * a2 + a * c1 * R + c2 * P * a1);
double Dz = a * b1 * R + b * Q * a2 + P * a1 * b2 - (a2 * b1 * P + a * Q * b2 + R * b * a1);
if (D == 0) {
cerr << "四点共面" << endl;
}
else {
x_centre = Dx / D;
y_centre = Dy / D;
z_centre = Dz / D;
banjing = sqrt((p1x - x_centre) * (p1x - x_centre) +
(p1y - y_centre) * (p1y - y_centre) +
(p1z - z_centre) * (p1z - z_centre));
}
std::cout << "x坐标" << x_centre << "y坐标" << y_centre << "z坐标" << z_centre << "半径为" << banjing << std::endl;
double array01[629];
for (int j = 0; j < cloud_input->size(); j++)
{
distance = sqrt(((cloud_input->points[j].x - x_centre) * (cloud_input->points[j].x - x_centre) +
(cloud_input->points[j].y - y_centre) * (cloud_input->points[j].y - y_centre) +
(cloud_input->points[j].z - z_centre) * (cloud_input->points[j].z - z_centre))) - banjing;
array01[j] = distance;
if (distance <= 0.1 && distance >= -0.1)
{
neidianji->points[neidianjishu].x = cloud_input->points[j].x;
neidianji->points[neidianjishu].y = cloud_input->points[j].y;
neidianji->points[neidianjishu].z = cloud_input->points[j].z;
neidianjishu = neidianjishu + 1;
}
else
{
waidianji->points[waidianjishu].x = cloud_input->points[j].x;
waidianji->points[waidianjishu].y = cloud_input->points[j].y;
waidianji->points[waidianjishu].z = cloud_input->points[j].z;
waidianjishu = waidianjishu + 1;
}
}
std::cout << "内点数为:" << neidianjishu << std::endl;
std::cout << "外点数为:" << waidianjishu << std::endl;
float neidianlv = 100 * neidianjishu / (neidianjishu + waidianjishu);
std::cout << neidianlv << std::endl;
neidianlv_indices = neidianlv;
jieshou_x = x_centre;
jieshou_y = y_centre;
jieshou_z = z_centre;
if (neidianlv >= 70)
{
zuijia_x = x_centre;
zuijia_y = y_centre;
zuijia_z = z_centre;
zuijia_banjing = banjing;
zuijia_neidianlv = neidianlv_indices;
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("拟合结果"));
viewer->addPointCloud<pcl::PointXYZ>(neidianji, "cloud");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 1, 1, "cloud");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud");
viewer->addPointCloud<pcl::PointXYZ>(waidianji, "sphere");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 0, 0, "sphere");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "sphere");
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
}
break;
}
if (neidianlv_indices >= neidianlv_index)
{
zuijia_x = jieshou_x;
zuijia_y = jieshou_y;
zuijia_z = jieshou_z;
zuijia_banjing = banjing;
zuijia_neidianlv = neidianlv_indices;
neidianlv_index = neidianlv_indices;
zuijia_x = jieshou_x;
jieshou_x = 0;
}
std::cout << "hhhhhh" << std::endl;
}
std::cout << "最佳拟合球参数:" << "球心坐标x" << zuijia_x << "球心坐标y" << zuijia_y << "球心坐标z" << zuijia_z << "球半径" << zuijia_banjing << std::endl;
std::cout << "内点率为" << zuijia_neidianlv << std::endl;
pcl::PointCloud<pcl::PointXYZ>::Ptr neidian(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr waidian(new pcl::PointCloud<pcl::PointXYZ>);
neidian->resize(cloud_input->size());
waidian->resize(cloud_input->size());
double neidian02 = 0;
double waidian02 = 0;
double array02[629];
for (int k = 0; k < cloud_input->size(); k++)
{
distance = sqrt(((cloud_input->points[k].x - zuijia_x) * (cloud_input->points[k].x - zuijia_x) +
(cloud_input->points[k].y - zuijia_y) * (cloud_input->points[k].y - zuijia_y) +
(cloud_input->points[k].z - zuijia_z) * (cloud_input->points[k].z - zuijia_z))) - zuijia_banjing;
array02[k] = distance;
if (distance <= 0.1 && distance >= -0.1)
{
neidian->points[neidian02].x = cloud_input->points[k].x;
neidian->points[neidian02].y = cloud_input->points[k].y;
neidian->points[neidian02].z = cloud_input->points[k].z;
neidian02 = neidian02 + 1;
}
else
{
waidian->points[waidian02].x = cloud_input->points[k].x;
waidian->points[waidian02].y = cloud_input->points[k].y;
waidian->points[waidian02].z = cloud_input->points[k].z;
waidian02 = waidian02 + 1;
}
}
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("拟合结果"));
viewer->addPointCloud<pcl::PointXYZ>(neidian, "cloud");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 1, 1, "cloud");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud");
viewer->addPointCloud<pcl::PointXYZ>(waidian, "sphere");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 0, 0, "sphere");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "sphere");
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
}
return 0;
}
最后的效果如下,白色为拟合的球体。
最佳拟合球参数:球心坐标x86.8652球心坐标y60.0072球心坐标z86.8682球半径9.96032
内点率为27.9809
五、pcd文件链接
链接:https://pan.baidu.com/s/13IJMh6ALlqAMAngaKVc80g
提取码:acdl
--来自百度网盘超级会员V4的分享
六、总结
如有不足,请各位大佬批评指正。