18 基于欧式聚类的扫描点云前景-背景分离方法
0 引言
问题背景:项目涉及手持式激光扫描点云的前景与背景分离问题,手持扫描仪通过环形标志点以及人工交互实现精确配准,得到包含前景和背景的扫描点云数据。
我们的任务是将点云前景(foregroud)与背景(background)数据分离开来。手持式扫描点云的操作过程如图所示。
输入:带噪声的点云数据,CAD数模,扫描分辨率范围等参数
输出:去噪的前景数据
1 点云数据分析
输入的点云以及CAD数模文件如图所示。
如图所示,图中有三组点云数据,扫描件的尺寸从50mm立方到200mm立方不等,扫描分辨率从0.6~0.8mm,前景与背景之间有放置物体.
2 基于欧式聚类的前景与背景分离算法
(1)点云下采样
目的是将不同扫描分辨率的点云做归一化处理,使得后续处理算法相关参数具备一定通用性。
pcl::VoxelGrid<pcl::PointXYZ> vg; pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>); vg.setInputCloud(cloud); vg.setLeafSize(0.6f, 0.6f, 0.6f); vg.filter(*cloud_filtered);
(2)欧式聚类分割
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>); tree->setInputCloud(cloud_filtered); std::vector<pcl::PointIndices> cluster_indices; pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec; ec.setClusterTolerance(3); // 聚类搜索半径 ec.setMinClusterSize(10000); ec.setMaxClusterSize(1000000); ec.setSearchMethod(tree); ec.setInputCloud(cloud_filtered); ec.extract(cluster_indices);
(3)识别前景点云
采用的方法是CAD数模外接矩形与点云外接矩形相似性度量方法。基本思路如下。
1. 计算CAD模型的外轮廓尺寸,采用的是proe二次开发技术
// 获取零件的外包络长方体 void getOutLineOfSolid() { ProError status; //ProMdldata mdldata; ProMdl pSldMdl; status = ProMdlCurrentGet(&pSldMdl); if (status != PRO_TK_NO_ERROR) { AfxMessageBox(_T("打开模型失败!")); return; } Pro3dPnt r_outline_points[2]; ProSolidOutlineGet((ProSolid)pSldMdl, r_outline_points); // 求解基坐标方向的外形轮廓 CStringW cstrOutLinePoints = L"外包络长方体的长宽高为: "; // 求边长并排序 float edges[3]; for (int i = 0; i < 3; i++) { edges[i] = r_outline_points[1][i] - r_outline_points[0][i]; } // 插入排个序,从大到小 for (int i = 1; i < 3;i++) { for (int j = i; j > 0; j--) { if (edges[j] > edges[j - 1]) { float temp = edges[j]; edges[j] = edges[j - 1]; edges[j - 1] = temp; } } } CStringW cstrEdgess; cstrEdgess.Format(L"长宽高1:(%.3f,%.3f,%.3f)" , edges[0], edges[1], edges[2]); cstrOutLinePoints += cstrEdgess; MessageBoxW(NULL, cstrOutLinePoints, L"手选曲面信息显示", MB_OK); }
输出结果是一个三维向量,CAD_edges[3],从大到小排列.
2. 计算点云的外轮廓尺寸,采用的是PCL中的技术
void minBox() {
// 确认点云已经读入 if (!MycloudFlag) { AfxMessageBox(_T("请先读入点云! ")); return; }
// pcl中的惯性矩拟合方法 pcl::MomentOfInertiaEstimation <pcl::PointXYZ> feature_extractor; feature_extractor.setInputCloud(mycloud); feature_extractor.compute(); std::vector <float> moment_of_inertia; std::vector <float> eccentricity; pcl::PointXYZ min_point_AABB; pcl::PointXYZ max_point_AABB; pcl::PointXYZ min_point_OBB; pcl::PointXYZ max_point_OBB; pcl::PointXYZ position_OBB; Eigen::Matrix3f rotational_matrix_OBB; float major_value, middle_value, minor_value; Eigen::Vector3f major_vector, middle_vector, minor_vector; Eigen::Vector3f mass_center; feature_extractor.getMomentOfInertia(moment_of_inertia); feature_extractor.getEccentricity(eccentricity); feature_extractor.getAABB(min_point_AABB, max_point_AABB); feature_extractor.getOBB(min_point_OBB, max_point_OBB, position_OBB, rotational_matrix_OBB); feature_extractor.getEigenValues(major_value, middle_value, minor_value); feature_extractor.getEigenVectors(major_vector, middle_vector, minor_vector); feature_extractor.getMassCenter(mass_center); boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("3D Viewer")); viewer->setBackgroundColor(0, 0, 0); viewer->addCoordinateSystem(1.0); viewer->initCameraParameters(); viewer->addPointCloud<pcl::PointXYZ>(mycloud); Eigen::Vector3f position(position_OBB.x, position_OBB.y, position_OBB.z); Eigen::Quaternionf quat(rotational_matrix_OBB); Eigen::Vector3f p1(min_point_OBB.x, min_point_OBB.y, min_point_OBB.z); Eigen::Vector3f p2(min_point_OBB.x, min_point_OBB.y, max_point_OBB.z); Eigen::Vector3f p3(max_point_OBB.x, min_point_OBB.y, max_point_OBB.z); Eigen::Vector3f p4(max_point_OBB.x, min_point_OBB.y, min_point_OBB.z); Eigen::Vector3f p5(min_point_OBB.x, max_point_OBB.y, min_point_OBB.z); Eigen::Vector3f p6(min_point_OBB.x, max_point_OBB.y, max_point_OBB.z); Eigen::Vector3f p7(max_point_OBB.x, max_point_OBB.y, max_point_OBB.z); Eigen::Vector3f p8(max_point_OBB.x, max_point_OBB.y, min_point_OBB.z); p1 = rotational_matrix_OBB * p1 + position; p2 = rotational_matrix_OBB * p2 + position; p3 = rotational_matrix_OBB * p3 + position; p4 = rotational_matrix_OBB * p4 + position; p5 = rotational_matrix_OBB * p5 + position; p6 = rotational_matrix_OBB * p6 + position; p7 = rotational_matrix_OBB * p7 + position; p8 = rotational_matrix_OBB * p8 + position; // 求解包络立方体边长 float edges[3]; edges[0] = pow(pow(pt1.x - pt2.x, 2.0) + pow(pt1.y - pt2.y, 2.0) + pow(pt1.z - pt2.z, 2.0), 0.5); edges[1] = pow(pow(pt2.x - pt3.x, 2.0) + pow(pt2.y - pt3.y, 2.0) + pow(pt2.z - pt3.z, 2.0), 0.5); edges[2] = pow(pow(pt2.x - pt6.x, 2.0) + pow(pt2.y - pt6.y, 2.0) + pow(pt2.z - pt6.z, 2.0), 0.5); for (size_t i = 1; i < 3; i++) { for (size_t j = i; j >= 1; j--) { if (edges[j]>edges[j - 1]) { float temp = edges[j]; edges[j] = edges[j - 1]; edges[j - 1] = temp; } } } char mybox[200]; *mybox = '\0'; sprintf(mybox, "(%.2f, %.2f, %.2f)", edges[0], edges[1], edges[2]); AfxMessageBox(_T("长宽高分别为: ") + (CString)CA2T(mybox)); }
输出结果也是一个三维向量,pts_edges[3],从小到大排列.
3. 相似性度量
采用的公式是欧式相对距离法,公式如下。
利用该公式寻找相似度最高的点云,将点云文件存储到对应目录下,完成前景分割工作。
3 效果展示
分割效果如下图所示。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步