天天看点

ransac算法_RANSAC直线拟合(五十)

ransac算法_RANSAC直线拟合(五十)

方法1:

/*************************************************Author:  SayheyheyheyDate:2019-7-4Description:根据伪代码实现通用的RANSAC模板自定义线性模型,实现两种方式的直线拟合**************************************************/#include #include #include #include #include #include #include using namespace cv;#include using namespace std;//数据点类型struct st_point{  st_point(){};  st_point(double X, double Y) :x(X), y(Y){};  double x;  double y;};/*** @brief 线性模型** Ax+By+C = 0;*/class linearModel{public:  //待估计参数  double A, B, C;public:  linearModel(){};  ~linearModel(){};  //使用两个点对直线进行初始估计  void Update(vector &data, set<int> &maybe_inliers){    assert(maybe_inliers.size() == 2);    //初始化的点不为2个,报错    //根据索引读取数据    vector<int> points(maybe_inliers.begin(), maybe_inliers.end());    st_point pts1 = data[points[0]];    st_point pts2 = data[points[1]];    //根据两个点计算直线参数(得到其中一组解,可以任意比例缩放)    double delta_x = pts2.x - pts1.x;    double delta_y = pts2.y - pts1.y;    A = delta_y;    B = -delta_x;    C = -delta_y*pts2.x + delta_x*pts2.y;  }  //返回点到直线的距离  double computeError(st_point point){    double numerator = abs(A*point.x + B*point.y + C);    double denominator = sqrt(A*A + B*B);    return numerator / denominator;  }  //根据一致点的集合对直线进行重新估计  double Estimate(vector &data, set<int> &consensus_set){    assert(consensus_set.size() >= 2);    //求均值 means    double mX, mY;    mX = mY = 0;    //for (auto &index: consensus_set){    for(int index = 0; index < consensus_set.size(); index++)    {      mX += data[index].x;      mY += data[index].y;    }    mX /= consensus_set.size();    mY /= consensus_set.size();    //求二次项的和 sum    double sXX, sYY, sXY;    sXX = sYY = sXY = 0;    //for (auto &index : consensus_set){    for (int index = 0; index < consensus_set.size(); index++)    {      st_point point;      point = data[index];      sXX += (point.x - mX)*(point.x - mX);      sYY += (point.y - mY)*(point.y - mY);      sXY += (point.x - mX)*(point.y - mY);    }        //解法1:求y=kx+b的最小二乘估计,然后再转换成一般形式    //参考 https://blog.csdn.net/hookie1990/article/details/91406309    bool isVertical = sXY == 0 && sXX < sYY;    bool isHorizontal = sXY == 0 && sXX > sYY;    bool isIndeterminate = sXY == 0 && sXX == sYY;    double k = 0;    double b = 0;    if (isVertical)    {    A = 1;    B = 0;    C = mX;    }    else if (isHorizontal)    {    A = 0;    B = 1;    C = mY;    }    else if (isIndeterminate)    {    A = 0;    B = 0;    C = 0;    }    else    {    k = (sYY - sXX + sqrt((sYY - sXX) * (sYY - sXX) + 4.0 * sXY * sXY)) / (2.0 * sXY);  //斜率    b = mY - k * mX;                              //截距    //正则化项,使得A^2+B^2 = 1;    double normFactor = 1 / sqrt(1 + k*k);    A = normFactor * k;    B = -normFactor;    C = normFactor*b;    }    //返回残差    if (isIndeterminate){    return 0;    }    double error = A*A*sXX + 2 * A*B*sXY + B*B*sYY;    error /= consensus_set.size();    return error;        解法2:    //if (sXX == 0){    //  A = 1;    //  B = 0;    //  C = -mX;    //}    //else{    //  A = sXY / sXX;    //  B = -1;    //  C = mY - A*mX;    //  //归一化令A^2+B^2 = 1;    //  double normFactor = sqrt(A*A + B*B);    //  A /= normFactor;    //  B /= normFactor;    //  C /= normFactor;    //}    //double error = A*A*sXX + 2 * A*B*sXY + B*B*sYY;    //error /= consensus_set.size();    //求平均误差    //return error;  }};/*** @brief 运行RANSAC算法** @param[in]  data  一组观测数据* @param[in]  n    适用于模型的最少数据个数* @param[in]  k    算法的迭代次数* @param[in]  t    用于决定数据是否适应于模型的阀值* @param[in]  d    判定模型是否适用于数据集的数据数目 * @param[in&out]  model  自定义的待估计模型,为该函数提供Update、computeError和Estimate三个成员函数*              运行结束后,模型参数被设置为最佳的估计值* @param[out]  best_consensus_set  输出一致点的索引值* @param[out]  best_error  输出最小损失函数*/template<typename T, typename U>int ransac(vector &data, int n, int k, double t, int d,  U &best_model, set<int> &best_consensus_set, double &best_error){  //1.初始化  int  iterations = 0;  //迭代次数  U maybe_model;      //使用随机选点初始化求得的模型  U better_model;      //根据符合条件的一致点拟合出的模型  int isFound = 0;          //算法成功的标志  set<int> maybe_inliers;        //初始随机选取的点(的索引值)  //best_error = DBL_MAX;  //初始化为最大值  best_error = 1.7976931348623158e+308;  default_random_engine rng(time(NULL));          //随机数生成器  uniform_int_distribution<int> dist(0, data.size() - 1);  //采用均匀分布  //2.主循环  while (iterations < k){    //3.随机选点    maybe_inliers.clear();    while (1){      int index = dist(rng);      maybe_inliers.insert(index);      if (maybe_inliers.size() == n){        break;      }    }    //4.计算初始值    maybe_model.Update(data, maybe_inliers);                //自定义函数,更新模型    set<int> consensus_set(maybe_inliers.begin(), maybe_inliers.end());    //选取模型后,根据误差阈值t选取的内点(的索引值)    //5.根据初始模型和阈值t选择内点      for (int i = 0; i < data.size(); i++){      double error_per_item = maybe_model.computeError(data[i]);      if (error_per_item < t){        consensus_set.insert(i);      }    }    //6.根据全部的内点重新计算模型    if (consensus_set.size() > d){      double this_error = better_model.Estimate(data, consensus_set);    //自定义函数,(最小二乘)更新模型,返回计算出的误差      //7.若当前模型更好,则更新输出量      if (this_error < best_error){        best_model = better_model;        best_consensus_set = consensus_set;        best_error = this_error;      }      isFound = 1;    }    ++iterations;  }  return isFound;}int main(){  //1.读入数据  //int data_size;    //输入第一行表示数据大小  //cin >> data_size;  //vector Points(data_size);  //for (int i = 0; i < data_size; i++){  //  cin >> Points[i].x >> Points[i].y;  //}  //测试用  clock_t start_time1 = clock();  vector Points;  //{ st_point(3, 4), st_point(6, 8), st_point(9, 12), st_point(15, 20), st_point(10, -10)};  //st_point(3, 4), st_point(6, 8), st_point(9, 12), st_point(15, 20), st_point(10, -10);  Points.push_back(st_point(3, 4));  Points.push_back(st_point(6, 8));  Points.push_back(st_point(9, 12));  Points.push_back(st_point(15, 20));  Points.push_back(st_point(10, -10));  int data_size = Points.size();  //2.设置输入量  int k = 50;        //最大迭代次数  int n = 2;        //适用于模型的最少数据个数  double t = 0.01;    //用于决定数据是否适应于模型的阀值  int d = data_size*0.5;  //判定模型是否适用于数据集的数据数目   //3.初始化输出量  linearModel best_model;      //最佳线性模型  set<int> best_consensus_set;  //记录一致点索引的set  double best_error;        //最小残差  //4.运行RANSAC        int status = ransac(Points, n, k, t, d, best_model, best_consensus_set, best_error);  //5.输出  cout <<"best_model: "<< best_model.A << " " << best_model.B << " " << best_model.C << endl;  //cout << best_consensus_set << endl;  cout <<"best_error: "<< best_error << endl;  clock_t end_time1 = clock();  double processTime1 = static_cast<double>(end_time1 - start_time1) / CLOCKS_PER_SEC * 1000;  cout << "processTime1: " << processTime1 << endl;  system("pause");  return 0;}
           
ransac算法_RANSAC直线拟合(五十)

参考文献:

https://blog.csdn.net/LaplaceSmoothing/article/details/94581854

方法2:

#include #include #include #include #include #include using namespace cv;#include // Date: 2018-01-09// Author: HSW//// RANSAC 直线拟合算法// 其他的类似//using namespace std;typedef struct st_Point{  double x;  double y;}st_Point;// 基于RANSAC算法的直线拟合// pstData: 指向存储数据的指针// dataCnt: 数据点个数// lineParameterK: 直线的斜率// lineParameterB: 直线的截距// minCnt: 模型(直线)参数估计所需的数据点的个数// maxIterCnt: 最大迭代次数// maxErrorThreshold: 最大误差阈值// consensusCntThreshold: 模型一致性判断准则// modelMeanError: 模型误差// 返回值:返回0表示获取最优模型, 否则表示未获取最优模型int ransacLiner(st_Point* pstData, int dataCnt, int minCnt, double maxIterCnt, int consensusCntThreshold, double maxErrorThreshod, double& A, double& B, double& C, double& modelMeanError){  default_random_engine rng;  uniform_int_distribution<unsigned> uniform(0, dataCnt - 1);  rng.seed(10); // 固定随机数种子  set<unsigned int> selectIndexs;     // 选择的点的索引  vector selectPoints;      // 选择的点  set<unsigned int> consensusIndexs;  // 满足一致性的点的索引  double line_A = 0;  double line_B = 0;  double line_C = 0;  modelMeanError = 0;  int isNonFind = 1;  unsigned int      bestConsensusCnt = 0; // 满足一致性估计的点的个数  int iter = 0;  while (iter < maxIterCnt)  {    selectIndexs.clear();    selectPoints.clear();    // Step1: 随机选择minCnt个点    while (1)    {      unsigned int index = uniform(rng);      selectIndexs.insert(index);      if (selectIndexs.size() == minCnt)      {        break;      }    }    // Step2: 进行模型参数估计 (y2 - y1)*x - (x2 - x1)*y + (y2 - y1)x2 - (x2 - x1)y2= 0    set<unsigned int>::iterator selectIter = selectIndexs.begin();    while (selectIter != selectIndexs.end())    {      unsigned int index = *selectIter;      selectPoints.push_back(pstData[index]);      selectIter++;    }    double deltaY = (selectPoints[1]).y - (selectPoints[0]).y;    double deltaX = (selectPoints[1]).x - (selectPoints[0]).x;    line_A = deltaY;    line_B = -deltaX;    line_C = -deltaY * (selectPoints[0]).x + deltaX * (selectPoints[0]).y;    // Step3: 进行模型评估: 点到直线的距离    int dataIter = 0;    double meanError = 0;    set<unsigned int> tmpConsensusIndexs;    while (dataIter < dataCnt)    {      double distance = (line_A * pstData[dataIter].x + line_B * pstData[dataIter].y + line_C) / sqrt(line_A*line_A + line_B*line_B);      distance = distance > 0 ? distance : -distance;      if (distance < maxErrorThreshod)      {        tmpConsensusIndexs.insert(dataIter);      }      meanError += distance;      dataIter++;    }    // Step4: 判断一致性: 满足一致性集合的最小元素个数条件 + 至少比上一次的好    if (tmpConsensusIndexs.size() >= bestConsensusCnt && tmpConsensusIndexs.size() >= consensusCntThreshold)    {      bestConsensusCnt = consensusIndexs.size();  // 更新一致性索引集合元素个数      modelMeanError = meanError / dataCnt;      consensusIndexs.clear();      consensusIndexs = tmpConsensusIndexs;        // 更新一致性索引集合      A = line_A;      B = line_B;      C = line_C;      isNonFind = 0;    }    iter++;  }  return isNonFind;}#define MAX_LINE_CORRECT_POINT_CNT   (40)#define MAX_LINE_NOISE_POINT_CNT     (5)int main(){  //st_Point dataPoints[MAX_LINE_CORRECT_POINT_CNT + MAX_LINE_NOISE_POINT_CNT];  //memset(dataPoints, 0, sizeof(dataPoints));  //int iter;  //for (iter = 0; iter < MAX_LINE_CORRECT_POINT_CNT; ++iter)  //{  //  dataPoints[iter].x = iter;  //  dataPoints[iter].y = iter * 2 + 5; // y = 2 * x + 5 数据  //}  //int totalCnt = MAX_LINE_CORRECT_POINT_CNT + MAX_LINE_NOISE_POINT_CNT;  //for (iter = MAX_LINE_CORRECT_POINT_CNT; iter < totalCnt; ++iter)  //{  //  dataPoints[iter].x = iter;  //  dataPoints[iter].y = iter * 2 + 1; // y = 2 * x + 1 噪声  //}  clock_t start_time1 = clock();  st_Point dataPoints[5];  dataPoints[0].x = 3;  dataPoints[0].y = 4;  dataPoints[1].x = 6;  dataPoints[1].y = 8;  dataPoints[2].x = 9;  dataPoints[2].y = 12;  dataPoints[3].x = 15;  dataPoints[3].y = 20;  dataPoints[4].x = 10;  dataPoints[4].y = -10;  //Points.push_back(st_point(6, 8));  //Points.push_back(st_point(9, 12));  //Points.push_back(st_point(15, 20));  //Points.push_back(st_point(10, -10));  double A = 0;  double B = 0;  double C = 0;  double meanError = 0;  //  参数不准确  if (!ransacLiner(dataPoints, 5, 2, 10, 2.5, 0.01, A, B, C, meanError))  {    cout << "A =         " << A << endl;    cout << "B =         " << B << endl;    cout << "C =         " << C << endl;    cout << "meanError = " << meanError << endl;  }  else  {    cout << "RANSAC Failed " << endl;  }  //  参数不准确  //if (!ransacLiner(dataPoints, totalCnt, 2, 30, 35, 0.1, A, B, C, meanError))  //{  //  cout << "A =         " << A << endl;  //  cout << "B =         " << B << endl;  //  cout << "C =         " << C << endl;  //  cout << "meanError = " << meanError << endl;  //}  //else  //{  //  cout << "RANSAC Failed " << endl;  //}   参数准确  //if (!ransacLiner(dataPoints, totalCnt, 2, 50, 35, 0.1, A, B, C, meanError))  //{  //  cout << "A =         " << A << endl;  //  cout << "B =         " << B << endl;  //  cout << "C =         " << C << endl;  //  cout << "meanError = " << meanError << endl;  //}  //else  //{  //  cout << "RANSAC Failed " << endl;  //}  clock_t end_time1 = clock();  double processTime1 = static_cast<double>(end_time1 - start_time1) / CLOCKS_PER_SEC * 1000;  cout << "processTime1"<< processTime1 << endl;  system("pause");  return 0;}
           
ransac算法_RANSAC直线拟合(五十)

参考文献:

https://blog.csdn.net/hit1524468/article/details/80375495

机器人实时纠偏系统(一)机器人实时纠偏(二)OpenCV+VS开发环境配置(三)结构光视觉的焊接机器人纠偏(四)结构光视觉的机器人焊接(五)结构光视觉的机器人焊接(六)机器人初始点导引(七)MATLAB标定相机参数(八)机器人的手眼标定(九)机器人坐标获取(十)机器人调试(十一)TCP/IP客户端API编程(十二)结构光传感器上位机界面多线程编程(十三)TCP&UDP(十四)C/C++ Programing(十五)机器人扫描与跟踪调试(十六)结构光传感器库函数(十七)结构光传感器编程(十八)C/C++ Programing(十九)C/C++ Programing(二十)结构光传感器编程(二十一)DX200操作要领(二十二)DX200操作要领(二十三)工装轴协调(二十四)无夹具协调(二十五)图像处理调试(二十六)STM32MODBUS_CRC编程(二十七)

在C++中调用Matlab函数(二十八)

机器人手眼标定MATLAB及C++实现

机器人位姿运算及Eigen的使用(三十)

OpenCV与Eigen矩阵运算(三十一)

VS中数据读写及OpenCV拟合(三十二)

VS2013配置OpenGL库(三十三)

曲线拟合/插值(三十四)

曲线拟合绘制滤波及机器人平移(三十五)

DX200操作要领—示教1(三十六)

直接打开与平移变换(三十七)PAM与镜像平移变换(三十八)

修改与编辑程序(三十九)

YRC1000 宏程序命令(四十)

程序编辑与试运行(四十一)

程序编辑与再现(四十二)

再现(四十三)

程序管理(四十四)

便捷功能(四十五)

便捷功能(四十六)

椭圆拟合(四十七)

RANSAC直线拟合(四十八)

读写CSV文件类(四十九)

ransac算法_RANSAC直线拟合(五十)