アプリケーションのポイントは、既に設定されている画像のリストから画像を認識することです。画像のリストには、SIFT 記述子が抽出され、ファイルに保存されています。ここで興味深いものは何もありません:
std::vector<cv::KeyPoint> detectedKeypoints;
cv::Mat objectDescriptors;
// Extract data
cv::SIFT sift;
sift.detect(image, detectedKeypoints);
sift.compute(image, detectedKeypoints, objectDescriptors);
// Save the file
cv::FileStorage fs(file, cv::FileStorage::WRITE);
fs << "descriptors" << objectDescriptors;
fs << "keypoints" << detectedKeypoints;
fs.release();
次に、デバイスが写真を撮ります。SIFT 記述子も同様に抽出されます。ここでのアイデアは、記述子をファイルの記述子と比較することでした。私は OpenCV の FLANN マッチャーを使用してそれを行っています。画像ごとに類似性を定量化しようとしています。リスト全体を調べた後、最適な一致が得られるはずです。
const cv::Ptr<cv::flann::IndexParams>& indexParams = new cv::flann::KDTreeIndexParams(1);
const cv::Ptr<cv::flann::SearchParams>& searchParams = new cv::flann::SearchParams(64);
// Match using Flann
cv::Mat indexMat;
cv::FlannBasedMatcher matcher(indexParams, searchParams);
std::vector< cv::DMatch > matches;
matcher.match(objectDescriptors, readDescriptors, matches);
マッチング後、特徴ベクトル間で見つかった最も近い距離のリストを取得することがわかりました。最小距離を見つけ、それを使用して「適切な一致」をカウントし、それぞれのポイントのリストを取得することもできます。
// Count the number of mathes where the distance is less than 2 * min_dist
int goodCount = 0;
for (int i = 0; i < objectDescriptors.rows; i++)
{
if (matches[i].distance < 2 * min_dist)
{
++goodCount;
// Save the points for the homography calculation
obj.push_back(detectedKeypoints[matches[i].queryIdx].pt);
scene.push_back(readKeypoints[matches[i].trainIdx].pt);
}
}
これをより簡単に理解できるようにするために、コードの簡単な部分を示しています。一部はここにある必要がないことはわかっています。
続けて、このように適切な一致の数を数えるだけで十分だと思っていましたが、ほとんどの場合、記述子が最も多い画像を指すだけでした。この後に私が試みたのは、ホモグラフィの計算でした。目的は、それを計算して、有効なホモラフィかどうかを確認することでした。良い一致だけが、良い変換であるホモグラフィーを持つことが期待されていました。ホモグラフィの作成は、std::vector< cv::Point2f>であるobjとシーンでcv::findHomographyを使用するだけで行われました。オンラインで見つけたコードを使用して、ホモグラフィの有効性を確認しました。
bool niceHomography(cv::Mat H)
{
std::cout << H << std::endl;
const double det = H.at<double>(0, 0) * H.at<double>(1, 1) - H.at<double>(1, 0) * H.at<double>(0, 1);
if (det < 0)
{
std::cout << "Homography: bad determinant" << std::endl;
return false;
}
const double N1 = sqrt(H.at<double>(0, 0) * H.at<double>(0, 0) + H.at<double>(1, 0) * H.at<double>(1, 0));
if (N1 > 4 || N1 < 0.1)
{
std::cout << "Homography: bad first column" << std::endl;
return false;
}
const double N2 = sqrt(H.at<double>(0, 1) * H.at<double>(0, 1) + H.at<double>(1, 1) * H.at<double>(1, 1));
if (N2 > 4 || N2 < 0.1)
{
std::cout << "Homography: bad second column" << std::endl;
return false;
}
const double N3 = sqrt(H.at<double>(2, 0) * H.at<double>(2, 0) + H.at<double>(2, 1) * H.at<double>(2, 1));
if (N3 > 0.002)
{
std::cout << "Homography: bad third row" << std::endl;
return false;
}
return true;
}
この背後にある数学を理解していないので、テスト中に、この関数をホモグラフィの行列式が正であるかどうかの単純なチェックに置き換えることがありました。問題は、ここで問題が発生し続けたことです。同形異義語は、すべて悪いか、そうであってはならないときに良いものでした (行列式だけをチェックしていたとき)。
私は実際にホモグラフィを使用し、多くのポイントについて、ソース画像での位置を使用して宛先画像での位置を計算する必要があると考えました。次に、これらの平均距離を比較します。理想的には、正しい画像の場合、平均距離が非常に明らかに小さいことが理想的です。これはまったく機能しませんでした。すべての距離は巨大でした。正しい位置を計算するためにホモグラフィを逆に使用したのではないかと思いましたが、objとシーンを相互に切り替えると、同様の結果が得られました。
私が試したその他のことは、SIFT の代わりに SURF 記述子、FLANN の代わりに BFMatcher (ブルート フォース)、最小距離に応じた数値の代わりにすべての画像のn 個の最小距離を取得すること、またはグローバルな最大距離に応じて距離を取得することでした。これらのアプローチはどれも明確な良い結果をもたらしませんでした。
私の唯一の次の戦略は、画像を鮮明にするか、またはセグメンテーションに使用されるいくつかのローカルしきい値またはアルゴリズムを使用してバイナリ画像に変換することです. 私の仕事で誰かが見ることができる提案や間違いを探しています。
これが関連しているかどうかはわかりませんが、これをテストしている画像をいくつか追加しました。多くの場合、テスト画像では、ほとんどの SIFT ベクトルが絵画よりもフレーム (コントラストが高い) に由来します。これが、画像を鮮明にすることがうまくいくと考えている理由ですが、以前に行ったことが間違っている場合に備えて、深く掘り下げたくありません。
画像のギャラリーは、タイトルの説明とともにここにあります。かなり解像度の高い画像ですので、参考になればと思います。