cvl-robot's diary

研究ノート メモメモ https://github.com/dotchang/

ofxCvを介してOpenCV3.0alphaのAKAZE特徴量を使う

OpenCV3.0にSIFTやSURF以上の性能であると評判の高いデスクリプターのAKAZE/KAZE[2][3][4][5]が採用されるそうです。これをopenframeworksのaddonのofxCv経由で動かしてみたいと思います。

f:id:cvl-robot:20140930153814p:plain

opencv3.0alphaのwindows版プリビルド版[1](ダウンロードリンクは[1]のページ下側)をE:\opencv300にインストールしたとします。ソースコードのパスは、

E:\opencv300\sources\samples\cpp\tutorial_code\features2D\AKAZE_match.cpp

です。テスト用の画像graf1.png, graf2.pngとホモグラフィ―行列H1to3p.xmlは一つ上の階層にあります。

 

ofxCvをOpenCV3.0でも使えるようにするための準備

openCV3.0では、いくつかの定数の定義や、cvMatの廃止などの修正が加えられています。ofxCvをopenCV3.0で使おうとすると、このあたりの名前の齟齬に関するエラーが出てきますので、本家が対応してくれるまでののいい加減な手当として、適当に定義をし直します。

Wrappers.h

#ifndef CV_RGBA2GRAY
#define CV_RGBA2GRAY cv::COLOR_RGBA2GRAY
#endif

#ifndef CV_RGB2GRAY
#define CV_RGB2GRAY cv::COLOR_RGB2GRAY
#endif

Wrappers.cpp

#ifndef CV_DIST_L2
#define CV_DIST_L2 cv::DIST_L2
#endif

Running.cpp

#ifndef CV_RGB2GRAY
#define CV_RGB2GRAY cv::COLOR_RGB2GRAY
#endif

ContourFinder.cpp

#ifndef CV_RGB2HSV
#define CV_RGB2HSV cv::COLOR_RGB2HSV
#endif

#ifndef CV_RGB2GRAY
#define CV_RGB2GRAY cv::COLOR_RGB2GRAY
#endif

#ifndef CV_RGBA2GRAY
#define CV_RGBA2GRAY cv::COLOR_RGBA2GRAY
#endif

#ifndef CV_CHAIN_APPROX_SIMPLE
#define CV_CHAIN_APPROX_SIMPLE cv::CHAIN_APPROX_SIMPLE
#endif

#ifndef CV_CHAIN_APPROX_NONE
#define CV_CHAIN_APPROX_NONE cv::CHAIN_APPROX_NONE
#endif

#ifndef CV_RETR_LIST
#define CV_RETR_LIST cv::RETR_LIST
#endif

#ifndef CV_RETR_EXTERNAL
#define CV_RETR_EXTERNAL cv::RETR_EXTERNAL
#endif

Utilities.cpp (#ifndef ~ #endif省略)

#define CV_RGB2RGBA cv::COLOR_RGB2RGBA
#define CV_RGBA2RGB cv::COLOR_RGBA2RGB

#define CV_RGB2BGRA cv::COLOR_RGB2BGRA

#define CV_RGBA2BGR cv::COLOR_RGBA2BGR

#define CV_BGR2RGB cv::COLOR_BGR2RGB

#define CV_BGRA2RGBA cv::COLOR_BGRA2RGBA

#define CV_BGR2GRAY cv::COLOR_BGR2GRAY
#define CV_RGB2GRAY cv::COLOR_RGB2GRAY
#define CV_GRAY2RGB cv::COLOR_GRAY2RGB
#define CV_GRAY2RGBA cv::COLOR_GRAY2RGBA
#define CV_BGRA2GRAY cv::COLOR_BGRA2GRAY
#define CV_RGBA2GRAY cv::COLOR_RGBA2GRAY
#define CV_BGR5652BGR cv::COLOR_BGR5652BGR
#define CV_BGR5652RGB cv::COLOR_BGR5652RGB
#define CV_BGR5652BGRA cv::COLOR_BGR5652BGRA
#define CV_BGR5652RGBA cv::COLOR_BGR5652RGBA
#define CV_BGR5652GRAY cv::COLOR_BGR5652GRAY
#define CV_BGR5552BGR cv::COLOR_BGR5552BGR
#define CV_BGR5552RGB cv::COLOR_BGR5552RGB
#define CV_BGR5552BGRA cv::COLOR_BGR5552BGRA
#define CV_BGR5552RGBA cv::COLOR_BGR5552RGBA
#define CV_BGR5552GRAY cv::COLOR_BGR5552GRAY
#define CV_BGR2XYZ cv::COLOR_BGR2XYZ
#define CV_RGB2XYZ cv::COLOR_RGB2XYZ
#define CV_XYZ2BGR cv::COLOR_XYZ2BGR
#define CV_XYZ2RGB cv::COLOR_XYZ2RGB
#define CV_BGR2YCrCb cv::COLOR_BGR2YCrCb
#define CV_RGB2YCrCb cv::COLOR_RGB2YCrCb
#define CV_YCrCb2BGR cv::COLOR_YCrCb2BGR
#define CV_YCrCb2RGB cv::COLOR_YCrCb2RGB
#define CV_BGR2HSV cv::COLOR_BGR2HSV
#define CV_RGB2HSV cv::COLOR_RGB2HSV
#define CV_BGR2Lab cv::COLOR_BGR2Lab
#define CV_RGB2Lab cv::COLOR_RGB2Lab
#define CV_BayerGB2BGR cv::COLOR_BayerGB2BGR
#define CV_BayerBG2RGB cv::COLOR_BayerBG2RGB
#define CV_BayerGB2RGB cv::COLOR_BayerGB2RGB
#define CV_BayerRG2RGB cv::COLOR_BayerRG2RGB
#define CV_BGR2Luv cv::COLOR_BGR2Luv
#define CV_RGB2Luv cv::COLOR_RGB2Luv
#define CV_BGR2HLS cv::COLOR_BGR2HLS
#define CV_RGB2HLS cv::COLOR_RGB2HLS
#define CV_HSV2BGR cv::COLOR_HSV2BGR
#define CV_HSV2RGB cv::COLOR_HSV2RGB
#define CV_Lab2BGR cv::COLOR_Lab2BGR
#define CV_Lab2RGB cv::COLOR_Lab2RGB
#define CV_Luv2BGR cv::COLOR_Luv2BGR
#define CV_Luv2RGB cv::COLOR_Luv2RGB
#define CV_HLS2BGR cv::COLOR_HLS2BGR
#define CV_HLS2RGB cv::COLOR_HLS2RGB
#define CV_BayerBG2RGB_VNG cv::COLOR_BayerBG2RGB_VNG
#define CV_BayerGB2RGB_VNG cv::COLOR_BayerGB2RGB_VNG
#define CV_BayerRG2RGB_VNG cv::COLOR_BayerRG2RGB_VNG
#define CV_BayerGR2RGB_VNG cv::COLOR_BayerGR2RGB_VNG
#define CV_BGR2HSV_FULL cv::COLOR_BGR2HSV_FULL
#define CV_RGB2HSV_FULL cv::COLOR_RGB2HSV_FULL
#define CV_BGR2HLS_FULL cv::COLOR_BGR2HLS_FULL
#define CV_RGB2HLS_FULL cv::COLOR_RGB2HLS_FULL
#define CV_HSV2BGR_FULL cv::COLOR_HSV2BGR_FULL
#define CV_HSV2RGB_FULL cv::COLOR_HSV2RGB_FULL
#define CV_HLS2BGR_FULL cv::COLOR_HLS2BGR_FULL
#define CV_HLS2RGB_FULL cv::COLOR_HLS2RGB_FULL
#define CV_LBGR2Lab cv::COLOR_LBGR2Lab
#define CV_LRGB2Lab cv::COLOR_LRGB2Lab
#define CV_LBGR2Luv cv::COLOR_LBGR2Luv
#define CV_LRGB2Luv cv::COLOR_LRGB2Luv
#define CV_Lab2LBGR cv::COLOR_Lab2LBGR
#define CV_Lab2LRGB cv::COLOR_Lab2LRGB
#define CV_Luv2LBGR cv::COLOR_Luv2LBGR
#define CV_Luv2LRGB cv::COLOR_Luv2LRGB
#define CV_BGR2YUV cv::COLOR_BGR2YUV
#define CV_RGB2YUV cv::COLOR_RGB2YUV
#define CV_YUV2BGR cv::COLOR_YUV2BGR
#define CV_YUV2RGB cv::COLOR_YUV2RGB

 これらを各ファイルの冒頭に追加します。抜けがあるかもしれませんが、同じ要領で旧名を追加してください。

またいくつかのcppファイル内でエラーが出ます。ofxCv用ラッパー関数を基本使わないと割り切ってコメントアウトしてしまいます。

Wrapper.cppのcvMatに関わるところをコメントアウト

Kalman.cppのKF.transitionMatrix = *(Mat_<T> をKF.transitionMatrix = (Mat_<T>に変更。

これでとりあえず、ofxCvでopencv3.0が使えるようになりました。

AKAZEのテスト

opencv3.0-alphaとbetaで変更があったようです。下記を参考に。

At The Intersection of Medical Science and Engineering AKAZE Accelerated KAZE Features

 cv::Ptr<akaze>= cv::AKAZE::create();

 

サンプルのソースコードに少しだけエラー対策の修正と不要なホモグラフィー関係の削除をしてやるとこんな感じ。cv::Mat渡しの関数にしてやれば、ofxCv固有のコードは不要です。

const float inlier_threshold = 2.5f; // Distance threshold to identify inliers
const float nn_match_ratio = 0.8f; // Nearest neighbor matching ratio

int akaze_match(cv::Mat& img1, cv::Mat& img2)
{
  vector<cv::KeyPoint> kpts1, kpts2;
  cv::Mat desc1, desc2;

  cv::AKAZE akaze;
  akaze(img1, cv::noArray(), kpts1, desc1);
  akaze(img2, cv::noArray(), kpts2, desc2);

  cv::BFMatcher matcher(cv::NORM_HAMMING);
  vector< vector<cv::DMatch> > nn_matches;
  matcher.knnMatch(desc1, desc2, nn_matches, 2);

  vector<cv::KeyPoint> matched1, matched2, inliers1, inliers2;
  vector<cv::DMatch> good_matches;

  for(size_t i = 0; i < nn_matches.size(); i++) {
    cv::DMatch first = nn_matches[i][0];
    float dist1 = nn_matches[i][0].distance;
    float dist2 = nn_matches[i][1].distance;

    if(dist1 < nn_match_ratio * dist2) {
      if( (kpts1.size() > first.queryIdx) && (kpts2.size() > first.trainIdx) ){
        matched1.push_back(kpts1[first.queryIdx]);
        matched2.push_back(kpts2[first.trainIdx]);
      }
    }
  }

  for(unsigned i = 0; i < matched1.size(); i++) {
    cv::Mat col = cv::Mat::ones(3, 1, CV_64F);
    col.at<double>(0) = matched1[i].pt.x;
    col.at<double>(1) = matched1[i].pt.y;

    col /= col.at<double>(2);
    double dist = sqrt( pow(col.at<double>(0) - matched2[i].pt.x, 2) +
  pow(col.at<double>(1) - matched2[i].pt.y, 2));

    if(dist < inlier_threshold) {
      int new_i = static_cast<int>(inliers1.size());
      inliers1.push_back(matched1[i]);
      inliers2.push_back(matched2[i]);
      good_matches.push_back(cv::DMatch(new_i, new_i, 0));
    }
  }

  cv::Mat res;
  cv::drawMatches(img1, inliers1, img2, inliers2, good_matches, res);
  cv::imshow("res", res);
  cv::waitKey(1);

  double inlier_ratio = inliers1.size() * 1.0 / matched1.size();
  cout << "A-KAZE Matching Results" << endl;
  cout << "*******************************" << endl;
  cout << "# Keypoints 1: \t" << kpts1.size() << endl;
  cout << "# Keypoints 2: \t" << kpts2.size() << endl;
  cout << "# Matches: \t" << matched1.size() << endl;
  cout << "# Inliers: \t" << inliers1.size() << endl;
  cout << "# Inliers Ratio: \t" << inlier_ratio << endl;
  cout << endl;

  return 0;
}

試しに右と左で同じ絵を与えて対応点を探すとこんな感じ。

f:id:cvl-robot:20140930162016p:plain

inlier_ratio を調べることで簡単な物体認識に使うことが出来ます。0~1に変化するパラメータで、1に近いほど類似度が高いことを示しています。

 

余談ですが、そのうちこれを使って、PDFファイルの同じページ検出器を作ろうと思います。ただし、その場合、簡単なハッシュ方式を用いた方が速くて良いかもしれません。この目的は、scansnapで自炊していると、ノリで紙が貼りついてしまって複数ページを巻き込んでしまうことがあり、仕方なく再読み込みしたために同じページが複数あるPDFをきれいに編集しなおしたい、ということがしばしばあるためです。世の中的には、まあ、そんなに需要は無いかなぁ・・・?

 

追記:OpenCV3.0以降のIplImageの取り扱いメモ

cv::MatのコンストラクタにIplImageを渡して初期化、が出来なくなっているので、明示的に関数を介して初期化する。[6]を参照して、cvarrToMatという関数の使い方を確認します。

cv::Mat mat = cv::cvarrToMat(iplImage);

 C形式の関数を使う際には、includeを明示的に行う必要があるそうです。[7]

#include <opencv2/calib3d/calib3d_c.h>
#include <opencv2/core/core_c.h>
#include <opencv2/highgui/highgui_c.h>
#include <opencv2/imgproc/imgproc_c.h>
#include <opencv2/photo/photo_c.h>

 

 

staticlibのリンク指定はこんな感じ

// x64
#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\opencv_core300.lib")
#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\opencv_imgproc300.lib")
#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\opencv_highgui300.lib")
#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\opencv_objdetect300.lib")
//#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\opencv_contrib300.lib")
#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\opencv_features2d300.lib")
#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\opencv_flann300.lib")
#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\opencv_imgcodecs300.lib")
//#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\opencv_gpu300.lib")
//#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\opencv_haartraining_engined.lib")
//#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\opencv_legacy300.lib")
#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\opencv_ts300.lib")
#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\opencv_video300.lib")
#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\IlmImf.lib")
#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\ippicvmt.lib")
#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\libjasper.lib")
#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\libjpeg.lib")
#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\libpng.lib")
#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\libtiff.lib")
#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\libwebp.lib")
#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\staticlib\\zlib.lib")

ダイナミックリンクの指定の場合

#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\lib\\opencv_ts300.lib")
#pragma comment(lib,"E:\\opencv300\\build\\x64\\vc11\\lib\\opencv_world300.lib")

C言語インターフェースの関数も含まれているようです。

 

[1] OpenCV 3.0 alpha | OpenCV

[2] AKAZE特徴量の紹介と他特徴量との比較 - 遥かへのスピードランナー

[3] OpenCV3.0.0-alphaの特徴抽出・マッチングまとめ - whoopsidaisies's diary

[4] OpenCV 備忘録: OpenCV 3.0 alpha の新機能を試してみた。(1)

[5] horiken4の

 

[6] [C++/OpenCV] cv::MatとIplImageの相互変換|プログラミング備忘録+その他いろいろなブログ

[7] OpenCV3.0alpha 変更点レビュー ~ラッパー開発者の視点から~ - schima.hatenablog.com