3D点云标注

1 点云点选

  • 同时按下shift键和鼠标左键选择点云
  • 主要是利用registerPointPickingCallback来对点云上的点进行选择
  • 这里是设置了选择6的点生成矩形框(为什么是6个点,因为≤3个点时0BB框只是框选了一个平面,而且点选不是很灵敏有时点了它不显示所以多选一点也好,这个点数也可以自己设置)

2 点云框选

  • 按下x键可对点云进行框选,再次按x退出框选模式
  • 主要是利用setShapeRenderingProperties来对点云上的区域进行选择
  • 可以自己定义一些颜色为片选区域的点云赋色,并将已经框选的区域设为不可再次框选
  • 优点是框选比较方便,缺点是会选中框选射线区域内所有的点云
  • 对于可以用俯视图来标注的道路交通点云还好,对于不能单从一个视角来标注的就有些麻烦
  • point-cloud-annotation-tool用的大概就是这个方法,不过它可以调整标注框的大小还是很方便的

3 其他操作

  • 这些都是pcl vtk里的交互风格
  • shit+鼠标左键拖动点云可以实现对点云的平移
  • ctrl+鼠标左键拖动点云可以实现对点云的旋转
  • +、-号可以实现点云点的放大缩小
  • 按q退出

4 生成标注框

  • 利用每次点选和框选的点云生成AABB(绿色)以及OBB(红色)包围框
  • 并且在项目路径下分别保存每个AABB框和OBB框的长宽高以及几何中心坐标x,y,z

5 效果图

在这里插入图片描述
在这里插入图片描述

下图可以看出框选区域射线范围内点云都被选中

在这里插入图片描述
在这里插入图片描述

保存AABB和OBB框的长宽高和中心点坐标信息
在这里插入图片描述

6 完整代码

#include <pcl/io/pcd_io.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/common/common.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/features/moment_of_inertia_estimation.h>
#include <fstream>
#include <vector>

using namespace std;
typedef pcl::PointXYZ PointT;
typedef pcl::PointCloud<PointT> PointCloudT;

//pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
PointCloudT::Ptr cloud(new PointCloudT);
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("PointCloudLabel"));
int num = 0;

vector<PointT> v;
vector<vector<float>> AABB1;
vector<vector<float>> OBB1;
PointT p;
vector<float> p1;
vector<float> p2;

//定义颜色数组用于可视化
float colors[] = {
    
    
	255, 0,   0,   // red 		1
	0,   255, 0,   // green		2
	0,   0,   255, // blue		3
	255, 255, 0,   // yellow	4
	0,   255, 255, // light blue5
	255, 0,   255, // magenta   6
	255, 255, 255, // white		7
	255, 128, 0,   // orange	8
	255, 153, 255, // pink		9
	51,  153, 255, //			10
	153, 102, 51,  //			11
	128, 51,  153, //			12
	153, 153, 51,  //			13
	163, 38,  51,  //			14
	204, 153, 102, //			15
	204, 224, 255, //			16
	128, 179, 255, //			17
	206, 255, 0,   //			18
	255, 204, 204, //			19
	204, 255, 153, //			20
	34, 139, 34,
	139, 101, 8,
	131, 111, 255,
	99, 184, 255,
	0, 255, 127,
	82, 139, 139,
};
//计算点云密度
float Density(pcl::PointCloud<pcl::PointXYZ>::Ptr incloud, int k)
{
    
    
	pcl::KdTreeFLANN<pcl::PointXYZ> kdtree;  //创建一个快速k近邻查询,查询的时候若该点在点云中,则第一个近邻点是其本身
	kdtree.setInputCloud(incloud);
	//int k = 100;
	float everagedistance = 0;
	for (int i = 0; i < incloud->size() / 2; i++)
	{
    
    
		vector<int> nnh;
		vector<float> squaredistance;
		//  pcl::PointXYZ p;
		//   p = cloud->points[i];
		kdtree.nearestKSearch(incloud->points[i], k, nnh, squaredistance);
		everagedistance += sqrt(squaredistance[1]);
		//   cout<<everagedistance<<endl;
	}
	everagedistance = everagedistance / (incloud->size() / 2);
	//cout << "everage distance is : " << everagedistance << endl;
	return everagedistance;
}

void WriteAABB()
{
    
    
	ofstream out("AABB.txt");
	for (vector<vector<float>> ::iterator it = AABB1.begin(); it != AABB1.end(); ++it)
	{
    
    
		out << (*it)[0] << "  " << (*it)[1] << "  " << (*it)[2] << "  "
			<< (*it)[3] << "  " << (*it)[4] << "  " << (*it)[5] << endl;
	}
}
void WriteOBB()
{
    
    
	ofstream out("OBB.txt");
	for (vector<vector<float>> ::iterator it = OBB1.begin(); it != OBB1.end(); ++it)
	{
    
    
		out << (*it)[0] << "  " << (*it)[1] << "  " << (*it)[2] << "  "
			<< (*it)[3] << "  " << (*it)[4] << "  " << (*it)[5] << endl;
	}
}
int a = 0, b = 0, c = 1;
void WriteBox(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)
{
    
    
	p1.clear();
	p2.clear();
	//pcl::PointCloud<pcl::PointXYZ> cloud1;
	pcl::MomentOfInertiaEstimation <pcl::PointXYZ> feature_extractor;//实例化一个对象
	feature_extractor.setInputCloud(cloud);//设置输入点云
	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);//OBB对应的相关参数
	feature_extractor.getEigenValues(major_value, middle_value, minor_value);//三个特征值
	feature_extractor.getEigenVectors(major_vector, middle_vector, minor_vector);//三个特征向量
	feature_extractor.getMassCenter(mass_center);//计算质心

	//viewer->addCoordinateSystem(1.0);
	//viewer->resetCamera();
	//viewer->initCameraParameters();
	std::stringstream ss;
	std::string OBB;
	ss << a++;
	ss >> OBB;
	OBB += "_OBB";
	std::stringstream ss1;
	std::string AABB;
	ss1 << b++;
	ss1 >> AABB;
	AABB += "_AABB";
	//pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>(cloud, 0, 255, 0),
	//viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 5, "happy"+a);
	viewer->addCube(min_point_AABB.x, max_point_AABB.x, min_point_AABB.y, max_point_AABB.y, min_point_AABB.z, max_point_AABB.z, 0.0, 1.0, 0.0, AABB);
	viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, 0.1, AABB);//图形的不透明度
	viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, 4, AABB);//线宽
	Eigen::Vector3f position(position_OBB.x, position_OBB.y, position_OBB.z);
	std::cout << "---第 " << c << " 个框---" << std::endl;
	std::cout << "size of cloud :" << cloud->points.size() << endl;
	std::cout << "position_OBB: " << position_OBB << endl;
	//std::cout << "mass_center: " << mass_center << endl;//中心坐标
	Eigen::Quaternionf quat(rotational_matrix_OBB);
	// position:中心位置;quat:旋转矩阵
	viewer->addCube(position, quat, max_point_OBB.x - min_point_OBB.x, max_point_OBB.y - min_point_OBB.y, max_point_OBB.z - min_point_OBB.z, OBB);
	viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 0, 0, OBB);
	viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, 0.1, OBB);
	viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, 4, OBB);
	viewer->setRepresentationToWireframeForAllActors();//将所有actor的可视化表示更改为线框表示
	pcl::PointXYZ center(mass_center(0), mass_center(1), mass_center(2));
	// 获取主轴major_vector,中轴middle_vector,辅助轴minor_vector
	pcl::PointXYZ x_axis(major_vector(0) + mass_center(0), major_vector(1) + mass_center(1), major_vector(2) + mass_center(2));
	pcl::PointXYZ y_axis(middle_vector(0) + mass_center(0), middle_vector(1) + mass_center(1), middle_vector(2) + mass_center(2));
	pcl::PointXYZ z_axis(minor_vector(0) + mass_center(0), minor_vector(1) + mass_center(1), minor_vector(2) + mass_center(2));
	//viewer->addLine(center, x_axis, 1.0f, 0.0f, 0.0f, "major eigen vector");
	//viewer->addLine(center, y_axis, 0.0f, 1.0f, 0.0f, "middle eigen vector");
	//viewer->addLine(center, z_axis, 0.0f, 0.0f, 1.0f, "minor eigen vector");

	//std::cout << "moment_of_inertia :" << moment_of_inertia.size() << endl;
	//std::cout << "eccentricity :" << eccentricity.size() << endl;
	std::cout << "OBB:center :" << center << endl;
	std::cout << "OBB:长宽高:(" << max_point_OBB.x - min_point_OBB.x << ", " << max_point_OBB.y - min_point_OBB.y << ", " << max_point_OBB.z - min_point_OBB.z << ")" << endl;
	std::cout << "AABB:center;(" << (min_point_AABB.x + max_point_AABB.x) / 2 << "," << (min_point_AABB.y + max_point_AABB.y) / 2 << "," << (min_point_AABB.z + max_point_AABB.z) / 2 << ")" << endl;
	std::cout << "AABB:长宽高:(" << max_point_AABB.x - min_point_AABB.x << "," << max_point_AABB.y - min_point_AABB.y << "," << max_point_AABB.z - min_point_AABB.z << ")" << endl;
	//std::cout << "center :(" << (max_point_OBB.x- min_point_OBB.x)/2
	//	<< ","<< (max_point_OBB.y - min_point_OBB.y) / 2 
	//	<< "," << (max_point_OBB.z - min_point_OBB.z) / 2 <<")"<< endl;
	//cloud1 = *cloud;
	p1.push_back(max_point_AABB.x - min_point_AABB.x);
	p1.push_back(max_point_AABB.y - min_point_AABB.y);
	p1.push_back(max_point_AABB.z - min_point_AABB.z);
	p1.push_back((min_point_AABB.x + max_point_AABB.x) / 2);
	p1.push_back((min_point_AABB.y + max_point_AABB.y) / 2);
	p1.push_back((min_point_AABB.z + max_point_AABB.z) / 2);
	AABB1.push_back(p1);
	p2.push_back(max_point_OBB.x - min_point_OBB.x);
	p2.push_back(max_point_OBB.y - min_point_OBB.y);
	p2.push_back(max_point_OBB.z - min_point_OBB.z);
	p2.push_back(center.x);
	p2.push_back(center.y);
	p2.push_back(center.z);
	OBB1.push_back(p2);
	cloud->clear();
	c++;
	//v.clear();
}

int i = 0;
unsigned char *pointmark;//点云分类标记
void pp_callback_AreaSelect(const pcl::visualization::AreaPickingEvent& event, void* args)
{
    
    
	pcl::PointCloud<PointT>::Ptr clicked_points_3d(new PointCloudT);
	//存储索引
	vector< int > indices;
	if (event.getPointsIndices(indices) == -1)
		return;
	for (int i = 0; i < indices.size(); ++i)
	{
    
    
		if (pointmark[indices[i]] == 0)
		{
    
    
			clicked_points_3d->points.push_back(cloud->points.at(indices[i]));
			a++;
			pointmark[indices[i]] = 1;//将点云标记为已圈选状态
		}
	}
	pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> red(clicked_points_3d, colors[i * 3], colors[i * 3 + 1], colors[i * 3 + 2]);
	std::stringstream ss2;
	std::string cloudName;
	ss2 << num++;
	ss2 >> cloudName;
	cloudName += "_cloudName";
	viewer->addPointCloud(clicked_points_3d, red, cloudName);
	//viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 10, cloudName);
	WriteBox(clicked_points_3d);
	//保存点云
	//pcl::io::savePCDFileBinary(cloudName += ".pcd", *clicked_points_3d);
	i++;
	//std::cout << "第 " << i << " 个类别" << std::endl;
	//std::cout << "该类点数:" << a << std::endl;
}

pcl::PointCloud<PointT>::Ptr clicked_points_3d1(new PointCloudT);
void
pp_callback_PointsSelect(const pcl::visualization::PointPickingEvent& event, void* args)

{
    
    

	if (event.getPointIndex() == -1)
		return;
	PointT current_point;
	event.getPoint(current_point.x, current_point.y, current_point.z);
	//将选中的点云存在Points里面
	clicked_points_3d1->points.push_back(current_point);

	// 将选中的点赋为红色
	pcl::visualization::PointCloudColorHandlerCustom<PointT> red(clicked_points_3d1, 255, 0, 0);
	viewer->removePointCloud("clicked_points");
	viewer->addPointCloud(clicked_points_3d1, red, "clicked_points");
	viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 5, "clicked_points");
	//std::cout << current_point.x << " " << current_point.y << " " << current_point.z << std::endl;
	p.x = current_point.x;
	p.y = current_point.y;
	p.z = current_point.z;
	//v.push_back(p);
	if (clicked_points_3d1->size() > 5)
	{
    
    
		WriteBox(clicked_points_3d1);
	}

}
//void boxForCloud(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)
//{
    
    
//	//pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());
//	//if (pcl::io::loadPCDFile("F:/information/car_0001.pcd", *cloud) == -1)
//	//	return (-1);
//
//
//}


void main()

{
    
    
	float density = 0;
	std::string filename("../rabbit.pcd");
	if (pcl::io::loadPCDFile(filename, *cloud))
	{
    
    
		std::cerr << "ERROR: Cannot open file " << filename << "! Aborting..." << std::endl;
		return;
	}
	//getMinMax3D用到的头文件#include <pcl/common/common.h>
	pcl::PointXYZ p_min , p_max ;
	pcl::getMinMax3D(*cloud, p_min, p_max);
	density = Density(cloud, 50);
	std::cout << "点云点数:" << cloud->points.size() << std::endl;
	std::cout << "点云密度:" << density << std::endl;
	std::cout <<"min:"<< p_min << " max:" << p_max << std::endl;
	
	pointmark = new unsigned char[cloud->size()];
	for (size_t i = 0; i < cloud->size(); i++)
	{
    
    
		pointmark[i] = 0;//初始化点云分类标识,0为未标记点
	}
	//viewer->addPointCloud(cloud, "bunny");
	viewer->setBackgroundColor(0, 0, 0);
	viewer->addPointCloud<pcl::PointXYZ>(cloud, "sample cloud");
	//viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 5, "sample cloud");
	viewer->resetCamera();
	//viewer->setCameraPosition(0, 0, -2, 0, -1, 0, 0);
	viewer->registerPointPickingCallback(pp_callback_PointsSelect, (void*)&cloud);
	viewer->registerAreaPickingCallback(pp_callback_AreaSelect, (void*)&cloud);
	std::cout << "Shift+click on  points" << std::endl;
	std::cout << "press x to chose the area" << std::endl;
	viewer->spin();
	WriteAABB();//写出点数据
	WriteOBB();
	std::cout << "done." << std::endl;
	while (!viewer->wasStopped())
	{
    
    
		viewer->spinOnce(100);
		boost::this_thread::sleep(boost::posix_time::microseconds(100000));
	}

}


————————————————
版权声明:本文为CSDN博主「树和猫」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_68312479/article/details/126312813

猜你喜欢

转载自blog.csdn.net/weixin_44606139/article/details/127145030
今日推荐