OpenCV cvFitLine - eiichiromomma/CVMLAB GitHub Wiki

(OpenCV) 直線フィッティング

座標点の集合から直線のフィッティングをする

基本的な流れ

座標点の集合を作成して、cvFitLineで最小二乗を使ったフィッティングをする。 cvSeqの使い方がミソ。

CvSeq(シーケンス)の作成

  1. CvMemStorageで空のストレージを作成

  2. cvCreateSeqで詰め込むデータの種類を指定

    CvMemStorage* storage = cvCreateMemStorage(0); CvSeq* point_seq = cvCreateSeq( CV_32FC2, sizeof(CvSeq), sizeof(CvPoint2D32f), storage );

ここではfloatのCvPoint2D32f型(中身はfloatのxとy)を詰め込む設定。

使用した後は

cvClearSeq(point_seq);
cvReleaseMemStorage(&storage);

として解放する。 やらないと繰り返し使う際にメモリを食い潰す。

座標点の詰め込み

(fx,fy)という座標を与える場合

cvSeqPush(point_seq, &cvPoint2D32f(fx,fy));

これをループで繰り返してCvSeqに座標点を蓄える。

フィッティング

float *line = new float[4];
cvFitLine(point_seq,CV_DIST_L2,0,0.01,0.01,line);

第一引数

CvSeq

第二引数

近似の種類。 CV_DIST_L2は最小二乗法。 他に

  • CV_DIST_L1
  • CV_DIST_L12
  • CV_DIST_FAIR
  • CV_DIST_WELSCH
  • CV_DIST_HUBER

がある。詳細はリファレンス参照。

第三、四、五引数

パラメータ。0, 0.01, 0.01が推奨値。

第六引数

近似直線の情報の受け取り先。ここでは2次元なので4個入る配列。3次元の場合は6個。 (line[0],line[1])が正規化されたベクトルを表わし、(line[2],line[3])が直線の通る点を表わす。

サンプルソース

10点の座標(fx,fy)から最小二乗法で傾きと通過点を求める。 おまけで大津の直線度。

    #include <cv.h>
    #include <stdio.h>
    #include <math.h>
    float myLinearity(CvSeq *);
    int main(void)
    {
      int i;
      double fx[] = {0.0, 0.301, 0.477, 0.602, 0.699, 0.778, 0.845, 0.903, 0.954, 1.0};
      double fy[] = {3.874, 3.202, 2.781, 2.49, 2.274, 2.156, 1.934, 1.74, 1.653, 1.662};
      float *line = new float[4];
      float linearity=0.0f;
      //入れ物の確保
      CvMemStorage* storage = cvCreateMemStorage(0);
      //3次元の場合はCV_32FC2がCV_32FC3に、CvPoint2D32fがCvPoint3D32fになる
      CvSeq* point_seq = cvCreateSeq( CV_32FC2, sizeof(CvSeq), sizeof(CvPoint2D32f), storage );
      for (i=0; i<10; i++){
        //値の追加はcvSeqPush
        cvSeqPush(point_seq, &cvPoint2D32f(fx[i],fy[i]));
      }
      linearity = myLinearity(point_seq);
      cvFitLine(point_seq,CV_DIST_L2,0,0.01,0.01,line);
      fprintf(stdout,"v=(%f,%f),vy/vx=%f,(x,y)=(%f,%f), Linearity=%f\n",line[0],line[1],line[1]/line[0],line[2],line[3],linearity);
      cvClearSeq(point_seq);
      cvReleaseMemStorage(&storage);
      return 0;
    }
    //大津の直線度
    float myLinearity(CvSeq *seq)
    {
      int i;
      CvPoint2D32f *p;
      float *x = new float[seq->total];
      float *y = new float[seq->total];
      float x_bar=0.0, y_bar=0.0;
      float u11=0.0, u20=0.0, u02=0.0;
      float linearity=0.0;
      //吸い出し cvGetSeqElemでポインタを得るのでキャストして取得
      for (i=0; i < seq->total; i++){
        p=(CvPoint2D32f*)cvGetSeqElem(seq,i);
        x[i]=p->x;
        y[i]=p->y;
      }
      //x_bar, y_bar
      for (i=0; i < seq->total; i++){
        x_bar+=x[i];
        y_bar+=y[i];
      }
      x_bar/=seq->total;
      y_bar/=seq->total;
      //セントラルモーメント
      for (i=0; i < seq->total; i++){
        u11+=((x[i]-x_bar)*(y[i]-y_bar));
        u20+=pow(x[i]-x_bar,2.0f);
        u02+=pow(y[i]-y_bar,2.0f);
      }
      u11/=seq->total;
      u20/=seq->total;
      u02/=seq->total;
      //直線度の算出
      linearity = sqrt(4*pow(u11,2.0f)+pow(u20-u02,2.0f))/(u20+u02);
      return linearity;
    }

結果

v=(0.399377,-0.916787),vy/vx=-2.295543,(x,y)=(0.655900,2.376600), Linearity=0.999105
⚠️ **GitHub.com Fallback** ⚠️