PCL点云学习七(从距离图像重建曲面)

论文阅读

原文:Fast Range Image Segmentation and Smoothing using Approximate Surface Reconstruction and Region Growing
1.摘要/简介

  • 利用局部平面信息和临近点平滑测量点
  • 将图像分割为平面区域和其他的几何单元
  • 可以高速处理距离图像
  1. 相关
  • 为了在复杂环境下获取目标部分,常用方法如下:
    <1>: 检测水平支撑平面
    <2>: 在这些平面上提取聚类点
    <2>: 再执行辨识、分类或者分钟对应点集
  • 通常存在一个问题:如何将3D数据转换为相应几何特征/局部平面
  • 文章利用网格局部的邻域点和区域增长法,来近似距离图像中的平面和几何
    利用网格局部的邻域点高效计算法线(双边滤波器),并估计曲率,来平滑3D点
  • 在机器视觉领域,按照原理通常可以将曲面重建分为3类:
    <1> 使用随机一致性(RANSAC):
    找到最适合前点集的简单集合模型,剔除内点,再分割区域的剩下点:
    ex1:Silva,确定联通区域,并按对应与应用RANSAC,
    ex2:Gotardo,使用预分割计算边缘映射,使用MSAC(基于M估计的随机采样一致性)来拟合模型
    ex3:Schnabel,使用无序提取简单的几何模型(平面、圆柱、球体),分解原始无序点云为八叉树,并应用到原始点云
    论文的文献提出了一种适用于TOF相机的方法,处理相机误差,加速处理与图像结构相似数据,在检测时:检测到主平面后,利用Schnabel提出的八叉树基元检测处理提取的平面,并对图像进行积分,加速分割、提取目标。该方法适用与kinect
    <2> 3D霍夫变化(Hough)
    在2D图像上查找实时上的直线和圆,扩展到3D中,通过在各个方向中找到具有最大值的平面和直方图。
    论文的文献提出过一种快速分割方法:使用相似参数空间进行霍夫变化,在发现空间上预先分割点集和区域,对于每个点集获得一个独特的平面,在通过一个特殊的POS过程(合并参数相似的区域)补偿离散化误差,但是可能导致两个不相邻区域合并
    <3> 区域增长法
    方法1:将数据变为类似与图像结构的数据。
    ex1:Hähnel 将3D激光扫描为网状结构,合并可能位于同一平面的区域,
    ex2:Poppinga;将该方法应用于机载相机上,并使用重构了算法:增长区域通过添加临近点中小于指定阈值的点,此时平面的质心和协防差也会同时更新。
    方法2:对于深度图像使用,计算局曲率,区域增长指导曲率达到指定值停止。
    ex1:Harati;计算一条边缘映射,查找连续的局部区域
    ex2:Rabbani;将局部区域点映射到平面上,让后计算每个点到平面距离
    ex3:Cupec;在深度图像上使用2.5D Delaunay 三角化获取三角化网格,在使用当前点到区域内所有网格的最大距离决定是否添加。
  • 在3D激光激光常用的曲面重建算法为:先在切割平面提取多条直线,然后合并相邻线到局部平面面片中。
  • RANSAC和霍夫变化缺点:当两个分割区域存在同一公共点时,会将两个区域合并,直到不相同的区域
  • 霍夫变化受离散化影响
  • 所以文章采用类似图像的数据(距离图像),直接推导曲面重建,使用局部环形区域估计法线和曲率,使用双边滤波器平滑深度值,支持多种区域增长方法。
  1. 快速网格构造和区域重建
  • 整个过程可以分为如下流程:
    1):从图像邻域内推导网格
    2):使用网格邻域近似估计局部曲面的法线和曲率
    3):使用双边滤波器平滑法线和点集
    4):使用区域增长法分割区域
  • 整个过程的邻域点来自于网格结构或者通过搜索树构造的非结构数据
  • 快速网格重建的拓扑结构(点检测)
    遍历深度图像 R R R上的每一点 p i = R ( u , v ) p_i=R(u,v) pi=R(u,v),
    1):检测点 p i p_i pi的邻域点, R ( u , v + 1 ) , R ( u + 1 , v + 1 ) , R ( u + 1 , v ) R(u,v+1),R(u+1,v+1),R(u+1,v) R(u,v+1),R(u+1,v+1),R(u+1,v)的值是否有效,所有边与点 p i p_i pi,和其三个邻域值构成向量是否存在遮挡,如果通过检测不为遮挡点,就可用于构建网格
    × 遮挡检测:如果将邻域值构成的向量与,视点与其测量值的连线共线,那么表示存在遮挡,有一个值无效,检测方法表达式如下:
    ξ θ : \xi_\theta: ξθ:最大角度容忍误差
    ξ d : \xi_d: ξd:最大长度允许误差
    v a l i d = ( ∣ p i ∗ p j ∣ ≤ c o s ξ θ ) ∧ ( ∣ ∣ p i − p j ∣ ∣ 2 ≤ ξ d 2 ) valid=(|p_i*p_j| \leq cos\xi_\theta)\wedge(||p_i-p_j||^2 \leq \xi^2_d) valid=(pipjcosξθ)(pipj2ξd2)
  • 快速网格重建的拓扑结构(网格存在4种):
    在这里插入图片描述
    方形网格:顺序连接 R ( u , v ) , R ( u , v + 1 ) , R ( u + 1 , v + 1 ) , R ( u + 1 , v ) R(u,v),R(u,v+1),R(u+1,v+1),R(u+1,v) R(u,v),R(u,v+1),R(u+1,v+1),R(u+1,v)
    在这里插入图片描述
    左切网格:分别链接方格对角点
    在这里插入图片描述
    自适应网格:沿对角线较短的方向切割网格,论文采用这种方式进行三角化,再三角化过程中,单个无效邻域点会导致值添加了一个三角形,在最后我们会剔除所有不构成多边形的三角形顶点。
    2):快速计算平面法线和曲率
    a:原始点云
    在这里插入图片描述

采用 p i p_i pi邻域点的面法线的权重计算平面法线 n i n_i ni,使用叉积计算差分向量得到法线,选取适当的比例(与其三角化面积相关,避免了归一化),法线 n i n_i ni的计算公式如下:
p j , a , p j , b , p j , c : p_{j,a},p_{j,b},p_{j,c}: pj,a,pj,b,pj,c:三角 j j j的三个顶点
n i = ∑ j = 0 N T ( p j , a − p j , b ) × ( p j , a − p j , c ) ∣ ∣ ∑ j = 0 N T ( p j , a − p j , b ) × ( p j , a − p j , c ) ∣ ∣ n_i=\frac{\sum_{j=0}^{N_T}(p_{j,a}-p_{j,b})×(p_{j,a}-p_{j,c})}{||\sum_{j=0}^{N_T}(p_{j,a}-p_{j,b})×(p_{j,a}-p_{j,c})||} ni=j=0NT(pj,apj,b)×(pj,apj,c)j=0NT(pj,apj,b)×(pj,apj,c)
实际中,只需要遍历所有差分向量并进行叉积,并将其法线相关的点相加,并在最后归一化所有点的法线,得到下图b:
在这里插入图片描述
3):使用双边滤波器平滑法线和点集
去除噪声,对点与法相进行平滑;
N i : N_i: Ni:距离为1的环形邻域,即直接连接到点 p i p_i pi的点集
c I : c_I: cI:归一化函数
p i = ∑ j ∈ N i w i j p i ∑ j ∈ N i w i j ; n i = ∑ j ∈ N i w i j n i ∑ j ∈ N i w i j w i j = 距 离 因 素 ∗ 法 线 因 素 ∗ 强 度 因 素 ( 可 选 ) = e ∣ ∣ p i − p j ∣ ∣ e ∣ ∣ n i − n j ∣ ∣ e ∣ ∣ I i − I j ∣ ∣ / c I p_i=\frac{\sum_{j\in N_i}w_{ij}p_i}{\sum_{j\in N_i}w_{ij}};n_i=\frac{\sum_{j\in N_i}w_{ij}n_i}{\sum _{j\in N_i}w_{ij}}\\ w_{ij}=距离因素*法线因素*强度因素(可选)=e^{||p_i-p_j||}e^{||n_i-n_j||}e^{||I_i-I_j||/c_I} pi=jNiwijjNiwijpi;ni=jNiwijjNiwijniwij=线()=epipjeninjeIiIj/cI
最后得到下图C:
在这里插入图片描述
4):使用区域增长法分割区域
对给定点(起始点的集合和队列)进行如下操作:
1)通过迭代选择下一个起始点
2)初始化区域增长的兴趣点
3)将起始点插入一个空的处理队列
4)内循环:处理队列:当起处理队列不为空,从队列中提取点。
5)内循环:检测其与区域模型的匹配性,如果匹配添加,不匹配停止
6)内循环:添加当前点的邻域到处理队列

  • 作者已经将2,4,5,6步骤封装,并实现了几种区域增长模型,并分割
    (1):近似平面分割(Approximate Plane Segmentation):
    使用起始点和其法线初始化区域模型的质心和法线,在进行区域匹配时,只需要检查区域法线 n i n_i ni和点法线的夹角,为了更新模型参数,递增的更新平面质心(而不时协防差),从中导出法线,即:通过预先计算网格邻域上的曲面法线,并通过平均点法线来逼近平面法线,我们可以大大减少计算量。。
    (2):提取局部平滑曲面(Extracting Locally Smooth Surfaces):
    简单的几何分割和提取可以通过邻域点实现,通过检测局部曲率变化,应用一种典型的区域分割如图d:
    在这里插入图片描述
  • 几何模型的选取
    为了找到最适合区域的几何模型,使用RANSAC,为了提高效率,论文中只有当一个区域无法找到fully exlplain 才会使用。
    在这里插入图片描述
    左边:提取到平面(黄色)、圆柱(淡蓝)、球体(紫色)
    右边:将提取的五一映射到平面
  • 其他:为了增加算法的鲁棒性,假设原始数据存在等向噪声(Isotropic Noise Model),噪声模型为: N ( 0 , σ 2 ) N(0,\sigma^2) N(0,σ2),作者找到适合的 σ \sigma σ(kinect相机), σ ( d ) = 0.00263 d 2 − 0.00518 d + 0.00752 \sigma(d)=0.00263d^2-0.00518d+0.00752 σ(d)=0.00263d20.00518d+0.00752
  • 算法时间(SegComp Data Sets数据集)
    在这里插入图片描述

PCL库

使用:
核心代码:

    pcl::search::KdTree<pcl::PointWithRange>::Ptr tree(new pcl::search::KdTree<pcl::PointWithRange>());
    tree->setInputCloud(rangeImage);

    pcl::PolygonMesh triangles;//三角化
	tri->setTrianglePixelSize(size);    //三角化网格环状邻域大小
	tri->setInputCloud(rangeImage);      //输出带你元
	tri->setSearchMethod(tree);         //搜索方法
	tri->setTriangulationType(pcl::OrganizedFastMesh::TriangulationType::TRIANGLE_ADAPTIVE_CUT);//自适应
	tri->reconstruct(triangles);  

实例:来自pcl点云从入门到精通

#include <pcl/range_image/range_image.h>
#include <pcl/range_image/range_image_planar.h>
#include <pcl/io/io.h>
#include <pcl/io/pcd_io.h>
#include <pcl/features/integral_image_normal.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/point_types.h>
#include <pcl/console/print.h>
#include <pcl/surface/organized_fast_mesh.h>
#include <pcl/console/time.h>
#include <Eigen/StdVector>
#include <Eigen/Geometry>
#include <iostream>
#include <pcl/surface/impl/organized_fast_mesh.hpp>
#include <pcl/common/common_headers.h>
#include <pcl/visualization/range_image_visualizer.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/console/parse.h>
#include <pcl/visualization/common/float_image_utils.h>
#include <pcl/io/png_io.h>
#include <pcl/surface/organized_fast_mesh.h>
using namespace pcl::console;
using namespace pcl;
using namespace std;
int main(int argc, const char **argv)
{
    int width = 640;
    int height = 480;
    float cx = 320, cy = 240, fx = 525, fy = 525;
    int type = 0, size = 2;
    pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>());
    if (io::loadPCDFile("/home/n1/notes/pcl/CPR/1.pcd", *cloud))
    {
        cout << "读取文件成功!" << endl;
    }
    Eigen::Affine3f sensorPose = Eigen::Affine3f::Identity(); //传感器姿态
    pcl::RangeImage::CoordinateFrame coordinate_frame=pcl::RangeImage::CAMERA_FRAME;//设置坐标系
    float noiselevel=0;
    float minRange=0.0f;

    pcl::RangeImagePlanar::Ptr rangeImage(new pcl::RangeImagePlanar()); //创建深度图像
    rangeImage->createFromPointCloudWithFixedSize(*cloud,width,height,cx,cy,fx,fy,sensorPose,coordinate_frame);//创建深度图像
    float* ranges = rangeImage->getRangesArray();  //获取距离数组
    unsigned char* rgb_image=pcl::visualization::FloatImageUtils::getVisualImage(ranges, rangeImage->width, rangeImage->height);
    pcl::io::saveRgbPNGFile("/home/n1/notes/pcl/CPR/rangimage.png", rgb_image, rangeImage->width, rangeImage->height);

    pcl::OrganizedFastMesh<pcl::PointWithRange>::Ptr tri(new pcl::OrganizedFastMesh<pcl::PointWithRange>());
    pcl::search::KdTree<pcl::PointWithRange>::Ptr tree(new pcl::search::KdTree<pcl::PointWithRange>());
    tree->setInputCloud(rangeImage);
    pcl::PolygonMesh triangles;//三角化
	tri->setTrianglePixelSize(size);    //三角化网格大小
	tri->setInputCloud(rangeImage);      //输出带你元
	tri->setSearchMethod(tree);         //搜索方法
	tri->setTriangulationType(pcl::OrganizedFastMesh<pcl::PointWithRange>::TriangulationType::TRIANGLE_ADAPTIVE_CUT);
    //        TRIANGLE_RIGHT_CUT,   0
    //    TRIANGLE_LEFT_CUT,        1
    //    TRIANGLE_ADAPTIVE_CUT,    2
    //    QUAD_MESH                 3
	tri->reconstruct(triangles);        //结构化
	boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer (new pcl::visualization::PCLVisualizer ("点云库PCL从入门到精通"));
	viewer->setBackgroundColor(0.5,0.5,0.5);
	viewer->addPolygonMesh(triangles,"tin");
	viewer->addCoordinateSystem();
	while (!viewer->wasStopped())
	{

		pcl_sleep (0.01);
		viewer->spinOnce ();

	}

    return 0;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
dll函数接口: ////******** 初始化默认参数 ********// //extern "C" int __stdcall ZSY3DViewerInit(); //******** 读取历史数据到cloud ********// extern "C" int __stdcall ZSY3DReadHistoryData(char *file_dir); //******** 读取txt数据到cloud ********// extern "C" int __stdcall ZSY3DReadTxtData(char *file_dir); //******** 读取单个点数据到cloud ********// extern "C" int __stdcall ZSY3DReadSingleData(float x, float y, float z); //******** 读取所有点数据到cloud ********// extern "C" int __stdcall ZSY3DReadNowData(float *x, float *y, float *z, int count); //******** vtk读取txt文件并显示 ********// extern "C" int __stdcall ZSY3DReadFile_TXT(char *file_dir); //******** vtk读取obj文件并显示 ********// extern "C" int __stdcall ZSY3DReadFile_OBJ(char *file_dir); //******** vtk读取vtk文件并显示 ********// extern "C" int __stdcall ZSY3DReadFile_VTK(char *file_dir); //******** vtk读取ply文件并显示 ********// extern "C" int __stdcall ZSY3DReadFile_PLY(char *file_dir); //******** cloud下采样处理 ********// extern "C" int __stdcall ZSY3DDownSampling(float leaf_size); //******** cloud均匀采样处理 ********// extern "C" int __stdcall ZSY3DBalanceSampling(float radiusSearch = 0.01f); //******** cloud增采样处理 ********// extern "C" int __stdcall ZSY3DIncreaseSampling(float radius = 0.03f, float StepSize = 0.02f); //******** cloud直通滤波采样处理 ********// extern "C" int __stdcall ZSY3DStraightSampling(char *fieldName = "z", float limits_min = 0.0f, float limits_max = 0.1f, bool limitsNegative = true); //******** cloud统计滤波采样处理 ********// extern "C" int __stdcall ZSY3DStatisticsSampling(float meanK = 50.0f,float stddevMulThresh = 1.0f); //******** cloud半径滤波采样处理 ********// extern "C" int __stdcall ZSY3DRadiusSampling(float radiusSearch = 0.8f, float minNeighborsInRadius = 2.0f); //******** cloud数据进行渲染,并显示 ********// extern "C" int __stdcall ZSY3DShowPointCloud(); //******** cloud数据进行VTK三维重建(三角面绘制),并显示 ********// extern "C" int __stdcall ZSY3DDelaunayBuild(bool depth_color); //******** cloud数据进行VTK三维重建(曲面体绘制),并显示 ********// extern "C" int __stdcall ZSY3DSurfaceBu
PCL(Point Cloud Library)是一个开源的计算机视觉库,其中内置了许多点云处理的算法,包括点云曲面重建点云曲面重建是将无序的点云数据转换为连续的曲面模型,常用于三维建模、机器人视觉导航、医学图像处理等领域。PCL提供了多种点云曲面重建算法,包括基于网格的方法和基于隐式曲面的方法。本文将介绍其中的一种基于网格的方法——Poisson重建。 Poisson重建算法的基本思想是,利用点云数据构建一个无向加权图,并将重建曲面模型视为该图的等势面。在该图上进行拉普拉斯平滑,得到的曲面为最小化拉普拉斯能量的解。 下面是Poisson重建的具体步骤: 1. 对点云进行预处理,去除离群点、滤波、下采样等操作,以减少噪声和计算量。 2. 构建点云的法向量估计算法。Poisson重建算法需要法向量信息作为重建的基础,PCL提供了多种法向量估计算法,如基于协方差矩阵的法向量估计、基于法向量的一致性检测等。 3. 构建无向加权图。Poisson重建算法将点云数据视为一个无向加权图,其中每个点表示一个顶点,每个点之间根据一定的规则连接一条边,边权重表示两个点之间的相似度。PCL中常用的连接规则为K近邻和半径搜索。 4. 执行Poisson重建算法。在无向加权图上进行拉普拉斯平滑,得到的曲面为最小化拉普拉斯能量的解。Poisson重建算法还可以对结果进行后处理,如光滑、去除孔洞等。 下面是Poisson重建算法的Python实现代码: ``` import pcl # 加载点云数据 cloud = pcl.load('input_cloud.pcd') # 预处理 cloud_filtered = cloud.make_statistical_outlier_filter().filter() cloud_downsampled = cloud_filtered.make_voxel_grid_filter().filter() cloud_normals = cloud_downsampled.make_normal_estimation().compute() # 构建无向加权图 search_tree = cloud_downsampled.make_kdtree() mls = cloud_downsampled.make_moving_least_squares() mls.set_search_radius(0.1) mls.set_polynomial_order(2) mls.set_upsampling_method(pcl.MovingLeastSquares.NONE) cloud_smoothed = mls.process() poisson = cloud_smoothed.make_poisson_reconstruction() poisson.set_depth(9) poisson.set_iso_divide(8) poisson.set_point_weight(4) poisson.set_samples_per_node(1.5) poisson.set_confidence(false) poisson.set_output_polygons(true) reconstructed = poisson.reconstruct() ``` 其中,'input_cloud.pcd'为点云数据文件名,需要先使用PCL进行格式转换。以上代码仅为示例,具体参数需要根据实际应用场景进行调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值