前言
随着工业自动化、智能化的不断推进,机器视觉(2D/3D)在工业领域的应用和重要程度也同步激增(识别、定位、抓取、测量,缺陷检测等),而针对不同作业场景进行解决方案设计时,通常会借助PCL、OpenCV、Eigen等简单方便的开源算法库进行方案的快速验证和迭代以满足作业场景下的目标需求。
为了让对工业机器视觉方向感兴趣的同学能够少走一些弯路,故推出了此一系列简易入门教程示例,让初次使用者能够最简单直观地感受到当前所用算法模块的执行效果。
后续会逐步扩增与工业机器视觉相关的一些其它内容,如:
项目案例剖析、场景数据分析、基础算法模块、相机评测 等;
如有兴趣可加入群聊(若入群二维码被屏蔽,则可以通过Q群(1032861997)或评论、私信博主“群聊”,邀请入群),与同道同学及圈内同行一起交流讨论。
程序说明
展示基于邻域点云的法线估计效果;
输出结果
代码示例
/*
* @File: normal_estimation_nn.cpp
* @Brief: pcl course
* @Description: 展示基于邻域点云的法线估计
* @Version: 0.0.1
* @Author: MuYv
*/
#include <iostream>
#include <string>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/io/pcd_io.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/features/normal_3d.h>
#include <pcl/features/normal_3d_omp.h>
#include <pcl/visualization/pcl_visualizer.h>
int main(int argc, char** argv){
if(argc != 2){
std::cout<<"Usage: exec cloud_file_path"<<std::endl;
return -1;
}
const std::string kCloudFilePath = argv[1]; // ../clouds/table.pcd
// 定义变量
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_src(new pcl::PointCloud<pcl::PointXYZ>());
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>());
pcl::PointCloud<pcl::Normal>::Ptr cloud_normals(new pcl::PointCloud<pcl::Normal>());
// 成功返回0,失败返回-1
if(-1 == pcl::io::loadPCDFile(kCloudFilePath,*cloud_src)){
std::cout<<"load pcd file failed. please check it."<<std::endl;
return -2;
}
// 创建体素栅格(模板)类对象,点数据类型为 pcl::PointXYZ
pcl::VoxelGrid<pcl::PointXYZ> vg;
// 设置叶子大小,也即体素格子尺寸,此处设置lx,ly,lz为相同数值,即为正方形
const float kLeafSize = 0.05f; // 单位与输入点云的尺度单位保持一致
vg.setLeafSize(kLeafSize, kLeafSize, kLeafSize);
// 设置输入点云,注意:此处传入的是点云类对象的智能指针
vg.setInputCloud(cloud_src);
// 执行过滤,并带出处理后的点云数据,注意:此处传入的是点云类对象
vg.filter(*cloud_filtered);
// 创建基于邻域的法向估计类对象
// pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> ne; // 基于omp并行加速,需配置开启OpenMP
// ne.setNumberOfThreads(10);
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
// 创建一个空的kdtree对象,并把它传递给法线估计对象,用于创建基于输入点云数据的邻域搜索kdtree
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
// 传入待估计法线的点云数据,智能指针
ne.setInputCloud(cloud_filtered);
// 传入kdtree对象,智能指针
ne.setSearchMethod(tree);
// 设置邻域搜索半径
ne.setRadiusSearch(kLeafSize*5); // 上边做了一次体素降采样,所以设置半径时,要考虑到此时的点云空间间距
// // 也可以设置最近邻点个数
// ne.setKSearch(25);
// 设置视点源点,用于调整点云法向(指向视点),默认(0,0,0)
ne.setViewPoint(0,0,0);
// 计算法线数据
ne.compute(*cloud_normals);
// 如有需要,可以通过concatenateFields函数将point和normal组合起来形成PointNormal点云数据
pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normal(new pcl::PointCloud<pcl::PointNormal>());
pcl::concatenateFields(*cloud_filtered, *cloud_normals, *cloud_with_normal);
// 创建可视化对象
pcl::visualization::PCLVisualizer viewer("viewer");
// 将当前窗口,拆分成横向的2个可视化窗口,以viewport区分(v1/v2)
int v1;
int v2;
//窗口参数分别对应 x_min, y_min, x_max, y_max, viewport
viewer.createViewPort(0.0, 0.0, 0.5, 1.0, v1);
viewer.createViewPort(0.5, 0.0, 1.0, 1.0, v2);
// 添加2d文字标签
viewer.addText("v1", 10,10, 20, 1,0,0, "viewport_v1", v1);
viewer.addText("v2", 10,10, 20, 0,1,0, "viewport_v2", v2);
// 添加坐标系
viewer.addCoordinateSystem(0.5); // 单位:m
// 设置可视化窗口背景色
viewer.setBackgroundColor(0.2,0.2,0.2); // r,g,b 0~1之间
// 向v1窗口中添加点云
viewer.addPointCloud(cloud_src,"cloud_src",v1);
// 设置单一颜色
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> color(cloud_filtered, 255, 0, 0);
// 向v2窗口中添加点云
viewer.addPointCloud(cloud_filtered,color,"cloud_filtered",v2);
// 根据点云id,设置点云可视化属性,此处将可视化窗口中的点大小调整为2级
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "cloud_filtered");
// 向v2窗口中添加法线可视化
viewer.addPointCloudNormals<pcl::PointXYZ, pcl::Normal>(cloud_filtered, cloud_normals, 1, 0.05, "cloud_normals", v2);
// 关闭窗口则退出
while(!viewer.wasStopped()){
viewer.spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
return 0;
}
总结
基于邻域的点云法线估计,通常可通过设置“最邻近点数”或“搜索半径”来获取用于计算当前点法线的参考数据;
参考数据的搜索方式及相应的参数数值,需根据点云的具体情况进行设置;
扫描二维码关注公众号,回复:
14349016 查看本文章
注:部分测试所用点云数据来源于网络,如有侵权,请联系博主删除,谢谢。