摘要:#include#include//标准C++库中的输入输出类相关头文件。#include#include//p
环境vs2022+pcl1.14.1
操作说明:
框选工具:
B键:创建选择框
D键:删除选择框
Delete键:删除框内点云
框选操作:
[/]:缩放框大小
方向键:XY平面移动框
K/L键:Z轴方向移动框
视图控制:
鼠标左键拖动:旋转视图
鼠标滚轮:缩放视图
鼠标左键点击:选取点并显示坐标
显示信息:
左上角:显示框内点云数量
点击位置:显示选中点的坐标
#include#include//标准C++库中的输入输出类相关头文件。#include
#include
//pcd 读写类相关的头文件。#include
#include
//PCL中支持的点类型头文件。#include
#include
#include#include
#include
#include
#include
#include
#include
// 添加计算质心的头文件using std::cout;using std::endl;// 全局变量std::vector
::Ptr> selected_clouds;std::vector box_coordinates;bool has_box = false; // 标记是否已经创建了BOXbool box_being_modified = false; // 标记BOX是否正在被修改float box_scale = 0.1f; // BOX缩放步长float box_move_step = 0.1f; // BOX移动步长double last_mouse_x = 0, last_mouse_y = 0; // 上一次鼠标位置pcl::visualization::PCLVisualizer::Ptr g_viewer;pcl::PointCloud
::Ptr g_cloud;Eigen::Vector4f cloud_center; // 添加点云中心点坐标std::string points_count_text = "Points in box: 0"; // 添加点云数量显示文本// 更新BOX内点云数量显示void updatePointsCount { if (!has_box || box_coordinates.empty) { points_count_text = "Points in box: 0"; } else { const Eigen::Vector4f& min_pt = box_coordinates[0]; const Eigen::Vector4f& max_pt = box_coordinates[1]; int count = 0; // 计算BOX内的点数 for (const auto& point : g_cloud->points) { if (point.x >= min_pt[0] && point.x = min_pt[1] && point.y = min_pt[2] && point.z updateText(points_count_text, 10, 680, "points_count");}// 更新BOX显示void updateBoxDisplay(const Eigen::Vector4f& min_pt, const Eigen::Vector4f& max_pt) { g_viewer->removeShape("box"); g_viewer->addCube(min_pt[0], max_pt[0], min_pt[1], max_pt[1], min_pt[2], max_pt[2], 1.0, 0.0, 0.0, "box"); g_viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION, pcl::visualization::PCL_VISUALIZER_REPRESENTATION_WIREFRAME, "box"); g_viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0.0, 1.0, 0.0, "box"); // 更新点云数量显示 updatePointsCount;}// 点拾取回调函数void pointPickingCallback(const pcl::visualization::PointPickingEvent& event) { float x, y, z; if (event.getPointIndex != -1) { event.getPoint(x, y, z); cout addSphere(pcl::PointXYZ(x, y, z), 0.01, 1.0, 0.0, 0.0, sphere_name); // 添加文本标签 std::string text = "(" + std::to_string(x).substr(0, 4) + "," + std::to_string(y).substr(0, 4) + "," + std::to_string(z).substr(0, 4) + ")"; g_viewer->addText3D(text, pcl::PointXYZ(x, y, z), 0.02, 1.0, 1.0, 1.0, sphere_name + "_text"); }}// 删除BOX内的点云void deletePointsInBox { if (!has_box || box_coordinates.empty) return; // 创建新的点云来存储保留的点 pcl::PointCloud
::Ptr new_cloud(new pcl::PointCloud
); // 获取BOX的最小和最大点 const Eigen::Vector4f& min_pt = box_coordinates[0]; const Eigen::Vector4f& max_pt = box_coordinates[1]; // 遍历所有点,保留BOX外的点 for (const auto& point : g_cloud->points) { if (point.x max_pt[0] || point.y max_pt[1] || point.z max_pt[2]) { new_cloud->points.push_back(point); } } // 更新点云 new_cloud->width = new_cloud->points.size; new_cloud->height = 1; new_cloud->is_dense = false; // 更新显示 g_viewer->removePointCloud("cloud"); g_cloud = new_cloud; pcl::visualization::PointCloudColorHandlerRGBField
rgb(g_cloud); g_viewer->addPointCloud
(g_cloud, rgb, "cloud"); g_viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud");}// 键盘回调函数void keyboardCallback(const pcl::visualization::KeyboardEvent& event, void* viewer_void) { pcl::visualization::PCLVisualizer::Ptr viewer = *static_cast
(viewer_void); if (event.keyDown) { if ((event.getKeySym == "b" || event.getKeySym == "B") && !has_box) { // 创建BOX,中心在坐标原点 Eigen::Vector4f min_pt(-box_scale, -box_scale, -box_scale, 1.0); Eigen::Vector4f max_pt(box_scale, box_scale, box_scale, 1.0); updateBoxDisplay(min_pt, max_pt); box_coordinates.clear; box_coordinates.push_back(min_pt); box_coordinates.push_back(max_pt); has_box = true; } else if (event.getKeySym == "d" || event.getKeySym == "D") { // 删除BOX if (has_box) { viewer->removeShape("box"); box_coordinates.clear; has_box = false; } } else if (event.getKeySym == "Delete") { // 删除BOX内的点云 deletePointsInBox; } else if (has_box) { if (event.getKeySym == "bracketright") { // ]键 // 增大BOX,保持中心在原点 float new_scale = box_coordinates[1][0] * (1.0f + box_scale); box_coordinates[0] = Eigen::Vector4f(-new_scale, -new_scale, -new_scale, 1.0f); box_coordinates[1] = Eigen::Vector4f(new_scale, new_scale, new_scale, 1.0f); updateBoxDisplay(box_coordinates[0], box_coordinates[1]); } else if (event.getKeySym == "bracketleft") { // [键 // 缩小BOX,保持中心在原点 float new_scale = box_coordinates[1][0] * (1.0f - box_scale); box_coordinates[0] = Eigen::Vector4f(-new_scale, -new_scale, -new_scale, 1.0f); box_coordinates[1] = Eigen::Vector4f(new_scale, new_scale, new_scale, 1.0f); updateBoxDisplay(box_coordinates[0], box_coordinates[1]); } // 移动BOX else if (event.getKeySym == "Up") { box_coordinates[0][1] += box_move_step; box_coordinates[1][1] += box_move_step; updateBoxDisplay(box_coordinates[0], box_coordinates[1]); } else if (event.getKeySym == "Down") { box_coordinates[0][1] -= box_move_step; box_coordinates[1][1] -= box_move_step; updateBoxDisplay(box_coordinates[0], box_coordinates[1]); } else if (event.getKeySym == "Left") { box_coordinates[0][0] -= box_move_step; box_coordinates[1][0] -= box_move_step; updateBoxDisplay(box_coordinates[0], box_coordinates[1]); } else if (event.getKeySym == "Right") { box_coordinates[0][0] += box_move_step; box_coordinates[1][0] += box_move_step; updateBoxDisplay(box_coordinates[0], box_coordinates[1]); } // 修改Z轴方向的移动控制键 else if (event.getKeySym == "k" || event.getKeySym == "K") { box_coordinates[0][2] += box_move_step; box_coordinates[1][2] += box_move_step; updateBoxDisplay(box_coordinates[0], box_coordinates[1]); } else if (event.getKeySym == "l" || event.getKeySym == "L") { box_coordinates[0][2] -= box_move_step; box_coordinates[1][2] -= box_move_step; updateBoxDisplay(box_coordinates[0], box_coordinates[1]); } } }}// 鼠标回调函数void mouseCallback(const pcl::visualization::MouseEvent& event, void* viewer_void) { if (event.getButton == pcl::visualization::MouseEvent::LeftButton) { if (event.getType == pcl::visualization::MouseEvent::MouseButtonPress) { last_mouse_x = event.getX; last_mouse_y = event.getY; } }}int main { g_cloud.reset(new pcl::PointCloud
); char strFilepath[256] = "d:\\rabbit.pcd"; if (-1 == pcl::io::loadPCDFile(strfilepath, *g_cloud)) { cout setBackgroundColor(0, 0, 0); g_viewer->addCoordinateSystem(1.0); g_viewer->initCameraParameters; // 设置点云颜色(根据高度) float min_z = FLT_MAX; float max_z = -FLT_MAX; for (const auto& point : g_cloud->points) { if (point.z max_z) max_z = point.z; } for (auto& point : g_cloud->points) { float normalized_z = (point.z - min_z) / (max_z - min_z); point.r = uint8_t(normalized_z * 255); point.g = 0; point.b = uint8_t((1 - normalized_z) * 255); } // 添加点云到可视化器并设置其属性 pcl::visualization::PointCloudColorHandlerRGBField
rgb(g_cloud); g_viewer->addPointCloud
(g_cloud, rgb, "cloud"); g_viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud"); // 计算点云的边界框 double min_pt[3] = {DBL_MAX, DBL_MAX, DBL_MAX}; double max_pt[3] = {-DBL_MAX, -DBL_MAX, -DBL_MAX}; for (const auto& point : g_cloud->points) { min_pt[0] = std::min(min_pt[0], (double)point.x); min_pt[1] = std::min(min_pt[1], (double)point.y); min_pt[2] = std::min(min_pt[2], (double)point.z); max_pt[0] = std::max(max_pt[0], (double)point.x); max_pt[1] = std::max(max_pt[1], (double)point.y); max_pt[2] = std::max(max_pt[2], (double)point.z); } // 计算点云的中心和大小 double center[3] = { (max_pt[0] + min_pt[0]) / 2.0, (max_pt[1] + min_pt[1]) / 2.0, (max_pt[2] + min_pt[2]) / 2.0 }; double size = std::max(std::max( max_pt[0] - min_pt[0], max_pt[1] - min_pt[1]), max_pt[2] - min_pt[2] ); // 设置更合适的相机参数 g_viewer->setCameraPosition( size * 2, size * 2, size * 2, // 相机位置 0, 0, 0, // 观察点(原点) 0, 0, 1 // 向上向量 ); g_viewer->setCameraClipDistances(size * 0.01, size * 100); // 设置更大范围的裁剪距离 // 计算点云中心点 cloud_center = Eigen::Vector4f::Zero; for (const auto& point : g_cloud->points) { cloud_center[0] += point.x; cloud_center[1] += point.y; cloud_center[2] += point.z; } cloud_center[0] /= g_cloud->points.size; cloud_center[1] /= g_cloud->points.size; cloud_center[2] /= g_cloud->points.size; cloud_center[3] = 1.0f; // 齐次坐标的w分量设为1 // 注册回调函数 g_viewer->registerKeyboardCallback(keyboardCallback, (void*)&g_viewer); g_viewer->registerMouseCallback(mouseCallback, (void*)&g_viewer); g_viewer->registerPointPickingCallback(pointPickingCallback); // 添加使用说明,增加行距 g_viewer->addText( "Controls:\n\n" "B: Add new box\n\n" "D: Delete box\n\n" "Delete: Delete points in box\n\n" "Left click: Pick point\n\n" "Mouse wheel: Zoom\n\n" "Left/Right drag: Rotate\n\n" "[/]: Scale box\n\n" "Arrow keys: Move box (XY)\n\n" "K/L: Move box (Z)", 10, 10, 15, 1.0, 1.0, 1.0, "help_text" // 增加字体大小为22 ); // 调整点云数量显示的位置到左上角 g_viewer->addText(points_count_text, 10, 680, 15, 1.0, 1.0, 1.0, "points_count"); // 为了处理大规模点云,设置八叉树 float resolution = 0.01f; pcl::octree::OctreePointCloudChangeDetector
octree(resolution); octree.setInputCloud(g_cloud); octree.addPointsFromInputCloud; // 主循环 while (!g_viewer->wasStopped) { g_viewer->spinOnce(100); } return 0;}
来源:新手村养牛