提取墙体边界线【C++PCL】

作者:迅卓科技
简介:本人从事过多项点云项目,并且负责的项目均已得到好评!
公众号:迅卓科技,一个可以让您可以学习点云的好地方

目录

        1.前言

        2.开发环境

        3.示例(效果图)及说明

        4.源码如下


1.前言

        我们团队注重每一个细节,确保代码的可读性、可维护性和可扩展性达到最高标准。我们严格遵循行业最佳实践,采用模块化和面向对象的设计原则,使得我们的代码结构清晰且易于理解。

        我们的点云服务更是引以为傲。我们的技术团队拥有深厚的专业知识和丰富的经验,能够提供高度准确和高效的点云处理服务。无论是点云的获取、存储还是分析,我们都能够满足客户的需求,并提供出色的解决方案。

        我们的技术实力也是无与伦比的,拥有广泛的技术背景和深入的专业知识。无论是算法优化、性能调优还是技术创新,我们都能够应对自如,并为客户提供最佳的技术支持。

2.开发环境

下载源代码并解压缩。
在Visual Studio中打开项目。
根据需要配置PCL库。
编译和运行项目。

3.示例(效果图)及说明

PGetMaxPlaneCoe函数:

        使用RANSAC算法从点云中提取最大平面的系数和内点索引。该函数能够鲁棒地识别点云中主要平面的特征,通过RANSAC迭代,计算最适合的平面模型,并返回其法向量和内点索引,从而实现对点云的平面分割。

PExtracting函数:

        根据给定的内点索引,从点云中提取或排除点,生成提取后的点云。这个功能强大的函数使用户能够根据特定需求,从原始点云中提取感兴趣的区域,或者排除掉一些干扰点,为后续处理提供了定制化的数据准备。

PGetPlaneCoe函数:

        使用RANSAC算法从点云中提取与给定法线方向垂直的平面,并将剩余的点云分割成多个平面。通过指定法线方向,该函数能够提取垂直于该方向的平面,从而在点云中捕捉到更多的几何信息,同时将剩余点云按照平面分割,实现对点云的多平面分析。

PAshape函数:

        使用凹壳算法对点云进行凹壳重构,生成凹壳点云。凹壳算法通过迭代过程,逐渐构建出点云的凹壳,形成具有几何形状信息的点云模型。该函数有助于提取点云中的整体形状特征,为形状分析和识别提供了基础。

PMultipleLines函数:

        使用RANSAC算法从点云中提取多条直线,并将直线分割成多个段。通过RANSAC算法,该函数能够有效地识别点云中的多条直线,同时将每条直线分割成不同的段,为点云的分段处理提供了可靠的工具。

Spherical_Search函数:

        在点云中进行球形搜索,找到给定球半径内的邻域点,并返回其索引。这个函数实现了基于球形搜索的点云分析,可以用于提取点云中局部区域的邻域点,为局部几何特征的计算提供了灵活而高效的方法。

getWall函数:

        根据点云中点的强度值,提取出墙面的点,并返回其索引。通过对点云的强度值进行筛选,该函数实现了对墙面点的提取,可应用于建筑结构的分析,为特定应用场景提供了有效的数据过滤工具。

4.源码如下

main.cpp

#include "File.h"
#include "Fliter.h"
#include <iostream>
#include <algorithm>
#include <pcl/io/pcd_io.h>
#include <pcl/console/time.h>


int main()
{
	//Initialize data to prevent data from not being empty
	pcl::PointCloud<pcl::PointXYZI>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZI>);
	pcl::PointCloud<pcl::PointXYZI>::Ptr input_cloud(new pcl::PointCloud<pcl::PointXYZI>);
	pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_Boundary(new pcl::PointCloud<pcl::PointXYZI>);
	pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_Boundary_RGB(new pcl::PointCloud<pcl::PointXYZI>);
	pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_socket(new pcl::PointCloud<pcl::PointXYZI>);
	pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_socket_all(new pcl::PointCloud<pcl::PointXYZI>);
	pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_end(new pcl::PointCloud<pcl::PointXYZI>);

	pcl::ModelCoefficients::Ptr coefficients_underside(new pcl::ModelCoefficients);
	pcl::ModelCoefficients::Ptr coefficients_side(new pcl::ModelCoefficients);
	pcl::ModelCoefficients::Ptr coefficients_Line(new pcl::ModelCoefficients);

	pcl::PointIndices::Ptr inliers(new pcl::PointIndices);

	pcl::console::TicToc tt;
	tt.tic();
	//Load pointcloud from the inputPath
	//pcl::io::loadPCDFile("input/Cloud.pcd", *input_cloud);
	ReadFileToCloud("input/Cloud.txt", input_cloud);
	//here we creat a vector to put into the point cloud we will use next
	std::vector<pcl::PointCloud<pcl::PointXYZI>::Ptr> cloud_CutVector;
	//Using the PGetMaxPlaneCoe and PExtracting to get underside and the remain pointcloud
	PGetPlaneCoe(input_cloud, cloud_CutVector);


	for (int i = 0; i < cloud_CutVector.size(); ++i)
	{
		inliers->indices.clear();
		getWall(inliers, cloud_CutVector[i]);
		PExtracting(inliers, cloud_CutVector[i], cloud_socket, false);
		*cloud_socket_all += *cloud_socket;
	}

	pcl::io::savePCDFileBinary("output/cloud_socket_all.pcd", *cloud_socket_all);

	//Get the boundary of the underside
	for (int i = 0; i < cloud_CutVector.size(); ++i)
	{
		PAshape(cloud_CutVector[i], cloud_Boundary);

		std::vector<pcl::PointCloud<pcl::PointXYZI>::Ptr> cloud_Cut;
		PMultipleLines(cloud_Boundary, cloud_Cut, 2);

		cloud_Boundary->clear();
		for (int i = 0; i < cloud_Cut.size(); ++i)
		{
			*cloud_Boundary += *cloud_Cut[i];
		}

		uint8_t R = rand() % 255 + 1;
		uint8_t G = rand() % 255 + 1;
		uint8_t B = rand() % 255 + 1;
		pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_RGB(new pcl::PointCloud<pcl::PointXYZI>);
		*cloud_Boundary_RGB += *cloud_Boundary;
	}

	//For getting a good quality pointCloud,we will move forward a single step
	inliers->indices.clear();
	Spherical_Search(inliers, input_cloud, cloud_Boundary_RGB);
	cloud_Boundary_RGB->clear();
	PExtracting(inliers, input_cloud, cloud_Boundary_RGB, true);

	//For getting a good quality pointCloud,we will move forward a single step
	inliers->indices.clear();
	Spherical_Search(inliers, cloud_Boundary_RGB, cloud_socket_all);
	PExtracting(inliers, cloud_Boundary_RGB, cloud_end, true);

	//here you can load the point cloud to look wrong or right
	pcl::io::savePCDFileBinary("output/cloud_end.pcd", *cloud_end);
	std::cout << "Ok ! The progress is finished,and the time of this progress is :" << tt.toc() / 1000 << "s!" << std::endl;
}

Fliter.h

#pragma once
#include <Eigen/Core>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/common/angles.h>
#include <pcl/common/transforms.h>
#include <pcl/features/boundary.h> //边界提取
#include <pcl/common/transforms.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/filters/passthrough.h>
#include <pcl/kdtree/kdtree_flann.h> 
#include <pcl/surface/concave_hull.h>
#include <pcl/features/normal_3d_omp.h>	
#include <pcl/filters/extract_indices.h>	
#include <pcl/filters/project_inliers.h>
#include <pcl/segmentation/region_growing.h>
#include <pcl/sample_consensus/model_types.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/segmentation/extract_clusters.h>

void PGetMaxPlaneCoe(pcl::PointCloud<pcl::PointXYZI>::Ptr& cloud, pcl::ModelCoefficients::Ptr& coefficients, pcl::PointIndices::Ptr& inliers, double Threshold);

void PExtracting(pcl::PointIndices::Ptr& inliers, pcl::PointCloud<pcl::PointXYZI>::Ptr& cloud, pcl::PointCloud<pcl::PointXYZI>::Ptr& cloud_extracted, bool negative);

void PGetPlaneCoe(pcl::PointCloud<pcl::PointXYZI>::Ptr& cloud, std::vector<pcl::PointCloud<pcl::PointXYZI>::Ptr>& cloud_Plane);

void PAshape(pcl::PointCloud<pcl::PointXYZI>::Ptr& cloud, pcl::PointCloud<pcl::PointXYZI>::Ptr& cloud_hull);

void PMultipleLines(pcl::PointCloud<pcl::PointXYZI>::Ptr cloud, std::vector<pcl::PointCloud<pcl::PointXYZI>::Ptr>& cloud_Cut, int n);

void Spherical_Search(pcl::PointIndices::Ptr& inliers, pcl::PointCloud<pcl::PointXYZI>::Ptr cloud, pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_search);

void getWall(pcl::PointIndices::Ptr& inliers, pcl::PointCloud<pcl::PointXYZI>::Ptr cloud);

Fliter.cpp

#include "Fliter.h"
#include <pcl/io/pcd_io.h>


void PGetMaxPlaneCoe(pcl::PointCloud<pcl::PointXYZI>::Ptr& cloud, pcl::ModelCoefficients::Ptr& coefficients, pcl::PointIndices::Ptr& inliers, double Threshold)
{
    pcl::SACSegmentation<pcl::PointXYZI> seg;
    seg.setOptimizeCoefficients(true);
    seg.setModelType(pcl::SACMODEL_PLANE);    //分割模型
    seg.setMethodType(pcl::SAC_RANSAC);       //随机参数估计方法
    seg.setMaxIterations(1000);               //最大的迭代的次数
    seg.setDistanceThreshold(Threshold);      //设置阀值
    seg.setInputCloud(cloud);
    seg.segment(*inliers, *coefficients);
}

void PExtracting(pcl::PointIndices::Ptr& inliers, pcl::PointCloud<pcl::PointXYZI>::Ptr& cloud, pcl::PointCloud<pcl::PointXYZI>::Ptr& cloud_extracted, bool negative)
{
    //cloud_extracted->clear();
    pcl::ExtractIndices<pcl::PointXYZI> extract;
    extract.setInputCloud(cloud);
    extract.setIndices(inliers);
    extract.setNegative(negative);//如果设为true,可以提取指定index之外的点云
    extract.filter(*cloud_extracted);
}


void PGetPlaneCoe(pcl::PointCloud<pcl::PointXYZI>::Ptr& cloud, std::vector<pcl::PointCloud<pcl::PointXYZI>::Ptr>& cloud_Plane)
{

    pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients);
    pcl::PointIndices::Ptr inliers(new pcl::PointIndices);
    pcl::SACSegmentation<pcl::PointXYZI> seg;
    seg.setOptimizeCoefficients(true);  
    seg.setModelType(pcl::SACMODEL_PERPENDICULAR_PLANE);
    seg.setMethodType(pcl::SAC_RANSAC);    
    seg.setDistanceThreshold(0.04);      

    float angle = 5;
    float EpsAngle = pcl::deg2rad(angle);  
    Eigen::Vector3f Axis(0.0, 1.0, 0.0);
    seg.setAxis(Axis);                    
    seg.setEpsAngle(EpsAngle);          
    seg.setInputCloud(cloud);      
    seg.segment(*inliers, *coefficients); 
    pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_E(new pcl::PointCloud<pcl::PointXYZI>);
    PExtracting(inliers, cloud, cloud_E, true);
    //pcl::io::savePCDFileBinary("output/cloud_E.pcd", *cloud_E);

    int n_size = cloud_E->points.size();

    while (cloud_E->points.size() > 0.01 * n_size)
    {
        pcl::PointIndices::Ptr inliers(new pcl::PointIndices);
        pcl::ModelCoefficients::Ptr coefficients_underside(new pcl::ModelCoefficients);
        pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_plane(new pcl::PointCloud<pcl::PointXYZI>);
        pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_else(new pcl::PointCloud<pcl::PointXYZI>);
        //Using the PGetMaxPlaneCoe and PExtracting to get underside and the remain pointcloud
        PGetMaxPlaneCoe(cloud_E, coefficients_underside, inliers, 0.02);
        PExtracting(inliers, cloud_E, cloud_plane, false);
        PExtracting(inliers, cloud_E, cloud_else, true);
 
        //pcl::io::savePCDFileBinary("output/cloud_plane.pcd", *cloud_plane);
        cloud_Plane.push_back(cloud_plane);
        *cloud_E = *cloud_else;
    }
}

void PAshape(pcl::PointCloud<pcl::PointXYZI>::Ptr& cloud, pcl::PointCloud<pcl::PointXYZI>::Ptr& cloud_hull)
{
    pcl::ConcaveHull<pcl::PointXYZI> chull;
    chull.setInputCloud(cloud); // 输入点云为投影后的点云
    chull.setAlpha(0.1);        // 设置alpha值为0.1
    chull.reconstruct(*cloud_hull);
}

void PMultipleLines(pcl::PointCloud<pcl::PointXYZI>::Ptr cloud, std::vector<pcl::PointCloud<pcl::PointXYZI>::Ptr>& cloud_Cut, int n)
{
    pcl::PointIndices::Ptr inliers(new pcl::PointIndices());
    pcl::SACSegmentation<pcl::PointXYZI> seg;   
    seg.setOptimizeCoefficients(true);          
    seg.setModelType(pcl::SACMODEL_LINE);                  
    seg.setMethodType(pcl::SAC_RANSAC);               
    seg.setMaxIterations(1000);                         
    seg.setDistanceThreshold(0.01);                      

    int nr_point = cloud->points.size();
    while (cloud->points.size() > 0.1 * nr_point)// 从0循环到5执行6次,并且每次cloud的点数必须要大于原始总点数的0.1倍
    {
        pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_line(new pcl::PointCloud<pcl::PointXYZI>);
        pcl::PointCloud<pcl::PointXYZI>::Ptr outside(new pcl::PointCloud<pcl::PointXYZI>);

        pcl::ModelCoefficients coefficients;
        seg.setInputCloud(cloud);                     					 
        seg.segment(*inliers, coefficients);             

        pcl::ExtractIndices<pcl::PointXYZI> extract;   
        extract.setInputCloud(cloud);
        extract.setIndices(inliers);
        extract.setNegative(false);                   
        extract.filter(*cloud_line);
        if (cloud_line->points.size() > 50)
        {
            cloud_Cut.push_back(cloud_line);
        }
        extract.setNegative(true);                    
        extract.filter(*outside);                     
        cloud.swap(outside);                          
    }
}

void Spherical_Search(pcl::PointIndices::Ptr& inliers, pcl::PointCloud<pcl::PointXYZI>::Ptr cloud, pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_search)
{
    double spherical_radius = 0.05;
    pcl::KdTreeFLANN<pcl::PointXYZI> kdtree; //创建KdTreeFLANN 对象 注意<>这里的点云类型要保持一致
    kdtree.setInputCloud(cloud);//添加点云
    std::vector<int> pointIdxRadiusSearch;
    std::vector<float> pointRadiusSquaredDistance;//邻域点的待搜索点距离的平方
    std::vector<float> pointIndex;
    pcl::PointXYZI point_source;
    for (int i = 0; i < cloud_search->points.size(); ++i)
    {
        if (kdtree.radiusSearch(cloud_search->points[i], spherical_radius, pointIdxRadiusSearch, pointRadiusSquaredDistance) > 0)
        {
            for (size_t j = 0; j < pointIdxRadiusSearch.size(); ++j)
            {
                auto it = find(pointIndex.begin(), pointIndex.end(), pointIdxRadiusSearch[j]);
                if (it == pointIndex.end())//防止重复点云
                {
                    pointIndex.push_back(pointIdxRadiusSearch[j]);
                    inliers->indices.push_back(pointIdxRadiusSearch[j]);
                }
            }
        }
    }
}

void getWall(pcl::PointIndices::Ptr& inliers, pcl::PointCloud<pcl::PointXYZI>::Ptr cloud)
{
    std::vector<double>iden;
    for (int i = 0; i < cloud->points.size(); ++i)
    {
        iden.push_back(cloud->points[i].intensity);
    }
    std::sort(iden.begin(), iden.end());

    double bigIden = iden.at(iden.size() - 1);
    double range = 0.003;

    for (int i = 0; i < cloud->points.size(); ++i)
    {
        if (bigIden - cloud->points[i].intensity < range)
        inliers->indices.push_back(i);
    }
}

File.h

//格式文件读取
#include <string>
#include <pcl/io/pcd_io.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>

int ReadFileToCloud(const std::string& pointCloudPath, pcl::PointCloud<pcl::PointXYZI>::Ptr& cloud);

File.cpp

#include "File.h"

int ReadFileToCloud(const std::string& pointCloudPath, pcl::PointCloud<pcl::PointXYZI>::Ptr& cloud)
{
#if true
    std::ifstream m_filestream;
    m_filestream.open(pointCloudPath.c_str(), std::ios_base::in);

    if (m_filestream.is_open())
    {
        while (m_filestream.peek() != EOF)
        {
            char buf[128] = { 0 };
            m_filestream.getline(buf, 128);

            ///
            std::string strPt = buf;
            pcl::PointXYZI pt;
            try
            {
                int b = 0; int e = 0;
                e = strPt.find(' ', b);
                if (-1 == e) continue;
                pt.x = atof(strPt.substr(0, e - b).c_str());

                b = e + 1;
                e = strPt.find(' ', b);
                if (-1 == e) continue;
                pt.y = atof(strPt.substr(b, e - b).c_str());

                b = e + 1;
                e = strPt.find(' ', b);
                if (-1 == e) continue;
                pt.z = atof(strPt.substr(b, e - b).c_str());

                b = e + 1;
                e = strPt.find(' ', b);
                pt.intensity = atof(strPt.substr(b, e - b).c_str());
            }
            catch (const std::exception&)
            {
                continue;
            }
            ///
            cloud->push_back(pt);
        }
        m_filestream.close();
    }
#endif

    if (cloud->size() == 0)
    {
        return 0;
    }
    return cloud->size();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

迅卓科技

绝对好东西

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值