OpenCV Stereo Matching の性能比較 1

前回の投稿から少し時間が空いてしまいました...が,ブログ更新が飽きたというわけではなく,ちょっと更新するほどのまとまったネタができなかったわけです.というわけで,今日はつくばチャレンジで使うステレオマッチングメソッドの比較・選定を行いました.とりあえず,OpenCVでロジックが提供されている下記の3つのアルゴリズムを比較検討しました.

  1. Block Matching Method
  2. Stereo Block Matching Method
  3. Stereo Graph Cut Method

0. 実験に用いたベースデータ

 実験には,手軽に手に入る Tsukuba の視差画像を用いました.

左画像 サイズ 384 x 288
f:id:rkoichi2001:20160625230555p:plain
右画像 サイズ 384 x 288
f:id:rkoichi2001:20160625230603p:plain

1. Block Matching Method

 一番オーソドックスなアルゴリズムです.実験結果は下記の通り.
実行時間 3.3464ms (334.646 / 100)
f:id:rkoichi2001:20160625232749p:plain

2. Semi Global Matching Method

 一番良く使われているアルゴリズム?ではないでしょうか.
実行時間 16.6549ms (1665.49 / 100)
f:id:rkoichi2001:20160625231149p:plain

3. Stereo Graph Cut Method

 実行時間が結構長いです..が,結果も一番きれいです.解説にあるとおり,オフラインでの使用前提ですね.
実行時間 6664.27ms (66642.7 / 10)
f:id:rkoichi2001:20160625233306p:plain

実際には,パラメータを適切に調整して比較したわけではないので純粋に比較できているわけでは無いです.次のエントリではこの3つの手法を自分が撮影した画像に適用して見たいと思います.下記,実験に使ったソースコードをおいておきます.

1. Block Matching Method

#include <iostream>
using namespace std;

#include "opencv2/opencv.hpp"

int main() {
  const char *rightImg = "/home/koichi/data/tsukuba/scene1.row3.col4.ppm";
  const char *leftImg = "/home/koichi/data/tsukuba/scene1.row3.col2.ppm";

  // const char *rightImg = "/home/koichi/Desktop/Stereo Pair/pentR.bmp";
  // const char *leftImg = "/home/koichi/Desktop/Stereo Pair/pentL.bmp";

  IplImage *imgR, *imgL, *dst;
  IplImage *dispLeft, *dispRight;
  int disp = 32;
  //  CvStereoGCState *state = cvCreateStereoGCState(disp, 4);
  CvStereoBMState *state = cvCreateStereoBMState(CV_STEREO_BM_BASIC, disp);
  state->SADWindowSize = 9;

  // . Load Image.
  imgR = cvLoadImage(rightImg, CV_LOAD_IMAGE_GRAYSCALE);
  imgL = cvLoadImage(leftImg, CV_LOAD_IMAGE_GRAYSCALE);

  CvSize size;
  size.width = imgR->width;
  size.height = imgR->height;

  // . Allocate Memory
  dispLeft = cvCreateImage(size, IPL_DEPTH_16S, 1);
  dispRight = cvCreateImage(size, IPL_DEPTH_16S, 1);
  dst = cvCreateImage(size, IPL_DEPTH_8U, 1);

  {
    cv::TickMeter meter;
    meter.start();

    for (int i = 0; i < 100; i++) {
      // Calcuate Disparity
      cvFindStereoCorrespondenceBM(imgL, imgR, dispLeft, state);
    }

    meter.stop();
    std::cout << "Average Time for Calculation : "
              << meter.getTimeMilli() / 100.0 << std::endl;
  }
  double min, max;
  cvMinMaxLoc(dispLeft, &min, &max);

  cvConvertScale(dispLeft, dst, 255 / (max - min), 255 / min);
  // cvNormalize(dispLeft, dst, 1, 0, CV_MINMAX);

  const char *winName = "Stereo Correspondence";

  cvNamedWindow("Right Image", CV_WINDOW_AUTOSIZE);
  cvNamedWindow("Left Image", CV_WINDOW_AUTOSIZE);
  cvNamedWindow("Disparity", CV_WINDOW_AUTOSIZE);

  cvShowImage("Right Image", imgR);
  cvShowImage("Left Image", imgL);
  cvShowImage("Disparity", dst);

  cvWaitKey(0);

  // cvReleaseStereoGCState(&state);
  cvDestroyAllWindows();

  cvReleaseImage(&imgR);
  cvReleaseImage(&imgL);

  return 0;
}

2. Semi Global Matching Method

#include <iostream>
#include <string>
using namespace std;

#include "opencv2/opencv.hpp"

int main() {
  string rightImg = "/home/koichi/data/tsukuba/scene1.row3.col4.ppm";
  string leftImg = "/home/koichi/data/tsukuba/scene1.row3.col2.ppm";

  cv::Mat imgR = cv::imread(rightImg, CV_8UC1);
  cv::Mat imgL = cv::imread(leftImg, CV_8UC1);
  cv::Mat dispMat(imgR.rows, imgR.cols, CV_8UC1);

  int disp = 32;
  int minDisparity = 0;
  int numDisparities = 32;
  int SADWindowSize = 3;
  int P1 = 0;
  int P2 = 0;
  int disp12MaxDiff = 0;
  int preFilterCap = 0;
  int uniquenessRatio = 0;
  int speckleWindowSize = 0;
  int speckleRange = 0;
  bool fullDp = false;

  cv::StereoSGBM sgbm(minDisparity, numDisparities, SADWindowSize, P1, P2,
                      disp12MaxDiff, preFilterCap, uniquenessRatio,
                      speckleWindowSize, speckleRange, fullDp);

  {
    cv::TickMeter meter;
    meter.start();

    for (int i = 0; i < 100; i++) {
      sgbm(imgL, imgR, dispMat);
    }

    meter.stop();
    std::cout << "Average Time for Calculation : "
              << meter.getTimeMilli() / 100.0 << std::endl;
  }

  double min, max;
  cv::minMaxLoc(dispMat, &min, &max);
  cv::convertScaleAbs(dispMat, dispMat, 255 / (max - min), 255 / min);

  cv::namedWindow("Right Image", CV_WINDOW_AUTOSIZE);
  cv::namedWindow("Left Image", CV_WINDOW_AUTOSIZE);
  cv::namedWindow("Disparity", CV_WINDOW_AUTOSIZE);

  cv::imshow("Right Image", imgR);
  cv::imshow("Left Image", imgL);
  cv::imshow("Disparity", dispMat);

  cvWaitKey(0);
  cvDestroyAllWindows();

  return 0;
}

3. Stereo Graph Cut Method

#include <iostream>
using namespace std;

#include "opencv2/opencv.hpp"

int main() {
  const char *rightImg = "/home/koichi/data/tsukuba/scene1.row3.col4.ppm";
  const char *leftImg = "/home/koichi/data/tsukuba/scene1.row3.col2.ppm";

  // const char *rightImg = "/home/koichi/Desktop/Stereo Pair/pentR.bmp";
  // const char *leftImg = "/home/koichi/Desktop/Stereo Pair/pentL.bmp";

  IplImage *imgR, *imgL, *dst;
  IplImage *dispLeft, *dispRight;
  int disp = 32;
  CvStereoGCState *state = cvCreateStereoGCState(disp, 4);

  // . Load Image.
  imgR = cvLoadImage(rightImg, CV_LOAD_IMAGE_GRAYSCALE);
  imgL = cvLoadImage(leftImg, CV_LOAD_IMAGE_GRAYSCALE);

  CvSize size;
  size.width = imgR->width;
  size.height = imgR->height;

  // . Allocate Memory
  dispLeft = cvCreateImage(size, IPL_DEPTH_16S, 1);
  dispRight = cvCreateImage(size, IPL_DEPTH_16S, 1);
  dst = cvCreateImage(size, IPL_DEPTH_8U, 1);

  // Calcuate Disparity
  {
    cv::TickMeter meter;
    meter.start();
    int repeat = 10;

    for (int i = 0; i < repeat; i++) {
      cvFindStereoCorrespondenceGC(imgL, imgR, dispLeft, dispRight, state, 0);
    }

    meter.stop();
    std::cout << "Average Time for Calculation : "
              << meter.getTimeMilli() / repeat << std::endl;
  }

  cvConvertScale(dispLeft, dst, -256 / disp);

  const char *winName = "Stereo Correspondence";

  cvNamedWindow("Right Image", CV_WINDOW_AUTOSIZE);
  cvNamedWindow("Left Image", CV_WINDOW_AUTOSIZE);
  cvNamedWindow("Disparity", CV_WINDOW_AUTOSIZE);

  cvShowImage("Right Image", imgR);
  cvShowImage("Left Image", imgL);
  cvShowImage("Disparity", dst);

  cvWaitKey(0);

  // cvReleaseStereoGCState(&state);
  cvDestroyAllWindows();

  cvReleaseImage(&imgR);
  cvReleaseImage(&imgL);

  return 0;
}