pcl最小图割的分割

一、概念

最小图割的分割:

图论中的最小割(min-cut)广泛应用在网络规划,求解桥问题,图像分割等领域。 最小割算法是图论中的一个概念,其作用是以某种方式,将两个点分开,当然这两个点中间可能是通过无数的点再相连的。

如果要分开最左边的点和最右边的点,红绿两种割法都是可行的,但是红线跨过了三条线,绿线只跨过了两条。单从跨线数量上来论 可以得出绿线这种切割方法更优的结论。但假设线上有不同的权值,那么最优切割则和权值有关了。

算法主要思想:

1.建图:对于给定的点云,算法将包含点云中每一个点的图构造为一组普通顶点和另外两个称为源点和汇点的 顶点。每个普通顶点都有边缘,将对应的点与其最近的邻居连接起来。

2.算法为每条边缘分配权重。有三种不同的权:首先,它将权重分配到云点之间的边缘。这个权重称为平滑成本,

由公式计算:

                                              

这里dist是点之间的距离。距离点越远,边被切割的可能性就越大。

3. 算法设置数据成本。它包括前景和背景惩罚。第一个是将云点与源顶点连接起来并具有用户定义的常量值的边缘的权重。

4. 对点云的前景点和背景点进行划分

二、实践1

#include <iostream>

#include <vector>

#include <pcl/point_types.h>

#include <pcl/io/pcd_io.h>

#include <pcl/search/search.h>

#include <pcl/search/kdtree.h>

#include <pcl/visualization/cloud_viewer.h>

#include <pcl/filters/passthrough.h>

#include <pcl/segmentation/region_growing_rgb.h>

#include <pcl/console/print.h>

#include <pcl/console/parse.h>

#include <pcl/console/time.h>

#include <pcl/features/normal_3d.h>

using namespace pcl::console;

int

main (int argc, char** argv)

{



    if(argc<2)

    {

         std::cout<<".exe xx.pcd -b_n 0 -kn 50 -st_n 30 -ct_n 0.05 -bc 0 -fc 10 -nc 0 -dt 10 -ct 6 -rt 5 -mt 600" <<endl;



         return 0;

    }

    time_t start,end,diff[5],option;

    start = time(0);

    bool Bool_Cuting=false,b_n=false;

    int MinClusterSize=600,KN_normal=50;

    float far_cuting=10,near_cuting=0,DistanceThreshold=10.0,ColorThreshold=6,RegionColorThreshold=5,SmoothnessThreshold=30.0,CurvatureThreshold=0.05;



    parse_argument (argc, argv, "-b_n", b_n);

    parse_argument (argc, argv, "-kn", KN_normal);

    parse_argument (argc, argv, "-st_n", SmoothnessThreshold);

    parse_argument (argc, argv, "-ct_n", CurvatureThreshold);





    parse_argument (argc, argv, "-bc", Bool_Cuting);

    parse_argument (argc, argv, "-fc", far_cuting);

    parse_argument (argc, argv, "-nc", near_cuting);

   

    parse_argument (argc, argv, "-dt", DistanceThreshold);

    parse_argument (argc, argv, "-ct", ColorThreshold);

    parse_argument (argc, argv, "-rt", RegionColorThreshold);

    parse_argument (argc, argv, "-mt", MinClusterSize);







    //frist step load the point cloud

    pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZRGB>);

    if ( pcl::io::loadPCDFile <pcl::PointXYZRGB> (argv[1], *cloud) == -1)

    {

         std::cout << "Cloud reading failed." << std::endl;

         return (-1);

    }

    end = time(0);

    diff[0] = difftime (end, start);

    PCL_INFO ("\Loading pcd file takes(seconds): %d\n", diff[0]);

    pcl::search::Search <pcl::PointXYZRGB>::Ptr tree = boost::shared_ptr<pcl::search::Search<pcl::PointXYZRGB> > (new pcl::search::KdTree<pcl::PointXYZRGB>);



    //Noraml estimation step(1 parameter)

    pcl::search::Search<pcl::PointXYZRGB>::Ptr tree1 = boost::shared_ptr<pcl::search::Search<pcl::PointXYZRGB> > (new pcl::search::KdTree<pcl::PointXYZRGB>);

    pcl::PointCloud <pcl::Normal>::Ptr normals (new pcl::PointCloud <pcl::Normal>);

    pcl::NormalEstimation<pcl::PointXYZRGB, pcl::Normal> normal_estimator;

    normal_estimator.setSearchMethod (tree);

    normal_estimator.setInputCloud (cloud);

    normal_estimator.setKSearch (KN_normal);

    normal_estimator.compute (*normals);

    end = time(0);

    diff[1] = difftime (end, start)-diff[0];

    PCL_INFO ("\Estimating normal takes(seconds): %d\n", diff[1]);

    //Optional step: cutting away the clutter scene too far away from camera

    pcl::IndicesPtr indices (new std::vector <int>);

    if(Bool_Cuting)//是否通过z轴范围对待处理数据进行裁剪

    {



         pcl::PassThrough<pcl::PointXYZRGB> pass;

         pass.setInputCloud (cloud);

         pass.setFilterFieldName ("z");

         pass.setFilterLimits (near_cuting, far_cuting);

         pass.filter (*indices);

    }

    // Region growing RGB

    pcl::RegionGrowingRGB<pcl::PointXYZRGB> reg;

    reg.setInputCloud (cloud);

    if(Bool_Cuting)reg.setIndices (indices);

    reg.setSearchMethod (tree);

    reg.setDistanceThreshold (DistanceThreshold);

    reg.setPointColorThreshold (ColorThreshold);

    reg.setRegionColorThreshold (RegionColorThreshold);

    reg.setMinClusterSize (MinClusterSize);

    if(b_n)

    {

         reg.setSmoothModeFlag(true);

         reg.setCurvatureTestFlag(true);



         reg.setInputNormals (normals);

         reg.setSmoothnessThreshold (SmoothnessThreshold / 180.0 * M_PI);

         reg.setCurvatureThreshold (CurvatureThreshold);

    }

    std::vector <pcl::PointIndices> clusters;

    reg.extract (clusters);

    end = time(0);

    diff[2] = difftime (end, start)-diff[0]-diff[1];

    PCL_INFO ("\RGB region growing takes(seconds): %d\n", diff[2]);



    pcl::PointCloud <pcl::PointXYZRGB>::Ptr colored_cloud = reg.getColoredCloud ();

    pcl::visualization::CloudViewer viewer ("点云库PCL学习教程第二版实例-基于颜色的区域生长算法实现点云分割");

    viewer.showCloud (colored_cloud);

    while (!viewer.wasStopped ())

    {

         boost::this_thread::sleep (boost::posix_time::microseconds (100));

    }



    return (0);

}

CMakeLists文件如下所示:

cmake_minimum_required(VERSION 2.8 FATAL_ERROR)

project(color_based_region_growing_segmentation)

find_package(PCL 1.5 REQUIRED)

include_directories(${PCL_INCLUDE_DIRS})

link_directories(${PCL_LIBRARY_DIRS})

add_definitions(${PCL_DEFINITIONS})

add_executable (min_cut_segmentation min_cut_segmentation.cpp)

target_link_libraries (min_cut_segmentation ${PCL_LIBRARIES})

Ubuntu20.04下的执行命令行如下:

./min_cut_segmentation min_cut_segmentation_tutorial.pcd -bc 1 -fc 1.0 -nc 0 -cx 68.97 -cy -18.55 -cz 0.57 -s 0.25 -r 3.0433856 -non 14 -sw 0.8

执行效果如下所示:

三、实践2

#include <string>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/search/organized.h>
#include <pcl/search/kdtree.h>
#include <pcl/features/normal_3d_omp.h>//利用多线程计算法向量相关头文件
#include <pcl/filters/conditional_removal.h>
#include <pcl/segmentation/extract_clusters.h>
#include <pcl/segmentation/impl/extract_clusters.hpp>

#include <pcl/features/don.h>
 // for visualization
#include <pcl/visualization/pcl_visualizer.h>

using namespace pcl;
using namespace std;


pcl::PointCloud<pcl::PointXYZRGB>::Ptr getColoredCloud(pcl::PointCloud<pcl::PointXYZ>::Ptr input_, std::vector <pcl::PointIndices> clusters_, float r, float g, float b)
{
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr colored_cloud;

	if (!clusters_.empty())
	{
		colored_cloud = (new pcl::PointCloud<pcl::PointXYZRGB>)->makeShared();

		srand(static_cast<unsigned int> (time(0)));
		std::vector<unsigned char> colors;
		for (size_t i_segment = 0; i_segment < clusters_.size(); i_segment++)
		{
			colors.push_back(static_cast<unsigned char> (rand() % 256));
			colors.push_back(static_cast<unsigned char> (rand() % 256));
			colors.push_back(static_cast<unsigned char> (rand() % 256));
		}

		colored_cloud->width = input_->width;
		colored_cloud->height = input_->height;
		colored_cloud->is_dense = input_->is_dense;
		for (size_t i_point = 0; i_point < input_->points.size(); i_point++)
		{
			pcl::PointXYZRGB point;
			point.x = *(input_->points[i_point].data);
			point.y = *(input_->points[i_point].data + 1);
			point.z = *(input_->points[i_point].data + 2);
			point.r = r;
			point.g = g;
			point.b = b;
			colored_cloud->points.push_back(point);
		}

		std::vector< pcl::PointIndices >::iterator i_segment;
		int next_color = 0;
		for (i_segment = clusters_.begin(); i_segment != clusters_.end(); i_segment++)
		{
			std::vector<int>::iterator i_point;
			for (i_point = i_segment->indices.begin(); i_point != i_segment->indices.end(); i_point++)
			{
				int index;
				index = *i_point;
				colored_cloud->points[index].r = colors[3 * next_color];
				colored_cloud->points[index].g = colors[3 * next_color + 1];
				colored_cloud->points[index].b = colors[3 * next_color + 2];
			}
			next_color++;
		}
	}

	return (colored_cloud);
}

int
main(int argc, char *argv[])
{
	int VISUAL = 1, SAVE = 0;//0 indicate shows nothing, 1 indicate shows very step output 2 only shows the final results
  ///The smallest scale to use in the DoN filter.
	double scale1, mean_radius;

	///The largest scale to use in the DoN filter.
	double scale2;

	///The minimum DoN magnitude to threshold by
	double threshold;

	///segment scene into clusters with given distance tolerance using euclidean clustering
	double segradius;//欧几里得聚类分割的阈值

	if (argc < 6)
	{
		cerr << "usage: " << argv[0] << " inputfile smallscale(5) largescale(10) threshold(0.1) segradius(1.5) VISUAL(1) SAVE(0)" << endl;
		cerr << "usage: " << "smallscale largescale  segradius :multiple with mean radius of point cloud " << endl;
		exit(EXIT_FAILURE);
	}

	/// the file to read from.
	string infile = argv[1];
	/// small scale
	istringstream(argv[2]) >> scale1;
	/// large scale
	istringstream(argv[3]) >> scale2;
	istringstream(argv[4]) >> threshold;   // threshold for DoN magnitude,条件滤波器的阈值
	istringstream(argv[5]) >> segradius;   // threshold for radius segmentation
	istringstream(argv[6]) >> VISUAL;
	istringstream(argv[7]) >> SAVE;
	// Load cloud in blob format
	pcl::PointCloud<PointXYZRGB>::Ptr cloud(new pcl::PointCloud<PointXYZRGB>);
	pcl::io::loadPCDFile(infile.c_str(), *cloud);
	// Create a search tree, use KDTreee for non-organized data.
	//为无序点云创建一个搜索方式
	pcl::search::Search<PointXYZRGB>::Ptr tree;
	if (cloud->isOrganized())
	{
		tree.reset(new pcl::search::OrganizedNeighbor<PointXYZRGB>());
	}
	else
	{
		tree.reset(new pcl::search::KdTree<PointXYZRGB>(false));
	}

	// Set the input pointcloud for the search tree
	tree->setInputCloud(cloud);
	//caculate the mean radius of cloud and mutilply with corresponding input
	{
		int size_cloud = cloud->size();
		int step = size_cloud / 10;
		double total_distance = 0;
		int i, j = 1;
		for (i = 0; i < size_cloud; i += step, j++)
		{
			std::vector<int> pointIdxNKNSearch(2);
			std::vector<float> pointNKNSquaredDistance(2);
			tree->nearestKSearch(cloud->points[i], 2, pointIdxNKNSearch, pointNKNSquaredDistance);//搜索最近邻的两个点
			total_distance += pointNKNSquaredDistance[1] + pointNKNSquaredDistance[0];//增加第一个点和第二个点与中心点之间的距离
		}
		mean_radius = sqrt((total_distance / j));
		cout << "mean radius of cloud is: " << mean_radius << endl;
		scale1 *= mean_radius;//小尺度辅助半径
		scale2 *= mean_radius;//大尺度辅助半径
		segradius *= mean_radius;
	}


	if (scale1 >= scale2)
	{
		cerr << "Error: Large scale must be > small scale!" << endl;
		exit(EXIT_FAILURE);
	}

	// Compute normals using both small and large scales at each point
	//对每一个点分别使用小尺度和大尺度进行计算法线
	pcl::NormalEstimationOMP<PointXYZRGB, PointNormal> ne;
	ne.setInputCloud(cloud);
	ne.setSearchMethod(tree);

	/**
	 * NOTE: setting viewpoint is very important, so that we can ensure
	 * normals are all pointed in the same direction!
	 */
	ne.setViewPoint(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());//随机设定视点的坐标

	// calculate normals with the small scale
	cout << "Calculating normals for scale..." << scale1 << endl;
	pcl::PointCloud<PointNormal>::Ptr normals_small_scale(new pcl::PointCloud<PointNormal>);

	ne.setRadiusSearch(scale1);
	ne.compute(*normals_small_scale);//对输入点集利用小尺度计算法向量

	// calculate normals with the large scale
	cout << "Calculating normals for scale..." << scale2 << endl;
	pcl::PointCloud<PointNormal>::Ptr normals_large_scale(new pcl::PointCloud<PointNormal>);

	ne.setRadiusSearch(scale2);
	ne.compute(*normals_large_scale);//对输入点集利用大尺度计算法向量
	//visualize the normals
	if (VISUAL = 1)
	{
		cout << "click q key to quit the visualizer and continue" << endl;
		boost::shared_ptr<pcl::visualization::PCLVisualizer> MView(new pcl::visualization::PCLVisualizer("Showing normals with different scale"));
		pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZRGB> green(cloud, 0, 255, 0);
		int v1(0), v2(0);
		MView->createViewPort(0.0, 0.0, 0.5, 1.0, v1);
		MView->createViewPort(0.5, 0.0, 1.0, 1.0, v2);
		MView->setBackgroundColor(1, 1, 1, v1);
		MView->setBackgroundColor(255, 0, 0, v2);
		MView->addPointCloud(cloud, green, "small_scale", v1);
		MView->addPointCloud(cloud, green, "large_scale", v2);
		MView->addPointCloudNormals<pcl::PointXYZRGB, pcl::PointNormal>(cloud, normals_small_scale, 100, mean_radius * 10, "small_scale_normal");
		MView->addPointCloudNormals<pcl::PointXYZRGB, pcl::PointNormal>(cloud, normals_large_scale, 100, mean_radius * 10, "large_scale_normal");
		MView->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 8, "small_scale", v1);//设置小尺度点云的大小
		MView->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, 0, "small_scale", v1);//设置小尺度点云的透明度
		MView->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "large_scale", v1);//设置大尺度点云的大小
		MView->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, 0.5, "large_scale", v1);//设置大尺度点云的透明度
		MView->spin();

	}
	// Create output cloud for DoN results
	//创建一个对象存放DoN的结果
	PointCloud<PointNormal>::Ptr doncloud(new pcl::PointCloud<PointNormal>);
	copyPointCloud<PointXYZRGB, PointNormal>(*cloud, *doncloud);

	cout << "Calculating DoN... " << endl;
	// Create DoN operator
	pcl::DifferenceOfNormalsEstimation<PointXYZRGB, PointNormal, PointNormal> don;
	don.setInputCloud(cloud);
	don.setNormalScaleLarge(normals_large_scale);
	don.setNormalScaleSmall(normals_small_scale);

	//检查计算特征向量的要求是否得到满足
	if (!don.initCompute())
	{
		std::cerr << "Error: Could not intialize DoN feature operator" << std::endl;
		exit(EXIT_FAILURE);
	}

	// Compute DoN 计算DoN特征向量
	don.computeFeature(*doncloud);//对输入点集,计算每一个点的DoN特征向量,并输出。结果保存至doncloud


	//print some differencense of curvature
	{
		cout << "You may have some sense about the input threshold (curvature) next time for your data" << endl;
		int size_cloud = doncloud->size();
		int step = size_cloud / 10;
		for (int i = 0; i < size_cloud; i += step)
			cout << " " << doncloud->points[i].curvature << " " << endl;

	}

	//show the differences of curvature with both large and small scale 
	if (VISUAL = 1)
	{
		cout << "click q key to quit the visualizer and continue" << endl;
		boost::shared_ptr<pcl::visualization::PCLVisualizer> MView(new pcl::visualization::PCLVisualizer("Showing the difference of curvature of two scale"));
		pcl::visualization::PointCloudColorHandlerGenericField<pcl::PointNormal> handler_k(doncloud, "curvature");
		MView->setBackgroundColor(1, 1, 1);
		MView->addPointCloud(doncloud, handler_k); //显示估计出的法线
		MView->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3);
		MView->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, 0.5);
		MView->spin();
	}


	// Save DoN features
	pcl::PCDWriter writer;
	if (SAVE == 1) writer.write<pcl::PointNormal>("don.pcd", *doncloud, false);


	// Filter by magnitude
	cout << "Filtering out DoN mag <= " << threshold << "..." << endl;

	// Build the condition for filtering
	//创建条件滤波对象
	pcl::ConditionOr<PointNormal>::Ptr range_cond(
		new pcl::ConditionOr<PointNormal>()
	);
	range_cond->addComparison(pcl::FieldComparison<PointNormal>::ConstPtr(
		new pcl::FieldComparison<PointNormal>("curvature", pcl::ComparisonOps::GT, threshold))
	);//添加滤波条件,曲率高于阈值滤出
// Build the filter
//创建一个条件滤波器

/*显示说设置条件的方法被抛弃了
pcl::ConditionalRemoval<PointNormal> condrem (range_cond);//参数为之前创建的条件
 */
 //新的设置条件滤波器初始值的方法
	pcl::ConditionalRemoval<PointNormal> condrem;
	condrem.setCondition(range_cond);

	condrem.setInputCloud(doncloud);//设置输入点云don特征向量

	pcl::PointCloud<PointNormal>::Ptr doncloud_filtered(new pcl::PointCloud<PointNormal>);

	// Apply filter
	condrem.filter(*doncloud_filtered);//滤波之后的数据集保存至doncloud_filtered

	doncloud = doncloud_filtered;//条件滤波之后的点云

	// Save filtered output
	std::cout << "Filtered Pointcloud: " << doncloud->points.size() << " data points." << std::endl;

	if (SAVE == 1)writer.write<pcl::PointNormal>("don_filtered.pcd", *doncloud, false);


	//show the results of keeping relative small curvature points
	//显示曲率小的点
	if (VISUAL == 1)
	{
		cout << "click q key to quit the visualizer and continue" << endl;
		boost::shared_ptr<pcl::visualization::PCLVisualizer> MView(new pcl::visualization::PCLVisualizer("Showing the results of keeping relative small curvature points"));
		pcl::visualization::PointCloudColorHandlerGenericField<pcl::PointNormal> handler_k(doncloud, "curvature");
		MView->setBackgroundColor(1, 1, 1);
		MView->addPointCloud(doncloud, handler_k);
		MView->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3);
		MView->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, 0.5);
		MView->spin();
	}

	// Filter by magnitude
	//使用欧几里德聚类将场景分割成具有给定距离容差的聚类
	cout << "Clustering using EuclideanClusterExtraction with tolerance <= " << segradius << "..." << endl;

	pcl::search::KdTree<PointNormal>::Ptr segtree(new pcl::search::KdTree<PointNormal>);//设置无序点云的空间检索方式
	segtree->setInputCloud(doncloud);//输入点云为条件滤波之后的点云

	std::vector<pcl::PointIndices> cluster_indices;//聚类的索引,每一个元素都是一个聚类
	pcl::EuclideanClusterExtraction<PointNormal> ec;//实例化一个欧式聚类分割对象

	ec.setClusterTolerance(segradius);//设置近邻搜索的搜索半径
	ec.setMinClusterSize(50);//一个聚类的最少点数
	ec.setMaxClusterSize(100000);//一个聚类的最多点数
	ec.setSearchMethod(segtree);//设置检索方式
	ec.setInputCloud(doncloud);//设置输入点云
	ec.extract(cluster_indices);//将聚类的点云索引保存至cluster_indices
	if (VISUAL == 1)
	{//visualize the clustering results
		pcl::PointCloud <pcl::PointXYZ>::Ptr tmp_xyz(new pcl::PointCloud<pcl::PointXYZ>);
		copyPointCloud<pcl::PointNormal, pcl::PointXYZ>(*doncloud, *tmp_xyz);
		pcl::PointCloud <pcl::PointXYZRGB>::Ptr colored_cloud = getColoredCloud(tmp_xyz, cluster_indices, 0, 255, 0);//调用开头定义的函数

		cout << "click q key to quit the visualizer and continue!!" << endl;
		boost::shared_ptr<pcl::visualization::PCLVisualizer> MView(new pcl::visualization::PCLVisualizer("visualize the clustering results"));
		pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGB> rgbps(colored_cloud);
		MView->setBackgroundColor(1, 1, 1);
		MView->addPointCloud(colored_cloud, rgbps);
		MView->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3);
		MView->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, 0.5);
		MView->spin();

	}
	if (SAVE == 1)
	{
		std::cerr << cluster_indices.size() << " clusters." << std::endl;
		std::cout << "---------------------------------------------------------------------" << std::endl;
		int j = 0;
		for (std::vector<pcl::PointIndices>::const_iterator it = cluster_indices.begin(); it != cluster_indices.end(); ++it, j++)//第一层循环,创建一个迭代器依次访问cluster_indices中的每一个聚类
		{
			pcl::PointCloud<PointNormal>::Ptr cloud_cluster_don(new pcl::PointCloud<PointNormal>);
			for (std::vector<int>::const_iterator pit = it->indices.begin(); pit != it->indices.end(); ++pit)//使用一个迭代器,对每一个聚类中的所有点依次访问,并进行强制类型转换
			{
				cloud_cluster_don->points.push_back(doncloud->points[*pit]);
			}

			cloud_cluster_don->width = int(cloud_cluster_don->points.size());
			cloud_cluster_don->height = 1;
			cloud_cluster_don->is_dense = true;

			//Save cluster
			cout << " [" << j << "] PointCloud representing the Cluster: " << cloud_cluster_don->points.size() << " data points." << std::endl;
			stringstream ss;
			ss << "don_cluster_" << j << ".pcd";
			writer.write<pcl::PointNormal>(ss.str(), *cloud_cluster_don, false);
		}
}

}

CMakeLists文件和上面一样!

Ubuntu20.04下的执行命令行如下:

./don_segmentation region_growing_tutorial.pcd  6.5 8  0.1 1.5 1 0

屏幕打印信息:

mean radius of cloud is: 7.1868

Calculating normals for scale...46.7142

Calculating normals for scale...57.4944

click q key to quit the visualizer and continue

Calculating DoN...

You may have some sense about the input threshold (curvature) next time for your data

 0.0398626

 0.067342

 0.0130272

 0.0190825

 0.0759483

 0.0023054

 0.017644

 0.0289184

 0.00602476

 0.041621

 0.0623979

click q key to quit the visualizer and continue

Filtering out DoN mag <= 0.1...

Filtered Pointcloud: 8938 data points.

click q key to quit the visualizer and continue

Clustering using EuclideanClusterExtraction with tolerance <= 10.7802...

click q key to quit the visualizer and continue!

执行效果如下所示:

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙有肥鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值