お米風呂式

2次元と3次元のCVをやってる大学院生

リアルタイム特徴点マッチング

毎回画像を差し替えるのが面倒だった

 引き続きOpenCVを使って特徴点マッチングのお勉強中です。
 さて、前回までは、毎回マッチングしたい画像を差し替えてビルドしてを繰り返していました。でもそれってすごく面倒だなって。だからマッチングしたい画像1(例えば看板)とPCのカメラでキャプってる画像2(動画)をリアルタイムで特徴点マッチングできれば楽です。というか、やってる人が沢山いる。つまりそっちのが楽ってこと(根拠なし)。

サンプルコードがねえ

 これ困ります。OpenCVどころかC++始めたばかりの人間にサンプルコード無しは結構響く。

 とりあえず分解して考える。
基本的には
1.カメラから画像を取ってくる
2.それと画像1の特徴量抽出・記述する
3.マッチング
という流れをイメージ

1のコードを探す。ありましたと。

//カメラのキャプチャ
    cv::VideoCapture cap(0);
//キャプチャエラー
    if (!cap.isOpened()) return -1;

 で、あとは前回までのを流用して、画像1を用意。イメージとしてはフレーム、つまり毎秒カメラからの画像を取って、2.3.をくる返す。だからforで回せばいい。

int main(int argc, const char* argv[])
{
    
 
    //読み込む画像のパス
    cv::String scene1_path = "imgPath";
    //変数宣言
    cv::Mat im;
    //カメラのキャプチャ
    cv::VideoCapture cap(0);
    //キャプチャエラー
    if (!cap.isOpened()) return -1;
    
    while(1) {
        //カメラ映像の取得
        cap >> im;
        //画像の取得
        cv::Mat scene1 = cv::imread(scene1_path, 1);
        
        //ORB
            auto algorithm = cv::ORB::create();
        //特徴点抽出
        std::vector<cv::KeyPoint> keypoint1, keypoint2;
        algorithm->detect(scene1, keypoint1);
        algorithm->detect(im, keypoint2);
        // 特徴記述
        cv::Mat descriptor1, descriptor2;
        algorithm->compute(scene1, keypoint1, descriptor1);
        algorithm->compute(im, keypoint2, descriptor2);
        // マッチング (アルゴリズムにはBruteForceを使用)
        cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create("BruteForce-Hamming");
        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, im, keypoint2, match, dest);
        //映像の表示
        imshow("Camera", dest);
        //キー入力で終了
        if(cv::waitKey(30) >= 0) break;
    }
    return 0;
}

という感じ。

ウィンドウでかすぎ問題

 このコードでビルドすると、画像サイズのままウィンドウに表示されます。どういうことかって言うと、iPhoneとかで撮ると、軽く2k画質くらいいくわけで、それをそのまま表示するんだからモニターいっぱいに表示されても、まだ全体が見えないとかいう事になる。
この問題を解決するには
1.imshowのサイズを指定する方法を探す。
2.いっそ元画像を読み込む前に見やすいサイズにリサイズする
の2点を思いつきました。
 スマートなのは1.ですねどう考えても。考えなくても。簡単なのは2.ですね。何をもって簡単とか言っているかというと、リサイズ方法はわかっているから。1.のウィンドウサイズ指定方法はわかっていない。
 まぁ現段階だと自分のiPhoneカメラでしか撮らないから、毎回サイズは同じなので、リサイズでも支障はないはず。別の誰かやカメラを使う場合めんどくさい。だってアスペクト比変えたくないから、ピクセル単位じゃなくて比率でリサイズするから。
 とりあえず2.で進めといて、余裕があれば1.を調べて見ましょう。そうしましょう。