映像中にどんな物体が映っているかを特定することで、人物の認識や移動経路の追跡など様々な分析が可能となります。こうした画像認識の基礎技術には、画像中の特徴的な部分(特徴量)を計算する処理があり、OpenCVを利用することで特徴量の抽出を簡単に実装できます。
特徴量を計算する処理にも様々なアルゴリズムがあります。OpenCV2ではSIFT,SURF,ORBなどの手法を利用できますが、OpenCV3では新たにKAZEとAKAZEというアルゴリズムも利用可能になりました。ここでは、KAZEおよびAKAZEの簡単な説明と、実際にOpenCV3でAKAZEを使って特徴量を計算するプログラムについて解説します。

KAZEとAKAZE

KAZEは日本語の『風』から命名された手法で、コンピュータービジョンに関するトップカンファレンスの一つ『ECCV2012』で発表されました。特徴量を計算することで『2つの画像に同じものが映っているか』を判別できるようになりますが、物体を拡大・縮小・回転したり、カメラの焦点がぼけたり明るさが変わったりしても同じものと判別できるかどうかで画像認識の精度は大きく変わります。KAZEはSIFTやSURFなどと比べてそうした変化への耐性が強いため、認識精度を高めることができます。

AKAZE(Accelerated-KAZE)はKAZEのアイデアを元に生み出された手法で、KAZEと同様に高い認識精度を持ちつつ計算に必要な時間を大幅に短縮しています。また、SIFTやSURFには特許権が設定されており商用で利用するにはライセンスを受ける必要がありますが、AKAZEは商用/非商用を問わず利用できるため、ビジネス用途で画像処理を行う場合には有力な選択肢になります。

特徴量の抽出と対応関係の描画

以下3つの画像に映っている人物は撮影角度や大きさ、ポーズもバラバラですが、AKAZEで特徴点を抽出し、どの部位が同じとみなせるか対応関係を描画させてみましょう。

比較用の元画像

1.jpg
1.jpg
2.jpg
2.jpg
3.jpg
3.jpg

1.jpgと2.jpgを比較した結果を出力するサンプルコードを記載します。読み込む画像のパス部分を書き換えることで、2.jpgと3.jpgの比較結果も出力できます。

※依存ライブラリ(リンカーへ設定)
  • opencv_core300.lib
  • opencv_imgcodecs300.lib
  • opencv_features2d300.lib
※参照するDLL
  • opencv_core300.dll
  • opencv_imgcodecs300.dll
  • opencv_features2d300.dll
  • opencv_imgproc300.dll
  • opencv_flann300.dll
#include <opencv2/core.hpp>
  #include <opencv2/imgcodecs.hpp>
  #include <opencv2/features2d.hpp>

  int main(int argc, const char* argv[])
  {
      //読み込む画像のパス
    cv::String scene1_path = "C:/1.jpg";
    cv::String scene2_path = "C:/2.jpg";

    //書き出す画像のパス
    cv::String scene_12_path = "C:/12.jpg";


    //比較用画像を読み込む (アルファチャンネル非対応のため、IMREAD_COLORで強制する)
    cv::Mat scene1 = cv::imread(scene1_path, cv::IMREAD_COLOR);
    cv::Mat scene2 = cv::imread(scene2_path, cv::IMREAD_COLOR);

    //アルゴリズムにAKAZEを使用する
    auto algorithm = cv::AKAZE::create();

    // 特徴点抽出
    std::vector<cv::KeyPoint> keypoint1, keypoint2;
    algorithm->detect(scene1, keypoint1);
    algorithm->detect(scene2, keypoint2);

    // 特徴記述
    cv::Mat descriptor1, descriptor2;
    algorithm->compute(scene1, keypoint1, descriptor1);
    algorithm->compute(scene2, keypoint2, descriptor2);
    
    // マッチング (アルゴリズムにはBruteForceを使用)
    cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create("BruteForce");
    std::vector<cv::DMatch> match, match12, match21;
    matcher->match(descriptor1, descriptor2, match12);
    matcher->match(descriptor2, descriptor1, match21);
    //クロスチェック(1→2と2→1の両方でマッチしたものだけを残して精度を高める)
    for (size_t i = 0; i < match12.size(); i++)
    {
      cv::DMatch forward = match12[i];
      cv::DMatch backward = match21[forward.trainIdx];
      if (backward.trainIdx == forward.queryIdx)
      {
        match.push_back(forward);
      }
    }

    // マッチング結果の描画
    cv::Mat dest;
    cv::drawMatches(scene1, keypoint1, scene2, keypoint2, match, dest);

    //マッチング結果の書き出し
    cv::imwrite(scene_12_path, dest);

    return 0;
  }
  
関連記事