SURFによる特徴点抽出

18 2月 2010 Under: opencv2.x-samples

C++

#include <iostream>
#include <string>

#include <cv.h>
#include <highgui.h>

using namespace std;
using namespace cv;


int main(int argc, char *argv[])
{
  // (1)load Color Image
  const char *imagename = argc > 1 ? argv[1] : "../image/lenna.png";
  Mat colorImage = imread(imagename,1);
  if(colorImage.empty())
    return -1;

  // (2)convert Color Image to Grayscale for Feature Extraction
  Mat grayImage;
  cvtColor(colorImage, grayImage, CV_BGR2GRAY);

  // (3)initialize SURF class
  SURF calc_surf = SURF(500,4,2,true);

  // (4)extract SURF
  vector<KeyPoint> kp_vec;
  vector<float> desc_vec;		
  calc_surf(grayImage, Mat(), kp_vec, desc_vec);

  // (5)draw keypoints
  cout << "Image Keypoints: " << kp_vec.size() << endl;
#if 1
  vector<KeyPoint>::iterator it = kp_vec.begin(), it_end = kp_vec.end();
  for(; it!=it_end; ++it) {
    circle(colorImage, Point(it->pt.x, it->pt.y), 
	   saturate_cast<int>(it->size*0.25), Scalar(255,255,0));
  }
#else
  for(int i = 0; i < kp_vec.size(); i++) {
    KeyPoint* point = &(kp_vec[i]);
    Point center;  // Key Point's Center
    int radius;      // Radius of Key Point
    center.x = cvRound(point->pt.x);
    center.y = cvRound(point->pt.y);
    radius = cvRound(point->size*0.25);
    circle(colorImage, center, radius, Scalar(255,255,0), 1, 8, 0);
  }
#endif

  namedWindow("SURF",CV_WINDOW_AUTOSIZE);
  imshow("SURF", colorImage);
  waitKey(0);

  return 0;
}

// (1)指定ファイルをカラー画像として読み込みます.

コマンドライン引数で指定されたファイルを,カラー画像として読み込みます.

// (2)画像を,グレースケールに変換します.

SURF特徴の抽出は,グレースケール画像に対して行う必要があります.cvtColorでカラー画像をCV_8UC1のグレースケール画像に変換します.

// (3)SURFクラスを初期化します.

C++インタフェースではcv::SURFクラスを使用してSURF特徴を抽出します.SURFコンストラクタは,第一引数がFast Hessian Detectorの閾値,第二引数が特徴検出に用いられるオクターブ数,第三引数が各オクターブ内に存在するレイヤ数,第四引数は拡張ディスクリプタの使用有無を指定します.trueにした場合128次元のディスクリプタが取得されます(falseの場合は64次元).

// (4)グレースケール画像からSURF特徴を抽出します.

SURF特徴の抽出結果は,std::vector<cv::KeyPoint>に格納されます.ここには各キーポイントの位置,方向,スケールなどが格納されます.また,第四引数にstd::vector<float>を指定した場合,各キーポイント毎にディスクリプタの抽出処理を行います.拡張ディスクリプタを指定しなかった場合はキーポイントの数×64個の値が,指定した場合はキーポイントの数×128個の値が格納されます.このサンプルでは,kp_vec[i]に格納されたキーポイントに対応するディスクリプタは,desc_vec[128*i]からdesc_vec[128*i + 127]に格納されていることになります.

// (5)抽出したSURF特徴の位置とスケールを,カラー画像上へ描画します.

入力画像(カラー)に対して,SURF特徴の位置とスケールを円で描画します.

実行結果例

“SURFによる特徴点抽出” への12件のコメント

  1. arai5 より:

    こんにちは。
    SURFの記述子を特徴点が大きい順に3つだけ抽出することは可能でしょうか。
    よろしくお願いします。

    • takmin より:

      SURF記述子の何の「大きさ」を指しているかわかりませんが、スケールの大きさにせよ応答の強さにせよ、取得したKeypoints構造体の中をみて自分で選ぶ必要があると思います。

  2. kuni5 より:

    質問があります。
    std::vectorの中に格納された各キーポイントの座標をテキストファイルに書き出して参照したいのですが,どのようにしたらいいでしょうか?

    • takmin より:

      例えば、

      std::vector kp_vec;
      cv::FileStorage fs(“KeyPoints.txt”, cv::FileStorage::WRITE);
      cv::write(fs, “KeyPoints”, kp_vec);

      こんな感じでできます。

  3. come_flying より:

    こんにちは
    質問がございます.
    現在Opencv2.1のSURFを実行しています.
    そして,入力画像から1点も特徴点が見つからない場合(真っ白の画像など),プログラムが落ちてしまいます.
    こちらの対処法はないでしょうか?
    恐縮ですが,お力添え頂けると非常に嬉しいです.

    • idojun より:

      そのバグは2.2で修正されているはずですので,OpenCV2.2を利用していただくのが最も簡単な対策かと思います.

      • come_flying より:

        早い返信ありがとうございます.
        早速導入したいと思います.

  4. aoihitsuji より:

    >2.x系でもC言語風の書き方は可能ですし,ソースもかなり互換性があると思いますので,今後のためにも,特に問題がなければ2.x系に移行することをお勧めします..
    確かにそのほうが良いかもしれないですね.
    最新のものにバージョン変更して,最初から環境を構築してみようと思います.

    本当にありがとうございました.

  5. idojun より:

    なるほど.1.x系の環境がないので直ぐには実験できないのですが,OpenCV2.1+find_obj.exeはWindows環境で問題なく動くことを確認しています(VC2010,TBB=ON/OFF).
    2.x系でもC言語風の書き方は可能ですし,ソースもかなり互換性があると思いますので,今後のためにも,特に問題がなければ2.x系に移行することをお勧めします.

  6. aoihitsuji より:

    ご返信ありがとうございます。

    >問題がでる例というのは,サンプルのfind_obj.cppそのままでしょうか?

    いえ,今のMFC環境で使っているOpenCV内の関数は,
    とりあえず下記のみです.

    cvNamedWindow(“Object”, 1);

    char* object_filename = “object.png”;
    IplImage* object = cvLoadImage( object_filename, CV_LOAD_IMAGE_GRAYSCALE );
    CvSeq *objectKeypoints = 0, *objectDescriptors = 0;
    CvSeq *imageKeypoints = 0, *imageDescriptors = 0;
    CvMemStorage* storage = cvCreateMemStorage(0);
    CvSURFParams params = cvSURFParams(500,1);
    cvExtractSURF( object, 0, &objectKeypoints, &objectDescriptors, storage, params );

    cvShowImage( “Object”, object );
    cvWaitKey(0);
    cvDestroyWindow(“Object”);

    cvExtractSURF()をコメントアウトした状態だと,
    画像の表示等も上手くいきます.
    ただそのコメントアウトを外した途端に,Stack Overflowとなってしまいます.

    使っているOpenCVのバージョンは1.1です.

  7. idojun より:

    問題がでる例というのは,サンプルのfind_obj.cppそのままでしょうか?また,ご利用のOpenCVのバージョンを教えていただければ,こちらでも試すことができるのですが.

  8. aoihitsuji より:

    はじめまして.
    現在,MFC環境下でOpenCVを用いて画像処理を行っている者です.
    このようなコメント欄を活用していいものなのか,非常に恐縮なのですが,
    一つ質問させて頂ければと思います.

    これまでSURFの抽出を行うために,
    find_obj.cppをベースにシステム構築をしてきたのですが,
    新たにMFC環境下で同じことをしようとすると,
    どうしてもcvExtractSURF()の所でプログラムが落ちてしまいます.

    ビルドまでは上手くいっています.
    ただ,デバッグモードで実行すると,
    「0xC00000FD: Stack Overflow」
    と出力されるため,スタック領域をとりあえず広げてはみたものの,
    同様の結果となってしまいます.

    画像サイズは365×274と決して大きいわけでもなく,
    プログラムも大幅に簡略化しているため,
    上述のようなエラーが出てくることは非常に考えにくいのですが・・・

    どのような原因があると考えられますでしょうか?
    恐縮ですが,お力添え頂けると非常に嬉しいです.

コメントをどうぞ