PCL拟合多条直线

手写ransac拟合多条直线

投影+提取边缘点后的点云,然后拟合,直接上代码。
因为是投影到xy平面上的点云,所有只有k b 两个直线参数。
还是先看效果图吧。。。
恩,,每次选择拟合直线的点数放在了函数里,代码写的较差,见谅哈哈哈

当时只是上传代码,没做过多介绍,没想到有朋友关注评论,这里对原理做下解释,大家可以根据各自具体的情况和需求进行改进。
输入的点云类似矩形,我的目标是提取4条直线参数。
每一条直线提取通过迭代若干次得到,并选择当前直线上点数最多的作为第n条直线,具体过程是每次随机选取m(m>=3,可以自行选择不同参数调整观察效果)个点进行最小二乘拟合得到直线L,并记录L上一定阈值内的点数和点索引,最后选取最优直线。得到一条拟合直线后,会把属于这条L上的点索引去除掉。
然后继续进行下一条直线的拟合过程,最后根据拟合得到的直线数和剩余的点数判断是否停止迭代。
请添加图片描述

#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <iostream>
#include <string>     
#include<cmath>
#include <time.h>

using namespace std;

class fitLineRansac
{
  
public:
  vector<vector<float>> ransac_line(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, float dist, int iterate)
  {
    /*** 
     *		dist: 点到直线距离小于dist 即当前点在直线上
     *      iterate: 迭代次数
    ***/
    int allPts = cloud->points.size();
    vector<int> cur_ptsIdx(allPts);			//提取当前直线后,不在当前直线上的其他点索引
    for(int i=0; i<allPts; i++)
      cur_ptsIdx[i] = i;

    int r=0;
    vector<int> line_ptsIdx, all_ptsIdx(allPts);
    vector<vector<float>> lines;			//所有直线参数 [0]: k, [1]: b
    vector<float> cur_line(2);
    Eigen::Vector3f line_model, best_lineModel;
    while (1)
    { 
      int line_pts=0, tmp_pts;
      if(r >= 2) iterate = iterate / 3;
      if(cur_ptsIdx.size()<10 && cur_ptsIdx.size()>3) iterate = 4;
      for(int i=0; i<iterate; i++){
        line_model = leastSquare(cloud, cur_ptsIdx, dist);
        tmp_pts = line_model[2] / 1;
        if(tmp_pts > line_pts){
          line_pts = tmp_pts;
          best_lineModel = line_model;
          line_ptsIdx = tmp_ptsIdx;
        }
        tmp_ptsIdx.clear();
      }
      
      cur_line[0] = best_lineModel[0]; cur_line[1] = best_lineModel[1];
      lines.push_back(cur_line);
      cout<<"第 "<<r++<<" 次循环,直线参数:  "<<best_lineModel<<endl;
    //   cout<<"所有点的个数:  "<<cur_ptsIdx.size()<<endl;
    //   cout<<"当前直线上的点数:"<<line_ptsIdx.size()<<endl;
	
	//得到剩余点的索引
    for(int i=0; i<line_ptsIdx.size(); i++)
      all_ptsIdx[line_ptsIdx[i]] = 1;
    cur_ptsIdx.clear();
    for(int j=0; j<allPts; j++)
      if(!all_ptsIdx[j]) cur_ptsIdx.push_back(j);
      
      if(cur_ptsIdx.size() < 5){
        cout<<"点数剩余过少......."<<endl;
        break;  
      }
    } 
    view(cloud, lines);
    return lines;
  }

private:
  vector<int> tmp_ptsIdx;			//当前直线上的点数
  Eigen::Vector3f leastSquare(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, vector<int> pIdx, float dist)
  {
    //求解给定若干点的直线方程
    float a=0, B=0, c=0, d=0;          //a: x之和 b: y之和  c: x平方和  d: x*y之和  e: 样本数量
    int s = pIdx.size();
    vector<int> cur_ptsIdx = rangedRand(0, s, 4);					//4:每次选4点用最小二乘拟合直线
    int e = cur_ptsIdx.size();
    for(int i=0; i<e; i++){
      a += cloud->points[pIdx[cur_ptsIdx[i]]].x;
      B += cloud->points[pIdx[cur_ptsIdx[i]]].y;
      c += cloud->points[pIdx[cur_ptsIdx[i]]].x * cloud->points[pIdx[cur_ptsIdx[i]]].x;
      d += cloud->points[pIdx[cur_ptsIdx[i]]].x * cloud->points[pIdx[cur_ptsIdx[i]]].y;
    }
    float k, b;
    float tmp = e * c - a * a;
    if(abs(tmp) > 0.0005){
      b = (c*B - a*d) / tmp;
      k = (e*d - a*B) / tmp;
    }
    else{
      k=1;b=0;
    }
    
    //求每一个点到直线的距离,小于dist, 即在直线上
    int line_pnum=0;
    for(int i=0; i<s; i++){
      float d, numerator, denominator;             //分子分母        点到直线的距离 d = |kx - y + b| / sqrt(k^2 + 1)
      numerator = abs(k*cloud->points[pIdx[i]].x - cloud->points[pIdx[i]].y + b);
      denominator = sqrt(k*k + 1);
      d = numerator / denominator;
      if(d < dist){
        line_pnum++;
        tmp_ptsIdx.push_back(pIdx[i]);
      }
    }
    Eigen::Vector3f line_model;              
    line_model[0] = k;line_model[1] = b; line_model[2] = line_pnum;
    return line_model;
  }

  vector<int> rangedRand( int range_begin, int range_size, int n )
  {
    int i;vector<int> indices;
    // srand((unsigned)time(NULL));           //生成随机数种子
    for ( i = 0; i < n; i++ )
    {
      int u = rand()%range_size + range_begin; //生成[range_begin, range_begin+range_size]内的随机数
      // cout<<i<<": "<<u<<endl;
      indices.push_back(u);
    }
    return indices;
  }

  void view(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, vector<vector<float>> lines)
  { 
    cout<<"共找到   "<<lines.size()<<"  条直线!!!!!!!!"<<endl;
    pcl::visualization::PCLVisualizer viewer("Viewer");
    viewer.setBackgroundColor(0.5, 0.5, 0.5, 0);
    viewer.addPointCloud(cloud, "pc");
    viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "pc"); 
    for(int i=0; i<lines.size(); i++){
        pcl::PointXYZ p1(200, 200*lines[i][0]+lines[i][1], 0);
        pcl::PointXYZ p2(-500, -500*lines[i][0]+lines[i][1], 0);
        cout<<"直线     "<<i+1<<"   上两点坐标: "<<p1<<",  "<<p2<<endl;
        viewer.addLine(p1, p2, 240-40*i, 0, 0, "line"+to_string(i), 0);
    }
    while (!viewer.wasStopped()) {
    	viewer.spinOnce();
    }
  }

};

int main(int argc, char**argv) {
  // 加载点云模型
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
   pcl::io::loadPCDFile (argv[1], *cloud);
   
  fitLineRansac ransac;
  ransac.ransac_line(cloud, 7, 50);
  return 0;
}

  • 16
    点赞
  • 89
    收藏
    觉得还不错? 一键收藏
  • 22
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值