天天看點

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直線拟合(五十)