精华内容
下载资源
问答
  • //边界提取 #include <pcl/visualization/pcl_visualizer.h> #include <boost/thread/thread.hpp> using namespace std; int main() { pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::...
  • 点云边界提取

    2017-08-15 14:52:49
    可以对txt格式的点云进行边界提取,包括内边界
  • 点云边界提取.rar

    2021-02-10 22:18:42
    基于PCL库,用C++语言实现的点云边界提取算法,内含:实现代码,实验数据和详细实现过程介绍的文章。
  • } 法线估计法 可参考PCL:点云数据基于法线的边界提取(从最初的法线估计理论推导到最终的边界提取) #include #include #include #include #include #include #include #include #include #include #include #...

    经纬线扫描法

    经纬线扫描法的基本思想是:将经过坐标变换后的二维数据点集按照 x值的大小排序后存储在一个链表中,在二维平面建立点集的最小包围盒并分别计算出 x 和 y 的最大、最小值;令经线从 x 的最小值开始,取步长为dx,在 x 的取值范围内分别计算出每根经线的最大和最小 y 值,并将它们的索引值放在一个新建的链表中,扫描完整个 x 区间;同样的方法扫描纬线,最后将重复的索引值删掉。

    #include <iostream>
    #include <algorithm>
    #include <pcl/io/pcd_io.h>
    #include <pcl/point_types.h>
    
    void BoundaryExtraction(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_boundary, int resolution)
    {
    	pcl::PointXYZ px_min = *std::min_element(cloud->begin(), cloud->end(), [](pcl::PointXYZ pt1, pcl::PointXYZ pt2) {return pt1.x < pt2.x; });
    	pcl::PointXYZ px_max = *std::max_element(cloud->begin(), cloud->end(), [](pcl::PointXYZ pt1, pcl::PointXYZ pt2) {return pt1.x < pt2.x; });
    
    	float delta_x = (px_max.x - px_min.x) / resolution;
    	float min_y = INT_MAX, max_y = -INT_MAX;
    	std::vector<int> indexs_x(2 * resolution);
    	std::vector<std::pair<float, float>> minmax_x(resolution, { INT_MAX,-INT_MAX });
    	for (size_t i = 0; i < cloud->size(); ++i)
    	{
    		int id = (cloud->points[i].x - px_min.x) / delta_x;
    		if (cloud->points[i].y < minmax_x[id].first)
    		{
    			minmax_x[id].first = cloud->points[i].y;
    			indexs_x[id] = i;
    		}
    		else if (cloud->points[i].y > minmax_x[id].second)
    		{
    			minmax_x[id].second = cloud->points[i].y;
    			indexs_x[id + resolution] = i;
    		}
    	}
    
    	pcl::PointXYZ py_min = *std::min_element(cloud->begin(), cloud->end(), [](pcl::PointXYZ pt1, pcl::PointXYZ pt2) {return pt1.y < pt2.y; });
    	pcl::PointXYZ py_max = *std::max_element(cloud->begin(), cloud->end(), [](pcl::PointXYZ pt1, pcl::PointXYZ pt2) {return pt1.y < pt2.y; });
    
    	float delta_y = (py_max.y - py_min.y) / resolution;
    	float min_x = INT_MAX, max_x = -INT_MAX;
    	std::vector<int> indexs_y(2 * resolution);
    	std::vector<std::pair<float, float>> minmax_y(resolution, { INT_MAX,-INT_MAX });
    	for (size_t i = 0; i < cloud->size(); ++i)
    	{
    		int id = (cloud->points[i].y - py_min.y) / delta_y;
    		if (cloud->points[i].x < minmax_y[id].first)
    		{
    			minmax_y[id].first = cloud->points[i].x;
    			indexs_y[id] = i;
    		}
    		else if (cloud->points[i].x > minmax_y[id].second)
    		{
    			minmax_y[id].second = cloud->points[i].x;
    			indexs_y[id + resolution] = i;
    		}
    	}
    
    	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_xboundary(new pcl::PointCloud<pcl::PointXYZ>);
    	pcl::copyPointCloud(*cloud, indexs_x, *cloud_xboundary);
    	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_yboundary(new pcl::PointCloud<pcl::PointXYZ>);
    	pcl::copyPointCloud(*cloud, indexs_y, *cloud_yboundary);
    	*cloud_boundary = *cloud_xboundary + *cloud_yboundary;
    }
    
    int main(int argc, char** argv)
    {
    	clock_t start = clock();
    
    	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>), cloud_boundary(new pcl::PointCloud<pcl::PointXYZ>);
    	pcl::io::loadPCDFile("test.pcd", *cloud);
    	BoundaryExtraction(cloud, cloud_boundary, 200);
    
    	clock_t stop = clock();
    	double endtime = (double)(stop - start) / CLOCKS_PER_SEC;
    	std::cout << "time:" << endtime * 1000 << "ms" << std::endl;
    
    	pcl::io::savePCDFileBinary("boundary.pcd", *cloud_boundary);
    
    	system("pause");
    	return 0;
    }
    

    网格划分法

    网格划分法分为三个步骤:(1)网格划分(2)寻找边界网格(3)提取边界线。首先建立数据点集的最小包围盒,并用给定间隔的矩形网格将其分割;然后找出边界网格,将这些边界网格按顺序连接起来形成一条由边界网格组成的“粗边界”;再对每个边界网格按照某种规则判断其内的点是否为边界点,连接初始边界,并对此边界线进一步处理使其光滑。
    在这里插入图片描述
    在这里插入图片描述

    #include <iostream>
    #include <vector>
    #include <pcl/io/pcd_io.h>
    #include <pcl/point_types.h>
    #include <pcl/common/transforms.h>
    
    typedef pcl::PointCloud<pcl::PointXYZ> PointCloud;
    
    void BoundaryExtraction(PointCloud::Ptr cloud, PointCloud::Ptr cloud_boundary, int resolution)
    {
    	pcl::PointXYZ px_min = *std::min_element(cloud->begin(), cloud->end(), [](pcl::PointXYZ pt1, pcl::PointXYZ pt2) {return pt1.x < pt2.x; });
    	pcl::PointXYZ px_max = *std::max_element(cloud->begin(), cloud->end(), [](pcl::PointXYZ pt1, pcl::PointXYZ pt2) {return pt1.x < pt2.x; });
    	pcl::PointXYZ py_min = *std::min_element(cloud->begin(), cloud->end(), [](pcl::PointXYZ pt1, pcl::PointXYZ pt2) {return pt1.y < pt2.y; });
    	pcl::PointXYZ py_max = *std::max_element(cloud->begin(), cloud->end(), [](pcl::PointXYZ pt1, pcl::PointXYZ pt2) {return pt1.y < pt2.y; });
    	float x_min = px_min.x, x_max = px_max.x, y_min = py_min.y, y_max = py_max.y;
    
    	float L = sqrt((x_max - x_min)*(y_max - y_min) / resolution);
    	int x_num = (x_max - x_min) / L + 1;
    	int y_num = (y_max - y_min) / L + 1;
    	//std::cout << L << " " << x_num << " " << y_num << std::endl;
    
    	std::vector<std::vector<PointCloud::Ptr>> uv(x_num + 1, std::vector<PointCloud::Ptr>(y_num + 1));
    	for (int i = 0; i <= x_num; ++i)
    	{
    		for (int j = 0; j <= y_num; ++j)
    		{
    			PointCloud::Ptr ptcloud(new PointCloud);
    			uv[i][j] = ptcloud;
    		}
    	}
    	for (int i = 0; i < cloud->size(); ++i)
    	{
    		int x_id = (cloud->points[i].x - x_min) / L;
    		int y_id = (cloud->points[i].y - y_min) / L;
    		uv[x_id][y_id]->push_back(cloud->points[i]);
    	}
    
    	std::vector<std::vector<bool>> boundary_index(x_num + 1, std::vector<bool>(y_num + 1, false));
    	for (int i = 0; i <= x_num; ++i)
    	{
    		if (uv[i][0]->size())
    			boundary_index[i][0] = true;
    		if (uv[i][y_num]->size())
    			boundary_index[i][y_num] = true;
    	}		
    	for (int i = 0; i <= y_num; ++i)
    	{
    		if(uv[0][i]->size())
    			boundary_index[0][i] = true;
    		if (uv[x_num][i]->size())
    			boundary_index[x_num][i] = true;
    	}
    	for (int i = 1; i < x_num-1; ++i)
    	{
    		for (int j = 1; j < y_num-1; ++j)
    		{
    			if (uv[i][j]->size())
    			{
    				if (!uv[i - 1][j - 1]->size() || !uv[i][j - 1]->size() || !uv[i + 1][j - 1]->size() || !uv[i][j - 1]->size() ||
    					!uv[i + 1][j - 1]->size() || !uv[i + 1][j]->size() || !uv[i + 1][j + 1]->size() || !uv[i][j + 1]->size())
    					boundary_index[i][j] = true;
    			}
    		}
    	}
    
    	for (int i = 0; i <= x_num; ++i)
    	{
    		for (int j = 0; j <= y_num; ++j)
    		{
    			//std::cout << i << " " << j << " " << uv[i][j]->points.size() << std::endl;
    			if (boundary_index[i][j])
    			{
    				PointCloud::Ptr ptcloud(new PointCloud);
    				ptcloud = uv[i][j];
    				Eigen::Vector4f cloud_centroid;
    				pcl::compute3DCentroid(*ptcloud, cloud_centroid);
    				cloud_boundary->push_back(pcl::PointXYZ(cloud_centroid(0), cloud_centroid(1), cloud_centroid(2)));
    			}
    		}
    	}
    }
    
    int main(int argc, char** argv)
    {
    	clock_t start = clock();
    
    	PointCloud::Ptr cloud(new PointCloud), cloud_boundary(new PointCloud);
    	pcl::io::loadPCDFile("4icp5+6+icp_skeleton.pcd", *cloud);
    	BoundaryExtraction(cloud, cloud_boundary, 200*200);
    
    	clock_t stop = clock();
    	double endtime = (double)(stop - start) / CLOCKS_PER_SEC;
    	std::cout << "time:" << endtime * 1000 << "ms" << std::endl;
    
    	pcl::io::savePCDFile("boundary.pcd", *cloud_boundary);
    
    	system("pause");
    	return 0;
    }
    

    法线估计法

    可参考PCL:点云数据基于法线的边界提取(从最初的法线估计理论推导到最终的边界提取)

    #include <iostream>
    #include <vector>
    #include <ctime>
    #include <boost/thread/thread.hpp>
    #include <pcl/io/pcd_io.h>
    #include <pcl/visualization/pcl_visualizer.h>
    #include <pcl/console/parse.h>
    #include <pcl/features/eigen.h>
    #include <pcl/features/feature.h>
    #include <pcl/features/normal_3d.h>
    #include <pcl/features/normal_3d_omp.h>
    #include <pcl/impl/point_types.hpp>
    #include <pcl/features/boundary.h>
    #include <pcl/visualization/cloud_viewer.h>
    #include <pcl/filters/voxel_grid.h>
    using namespace std;
    
    int main(int argc, char **argv)
    {
    	clock_t start = clock();
    
    	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
    	if (pcl::io::loadPCDFile<pcl::PointXYZ>(argv[1], *cloud) == -1)
    	{
    		PCL_ERROR("COULD NOT READ FILE. \n");
    		return (-1);
    	}
    	//std::cout << "points size is:" << cloud->size() << std::endl;
    	
    	pcl::VoxelGrid<pcl::PointXYZ> voxel_grid;
    	voxel_grid.setLeafSize(std::stof(argv[2]), std::stof(argv[2]), std::stof(argv[2]));
    	voxel_grid.setInputCloud(cloud);
    	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
    	voxel_grid.filter(*cloud_filtered);
    	//std::cout << "filtered points size is:" << cloud_filtered->size() << std::endl;
    
    	pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
    	pcl::PointCloud<pcl::Boundary> boundaries;
    	pcl::BoundaryEstimation<pcl::PointXYZ, pcl::Normal, pcl::Boundary> est;
    	pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
    
    	//pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normEst;  //其中pcl::PointXYZ表示输入类型数据,pcl::Normal表示输出类型,且pcl::Normal前三项是法向,最后一项是曲率
    	pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> normEst;
    	normEst.setNumberOfThreads(12);  // 手动设置线程数,否则提示错误
    	normEst.setInputCloud(cloud_filtered);
    	normEst.setSearchMethod(tree);
    	normEst.setRadiusSearch(std::stof(argv[3]));  //法向估计的半径
    	 //normEst.setKSearch(9);  //法向估计的点数
    	normEst.compute(*normals);
    	//cout << "normal size is " << normals->size() << endl;
    
    	est.setInputCloud(cloud_filtered);
    	est.setInputNormals(normals);
    	est.setSearchMethod(tree);
    	est.setKSearch(500);  //一般这里的数值越高,最终边界识别的精度越好
    	est.compute(boundaries);
    
    	pcl::PointCloud<pcl::PointXYZ>::Ptr boundPoints(new pcl::PointCloud<pcl::PointXYZ>);
    	pcl::PointCloud<pcl::PointXYZ> noBoundPoints;
    	int countBoundaries = 0;
    	for (int i = 0; i<cloud_filtered->size(); i++) {
    		uint8_t x = (boundaries.points[i].boundary_point);
    		int a = static_cast<int>(x); //该函数的功能是强制类型转换
    		if (a == 1)
    		{
    			(*boundPoints).push_back(cloud_filtered->points[i]);
    			countBoundaries++;
    		}
    		else
    			noBoundPoints.push_back(cloud_filtered->points[i]);
    	}
    
    	clock_t stop = clock();
    	double endtime = (double)(stop - start) / CLOCKS_PER_SEC; 
    	std::cout << "time:" << endtime * 1000 << "ms" << std::endl;
    	std::cout << "boundary size is:" << countBoundaries << std::endl;
    	//pcl::io::savePCDFileASCII("boundary.pcd", *boundPoints);
    	//pcl::io::savePCDFileASCII("NoBoundpoints.pcd", noBoundPoints);
    	pcl::visualization::CloudViewer viewer("boundary");
    	viewer.showCloud(boundPoints);
    	while (!viewer.wasStopped())
    	{
    	}
    	return 0;
    }
    

    参考资料:点云的边界提取及角点检测算法研究

    展开全文
  • } } boost::shared_ptr<pcl::visualization::PCLVisualizer> MView(new pcl::visualization::PCLVisualizer("点云库PCL从入门到精通案例")); int v1(0); MView->createViewPort(0.0, 0.0, 0.5, 1.0, v1); ...
    #pragma warning(disable:4996)
    #include <iostream>
    #include <pcl/console/parse.h>
    #include <pcl/filters/extract_indices.h>
    #include <pcl/io/pcd_io.h>
    #include <pcl/point_types.h>
    
    #include <pcl/visualization/pcl_visualizer.h>
    #include <boost/thread/thread.hpp>
    #include <pcl/features/boundary.h>
    #include <math.h>
    #include <boost/make_shared.hpp>
    #include <pcl/point_types.h>
    #include <pcl/point_cloud.h>
    #include <pcl/visualization/cloud_viewer.h>
    #include <pcl/io/pcd_io.h>
    
    #include <pcl/visualization/range_image_visualizer.h>
    #include <pcl/features/normal_3d.h>
    
    #include <pcl/visualization/pcl_visualizer.h>
    #include <pcl/filters/covariance_sampling.h>
    #include <pcl/filters/normal_space.h>
    #include <pcl/kdtree/kdtree_flann.h>
    #include <pcl/features/boundary.h>
    #include <pcl/io/ply_io.h>
    
    
    int estimateBorders(pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, float re, float reforn)
    {
    
    	pcl::PointCloud<pcl::Boundary> boundaries;
    	pcl::BoundaryEstimation<pcl::PointXYZ, pcl::Normal, pcl::Boundary> boundEst;
    	pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normEst;
    	pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
    	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_boundary(new pcl::PointCloud<pcl::PointXYZ>);
    	normEst.setInputCloud(pcl::PointCloud<pcl::PointXYZ>::Ptr(cloud));
    	normEst.setRadiusSearch(reforn);
    	normEst.compute(*normals);
    
    	boundEst.setInputCloud(cloud);
    	boundEst.setInputNormals(normals);
    	boundEst.setRadiusSearch(re);
    	boundEst.setAngleThreshold(M_PI / 4);
    	boundEst.setSearchMethod(pcl::search::KdTree<pcl::PointXYZ>::Ptr(new pcl::search::KdTree<pcl::PointXYZ>));
    	boundEst.compute(boundaries);
    
    	for (int i = 0; i < cloud->points.size(); i++)
    	{
    
    		if (boundaries[i].boundary_point > 0)
    		{
    			cloud_boundary->push_back(cloud->points[i]);
    		}
    	}
    
    	boost::shared_ptr<pcl::visualization::PCLVisualizer> MView(new pcl::visualization::PCLVisualizer("点云库PCL从入门到精通案例"));
    
    	int v1(0);
    	MView->createViewPort(0.0, 0.0, 0.5, 1.0, v1);
    	MView->setBackgroundColor(0.3, 0.3, 0.3, v1);
    	MView->addText("Raw point clouds", 10, 10, "v1_text", v1);
    	int v2(0);
    	MView->createViewPort(0.5, 0.0, 1, 1.0, v2);
    	MView->setBackgroundColor(0.5, 0.5, 0.5, v2);
    	MView->addText("Boudary point clouds", 10, 10, "v2_text", v2);
    
    	MView->addPointCloud<pcl::PointXYZ>(cloud, "sample cloud", v1);
    	MView->addPointCloud<pcl::PointXYZ>(cloud_boundary, "cloud_boundary", v2);
    	MView->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 0, 0, "sample cloud", v1);
    	MView->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0, 1, 0, "cloud_boundary", v2);
    	MView->addCoordinateSystem(1.0);
    	MView->initCameraParameters();
    
    	MView->spin();
    
    	return 0;
    }
    int
    main(int argc, char** argv)
    {
    	srand(time(NULL));
    
    	float re, reforn;
    	re = std::atof(argv[2]);
    	reforn = std::atof(argv[3]);
    	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_src(new pcl::PointCloud<pcl::PointXYZ>);
    
    
    
    	//Laden der PCD-Files 
    	pcl::io::loadPCDFile(argv[1], *cloud_src);
    
    	estimateBorders(cloud_src, re, reforn);
    
    	return 0;
    }
    
    

    在这里插入图片描述

    展开全文
  • setWindowName("alpha_shapes提取边界"); viewer->setBackgroundColor(0, 0, 0); viewer->addPointCloud(cloud, "cloud"); viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_...
  • alpha shapes提取点云边界特征(C++版本)基于PCL库写的alpha shapes算法,具体实现原理参考:https://blog.csdn.net/qq_36686437/article/details/115168516... 平面点云边界提取算法研究[D].长沙理工大学,2017.P51-53
  • 散乱数据点云边界特征自动提取算法_孙殿柱.caj
  • PCL_BoundaryEstimation_Point散乱数据点云边界特征自动提取算法
  • PCL提取点云边界轮廓

    千次阅读 2020-06-28 17:31:25
    提取点云边界轮廓 #include <iostream> #include <vector> #include <ctime> #include <boost/thread/thread.hpp> #include <pcl/io/ply_io.h> #include <pcl/io/pcd_io.h> #...

    提取点云边界轮廓
    在这里插入图片描述
    在这里插入图片描述

    #include <iostream>
    #include <vector>
    #include <ctime>
    #include <boost/thread/thread.hpp>
    #include <pcl/io/ply_io.h>
    #include <pcl/io/pcd_io.h>
    #include <pcl/visualization/pcl_visualizer.h>
    #include <pcl/console/parse.h>
    #include <pcl/features/eigen.h>
    #include <pcl/features/feature.h>
    #include <pcl/features/normal_3d.h>
    #include <pcl/impl/point_types.hpp>
    #include <pcl/features/boundary.h>
    #include <pcl/visualization/cloud_viewer.h>
    #include <pcl/visualization/pcl_visualizer.h>
    using namespace std;
    
    int main(int argc, char** argv)
    {
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
        // if (pcl::io::loadPCDFile<pcl::PointXYZ>("/home/yxg/pcl/pcd/mid.pcd",*cloud) == -1)
        if (pcl::io::loadPCDFile<pcl::PointXYZ>("C:\\Users\\fhlhc\\Desktop\\chairfilter_right.pcd", *cloud) == -1)
        {
            PCL_ERROR("COULD NOT READ FILE mid.pcl \n");
            return (-1);
        }
    
        std::cout << "points sieze is:" << cloud->size() << std::endl;
        pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
        pcl::PointCloud<pcl::Boundary> boundaries;
        pcl::BoundaryEstimation<pcl::PointXYZ, pcl::Normal, pcl::Boundary> est;
        pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
        
        pcl::KdTreeFLANN<pcl::PointXYZ> kdtree;  //创建一个快速k近邻查询,查询的时候若该点在点云中,则第一个近邻点是其本身
        kdtree.setInputCloud(cloud);
        int k =2;
        float everagedistance =0;
        for (int i =0; i < cloud->size()/2;i++)
        {
                vector<int> nnh ;
                vector<float> squaredistance;
                //  pcl::PointXYZ p;
                //   p = cloud->points[i];
                kdtree.nearestKSearch(cloud->points[i],k,nnh,squaredistance);
                everagedistance += sqrt(squaredistance[1]);
                //   cout<<everagedistance<<endl;
        }
        everagedistance = everagedistance/(cloud->size()/2);
        cout<<"everage distance is : "<<everagedistance<<endl;
    
    
    
    
    
        pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normEst;  //其中pcl::PointXYZ表示输入类型数据,pcl::Normal表示输出类型,且pcl::Normal前三项是法向,最后一项是曲率
        normEst.setInputCloud(cloud);
        normEst.setSearchMethod(tree);
        // normEst.setRadiusSearch(2);  //法向估计的半径
        normEst.setKSearch(9);  //法向估计的点数
        normEst.compute(*normals);
        cout << "normal size is " << normals->size() << endl;
    
        //normal_est.setViewPoint(0,0,0); //这个应该会使法向一致
        est.setInputCloud(cloud);
        est.setInputNormals(normals);
        //  est.setAngleThreshold(90);
        //   est.setSearchMethod (pcl::search::KdTree<pcl::PointXYZ>::Ptr (new pcl::search::KdTree<pcl::PointXYZ>));
        est.setSearchMethod(tree);
        est.setKSearch(50);  //一般这里的数值越高,最终边界识别的精度越好
        //  est.setRadiusSearch(everagedistance);  //搜索半径
        est.compute(boundaries);
    
        //  pcl::PointCloud<pcl::PointXYZ> boundPoints;
        pcl::PointCloud<pcl::PointXYZ>::Ptr boundPoints(new               pcl::PointCloud<pcl::PointXYZ>);
        pcl::PointCloud<pcl::PointXYZ> noBoundPoints;
        int countBoundaries = 0;
        for (int i = 0; i < cloud->size(); i++) {
            uint8_t x = (boundaries.points[i].boundary_point);
            int a = static_cast<int>(x); //该函数的功能是强制类型转换
            if (a == 1)
            {
                //  boundPoints.push_back(cloud->points[i]);
                (*boundPoints).push_back(cloud->points[i]);
                countBoundaries++;
            }
            else
                noBoundPoints.push_back(cloud->points[i]);
    
        }
        std::cout << "boudary size is:" << countBoundaries << std::endl;
        //  pcl::io::savePCDFileASCII("boudary.pcd",boundPoints);
    
        pcl::io::savePLYFileASCII("C:\\Users\\fhlhc\\Desktop\\Boundpoints.ply", *boundPoints);
       // pcl::io::savePLYFileASCII("C:\\Users\\fhlhc\\Desktop\\NoBoundpoints.ply", noBoundPoints);
    
    
        //双视口
        boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("test Viewer"));
        viewer->initCameraParameters();
        int v1(0), v2(0);
        //原始点云窗口
        viewer->createViewPort(0.0, 0.0, 0.5, 1.0, v1);
        viewer->setBackgroundColor(0, 0, 0, v1);
        viewer->addText("original", 10, 10, "v1 text", v1);
        viewer->addPointCloud<pcl::PointXYZ>(cloud, "sample cloud1", v1);
        viewer->addCoordinateSystem(1.0);
        viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "sample cloud1");
        //滤波窗口
        viewer->createViewPort(0.5, 0.0, 1.0, 1.0, v2);
        viewer->setBackgroundColor(0, 0, 0, v2);
        viewer->addText("提取边界", 10, 10, "v2 text", v2);
        viewer->addPointCloud<pcl::PointXYZ>(boundPoints, "sample cloud2", v2);
        viewer->addCoordinateSystem(1.0);
        viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "sample cloud2");
        while (!viewer->wasStopped())
        {
            viewer->spinOnce(100);  //刷新
            boost::this_thread::sleep(boost::posix_time::microseconds(100000));
        }
        return 0;
    }
    
    展开全文
  • 点云边界提取

    热门讨论 2012-06-30 17:40:27
    能够将散乱的点云数据边界点及特征点提取出来,并显示。
  • 基于车载激光点云的道路边界提取,方莉娜,杨必胜,车载激光扫描系统获取的道路环境点云数据量大、目标复杂,难以有效提取出道路的点云。本文通过分析道路边界的形状、强度,以及全
  • 资料来源:超人视觉免费启蒙三维课程入门(第六节)3D鞋点胶的点云边界提取 视觉: 机器视觉(2D、3D): 2D: 识别定位(对位贴合)(深度学习) 测量 缺陷(外观检测)(深度学习) 符号需求(一...

    资料来源:超人视觉免费启蒙三维课程入门(第六节)3D鞋点胶的点云边界(轮廓)提取

    相似案例:Halcon三维测量(3):鞋底涂胶+边缘提取

    Halcon学习方法强调:从案例当中学习:最重要是思路和算子的用法、为我们所用。不要机械式套用。

     

    视觉:

    1. 机器视觉(2D、3D):
      1. 2D:
        1. 识别定位(对位贴合)(深度学习)
        2. 测量
        3. 缺陷(外观检测)(深度学习)
        4. 符号需求(一维码、二维码、三维码、OCR)
        5. 视觉+运动控制板卡+机器人
      2. 3D:
        1. 鞋点胶
        2. 无序抓取:在Halcon【实例程序】中【方法】【夺目立体视觉】locate_pipe_joints-stereo.hdev案例中。
        3. 点云数据+深度学习
          1. 工具:Halcon、VisionPro、LabView、OpenCV、Sherlock、Evsion、Insight、Matlab、(Emgucv+C#)
          2. 推荐:Halcon===>>>>   OpenCV  ====>>>>    源码自己编写
          3. 语言主流:C#+halcon、C+++QT+halcon、C+++MFC+halcon
    2. 计算机视觉:
      1. 室外:(深度学习)
      2. 工业:

    视觉项目思路:(算法+软件框架)

    1、需求分析:2D还是3D的?

    2、项目评估仿真:

    3、合同

    4、方案设计:(硬件方案、软件方案)点云直接买现成的,因为精度更高。

    5、精度和速度,考虑硬件方案:

    6、概要设计

    7、详细设计

    8、编码

    9、测试调试

    10、交付

    11、维护


    鞋点胶边界检测思路:

    1、点的法向量重建:K邻域、体素。法向量非常重要:

    2、当前教程:(不是通用、不太灵活的方法)(初学者)

    1)获取点云切平面

    2)切平面与点云的交线

    3)交线在点云上的起点、终点求出来

    4)起点、终点就是外边界。

    起点、终点如何求?

    将交平面转化为一个2D图像求XLD轮廓。XLD上的起点、终点可求。

    激光三角成像:线轮廓+移动


    第一步:读入点云(鞋)的文件。

    第二步:分割出鞋子的点云集合。(去除噪声等。)

    第三步:仿射变换到与长轴和短轴平行的位置。(3D一样可以仿射变换):方便切平面的定义。

    第四步:定义切平面,求鞋的3D点云集合跟切平面的点云交集。

    第五步:把交集的点云坐标映射成2D的XLD轮廓,求每段轮廓起点、终点坐标。

    第六步:把得到的每段XLD的起点、终点坐标又映射转换成3D点云坐标。

    第七步:显示鞋的点云集合以及鞋的外边界的点云集合。




    资料来源:超人视觉免费启蒙三维课程入门(第七节)3D鞋点胶的点云边界提取

    超人视觉免费启蒙三维课程入门第八节(3D鞋点胶的点云边界提取)

     

    第八节还有一个链接

    第一步:加载并显示扫描生成的点云数据。

    dev_close_window ()
    dev_update_off ()
    dev_open_window (0, 0, 512, 512, 'black', WindowHandle)
    * 1、读取点云文件
    read_object_model_3d ('./2.om3', 'mm', [], [], ObjectModel3D, Status)
    visualize_object_model_3d (WindowHandle, ObjectModel3D, [], [], [], [], [], [], [], PoseOut)

    visualize_object_model_3d参数的详解:visualize_object_model_3d (窗口句柄, 3D模型, [内参], [外参], [参数名], [参数名对应的值], [标题信息], [模型信息], [窗口提示信息:比如旋转、放大、缩小], PoseOut)

    • 相机的内外参数
    • 参数名和参数名对应的值:对应下面的:GenParamNameGenParamValue:定义了:显示的颜色,透明度。可以定义显示参数。
    • poseOut:输出点云的姿态。相对于坐标系的平移和旋转。
    •  

    双击【变量窗口】中的ObjectModel3D变量可以打开【三维对象模型检查】。

     点击勾选【Display Models】,会打开【显示三维对象模型】,在显示窗口左下方状态栏中可以显示模型的姿态信息。(7个数字:偏移X,偏移Y,偏移Z,偏转x,偏转y,偏转z,旋转类型(旋转顺序));

    在【显示三维对象模型】窗口上面工具栏可以【插入代码】:

    插入代码如下:

    CamParam := ['area_scan_division',0.06,0,8.5e-006,8.5e-006,468,161,936,323]
    Pose := [-899.05,-1306.69,750249,3.85575,13.3872,36.4233,0]
    GenParamName := ['colored','alpha','color_0']
    GenParamValue := [12,0.9,'cyan']
    disp_object_model_3d (200000, ObjectModel3D, CamParam, Pose, GenParamName, GenParamValue)

    这里的这个显示函数为disp_object_model_3d,相比于visualize_object_model_3d的阻塞式显示不同,不会阻塞,刷新就消失。一般不用。

    插入的代码:

    • 其中会把内参(相机配置参数)和外参(当前的姿态信息)输出。同时对显示进行定义:GenParamNameGenParamValue:定义了:显示的颜色,透明度。
    • 如果曾经求解过法向量,那么就可以点击【显示三维对象模型的法向量】显示法向量,如果没有求过,不会显示。
    • 【显示三维对象模型的坐标系】:在halcon中红颜色代表的是X轴方向,绿颜色代表的是Y轴方向,蓝颜色代表的是Z轴方向。

    halcon中可以用create_pose — Create a 3D pose.来创建一个姿态

    在【显示三维对象模型】中:【Base Parameter】有点云的中心点、直径、外接矩形框。


    在视频中作者特别强调了:【案例教程中】【方法】【三维对象处理】中【moments_object_model_3d.hdev】这个教程。显示方法。

    第32行和第33行:显示参数列表

    GenParamName := ['lut','color_attrib','light_position','disp_pose','alpha']
    GenParamValue := ['color1','coord_z','0.0 0.0 -0.3 1.0','true',0.9]

     根据z轴坐标的高度不同,颜色不同。disp_pose显示坐标系:true。

     第39行和第40行:创建姿态。

    create_pose (-0.0005, -0.0005, 0.04, 280, 0, 20, 'Rp+T', 'gba', 'point', DispPose1)
    create_pose (0, -0.0005, 0.04, 280, 0, 20, 'Rp+T', 'gba', 'point', DispPose2)

     第二步:去噪声

    通过connection_object_model_3d基于某种特征(三角网格(需要计算生成三角网格)、距离、)来将连通域断开,以此来实现:区域切割。独立集合对象。

    关于connection_object_model_3d算子,作者推荐核心案例教程:【方法】【三维对象处理】【connection_object_model_3d.hdev】

    通过分割完成后,用get_object_model_3d_params来计算每个连通域的点云个数。

    connection_object_model_3d (ObjectModel3D, 'distance_3d', 1000, ObjectModel3DConnected)
    get_object_model_3d_params (ObjectModel3DConnected, 'num_points', GenParamValue)

    第三步:滤波筛选

    关于点云筛选的两个案例:【select_points_object_model_3d_by_density.hdev】和【select_object_model_3d.hdev】代表了点云筛选的两种方法。

    *3、滤波筛选:
    select_object_model_3d (ObjectModel3DConnected, 'num_points', 'and', 120000, 200000, ObjectModel3DSelected)
    visualize_object_model_3d (WindowHandle, ObjectModel3DSelected[1], [], PoseOut, ['lut','color_attrib','disp_pose'], ['color1','coord_z','true'], [], [], [], PoseOut2)

    第四步:将鞋点云集合变换到原始坐标系下主轴-X、Y、Z轴

    所谓主轴:用最小外接长方体将点云囊括,长轴与X轴重合,短轴与Y轴重合,高度与Z轴重合。重合移动肯定会有一个姿态关系:pose。

    moments_object_model_3d (ObjectModel3DSelected, 'principal_axes', Pose)第二个参数意思求主轴,第三个参数输出pose姿态。moments_object_model_3d 用来求矩,还有两个参数分别是一阶矩,二阶矩。

    然后将姿态反变换:求姿态的反变换:这样变换就可以将长轴作为X轴,变换就涉及刚体变换(仿射变换)。变换就需要用到矩阵和姿态。

    点云模型刚体变换

    rigid_trans_object_model_3d:刚体变换算子。既可以平移,也可以旋转。

    * 4、将鞋点云集合变换到原始坐标系下主轴-X、Y、Z轴
    * 第二个参数意思求主轴,第三个参数,输出pose姿态。
    moments_object_model_3d (ObjectModel3DSelected[1], 'principal_axes',PoseOut1)
    pose_invert (PoseOut1, PoseInvert)
    
    * 这样变换就可以将长轴作为X轴,变换就涉及刚体变换(仿射变换)。变换就需要用到矩阵和姿态。
    * rigid_trans_object_model_3d (ObjectModel3DSelected[1], PoseInvert, ObjectModel3DRigidTrans)
    * visualize_object_model_3d (WindowHandle, ObjectModel3DRigidTrans, [], PoseInvert, [], [], [], [], [], PoseOut)
    

    仿射变换一样可以达到同样的效果。 放射变换需要的是一个矩阵。

    将姿态变换为矩阵:pose_to_hom_mat3d

    仿射变换:affine_trans_object_model_3d

    * 用仿射变换也可以实现这个变换:
    pose_to_hom_mat3d (PoseInvert, HomMat3D)   \\ 将姿态变换为矩阵
    affine_trans_object_model_3d (ObjectModel3DSelected[1], HomMat3D, ObjectModel3DAffineTrans)
    visualize_object_model_3d (WindowHandle, ObjectModel3DAffineTrans, [], PoseInvert, ['lut','color_attrib','disp_pose'], ['color1','coord_z','true'], [], [], [], PoseOut3)

    这样做的原因是为了方便沿着x轴做切平面的时候,方便分割。这就是确定长轴的原因。

    标准位置的好处:做切平面与鞋面垂直。与轴平行,沿轴切,不会乱。整齐。

     

    第五步:如果鞋子还是没有转正:那么就需要求一个最小外接box。

    推荐案例【smallest_bounding_box_object_model_3d.hdev】中的smallest_bounding_box_object_model_3d算子完成。

    求box之前可以做一个三角曲面重建:使得点云更加圆滑。

    三角曲面重建。将无序点云三角化。内部算子实际使用的是贪婪投影三角法。将有向点云投影到一个二维平面内,平面内三角化。根据平面内的三角拓扑关系,生成一个三角网格曲面模型。如果使用膨胀腐蚀,就会使网格变大变小。

    *三角曲面重建。将无序点云三角化。内部算子实际使用的是贪婪投影三角法。将有向点云投影到一个二维平面内,平面内三角化。根据平面内的三角拓扑关系,生成一个三角网格曲面模型。如果使用膨胀腐蚀,就会使网格变大变小。
    triangulate_object_model_3d (ObjectModel3DAffineTrans, 'greedy', [], [], TriangulatedObjectModel3D, Information)
    * 三角曲面重建需要点时间。如果点数太多的话,可以简化点云:
    * 三角曲面重建比较适用于表面连续比较光滑的曲面,或者点云密度比较均匀的情况。速度会比较快。否则会出现因点云不连续导致的奇形怪状。甚至悬空点。
    visualize_object_model_3d (WindowHandle, TriangulatedObjectModel3D, [], PoseOut3, ['lut','color_attrib','disp_pose'], ['color1','coord_z','true'], [], [], [], PoseOut4)
    

    * 三角曲面重建需要点时间。如果点数太多的话,可以简化点云
    * 三角曲面重建比较适用于表面连续比较光滑的曲面,或者点云密度比较均匀的情况。速度会比较快。否则会出现因点云不连续导致的奇形怪状。甚至悬空点。

    求最小外接box:

    * 最小外接box
    smallest_bounding_box_object_model_3d (TriangulatedObjectModel3D, 'oriented', Pose, Length1, Length2, Length3)
    gen_box_object_model_3d (Pose, Length1, Length2, Length3, ObjectModel3D1)
    pose_invert (Pose, PoseInvert1)
    rigid_trans_object_model_3d (TriangulatedObjectModel3D, PoseInvert1, ObjectModel3DRigidTrans)
    visualize_object_model_3d (WindowHandle, ObjectModel3DRigidTrans, [], [], ['lut','color_attrib','disp_pose'], ['color1','coord_z','true'], [], [], [], PoseOut5)
    * 联合显示:
    visualize_object_model_3d (WindowHandle, [ObjectModel3DRigidTrans,ObjectModel3D1], [], [], ['color_0','color_1','alpha_1', 'disp_pose'], ['green','gray',0.5,'true'], 'RectBOX', [], [], PoseOut2)
    

    特别注意联合显示时的操作:要注意将外面图像设置透明度。




    超人视觉免费启蒙三维课程入门第九讲-求切平面

     

    第六步:在box中做切平面,求与轮廓的交线。

    在X轴,做切平面,每次沿X轴移动指定均匀长度。切平面与点云有一个点云交集,把点云交集映射到二维平面上形成一个XLD二维的轮廓交线,交线的两端(起点、终点)就是我们需要的轮廓点。

    做切平面需要给出切平面的姿态。

    一段段的做切割:肯定要引入一个循环问题:

    推荐案例:inspect_3d_surface_intersections.hdev,

    *5、求切平面交线点云
    for Index1 := 0 to 50 by 1
        CutPlanePose := Pose
        CutPlanePose[0]:=Pose[0]-Length1/2+(Index1)*40+3   // pose中的参数值:第一个为沿着x轴的平移。
        CutPlanePose[3]:=0
        CutPlanePose[4]:=90       // pose第五个值为绕着y轴旋转90度,立起来。
        CutPlanePose[5]:=0
        * 产生平面,去看一下切平面的情况。第一个参数是姿态pose,第二、第三个参数是限定大小。
        gen_plane_object_model_3d (CutPlanePose, [-1, -1, 1, 1]*1, [-1, 1, 1, -1]*1,IntersectionPlane)
    *     visualize_object_model_3d (WindowHandle, [ObjectModel3DRigidTrans,IntersectionPlane], [], Pose,  ['color_0','color_1','alpha_1', 'disp_pose'], ['green','gray',0.5,'true'], [], [], [], PoseOut)
        visualize_object_model_3d (WindowHandle, [aim_object,IntersectionPlane], [], Pose,  ['disp_pose'], ['true'], [], [], [], PoseOut)
    
        * 得到XLD交线。
        intersect_plane_object_model_3d (IntersectionPlane, CutPlanePose, ObjectModel3DIntersection)
        visualize_object_model_3d (WindowHandle, ObjectModel3DIntersection, [], [], [], [], [], [], [], PoseOut7)
        
        * 然后求起点、终点。先变换到2维XLD。
        pose_invert (CutPlanePose,PoseInvert2)
        *确定投影平面在前面
        get_object_model_3d_params (ObjectModel3DIntersection, 'diameter_axis_aligned_bounding_box', Diameter)
        PoseInvert2[2]:=PoseInvert2[2]+Diameter  // 切平面往上抬升。沿着z轴往上升高一个直径大小。凸显效果哦。
        // 基于长轴x轴切割,投影在yoz投影面上。
        * 用平行于投影平面的相机(1:1的比例)
        Scale:=1
        CamParam:=[0, 0, 1.0 /Scale, 1.0/Scale, 0, 0, 500, 500]
        project_object_model_3d (IntersectionXld, ObjectModel3DIntersection, CamParam, PoseInvert2, 'data', 'lines')
        * XLD有可能是多段的。
        count_obj (IntersectionXld, Number)
        
        * 点按照由上到下
        Rows:=[]
        Columns:=[]
        Row:=[]
        Column:=[]
        for I := 1 to Number by 1
            select_obj (IntersectionXld, EdgeContour,I)
            get_contour_xld (EdgeContour, Row1, Colum)  // 获得XLD关键点坐标。
            Rows:=[Rows, Row]
            Columns:=[Columns, Column]
        endfor
        tuple_sort_index(Rows, Indices)
        tuple_length (Rows, Length)
        OrderRow:=[]
        OrderColumn:=[]
        * 点从上往下排序。
        if (Length>=1)
            for Row_Index:=0 to Length-1 by 1
                OrderRow:=[OrderRow, Row[Indices[Row_Index]]]
                OrderColumn:=[OrderColumn, Columns[[Row_Index]]]
            endfor
        endif
        gen_contour_polygon_xld (Intersection, OrderRow, OrderColumn)
        
        * 求最大和最小值(行方向)
        tuple_sort_index (OrderRow, Indices)
        tuple_length (OrderRow,Length)
        * 起点(XLD)
        StartRow:=OrderRow[Indices[0]]
        StartColumn:=OrderColumn[Indices[0]]
        
        * 终点(XLD)
        EndRow:=OrderRow[Indices[Length-1]]
        EndColumn:=OrderColumn[Indices[Length-1]]
        
        dev_display (Intersection)
        
        gen_cross_contour_xld (StartXP, StartRow, StartColumn, 6, 0.795296)
        gen_cross_contour_xld (EndXP, EndRow, EndColumn, 6, 0.785398)
        
        * 转成点云的坐标
        StartPose:=[CutPlanePose[0], StartRow, -StartColumn, 0, 0, 0, 0]
        EndPose:=[CutPlanePose[0], EndRow, -EndColumn, 0, 0, 0, 0]
        gen_sphere_object_model_3d (StartPose, 2, StartPoint)
        gen_sphere_object_model_3d (EndPose, 2, EndPoint)
        
    *     dev_display (Intersection)
        
        visualize_object_model_3d (WindowHandle, [StartPoint, EndPoint], [], [], [], [], [], [], [], PoseOut6)
        * 所有对点的边界点集合
        objectsOut:=[objectOut, StartPoint]
        objectsOut:=[objectOut, EndPoint]
        
        * 显示时的颜色
        Index_S:=0+Index1*2
        Index_E:=0+Index1*2+1
        color_S:='color_'+Index_S
        color_E:='color_'+Index_E
        
        colorsOut:=[colorsOut, color_S]
        colorsOut:=[colorsOut, color_E]
        
        colorvaluesOut:=[colorvaluesOut, 'blue']
        colorvaluesOut:=[colorvaluesOut, 'blue']
        
        all_x:=[all_x, CutPlanePose[0]]
        all_y:=[all_y, StartRow]
        
        all_x:=[all_x, CutPlanePose[0]]
        all_y:=[all_y, EndRow]
        
    endfor
    
    * 显示外边界模型点云。
    visualize_object_model_3d (WindowHandle, [objectsOut,ObjectModel3DRigidTrans], [], [], ['colorsOut','color_88'], ['colorvaluesOut','red'], [], [], [], PoseOut2)
    
    * 二维显示
    dev_open_window (0, 0, 512, 512, 'black', WindowHandle1)
    dev_set_color ('red')
    gen_cross_contour_xld (Start, all_x, all_y, 3, 0.785398)


    基于当前的算法稳定不好,而且有些边缘处理粗糙。最好的做法是基于法向量求解。

     

     

    展开全文
  • 提取平面点云轮廓一. 基于凸包的凹点挖掘算法:1. 提取点云的凸包2. 计算凸包每条边的顶点的点密度(即该点 K 个临近点到该点的距离平均值)3. 如果顶点点密度大于所在边的长度的 X 倍,则删除该边,并从内部点中选择...
  • 由平面上点的二维坐标定位, 提出区域“十”字算法进行切片数据边界提取, 获取切片数据的最外层点, 将检测到的边界点存回原始三维数据源, 完成预处理过程。实验结果证明, 该算法对边界点具有较强的识别能力, 能够...
  • 其次利用整体最小二乘和加权主元分析法对随机抽样一致算法进行改进,并基于该改进算法,确定折边两侧点云平面,利用两侧点云边界特性探测建筑物折边。通过实例分析,可以确定该算法提取速度快、冗余度少,在无效点云...
  • 三维点云数据的预处理与圆特征提取方法的研究,特征包括边界,角点,圆等等。
  • 最后,通过对相邻点的辅助判断来优化提取效果,该判断用于将边界点分类到正确的平面中。 拟议的方法已通过三个不同的地面激光扫描仪(TLS)数据集进行了测试,结果表明,这种混合方法可加快建筑物立面的提取和召回...
  • 针对以往散乱点云特征提取算法存在尖锐特征点提取不完整以及无法保留模型边界点的问题,提出了一种多个判别参数混合方法的特征提取算法。对点云构建k-d tree,利用k-d tree建立点云K邻域;针对每个K邻域计算数据点...
  • 散乱点云的孔洞识别和边界提取算法研究王春香,孟宏,张勇【摘要】针对逆向工程中已有孔洞识别算法执行效率低、孔洞边界点提取不完整等问题,提出一种新的基于KD树和K邻域搜索的点云孔洞识别及边界提取算法。...
  • 降采样后点云 提取边界点云,绿色为物体边界,阴影边界为青色,红色为veil点集(被这遮挡物边界和阴影边界内插点)
  • 边界提取采用PCL库里边的方法,基于法线估计来实现的边界检测与提取: 首先从原始点云上计算出法线,再由法线结合数据估计出边界。(这样解释还是特别抽像吧) ------------法线求解:(平面的法线是垂直于它的...
  • 针对现有的相似材料实验观测方法获取破裂区域信息效率低以及数据处理复杂的缺点,根据模型点云呈面分布的特征,提出一种图像辅助激光点云的相似材料模型破裂边界提取方法.将模型点云栅格化生成合成图像,并建立点云与...

空空如也

空空如也

1 2 3 4 5
收藏数 98
精华内容 39
关键字:

点云边界提取