NARF关键点检测及SAC-IA粗配准

一、生成对应深度图

C++

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/common/io.h>
#include <pcl/range_image/range_image.h>
#include <pcl/visualization/range_image_visualizer.h>
#include <boost/thread/thread.hpp>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/common/common_headers.h>
using namespace std;int main(int, char** argv)
{pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);//要配准变化的点云pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_target(new pcl::PointCloud<pcl::PointXYZ>);//目标点云(不变的)if (pcl::io::loadPCDFile<pcl::PointXYZ>("pcd/pig_view1.pcd", *cloud) == -1){PCL_ERROR("加载点云失败\n");}if (pcl::io::loadPCDFile<pcl::PointXYZ>("pcd/pig_view2.pcd", *cloud_target) == -1){PCL_ERROR("加载点云失败\n");}pcl::RangeImage::CoordinateFrame coordinate_frame = pcl::RangeImage::CAMERA_FRAME;Eigen::Affine3f scene_sensor_pose_src(Eigen::Affine3f::Identity());Eigen::Affine3f scene_sensor_pose_tgt(Eigen::Affine3f::Identity());scene_sensor_pose_src = Eigen::Affine3f(Eigen::Translation3f(cloud->sensor_origin_[0],cloud->sensor_origin_[1], cloud->sensor_origin_[2])) * Eigen::Affine3f(cloud->sensor_orientation_);scene_sensor_pose_tgt = Eigen::Affine3f(Eigen::Translation3f(cloud_target->sensor_origin_[0],cloud_target->sensor_origin_[1], cloud_target->sensor_origin_[2])) * Eigen::Affine3f(cloud_target->sensor_orientation_);//-------------从点云创建深度图像———————————float noise_level = 0.0;float min_range = 0.0f;int border_size = 1;pcl::RangeImage::Ptr range_image_src(new pcl::RangeImage);pcl::RangeImage::Ptr range_image_tgt(new pcl::RangeImage);range_image_src->createFromPointCloud(*cloud, pcl::deg2rad(0.02f), pcl::deg2rad(0.02f),pcl::deg2rad(360.0f), pcl::deg2rad(180.0f), scene_sensor_pose_src, coordinate_frame,noise_level, min_range, border_size);range_image_tgt->createFromPointCloud(*cloud_target, pcl::deg2rad(0.02f), pcl::deg2rad(0.02f),pcl::deg2rad(360.0f), pcl::deg2rad(180.0f), scene_sensor_pose_tgt, coordinate_frame,noise_level, min_range, border_size);boost::shared_ptr <pcl::visualization::PCLVisualizer> viewer1(new pcl::visualization::PCLVisualizer("3D Viewer1"));//创建窗口,并设置名字为3D Viewerpcl::visualization::PointCloudColorHandlerCustom < pcl::PointWithRange>range_image_color_handler1(range_image_src, 0, 0, 0);viewer1->setBackgroundColor(1, 1, 1); viewer1->addPointCloud(range_image_src, range_image_color_handler1, "range image");viewer1->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "range image");while (!viewer1->wasStopped()){viewer1->spinOnce(100);boost::this_thread::sleep(boost::posix_time::microseconds(100));}boost::shared_ptr <pcl::visualization::PCLVisualizer> viewer2(new pcl::visualization::PCLVisualizer("3D Viewer2"));//创建窗口,并设置名字为3D Viewerpcl::visualization::PointCloudColorHandlerCustom < pcl::PointWithRange>range_image_color_handler2(range_image_tgt, 0, 0, 0);viewer2->setBackgroundColor(1, 1, 1);viewer2->addPointCloud(range_image_tgt, range_image_color_handler2, "range image");viewer2->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "range image");while (!viewer2->wasStopped()){viewer2->spinOnce(100);boost::this_thread::sleep(boost::posix_time::microseconds(100));}boost::shared_ptr <pcl::visualization::PCLVisualizer> viewer3(new pcl::visualization::PCLVisualizer("3D Viewer3"));//创建窗口,并设置名字为3D Viewerpcl::visualization::PointCloudColorHandlerCustom < pcl::PointWithRange>range_image_color_handler3(range_image_src, 255, 0, 0);pcl::visualization::PointCloudColorHandlerCustom < pcl::PointWithRange>range_image_color_handler4(range_image_tgt, 0, 0, 0);viewer3->setBackgroundColor(1, 1, 1);viewer3->addPointCloud(range_image_src, range_image_color_handler3, "range image1");viewer3->addPointCloud(range_image_tgt, range_image_color_handler4, "range image2");viewer3->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "range image1");viewer3->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "range image2");while (!viewer3->wasStopped()){viewer3->spinOnce(100);boost::this_thread::sleep(boost::posix_time::microseconds(100));}return 0;
}

关键代码解析:

pcl::RangeImage::CoordinateFrame coordinate_frame = pcl::RangeImage::CAMERA_FRAME;Eigen::Affine3f scene_sensor_pose_src(Eigen::Affine3f::Identity());Eigen::Affine3f scene_sensor_pose_tgt(Eigen::Affine3f::Identity());scene_sensor_pose_src = Eigen::Affine3f(Eigen::Translation3f(cloud->sensor_origin_[0],cloud->sensor_origin_[1], cloud->sensor_origin_[2])) * Eigen::Affine3f(cloud->sensor_orientation_);scene_sensor_pose_tgt = Eigen::Affine3f(Eigen::Translation3f(cloud_target->sensor_origin_[0],cloud_target->sensor_origin_[1], cloud_target->sensor_origin_[2])) * Eigen::Affine3f(cloud_target->sensor_orientation_);//-------------从点云创建深度图像———————————float noise_level = 0.0;float min_range = 0.0f;int border_size = 1;pcl::RangeImage::Ptr range_image_src(new pcl::RangeImage);pcl::RangeImage::Ptr range_image_tgt(new pcl::RangeImage);range_image_src->createFromPointCloud(*cloud, pcl::deg2rad(0.02f), pcl::deg2rad(0.02f),pcl::deg2rad(360.0f), pcl::deg2rad(180.0f), scene_sensor_pose_src, coordinate_frame,noise_level, min_range, border_size);range_image_tgt->createFromPointCloud(*cloud_target, pcl::deg2rad(0.02f), pcl::deg2rad(0.02f),pcl::deg2rad(360.0f), pcl::deg2rad(180.0f), scene_sensor_pose_tgt, coordinate_frame,noise_level, min_range, border_size);
  1. pcl::RangeImage::CoordinateFrame coordinate_frame = pcl::RangeImage::CAMERA_FRAME;:这里定义了深度图像的坐标系,常用的坐标系包括相机坐标系 (CAMERA_FRAME) 和激光雷达坐标系 (LASER_FRAME)。

  2. Eigen::Affine3f scene_sensor_pose_src(Eigen::Affine3f::Identity());Eigen::Affine3f scene_sensor_pose_tgt(Eigen::Affine3f::Identity());:创建了两个 Affine3f 类型的对象,用于表示点云的姿态(位置和方向)。

  3. 通过以下代码计算了源点云和目标点云的姿态(位置和方向):

    scene_sensor_pose_src = Eigen::Affine3f(Eigen::Translation3f(cloud->sensor_origin_[0],cloud->sensor_origin_[1], cloud->sensor_origin_[2])) * Eigen::Affine3f(cloud->sensor_orientation_);
    scene_sensor_pose_tgt = Eigen::Affine3f(Eigen::Translation3f(cloud_target->sensor_origin_[0],cloud_target->sensor_origin_[1], cloud_target->sensor_origin_[2])) * Eigen::Affine3f(cloud_target->sensor_orientation_);
    
  4. 设置了创建深度图像所需的参数:

    • float noise_level = 0.0;:噪声水平,表示深度图像中的噪声级别。
    • float min_range = 0.0f;:最小测量范围,表示深度图像中的最小测量距离。
    • int border_size = 1;:边界大小,表示深度图像周围的边界大小。
  5. 创建了两个深度图像对象的指针:pcl::RangeImage::Ptr range_image_src(new pcl::RangeImage);pcl::RangeImage::Ptr range_image_tgt(new pcl::RangeImage);

  6. 通过以下代码,使用点云数据创建深度图像:

    range_image_src->createFromPointCloud(*cloud, pcl::deg2rad(0.02f), pcl::deg2rad(0.02f),pcl::deg2rad(360.0f), pcl::deg2rad(180.0f), scene_sensor_pose_src, coordinate_frame,noise_level, min_range, border_size);
    range_image_tgt->createFromPointCloud(*cloud_target, pcl::deg2rad(0.02f), pcl::deg2rad(0.02f),pcl::deg2rad(360.0f), pcl::deg2rad(180.0f), scene_sensor_pose_tgt, coordinate_frame,noise_level, min_range, border_size);
    

在这里,参数的设置会影响深度图像的质量和内容。例如,noise_levelmin_rangeborder_size 参数会影响深度图像的噪声水平、最小测量范围以及边界大小。而角度参数 pcl::deg2rad() 用于将角度转换为弧度。

结果:

输入点云的深度图 

输出点云的深度图  

 

 输入点云和输出点云放在一起的深度图 ,为了区别,我把输入点云深度图颜色改成红色

 

二、 NARF关键点检测

C++

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/common/io.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/common/common_headers.h>
#include <pcl/range_image/range_image.h>
#include <pcl/features/range_image_border_extractor.h>
#include <pcl/keypoints/narf_keypoint.h>
using namespace std;int main(int, char** argv)
{pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);//要配准变化的点云pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_target(new pcl::PointCloud<pcl::PointXYZ>);//目标点云(不变的)if (pcl::io::loadPCDFile<pcl::PointXYZ>("pcd/pig_view1.pcd", *cloud) == -1){PCL_ERROR("加载点云失败\n");}if (pcl::io::loadPCDFile<pcl::PointXYZ>("pcd/pig_view2.pcd", *cloud_target) == -1){PCL_ERROR("加载点云失败\n");}cout << "读取原点云个数: " << cloud->points.size() << endl;cout << "读取目标点云个数: " << cloud_target->points.size() << endl;pcl::RangeImage::CoordinateFrame coordinate_frame = pcl::RangeImage::CAMERA_FRAME;Eigen::Affine3f scene_sensor_pose_src(Eigen::Affine3f::Identity());Eigen::Affine3f scene_sensor_pose_tgt(Eigen::Affine3f::Identity());scene_sensor_pose_src = Eigen::Affine3f(Eigen::Translation3f(cloud->sensor_origin_[0],cloud->sensor_origin_[1], cloud->sensor_origin_[2])) * Eigen::Affine3f(cloud->sensor_orientation_);scene_sensor_pose_tgt = Eigen::Affine3f(Eigen::Translation3f(cloud_target->sensor_origin_[0],cloud_target->sensor_origin_[1], cloud_target->sensor_origin_[2])) * Eigen::Affine3f(cloud_target->sensor_orientation_);//-------------从点云创建深度图像———————————float noise_level = 0.0;float min_range = 0.0f;int border_size = 1;pcl::RangeImage::Ptr range_image_src(new pcl::RangeImage);pcl::RangeImage::Ptr range_image_tgt(new pcl::RangeImage);range_image_src->createFromPointCloud(*cloud, pcl::deg2rad(0.02f), pcl::deg2rad(0.02f),pcl::deg2rad(360.0f), pcl::deg2rad(180.0f), scene_sensor_pose_src, coordinate_frame,noise_level, min_range, border_size);range_image_tgt->createFromPointCloud(*cloud_target, pcl::deg2rad(0.02f), pcl::deg2rad(0.02f),pcl::deg2rad(360.0f), pcl::deg2rad(180.0f), scene_sensor_pose_tgt, coordinate_frame,noise_level, min_range, border_size);//range_image->setUnseenToMaxRange();//设置不能观察到的点都为远距离点//-----------提取NARF关键点-------------pcl::RangeImage& range_image1 = *range_image_src;pcl::RangeImageBorderExtractor range_image_border_extractor1;pcl::PointCloud<pcl::PointXYZ>::Ptr keypoints1(new pcl::PointCloud<pcl::PointXYZ>());pcl::NarfKeypoint narf_keypoint_detector1(&range_image_border_extractor1);narf_keypoint_detector1.setRangeImage(&range_image1);//指定深度图narf_keypoint_detector1.getParameters().support_size = 0.2f;//指定搜索空间球体的半径,指定计算感兴趣值时所使用的领域范围narf_keypoint_detector1.getParameters().add_points_on_straight_edges = true;//指点是否添加垂直边缘上的点narf_keypoint_detector1.getParameters().distance_for_additional_points = 0.5;pcl::PointCloud<int>keypoint_indices1;narf_keypoint_detector1.compute(keypoint_indices1);//计算索引keypoints1->points.resize(keypoint_indices1.size());keypoints1->height = keypoint_indices1.height;keypoints1->width = keypoint_indices1.width;for (std::size_t i = 0; i < keypoint_indices1.points.size(); ++i){keypoints1->points[i].getVector3fMap() = range_image_src->points[keypoint_indices1.points[i]].getVector3fMap();}cout << "NARF points 的原点云提取结果为 " << keypoints1->points.size() << endl;pcl::RangeImage& range_image2 = *range_image_tgt;pcl::RangeImageBorderExtractor range_image_border_extractor2;pcl::PointCloud<pcl::PointXYZ>::Ptr keypoints2(new pcl::PointCloud<pcl::PointXYZ>());pcl::NarfKeypoint narf_keypoint_detector2(&range_image_border_extractor2);narf_keypoint_detector2.setRangeImage(&range_image2);//指定深度图narf_keypoint_detector2.getParameters().support_size = 0.2f;//指定搜索空间球体的半径,指定计算感兴趣值时所使用的领域范围narf_keypoint_detector2.getParameters().add_points_on_straight_edges = true;//指点是否添加垂直边缘上的点narf_keypoint_detector2.getParameters().distance_for_additional_points = 0.5;pcl::PointCloud<int>keypoint_indices2;narf_keypoint_detector2.compute(keypoint_indices2);//计算索引keypoints2->points.resize(keypoint_indices2.size());keypoints2->height = keypoint_indices2.height;keypoints2->width = keypoint_indices2.width;for (std::size_t i = 0; i < keypoint_indices2.points.size(); ++i){keypoints2->points[i].getVector3fMap() = range_image_tgt->points[keypoint_indices2.points[i]].getVector3fMap();}cout << "NARF points 的目标点云提取结果为 " << keypoints2->points.size() << endl;//关键点显示boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer1(new pcl::visualization::PCLVisualizer("V1"));viewer1->setBackgroundColor(0, 0, 0);viewer1->setWindowName("NARF Key point extraction");pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> single_color1(cloud, 0.0, 255, 0.0);viewer1->addPointCloud<pcl::PointXYZ>(keypoints1, single_color1, "key cloud");//特征点viewer1->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "key cloud");while (!viewer1->wasStopped()){viewer1->spinOnce(100);boost::this_thread::sleep(boost::posix_time::microseconds(100));}boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer2(new pcl::visualization::PCLVisualizer("V2"));viewer2->setBackgroundColor(0, 0, 0);viewer2->setWindowName("NARF Key point extraction");pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> single_color2(cloud, 0.0, 255, 0.0);viewer2->addPointCloud<pcl::PointXYZ>(keypoints2, single_color2, "key cloud");//特征点viewer2->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "key cloud");while (!viewer2->wasStopped()){viewer2->spinOnce(100);boost::this_thread::sleep(boost::posix_time::microseconds(100));}boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer3(new pcl::visualization::PCLVisualizer("V3"));viewer3->setBackgroundColor(0, 0, 0);viewer3->setWindowName("NARF Key point extraction");pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> single_color3(cloud, 0.0, 255, 0.0);viewer3->addPointCloud<pcl::PointXYZ>(cloud, single_color3, "sample cloud");viewer3->addPointCloud<pcl::PointXYZ>(keypoints1, "key cloud");//特征点viewer3->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "key cloud");viewer3->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1.0, 0.0, 0.0, "key cloud");while (!viewer3->wasStopped()){viewer3->spinOnce(100);boost::this_thread::sleep(boost::posix_time::microseconds(100));}boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer4(new pcl::visualization::PCLVisualizer("V4"));viewer4->setBackgroundColor(0, 0, 0);viewer4->setWindowName("NARF Key point extraction");pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> single_color4(cloud, 0.0, 255, 0.0);viewer4->addPointCloud<pcl::PointXYZ>(cloud_target, single_color4, "sample cloud");viewer4->addPointCloud<pcl::PointXYZ>(keypoints2, "key cloud");//特征点viewer4->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "key cloud");viewer4->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1.0, 0.0, 0.0, "key cloud");while (!viewer4->wasStopped()){viewer4->spinOnce(100);boost::this_thread::sleep(boost::posix_time::microseconds(100));}return 0;
}

关键代码解析: 

pcl::RangeImage& range_image1 = *range_image_src;pcl::RangeImageBorderExtractor range_image_border_extractor1;pcl::PointCloud<pcl::PointXYZ>::Ptr keypoints1(new pcl::PointCloud<pcl::PointXYZ>());pcl::NarfKeypoint narf_keypoint_detector1(&range_image_border_extractor1);narf_keypoint_detector1.setRangeImage(&range_image1);//指定深度图narf_keypoint_detector1.getParameters().support_size = 0.2f;//指定搜索空间球体的半径,指定计算感兴趣值时所使用的领域范围narf_keypoint_detector1.getParameters().add_points_on_straight_edges = true;//指点是否添加垂直边缘上的点narf_keypoint_detector1.getParameters().distance_for_additional_points = 0.5;pcl::PointCloud<int>keypoint_indices1;narf_keypoint_detector1.compute(keypoint_indices1);//计算索引keypoints1->points.resize(keypoint_indices1.size());keypoints1->height = keypoint_indices1.height;keypoints1->width = keypoint_indices1.width;for (std::size_t i = 0; i < keypoint_indices1.points.size(); ++i){keypoints1->points[i].getVector3fMap() = range_image_src->points[keypoint_indices1.points[i]].getVector3fMap();}
  1. pcl::RangeImage& range_image1 = *range_image_src;:将源深度图像的引用保存到 range_image1 中,以便后续使用。

  2. pcl::RangeImageBorderExtractor range_image_border_extractor1;:创建深度图像边界提取器的实例,用于边界处理。

  3. pcl::PointCloud<pcl::PointXYZ>::Ptr keypoints1(new pcl::PointCloud<pcl::PointXYZ>());:创建一个点云对象 keypoints1 用于存储提取的关键点。

  4. pcl::NarfKeypoint narf_keypoint_detector1(&range_image_border_extractor1);:创建 NARF 关键点检测器的实例,并将边界提取器传递给它。

  5. narf_keypoint_detector1.setRangeImage(&range_image1);:指定 NARF 关键点检测器要使用的深度图像。

  6. narf_keypoint_detector1.getParameters().support_size = 0.2f;:设置搜索空间球体的半径,该参数影响关键点的计算感兴趣值时所使用的领域范围。较小的值可能导致检测到更细小的关键点。

  7. narf_keypoint_detector1.getParameters().add_points_on_straight_edges = true;:指定是否在垂直边缘上添加点。设置为 true 可以增加关键点的数量,但可能也增加噪声。

  8. narf_keypoint_detector1.getParameters().distance_for_additional_points = 0.5;:设置额外点之间的距离。该参数用于在一些特殊情况下添加额外的关键点。

  9. pcl::PointCloud<int> keypoint_indices1;:创建一个整数类型的点云对象,用于存储关键点的索引。

  10. narf_keypoint_detector1.compute(keypoint_indices1);:计算关键点的索引。

  11. keypoints1->points.resize(keypoint_indices1.size());:调整关键点点云的大小。

  12. 使用循环将关键点的坐标从深度图像的索引中提取并存储到 keypoints1 中。

结果:

输入点云的关键点

输出点云的关键点 

 

输入点云的关键点与输入点云一起展示

 

输出点云的关键点与输出点云一起展示 

三、NARF关键点检测及SAC-IA粗配准 

C++

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/common/io.h>
#include <pcl/keypoints/iss_3d.h>
#include <pcl/features/normal_3d.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/features/fpfh_omp.h>  
#include <pcl/common/common_headers.h>
#include <pcl/registration/ia_ransac.h>
#include <pcl/range_image/range_image.h>
#include <pcl/features/range_image_border_extractor.h>
#include <pcl/keypoints/narf_keypoint.h>using namespace std;
void extract_keypoint(pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, pcl::PointCloud<pcl::PointXYZ>::Ptr& keypoint)
{pcl::RangeImage::CoordinateFrame coordinate_frame = pcl::RangeImage::CAMERA_FRAME;Eigen::Affine3f scene_sensor_pose(Eigen::Affine3f::Identity());scene_sensor_pose = Eigen::Affine3f(Eigen::Translation3f(cloud->sensor_origin_[0],cloud->sensor_origin_[1], cloud->sensor_origin_[2])) * Eigen::Affine3f(cloud->sensor_orientation_);//-------------从点云创建深度图像———————————float noise_level = 0.0;float min_range = 0.0f;int border_size = 1;pcl::RangeImage::Ptr range_image(new pcl::RangeImage);range_image->createFromPointCloud(*cloud, pcl::deg2rad(0.02f), pcl::deg2rad(0.02f),pcl::deg2rad(360.0f), pcl::deg2rad(180.0f), scene_sensor_pose, coordinate_frame,noise_level, min_range, border_size);//range_image->setUnseenToMaxRange();//设置不能观察到的点都为远距离点//-----------提取NARF关键点-------------pcl::RangeImage& range_image0 = *range_image;pcl::RangeImageBorderExtractor range_image_border_extractor;pcl::NarfKeypoint narf_keypoint_detector(&range_image_border_extractor);narf_keypoint_detector.setRangeImage(&range_image0);//指定深度图narf_keypoint_detector.getParameters().support_size = 0.2f;//指定搜索空间球体的半径,指定计算感兴趣值时所使用的领域范围narf_keypoint_detector.getParameters().add_points_on_straight_edges = true;//指点是否添加垂直边缘上的点narf_keypoint_detector.getParameters().distance_for_additional_points = 0.5;pcl::PointCloud<int>keypoint_indices;narf_keypoint_detector.compute(keypoint_indices);//计算索引keypoint->points.resize(keypoint_indices.size());keypoint->height = keypoint_indices.height;keypoint->width = keypoint_indices.width;for (std::size_t i = 0; i < keypoint_indices.points.size(); ++i){keypoint->points[i].getVector3fMap() = range_image->points[keypoint_indices.points[i]].getVector3fMap();}}
pcl::PointCloud<pcl::FPFHSignature33>::Ptr compute_fpfh_feature(pcl::PointCloud<pcl::PointXYZ>::Ptr& keypoint)
{pcl::search::KdTree<pcl::PointXYZ>::Ptr tree;pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> n;n.setInputCloud(keypoint);n.setSearchMethod(tree);n.setKSearch(10);n.compute(*normals);pcl::PointCloud<pcl::FPFHSignature33>::Ptr fpfh(new pcl::PointCloud<pcl::FPFHSignature33>);pcl::FPFHEstimationOMP<pcl::PointXYZ, pcl::Normal, pcl::FPFHSignature33> f;f.setNumberOfThreads(8);f.setInputCloud(keypoint);f.setInputNormals(normals);f.setSearchMethod(tree);f.setRadiusSearch(50);f.compute(*fpfh);return fpfh;
}
pcl::PointCloud<pcl::PointXYZ>::Ptr sac_align(pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, pcl::PointCloud<pcl::PointXYZ>::Ptr s_k, pcl::PointCloud<pcl::PointXYZ>::Ptr t_k, pcl::PointCloud<pcl::FPFHSignature33>::Ptr sk_fpfh, pcl::PointCloud<pcl::FPFHSignature33>::Ptr tk_fpfh)
{pcl::SampleConsensusInitialAlignment<pcl::PointXYZ, pcl::PointXYZ, pcl::FPFHSignature33> scia;scia.setInputSource(s_k);scia.setInputTarget(t_k);scia.setSourceFeatures(sk_fpfh);scia.setTargetFeatures(tk_fpfh);scia.setMinSampleDistance(7);///参数:设置采样点之间的最小距离,满足的被当做采样点scia.setNumberOfSamples(250);设置每次迭代设置采样点的个数(这个参数多可以增加配准精度)scia.setCorrespondenceRandomness(4);//设置选择随机特征对应点时要使用的邻域点个数。值越大,特征匹配的随机性就越大/*scia.setEuclideanFitnessEpsilon(0.001);scia.setTransformationEpsilon(1e-10);scia.setRANSACIterations(30);*/pcl::PointCloud<pcl::PointXYZ>::Ptr sac_result(new pcl::PointCloud<pcl::PointXYZ>);scia.align(*sac_result);pcl::transformPointCloud(*cloud, *sac_result, scia.getFinalTransformation());return sac_result;
}
int main(int, char** argv)
{pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);//要配准变化的点云pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_target(new pcl::PointCloud<pcl::PointXYZ>);//目标点云(不变的)if (pcl::io::loadPCDFile<pcl::PointXYZ>("pcd/pig_view1.pcd", *cloud) == -1){PCL_ERROR("加载点云失败\n");}if (pcl::io::loadPCDFile<pcl::PointXYZ>("pcd/pig_view2.pcd", *cloud_target) == -1){PCL_ERROR("加载点云失败\n");}boost::shared_ptr<pcl::visualization::PCLVisualizer>viewer1(new pcl::visualization::PCLVisualizer("v1"));viewer1->setBackgroundColor(0, 0, 0);  //设置背景颜色为黑色// 对目标点云着色可视化 (red).pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>target_color1(cloud_target, 255, 0, 0);viewer1->addPointCloud<pcl::PointXYZ>(cloud_target, target_color1, "target cloud");viewer1->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "target cloud");// 对源点云着色可视化 (green).pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>input_color1(cloud, 0, 255, 0);viewer1->addPointCloud<pcl::PointXYZ>(cloud, input_color1, "input cloud");viewer1->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "input cloud");//while (!viewer1->wasStopped()){viewer1->spinOnce(100);boost::this_thread::sleep(boost::posix_time::microseconds(100));}////粗配准pcl::PointCloud<pcl::PointXYZ>::Ptr s_k(new pcl::PointCloud<pcl::PointXYZ>);pcl::PointCloud<pcl::PointXYZ>::Ptr t_k(new pcl::PointCloud<pcl::PointXYZ>);extract_keypoint(cloud, s_k);extract_keypoint(cloud_target, t_k);std::cout << "关键点数量" << s_k->size() << std::endl;std::cout << "关键点数量" << t_k->size() << std::endl;pcl::PointCloud<pcl::FPFHSignature33>::Ptr sk_fpfh = compute_fpfh_feature(s_k);pcl::PointCloud<pcl::FPFHSignature33>::Ptr tk_fpfh = compute_fpfh_feature(t_k);pcl::PointCloud<pcl::PointXYZ>::Ptr result(new pcl::PointCloud<pcl::PointXYZ>);result = sac_align(cloud, s_k, t_k, sk_fpfh, tk_fpfh);boost::shared_ptr<pcl::visualization::PCLVisualizer>viewer2(new pcl::visualization::PCLVisualizer("显示点云"));viewer2->setBackgroundColor(0, 0, 0);  //设置背景颜色为黑色// 对目标点云着色可视化 (red).pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>target_color2(cloud_target, 255, 0, 0);viewer2->addPointCloud<pcl::PointXYZ>(cloud_target, target_color2, "target cloud");viewer2->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "target cloud");// 对源点云着色可视化 (green).pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>input_color2(result, 0, 255, 0);viewer2->addPointCloud<pcl::PointXYZ>(result, input_color2, "input cloud");viewer2->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "input cloud");//while (!viewer2->wasStopped()){viewer2->spinOnce(100);boost::this_thread::sleep(boost::posix_time::microseconds(100));}return 0;
}

关键代码解析: 

我之前在iss关键点检测以及SAC-IA粗配准-CSDN博客

已经解释了大部分函数,这篇文章就不赘述了

range_image->setUnseenToMaxRange();

 这一行是上述代码被注释掉的部分,稍微解释一下

setUnseenToMaxRange() 被调用后,范围图像中未被看到的点(通常由传感器视野限制或遮挡造成)的范围值会被设置为最大可能的范围值

scia.setEuclideanFitnessEpsilon(0.001);
scia.setTransformationEpsilon(1e-10);
scia.setRANSACIterations(30);

这三行也是上述代码被注释掉的部分,稍微解释一下

  1. scia.setEuclideanFitnessEpsilon(0.001);

    • 这一行代码设置了模型配准中的欧氏距离收敛标准(Euclidean Fitness Epsilon)。
    • 欧氏距离是指在配准的过程中,通过优化变换矩阵,使得目标点云与源点云的每个点之间的欧氏距离小于该阈值。
    • 参数值 0.001 表示当欧氏距离小于或等于 0.001 时,认为配准已经收敛。
  2. scia.setTransformationEpsilon(1e-10);

    • 这一行代码设置了模型配准中的变换矩阵收敛标准(Transformation Epsilon)。
    • 变换矩阵收敛标准是指通过优化变换矩阵的过程中,变换矩阵的变化小于该阈值时,认为配准已经收敛。
    • 参数值 1e-10 是一个非常小的数,表示当变换矩阵的变化小于或等于 1e-10 时,认为配准已经收敛。
  3. scia.setRANSACIterations(30);

    • 这一行代码设置了模型配准中的 RANSAC 迭代次数。
    • RANSAC(Random Sample Consensus)是一种迭代优化的方法,通过随机采样来估计变换矩阵。
    • 参数值 30 表示进行 30 次 RANSAC 迭代来估计最优的变换矩阵。

影响:

  • 调整 setEuclideanFitnessEpsilon 的值会影响收敛的条件,更小的值可能导致更严格的收敛,但也可能增加算法运行时间。
  • 调整 setTransformationEpsilon 的值会影响变换矩阵的收敛条件,较小的值表示更精确的匹配,但也可能增加计算开销。
  • 调整 setRANSACIterations 的值会影响 RANSAC 迭代的次数,更多的迭代次数可能带来更精确的变换矩阵,但也增加了计算时间。

结果:

输入点云与输出点云

配准后的输入点云与输出点云,实际效果并不好,运行也慢,需要好几分钟

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/683435.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

动态内存管理:new和delete的底层探索

之前我们在C语言上是学过malloc和calloc还要realloc等函数来在堆上获取相应的内存&#xff0c;但是这些函数是存在缺陷的&#xff0c;今天引入对new和delete的学习&#xff0c;来了解new和delete的底层实现。 首先就是在C中我们为什么要对内存进行区域的分块&#xff1f; 答案…

ChatGPT高效提问—prompt实践(漏洞风险分析-重构建议-识别内存泄漏)

ChatGPT高效提问—prompt实践&#xff08;漏洞风险分析-重构建议-识别内存泄漏&#xff09; 1.1 漏洞和风险分析 ChatGPT还可以帮助开发人员预测代码的潜在风险&#xff0c;识别其中的安全漏洞&#xff0c;而不必先运行它&#xff0c;这可以让开发人员及早发现错误&#xff0…

【vscode】在vscode中如何导入自定义包

只需要额外添加这两条语句即可&#xff1a; import os,sys sys.path.append("../..") 需要注意的是&#xff0c;ipynb 文件打开的工作目录是文件本身的路径&#xff0c;而 py 文件打开的工作路径是 vscode 打开的路径。 相比较而言 pycharm 中创建好项目之后并不…

FT2232调试记录(2)

FT2232调试记录 &#xff08;1&#xff09;获取当前连接的FTDI设备通道个数:&#xff08;2&#xff09;获取当前连接的设备通道的信息:&#xff08;3&#xff09;配置SPI的通道:&#xff08;4&#xff09;如何设置GPIO:&#xff08;5&#xff09;DEMO测试&#xff1a; FT2232调…

【阅读笔记】空域保边降噪《Side Window Filtering》

1、保边滤波背景 保边滤波器的代表包括双边滤波、引导滤波&#xff0c;但是这类滤波器有一个问题&#xff0c;它们均将待处理的像素点放在了方形滤波窗口的中心。但如果待处理的像素位于图像纹理或者边缘&#xff0c;方形滤波核卷积的处理结果会导致这个边缘变模糊。 基于这个…

揭秘 2024 春晚刘谦魔术——代码还原

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、魔术大概流程 二、代码实现各个步骤 2.1 partition&#xff08;对半撕牌&#xff09; 2.2 bottom&#xff08;将 n 张牌置底…

基于微信小程序的智能社区服务小程序,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

谈谈Lombok的坑

Lombok 是一个 Java 库&#xff0c;通过注解的方式在编译时自动为类生成 getter、setter、equals、hashCode 等方法&#xff0c;以简化代码和提高开发效率。本文主要谈谈代码简化背后的代价。 引入Lombok之前是怎么做的 IDE中添加getter/setter, toString等代码&#xff1a; …

单链表的介绍

一.单链表的概念及结构 概念&#xff1a;链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表 中的指针链接次序实现的 。 结构&#xff1a;根据个人理解&#xff0c;链表的结构就像火车厢一样&#xff0c;一节一节连在一起的&#x…

实现安全性

实现安全性 问题陈述 Chris希望阅读位于服务器上的电子邮件消息。他将自己的登录信息发送到服务器已进行验证。因此,Chris决定用基于表单的验证来验证他的登录信息。但是,他首先决定只用基于表单的验证测试登录页面 。 解决方案 要解决上述问题,Chris需要执行以下任务: 用…

从零开始做题:逆向 ret2shellcode jarvisoj level1

1.题目信息 BUUCTF在线评测 2.原理 篡改栈帧上的返回地址为攻击者手动传入的shellcode所在缓冲区地址&#xff0c;并且该区域有执行权限。 rootpwn_test1604:/ctf/work/9# gdb ./level1 GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1 Copyright (C) 2016 Free Software Fou…

【C++航海王:追寻罗杰的编程之路】关于模板,你知道哪些?

目录 1 -> 泛型编程 2 -> 函数模板 2.1 -> 函数模板概念 2.2 -> 函数模板格式 2.3 -> 函数模板的原理 2.4 -> 函数模板的实例化 2.5 -> 函数参数的匹配原则 3 -> 类模板 3.1 -> 类模板的定义格式 3.2 -> 类模板的实例化 1 -> 泛型编…

分布式文件系统 SpringBoot+FastDFS+Vue.js【一】

分布式文件系统 SpringBootFastDFSVue.js【一】 一、分布式文件系统1.1.文件系统1.2.什么是分布式文件系统1.3.分布式文件系统的出现1.3.主流的分布式文件系统1.4.分布式文件服务提供商1.4.1.阿里OSS1.4.2.七牛云存储1.4.3.百度云存储 二、fastDFS2.1.fastDSF介绍2.2.为什么要使…

tuple的使用例题(三元组)

题目大意&#xff1a;给一定关系&#xff0c;判断后面给的跟前面的有无矛盾 一开始还在想一些构造的操作&#xff0c;后面实在想不出来看题解&#xff0c;就是暴力啊...... 但是这种数据结构tuple&#xff08;元组&#xff09;确实是没见过&#xff0c;于是写篇总结 见这篇ht…

【MySQL】外键约束的删除和更新总结

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-7niJLSFaPo0wso60 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

六、Redis之数据持久化及高频面试题

6.1 数据持久化 官网文档地址&#xff1a;https://redis.io/docs/manual/persistence/ Redis提供了主要提供了 2 种不同形式的持久化方式&#xff1a; RDB&#xff08;Redis数据库&#xff09;&#xff1a;RDB 持久性以指定的时间间隔执行数据集的时间点快照。AOF&#xff0…

浅谈Linux环境

冯诺依曼体系结构&#xff1a; 绝大多数的计算机都遵守冯诺依曼体系结构 在冯诺依曼体系结构下各个硬件相互配合处理数据并反馈结果给用户 其中控制器和运算器统称为中央处理器&#xff08;CPU&#xff09;&#xff0c;是计算机硬件中最核心的部分&#xff0c;像人类的大脑操控…

汽车零部件制造业MES系统解决方案

一、​汽车零部件行业现状 随着全球汽车产业不断升级&#xff0c;汽车零部件市场竞争日趋激烈&#xff0c;从上游的钢铁、塑料、橡胶等生产到下游的主机厂配套制造&#xff0c;均已成为全球各国汽车制造大佬战略目标调整的焦点&#xff0c;其意欲在汽车零部件行业快速开疆扩土&…

Days 32 ElfBoard GDT工作原理

GDT&#xff08;Gas Discharge Tubes&#xff09;&#xff0c;即陶瓷气体放电管。GDT是内部由一个或一个以上放电间隙内充有惰性气体构成的密闭器件。GDT电气性能取决于气体种类、气体压力、内部电极结构、制作工艺等因素。GDT可以承受高达数十甚至数百千 安培的浪涌电流冲击&a…

自定义Spring Boot Starter

引言 在Spring Boot的世界中&#xff0c;Starter 能够简化我们的开发过程&#xff0c;通过封装常见的依赖和自动配置。本篇旨在为有志于进一步简化Spring Boot应用配置的开发者提供指导&#xff0c;让我们一起创建一个自定义的Spring Boot Starter。 一、什么是 Spring Boot …