Opencv3について調べてみる

画像処理をすることに

 画像処理をするにあたり、OpenCVを使うことになりました。
 目的は、特徴点抽出・記述アルゴリズムの特性などを調べることです。

 C++を使えとのことなので(教授命令)言語はC++を使います。しかし。これまで使ったことがなかったので、並行して勉強する事になりますね(正直Python使いたい)。
 OpenCVには2系と3系があり、2系が主流らしいのですが、3系の方が新しいのでこちらを使っていきます。
 画像処理といってもたくさんありますが、当面は特徴点マッチングを行っていきます。

 もしこのブログを見てくださって、指摘をしてくださるありがたいお方がいましたら、どんどんコメントをお願い致します。


OpenCVで使えるアルゴリズム

OpenCV2.4

SIFT、SURF、BRIEF、BRISK、ORB、FREAK

OpenCV3.0

BRISK、ORB

OpenCV3.1.0

BRISK、ORB、KAZE、AKAZE

 3系からSIFTやSURFが削除されているのは、ライセンス関係です。これらは特許ライセンスがあるので、使うには面倒。その代わりとして主流なのがORBというアルゴリズム

 3.1からKAZEとAKAZEが追加されています(どちらもライセンスフリー
 KAZEは、SIFTやSURFの特徴をそのままに、これらの欠点であるエッジの検出を解消したアルゴリズムだそうです。
 AKAZEは、KAZEの高速化Verで、並列処理をすればめっちゃ早いらしい。

 という感じで、個人的には大は小を兼ねるといいますか、新しいものはだいたい素晴らしいって事で、AKAZEに注目しています。
 ちなみにKAZE理論は2012年に発表され、OpenCV3.1は最近アップデートされたので、情報が少ない。
 詳しく調べていくと非線形がどうだとか、ロバスト性がどうだとかでいまいちよくわからない。
 とりあえずサンプルコードを持ってきて動かしてみる所から始めます。正直時間が無いのでHelloWorldからやる暇がない。我が研究室のルールというか理念?である「いかに早く良いものを作れるか」に則るとコードをパクりまくって、とりあえず動かすしかないのです。

特徴量マッチング

コードはこちらを参考にさせてもらいました。
dronebiz.net

#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/xfeatures2d.hpp>

int main(int argc, const char* argv[])
{
    

    
    //読み込む画像のパス
    cv::String scene1_path = "imgPath1"
    cv::String scene2_path = "imgPath2";

    
    //書き出す画像のパス
    cv::String scene_12_path = "imgPath12";
    
    
    //比較用画像を読み込む (アルファチャンネル非対応のため、IMREAD_COLORで強制する)
    //-1(自動判別) 0(グレー) 1(デフォ)
    cv::Mat scene1 = cv::imread(scene1_path, 1);
    cv::Mat scene2 = cv::imread(scene2_path, 1);
    
    //AKAZE
    auto algorithm = cv::AKAZE::create();
    
    //KAZE
    //auto algorithm = cv::KAZE::create();
    
    //ORB
    //auto algorithm = cv::ORB::create();
    
    //BRISK
    //auto algorithm = cv::BRISK::create();
    
    //FAST
    //auto algorithm = cv::FastFeatureDetector::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;
}

結果がこれ

f:id:san4yone3:20161206223613j:plain

 対空標識もどきを置いてのマッチングをしたかったのですがダメダメですね。
 やはり特徴点マッチングだと空撮は難しいのですかね。

 ていうかコードで特徴点の数やマッチング数を指定できないのかな。もう少しそこらへんを調べてみたい。
 あと毎回画像Pathやコメントアウトアルゴリズムを切り替えるのも面倒なので、forでそこら辺回したい。

 しばらく空撮は置いておき、看板のマッチングという定番をやっていこうと思います。
 そもそも特徴点マッチングが何に使われているかもわからない状態なのでそれも調べないとですね。学ぶことが多くて大変です^^;

今日はとりあえずここまでとし、次回は

 ・看板などのマッチング(角度や回転の影響を調べる)
 ・コードをもっと合理的に書き直す
 ・特徴点の数の可変
 ・そもそも特徴点マッチングがどういうものに使われているのか
 これらを行いたいと考えています。