LoginSignup
43
39

More than 5 years have passed since last update.

OpenCVのcv::findHomographyについて

Posted at

findHomographyとは

2枚の画像間のホモグラフィ行列の推定に使う関数です.
OpenCVのリファレンスを見ると以下の3つのパターンで使用できることがわかります.

findHomography
cv::Mat findHomography(const Mat& srcPoints, const Mat& dstPoints, Mat& status, int method=0, double ransacReprojThreshold=3)
cv::Mat findHomography(const Mat& srcPoints, const Mat& dstPoints, vector<uchar>& status, int method=0, double ransacReprojThreshold=3)
cv::Mat findHomography(const Mat& srcPoints, const Mat& dstPoints, int method=0, double ransacReprojThreshold=3)

パラメータ

srcPoints -> 元平面の点群の座標. CV_32FC2 型の行列,あるいは vector<Point2f>で入力
dstPoints -> 目的平面の点群の座標. CV_32FC2 型の行列,あるいは vector<Point2f>で入力
method -> ホモグラフィ行列を求めるための手法

method入力値 説明
0 点の組をすべて利用する通常の手法
CV_RANSAC RANSACアルゴリズムに基づくロバストな手法
CV_LMEDS LMedS推定(Least-Median)によるロバストな手法

ransacReprojThreshold ->点の組を,インライア値(外れ値ではないもの)として扱うために許容される逆投影誤差の最大値(これは,RANSACメソッドでのみ利用されます).srcPoints と dstPoints がピクセル単位で表されているならば,このパラメータは1から10の範囲内の値が妥当です.

status -> ロバスト手法( CV_RANSAC または CV_LMEDS )を利用した場合に,オプションとして出力されるマスク. 入力マスク値は無視されることに注意してください.

RANSACで使用した点のみを出力する

RANSACかLMEDSを使ったときに出力されるstatusには,マッチ点数分の{0,1}が出力されます.
1の点は使用された点(inliner)で,0の点は使用されていない点(outliner)です.

表示プログラム

対応点の表示とRANSACで選ばれたインライアの対応点の表示をするプログラムはこんな感じです
(汚くてごめんなさい).

inliner
#include<iostream>
#include<vector>
#include<string>
#include<fstream>
#include"OpenCV3.h" //OpenCV読み込み自作ヘッダ

using namespace std;
using namespace cv;

int main(void)
{
    Mat Src1 = imread("graf1.png");
    Mat Src2 = imread("graf3.png");

    //キーポイント検出と特徴量記述
    vector<KeyPoint> keypoints1;
    vector<KeyPoint> keypoints2;
    Mat descriptors1, descriptors2;
    cv::Ptr<cv::AKAZE> akaze = cv::AKAZE::create();
    akaze->detectAndCompute(Src1, cv::Mat(), keypoints1, descriptors1);
    akaze->detectAndCompute(Src2, cv::Mat(), keypoints2, descriptors2);

    //マッチング(knnマッチング)
    vector<vector<cv::DMatch>> knnmatch_points;
    cv::BFMatcher match(cv::NORM_HAMMING);
    match.knnMatch(descriptors1, descriptors2, knnmatch_points, 2);

    //対応点を絞る
    const double match_par = 0.6; //候補点を残す場合のしきい値
    vector<cv::DMatch> goodMatch;
    //KeyPoint -> Point2d
    vector<cv::Point2f> match_point1;
    vector<cv::Point2f> match_point2;
    for (size_t i = 0; i < knnmatch_points.size(); ++i) {
        double distance1 = knnmatch_points[i][0].distance;
        double distance2 = knnmatch_points[i][1].distance;

        //第二候補点から距離値が離れている点のみ抽出(いい点だけ残す)
        if (distance1 <= distance2 * match_par) {
            goodMatch.push_back(knnmatch_points[i][0]);
            match_point1.push_back(keypoints1[knnmatch_points[i][0].queryIdx].pt);
            match_point2.push_back(keypoints2[knnmatch_points[i][0].trainIdx].pt);
        }
    }

    //ホモグラフィ行列推定
    cv::Mat masks;
    cv::Mat H = cv::findHomography(match_point1, match_point2, masks, cv::RANSAC, 3);

    //RANSACで使われた対応点のみ抽出
    vector<cv::DMatch> inlinerMatch;
    for (size_t i = 0; i < masks.rows; ++i) {
        uchar *inliner = masks.ptr<uchar>(i);
        if (inliner[0] == 1) {
            inlinerMatch.push_back(goodMatch[i]);
        }
    }

    //対応点の表示
    cv::Mat drawmatch;
    cv::drawMatches(Src1, keypoints1, Src2, keypoints2, goodMatch,drawmatch);
    imwrite("match_point.jpg",drawmatch);

    //インライアの対応点のみ表示
    cv::Mat drawMatch_inliner;
    cv::drawMatches(Src1,keypoints1,Src2,keypoints2,inlinerMatch,drawMatch_inliner);
    imwrite("match_inliner.jpg",drawMatch_inliner);

    imshow("DrawMatch", drawmatch);
    imshow("Inliner", drawMatch_inliner);
    waitKey();

    return 0;
}

出力結果

cv::findHomographyに入れる前の対応点
match_point.jpg

cv::findHomographyに入れた後(RANSACで求めたインライア)の対応点
match_inliner.jpg

こんな感じで表示,または利用ができます.

43
39
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
43
39