web-dev-qa-db-ja.com

OpenCV:findHomography()/ findFundamental()とRANSACを使用してインライアポイントを取得する方法

OpenCVは、RANSAC関数自体、または少なくともそれを呼び出して実行できるような形式では提供しません(例:cv::ransac(...))。 RANSACを使用できるすべての関数/メソッドには、それを有効にするフラグがあります。ただし、これは、ホモグラフィ/基本行列を推定した後にRANSACが計算するインライアで実際に何か他のことをしたい場合、たとえばOctaveまたは同様のソフトウェア/ポイントのライブラリでNiceプロットを作成したり、追加のアルゴリズムを適用したりする場合に常に役立つとは限りません。フィルタリングされた一致の残りのセットなど。

2つの画像を照合した後、1つは一致のベクトルを取得します。それに加えて、もちろん、マッチングプロセスで使用された2セットのキーポイント(各画像に1つ)があります。一致とキーポイントを使用して、ポイントの2つのベクトル(例:_cv::Point2f points_)を作成し、これらをfindHomography()に渡します。 this および this の投稿から、マスクを使用してインライアがどのように正確にマークされているかを発見し、その関数に渡しました。マスク内の各行は、インライア/アウトライアに関連しています。ただし、2つのポイントセットから行インデックス情報を使用する方法を理解できません。 OpenCVのソースコードを見ても、それほど遠くはありませんでした。 findFundamental()(署名とマスク部分に関してはfindHomography()と同様)では、compressPoints()を使用します。これは、2つのセットを何らかの形で組み合わせているようです。 1つへの入力(ソースポイントと宛先ポイント)として。マスクの性質を決定するためにテストしている間、私は2セットの一致したポイントを試しました(_cv::Keypoints_を_cv::Point2f_に変換-標準的な手順)。各セットには300ポイントが含まれているため、合計で600ポイントになります。返されるマスクには300行が含まれています(このトピックでは値は重要ではありません)。

編集:これを書いている間に私は答えを発見しました(以下を参照)が、誰かがこの情報をできるだけ早くコンパクトな形で必要とする場合に備えて、とにかくこの質問を投稿することにしました。 RANSACをサポートするOpenCVの関数の1つがまだ必要であることに注意してください。したがって、一連のポイントはあるが、ホモグラフィや基本行列を計算する意図がない場合、これは明らかに方法ではなく、OpenCVのAPIでこの障害を回避するのに役立つものを見つけることができなかったため、使用する必要があります。外部ライブラリ。

11
rbaleksandar

解決策は実際には非常に簡単です。私たちが知っているように、マスクの各行は、インライアまたはアウトライアがあるかどうかの情報を提供します。ただし、入力として2セットのポイントがあるので、1つの値を含む行が2つのポイントをどのように正確に表すのでしょうか。この種のインデックス付けの性質は、これら2つのポイントセットがfindHomography()に実際にどのように表示されるかを考えているときに頭に浮かびました(私の場合、2つの画像間のホモグラフィを計算していました)。両方のセットには、画像のペア間の一致から抽出されるという単純な事実のために、同じ数のポイントがあります。これは、マスクの行が2つのセットのポイントの実際のインデックスであり、2つの画像の一致のベクトルのインデックスでもあることを意味します。これに基づいて、一致したポイントの小さなサブセットを手動で参照することに成功しました。結果は期待どおりです。各cv::DMatchで参照されているキーポイントを使用して、一致の順序とそれらから抽出した2Dポイントを変更しないことが重要です。以下に、1組のインライアの簡単な例を示します。

for(int i = 0; i < matchesObjectScene.size(); ++i)
{
   // extract points from keypoints based on matches
   pointsObject.Push_back(keypointsObject.at(matchesObjectScene.at(i).queryIdx).pt);
   pointsScene.Push_back(keypointsScene.at(matchesObjectScene.at(i).trainIdx).pt);
}
// compute homography using RANSAC
cv::Mat mask;
cv::Mat H = cv::findHomography(pointsObject, pointsScene, CV_RANSAC, ransacThreshold, mask);

上記の例では、インライアを印刷すると

int maskRow = 10;
std::cout << "POINTS: object(" << pointsObject.at(maskRow).x << "," << pointsObject.at(maskRow).y << ") - scene(" << pointsScene.at(maskRow).x << "," << pointsScene.at(maskRow).y << ")" << std::endl;

そしてまた、今回はキーポイントを使用します(抽出された2Dポイントでも実行できます)

std::cout << "POINTS (via match-set): object(" << keypointsObject.at(matchesCurrentObject.at(maskRow).queryIdx).pt.x << "," << keypointsObject.at(matchesCurrentObject.at(maskRow).queryIdx).pt.y << ") - scene(" << keypointsScene.at(matchesCurrentObject.at(maskRow).trainIdx).pt.x << "," << keypointsScene.at(matchesCurrentObject.at(maskRow).trainIdx).pt.y << ")" << std::endl;

実際には同じ出力が得られます。

POINTS: object(462,199) - sscene(485,49)
POINTS (via match-set): object(462,199) - scene(485,49)

実際のインライアを取得するには、マスクの現在の行に実際に0またはゼロ以外の値が含まれているかどうかを確認する必要があります。

if((unsigned int)mask.at<uchar>(maskRow))
  // store match or keypoints or points somewhere where you can access them later
12
rbaleksandar

別のメモで。 RANSACは外れ値を拒否する抽象的な手法であるため、OpenCVにRANSACを単独で関数として存在させることは不可能な場合があります。 RANSACは、外れ値の拒否を実行するための基本モデルに依存しています。現在、基本モデルは非常に一般的です。それは何でもかまいません(必ずしもそれらの間で何らかの関係があるポイントではありません)。これが、RANSACが、findHomographyfindFundamentalMatなどの定義済みスコープを持ついくつかの定義済みタスクを実行する他の関数の機能としてのみ存在する理由である可能性があります。

1
Arun Kumar