局所特徴量検出

局所特徴量検出

特定の物体を、画像内から検出するコードを以下に示します。

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

int main(int argc, const char* argv[])
{
    
    //読み込む画像のパス
    cv::String scene1_path = "#";
    cv::String scene2_path = "#";
    
    //書き出す画像のパス
    cv::String scene_12_path = "#";
    
    //比較用画像を読み込む (アルファチャンネル非対応のため、IMREAD_COLORで強制する)
    //-1(自動判別) 0(グレー) 1(デフォ)
    cv::Mat scene1 = cv::imread(scene1_path, 0);
    cv::Mat scene2 = cv::imread(scene2_path, 0);
    
//AKAZE
    //auto algorithm = cv::AKAZE::create(cv::AKAZE::DESCRIPTOR_MLDB, 0, 3, 0.01f);
    
//KAZE
    //auto algorithm = cv::KAZE::create(false, false, 0.001f);
    
//ORB
    //auto algorithm = cv::ORB::create(300); //数
    
//BRISK
    auto algorithm = cv::BRISK::create(); //閾値
    
//SIFT
//    auto algorithm = cv::xfeatures2d::SIFT::create(100, //数?
//                                                    3, //レイヤー
//                                                    0.04, //コントラスト閾値
//                                                    10.0, //エッジ閾値
//                                                    1.6 //シグマ
//                                                    );
    
    // 特徴点抽出
    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);

    //*******************
    // 特徴量マッチング
    //*******************
    auto matchtype = algorithm->defaultNorm(); // SIFT, SURF: NORM_L2
    // BRISK, ORB, KAZE, A-KAZE: NORM_HAMMING
    cv::BFMatcher matcher(matchtype);
    std::vector<std::vector<cv::DMatch >> knn_matches;
    
    
    // 上位2点
    matcher.knnMatch(descriptor1, descriptor2, knn_matches, 2);
    
    
    //***************
    // 対応点を絞る
    //***************
    const auto match_par = .6f; //対応点のしきい値
    std::vector<cv::DMatch> good_matches;
    
    std::vector<cv::Point2f> match_point1;
    std::vector<cv::Point2f> match_point2;
    
    for (size_t i = 0; i < knn_matches.size(); ++i) {
        auto dist1 = knn_matches[i][0].distance;
        auto dist2 = knn_matches[i][1].distance;
        
        //良い点を残す(最も類似する点と次に類似する点の類似度から)
        if (dist1 <= dist2 * match_par) {
            good_matches.push_back(knn_matches[i][0]);
            match_point1.push_back(keypoint1[knn_matches[i][0].queryIdx].pt);
            match_point2.push_back(keypoint2[knn_matches[i][0].trainIdx].pt);
        }
    }
    
    
    //ホモグラフィ行列推定
    cv::Mat masks;
    cv::Mat H;
    if (match_point1.size() != 0 && match_point2.size() != 0) {
        H = cv::findHomography(match_point1, match_point2, masks, cv::RANSAC, 3.f);
    }
    
    //RANSACで使われた対応点のみ抽出
    std::vector<cv::DMatch> inlierMatches;
    for (auto i = 0; i < masks.rows; ++i) {
        uchar *inlier = masks.ptr<uchar>(i);
        if (inlier[0] == 1) {
            inlierMatches.push_back(good_matches[i]);
        }
    }
    
    
    
    // マッチング結果の描画
    cv::Mat dest;
    cv::drawMatches(scene1, keypoint1, scene2, keypoint2, good_matches, dest);
    
    //インライアの対応点のみ表示
    cv::drawMatches(scene1, keypoint1, scene2, keypoint2, inlierMatches, dest);
    
    if (!H.empty()) {
        
        //
        // 対象物体画像からコーナーを取得 ( 対象物体が"検出"される )
        std::vector<cv::Point2f> obj_corners(4);
        obj_corners[0] = cv::Point2f(.0f, .0f);
        obj_corners[1] = cv::Point2f(static_cast<float>(scene1.cols), .0f);
        obj_corners[2] = cv::Point2f(static_cast<float>(scene1.cols), static_cast<float>(scene1.rows));
        obj_corners[3] = cv::Point2f(.0f, static_cast<float>(scene1.rows));
        
        // シーンへの射影を推定
        std::vector<cv::Point2f> scene_corners(4);
        cv::perspectiveTransform(obj_corners, scene_corners, H);
        
        // コーナー間を線で結ぶ ( シーン中のマップされた対象物体 - シーン画像 )
        float w = static_cast<float>(scene1.cols);
        cv::line(dest, scene_corners[0] + cv::Point2f(w, .0f), scene_corners[1] + cv::Point2f(w, .0f), cv::Scalar(0, 255, 0), 4);
        cv::line(dest, scene_corners[1] + cv::Point2f(w, .0f), scene_corners[2] + cv::Point2f(w, .0f), cv::Scalar(0, 255, 0), 4);
        cv::line(dest, scene_corners[2] + cv::Point2f(w, .0f), scene_corners[3] + cv::Point2f(w, .0f), cv::Scalar(0, 255, 0), 4);
        cv::line(dest, scene_corners[3] + cv::Point2f(w, .0f), scene_corners[0] + cv::Point2f(w, .0f), cv::Scalar(0, 255, 0), 4);
    }
    
    //マッチング結果の書き出し
    cv::imwrite(scene_12_path, dest);
    
    return 0;
}