OpenCV CvCalibFilter - eiichiromomma/CVMLAB GitHub Wiki

(OpenCV) CvCalibFilter

Undistort

CvCalibFilterを使った歪み補正

CvCalibFilter

  • 1カメラの時はUndistortのみ。
  • 2カメラの時はRectifyも可。
  • 3カメラの時はPushとDrawまで可。
  • 4カメラ以上は未対応。

cvaux.hで定義されている。 キャリブレーションを楽に利用可能。

ビルドの際はライブラリにcvaux.libを含める。

ハマりポイント

OpenCVのdocsに置かれているキャリブレーションパターンのコーナー数は6x8なのだが、EtalonParamsで定義する時は各々+1する必要がある

サンプルソース

概要

USBカメラでのハンドリングが良いvideoInputを利用。OpenCV/videoInput Libraryによるビデオキャプチャ - Point at infinity などを参照。

使い方

OpenCV/docsにあるpattern.pdf)を印刷して硬い板に貼り、カメラの前でヒラヒラさせる。 色々な向きでカメラの視界いっぱいにヒラヒラさせる程良い。

CalibFilter.SetFrames()で設定した枚数に達するとUndistortされた映像とオリジナルの映像が表示される。ESCで終了。

    //CvCalibFilterを使った1カメラキャリブレーション 2008/5/19 Eiichiro Momma
    #include <stdio.h>
    #include <time.h>    
    #include <cv.h>
    #include <highgui.h>
    #include <cvaux.h>
    #include <videoInput.h>

    IplImage *current_frame_rgb;

    double EtalonParams[3] = { 7, 9, 3 };
    //EtalonParamsは{コーナー数+1、もう一方のコーナー数+1、実空間単位での一片のサイズ(怪しいので要調査)}
    //+1になっているのは内部の処理の関係?

    int main(int argc, char *argv[])
    {
      bool verbose = true;
      videoInput VI;
      int numDevices = VI.listDevices();
      if (numDevices > 0){
        VI.setupDevice(0,640,480);
      }
      unsigned char * myBuffer;
      int image_width = VI.getWidth(0);
      int image_height = VI.getHeight(0);

      current_frame_rgb = cvCreateImage(cvSize(image_width, image_height), IPL_DEPTH_8U, 3);
      myBuffer = (unsigned char*)(current_frame_rgb->imageData);

      cvNamedWindow( "Window 0", CV_WINDOW_AUTOSIZE);
      printf("WHEN THE COUNTER HITS 10, YOU'RE DONE \n");

      int c=0;
      CvCalibFilter CalibFilter;
      CalibFilter.SetEtalon( CV_CALIB_ETALON_CHESSBOARD, EtalonParams );
      //1台
      CalibFilter.SetCameraCount(1);
      //10枚利用(多い程良い)
      CalibFilter.SetFrames(10);
      IplImage* images[] = { current_frame_rgb};

      int prev_time = 0;
      while (c!=27){
        //更新待ち(無くても良いかも?)
        while(!VI.isFrameNew(0));
        VI.getPixels(0, myBuffer, false, true);
        bool found = CalibFilter.FindEtalon( images );
        CalibFilter.DrawPoints( images );
        cvShowImage("Window 0",current_frame_rgb);
        c = cvWaitKey( 150 );
        //キャリブレーションボードを発見したらXORにして反転させて知らせる
        if( found ){
          int cur_time = clock();
          if( cur_time >= prev_time + 1000 )
          {
            prev_time = cur_time;
            CalibFilter.Push();
            cvXorS( current_frame_rgb, cvScalarAll(255), current_frame_rgb );
          }
          if( CalibFilter.IsCalibrated() )
          {
            break;
          }
          cvShowImage("Window 0",current_frame_rgb);
          //1000ms待たせる
          c = cvWaitKey(1000);
        }
      }
      cvNamedWindow("org",1);
      cvNamedWindow("undist",1);

      while(c!=27){
        while(!VI.isFrameNew(0));
        VI.getPixels(0,myBuffer, false, true);
        cvShowImage("org",current_frame_rgb);
        //Undistortで上書き
        CalibFilter.Undistort(images, images);
        cvShowImage("undist",current_frame_rgb);
        c=cvWaitKey(100);
      }
      exit(0);
      cvDestroyAllWindows();
    }

CvCalibFilterを使って2カメラキャリブレーションを行なってのステレオ視

CVS版のStereoDemoがベース。

詳細はまた今度

内容

Undistortと殆ど同じ。 カメラを2つにしてCalibFilter.Rectifyするだけでキャリブレーション画像が得られる。

あとは距離計測するだけ。

そこそこの精度。

サンプルソース

    //CvCalibFilterを使った2カメラキャリブレーション+距離計測 2008/5/19 Eiichiro Momma
    #include <stdio.h>
    #include <time.h>    
    #include <cv.h>
    #include <highgui.h>
    #include <cvaux.h>
    #include <videoInput.h>

    IplImage *current_frame_rgb;

    double EtalonParams[3] = { 7, 9, 3 };
    //EtalonParamsは{コーナー数+1、もう一方のコーナー数+1、実空間単位での一片のサイズ(怪しいので要調査)}
    //+1になっているのは内部の処理の関係?

    int main(int argc, char *argv[])
    {
      bool verbose = true;
      videoInput VI;
      int i;
      int numDevices = VI.listDevices();
      if (numDevices > 2){
        numDevices = 2;
      }
      for (i = 0; i< numDevices; i++){
        VI.setupDevice(i,640,480);
      }
      IplImage **imList = new IplImage *[numDevices];
      char **myBufferList = new char *[numDevices];
      int image_width = VI.getWidth(0);
      int image_height = VI.getHeight(0);

      for (i=0; i<numDevices; i++){
        *(imList + i) = cvCreateImage(cvSize(VI.getWidth(i),VI.getHeight(i)),IPL_DEPTH_8U,3);
        if (*(imList + i) == NULL){
          return 1;
        }
        *(myBufferList + i) = (*(imList+i))->imageData;
      }

      char buf[16];
      for (i=0; i<numDevices; i++){
        sprintf(buf,"win%d",i);
        cvNamedWindow(buf,1);
      }

      int c=0;
      CvCalibFilter CalibFilter;
      CalibFilter.SetEtalon( CV_CALIB_ETALON_CHESSBOARD, EtalonParams );
      CalibFilter.SetCameraCount(numDevices);
      CalibFilter.SetFrames(10);

      int prev_time = 0;
      while (c!=27){
        //更新待ち(無くても良いかも?)
        for (i = 0; i< numDevices; i++){
          while(!VI.isFrameNew(i));
          VI.getPixels(i, (unsigned char*)(myBufferList[i]), false, true);
        }
        bool found = CalibFilter.FindEtalon( imList );
        CalibFilter.DrawPoints( imList );
        for (i=0; i<numDevices; i++){
          sprintf(buf,"win%d",i);
          cvShowImage(buf,imList[i]);
        }
        c = cvWaitKey( 150 );
        //キャリブレーションボードを発見したらXORにして反転させて知らせる
        if( found ){
          int cur_time = clock();
          if( cur_time >= prev_time + 1000 )
          {
            prev_time = cur_time;
            CalibFilter.Push();
            for (i = 0; i< numDevices; i++){
              cvXorS( imList[i], cvScalarAll(255), imList[i] );
            }
          }
          if( CalibFilter.IsCalibrated() )
          {
            break;
          }
          for (i=0; i<numDevices; i++){
            sprintf(buf,"win%d",i);
            cvShowImage(buf,imList[i]);
          }
          //1000ms待たせる
          c = cvWaitKey(1000);
        }
      }
      int maxdisp=100;
      cvNamedWindow("disparity",1);
      cvCreateTrackbar( "maxdisp", "disparity", &maxdisp, 255, 0 );
      IplImage *vis_disp = cvCreateImage(cvGetSize(imList[0]),IPL_DEPTH_8U,1);
      IplImage *gray0 = cvCloneImage(vis_disp);
      IplImage *gray1 = cvCloneImage(vis_disp);
      while(c!=27){
        for (i = 0; i< numDevices; i++){
          while(!VI.isFrameNew(i));
          VI.getPixels(i, (unsigned char*)myBufferList[i], false, true);
        }
        CalibFilter.Rectify(imList,imList);
        for (i=0; i<numDevices; i++){
          sprintf(buf,"win%d",i);
          cvShowImage(buf,imList[i]);
        }
        vis_disp->origin = imList[0]->origin;
        cvCvtColor(imList[0],gray0,CV_BGR2GRAY);
        cvCvtColor(imList[1],gray1,CV_BGR2GRAY);
        cvFindStereoCorrespondence( 
                       gray0,gray1,
                       CV_DISPARITY_BIRCHFIELD,
                       vis_disp,
                       maxdisp ,15,3,6,8,15);
        cvConvertScale(vis_disp, vis_disp, 255.f/maxdisp);
        cvShowImage("disparity", vis_disp);
        c=cvWaitKey(100);
      }
      exit(0);
      cvDestroyAllWindows();
    }

複数台のカメラによるステレオ視

そのまま使うと3台までしか使えず、Rectifyも非対応。(2台のみ)

残念ながらこのクラスではSSSDのような実装は無いらしい。 複数台あるカメラを互い違いにキャリブレーションして多点計測にしてみた。

サンプル

処理が重たくなるので320x240で使用。 精度は落ちる。

    //CvCalibFilterを使った複数カメラのキャリブレーション+距離計測 2008/5/20 Eiichiro Momma
    #include <stdio.h>
    #include <time.h>    
    #include <cv.h>
    #include <highgui.h>
    #include <cvaux.h>
    #include <videoInput.h>

    enum{
      MY_MAX_CAMERA=6
    };
    double EtalonParams[3] = { 7, 9, 3 };
    //EtalonParamsは{コーナー数+1、もう一方のコーナー数+1、実空間単位での一片のサイズ(怪しいので要調査)}
    //+1になっているのは内部の処理の関係?
    int main(int argc, char *argv[])
    {
      bool verbose = true;
      videoInput VI;
      int i;
      int numDevices = VI.listDevices();
      //CvCalibFilterの仕様でカメラは3台までで、Rectifyは2台の時のみ働く状態なので
      //互い違いに2台ずつキャリブレーションする
      if (numDevices > MY_MAX_CAMERA){
        numDevices = MY_MAX_CAMERA;
      }
      for (i = 0; i< numDevices; i++){
        //VGAレベルにするとマシンが死にそうになる
        VI.setupDevice(i,320,240);
      }
      IplImage **imList = new IplImage *[numDevices];
      char **myBufferList = new char *[numDevices];
      int image_width = VI.getWidth(0);
      int image_height = VI.getHeight(0);

      for (i=0; i<numDevices; i++){
        *(imList + i) = cvCreateImage(cvSize(VI.getWidth(i),VI.getHeight(i)),IPL_DEPTH_8U,3);
        if (*(imList + i) == NULL){
          return 1;
        }
        *(myBufferList + i) = (*(imList+i))->imageData;
      }

      char buf[16];
      for (i=0; i<numDevices; i++){
        sprintf(buf,"win%d",i);
        cvNamedWindow(buf,1);
      }

      int c=0;
      CvCalibFilter *CalibFilterList = new CvCalibFilter[numDevices -1];

      int prev_time = 0;
      int camNum;
      
      for (camNum = 0; camNum < numDevices-1; camNum++){
        CalibFilterList[camNum].SetEtalon( CV_CALIB_ETALON_CHESSBOARD, EtalonParams );
        CalibFilterList[camNum].SetCameraCount(2);
        CalibFilterList[camNum].SetFrames(10);
        IplImage* imPair[] = {imList[camNum], imList[camNum+1]};
        while (c!=27){
          for (i = 0; i< numDevices; i++){
            //更新待ち(無くても良いかも?)
            //while(!VI.isFrameNew(i));
            VI.getPixels(i, (unsigned char*)(myBufferList[i]), false, true);
          }
          bool found = CalibFilterList[camNum].FindEtalon( imPair);
          CalibFilterList[camNum].DrawPoints( imPair);
          for (i=0; i<numDevices; i++){
            sprintf(buf,"win%d",i);
            cvShowImage(buf,imList[i]);
          }
          c = cvWaitKey( 150 );
          //キャリブレーションボードを発見したらXORにして反転させて知らせる
          if( found ){
            int cur_time = clock();
            if( cur_time >= prev_time + 1000 )
            {
              prev_time = cur_time;
              CalibFilterList[camNum].Push();
              for (i = 0; i< 2; i++){
                cvXorS( imList[i+camNum], cvScalarAll(255), imList[i+camNum] );
              }
            }
            if( CalibFilterList[camNum].IsCalibrated() )
            {
              break;
            }
            for (i=0; i<2; i++){
              sprintf(buf,"win%d",i+camNum);
              cvShowImage(buf,imList[i+camNum]);
            }
            //1000ms待たせる
            c = cvWaitKey(1000);
          }
        }
      }
      cvDestroyAllWindows();
      //距離計測
      int maxdisp = 100;
      for (i=0; i<numDevices-1; i++){
        sprintf(buf,"disp%d",i);
        cvNamedWindow(buf,1);
        cvCreateTrackbar( "maxdisp", buf, &maxdisp, 255, 0 );
      }
      IplImage *vis_disp = cvCreateImage(cvGetSize(imList[0]),IPL_DEPTH_8U,1);
      IplImage *gray0 = cvCloneImage(vis_disp);
      IplImage *gray1 = cvCloneImage(vis_disp);
      IplImage *imPairx[] = {gray0, gray1};
      //Rectifyの結果を一枚の画像に
      IplImage *imRect = cvCreateImage(cvSize(2*vis_disp->width,(numDevices-1)*vis_disp->height),IPL_DEPTH_8U,1);
      cvNamedWindow("Rect",1);
      while(c!=27){
        for (i = 0; i< numDevices; i++){
          //while(!VI.isFrameNew(i));
          VI.getPixels(i, (unsigned char*)myBufferList[i], false, true);
        }
        for (i = 0; i< numDevices-1; i++){
          cvCvtColor(imList[i],gray0,CV_BGR2GRAY);
          cvCvtColor(imList[i+1],gray1,CV_BGR2GRAY);
          CalibFilterList[i].Rectify(imPairx,imPairx);
          cvSetImageROI(imRect,cvRect(0,i*gray0->height,gray0->width,gray0->height));
          cvCopyImage(gray0,imRect);
          cvResetImageROI(imRect);
          cvSetImageROI(imRect,cvRect(gray0->width,i*gray0->height,gray0->width,gray0->height));
          cvCopyImage(gray1,imRect);
          cvResetImageROI(imRect);
          cvShowImage("Rect",imRect);
          cvFindStereoCorrespondence( 
                         gray0,gray1,
                         CV_DISPARITY_BIRCHFIELD,
                         vis_disp,
                         maxdisp ,15,3,6,8,15);
          cvConvertScale(vis_disp, vis_disp, 255.f/maxdisp);
          sprintf(buf,"disp%d",i);
          cvShowImage(buf, vis_disp);
        }
        c=cvWaitKey(100);
      }
      exit(0);
      cvDestroyAllWindows();
    }
⚠️ **GitHub.com Fallback** ⚠️