技術屋卵の足跡

いつか振り返ったときにどれだけ這いずり回ったかを確認したい

OpenCVSharpで特徴点検出/マッチング

今回もOpenCVSharpについて。とりあえずもう触ることはないと思うのでアウトプットできることは覚えているうちにしてしまおう。

特徴点とは何かとか、特徴点検出のアルゴリズムの比較なんかは、いい記事がたくさんあったのでここではOpenCVSharpでどのアルゴリズムがどう使えるかを書こうと思う。

特徴点検出

使えるアルゴリズム

OpenCVでサポートされているアルゴリズムは以下の通りらしい。*1

  • cv::GFTTDetector
  • cv::AgastFeatureDetector
  • cv::FastFeatureDetector
  • cv::MSER
  • cv::BRISK
  • cv::KAZE
  • cv::ORB
  • cv::AKAZE
  • cv::xfeatures2d::StarDetector
  • cv::xfeatures2d::MSDDetector
  • cv::xfeatures2d::LATCH
  • cv::xfeatures2d::LUCID
  • cv::xfeatures2d::DAISY
  • cv::xfeatures2d::SIFT
  • cv::xfeatures2d::SURF

Source qiita.com

上の中でOpenCVSharp v.2.4.10 で定義を見つけられたアルゴリズムは以下の通り。 OpenCvSharp.CPlusPlusOpenCvSharp.CPlusPlus.Cv2名前空間以外に存在したらここではリストアップできてないけどこれだけあれば十分に思える。

  • OpenCvSharp.CPlusPlus.Cv2.GoodFeaturesToTrack
  • OpenCvSharp.CPlusPlus.FastFeatureDetector
  • OpenCvSharp.CPlusPlus.MSER
  • OpenCvSharp.CPlusPlus.BRISK
  • OpenCvSharp.CPlusPlus.ORB
  • OpenCvSharp.CPlusPlus.StarDetector
  • OpenCvSharp.CPlusPlus.SIFT
  • OpenCvSharp.CPlusPlus.SURF

使い方

上に上げたもの中でGoodFeatureToTrackだけメソッドとして提供されていて、ほかはFeatureDetectorのインターフェースを持っている。

GoodFeatureToTrack

var img = Cv2.ImRead("feature.png");
var gray = new Mat(img.Rows, img.Cols, MatType.CV_8UC1);
var mask = new Mat(img.Rows, img.Cols, MatType.CV_8UC1, new Scalar(255));
gray = img.CvtColor(ColorConversion.RgbToGray);
//検出、パラメータは適当
var corners = Cv2.GoodFeaturesToTrack(gray, 50, 0.01, 10, mask, 10, true, 5);
//KeyPointではないので地道に点打ち
foreach(var p in corners)
{
    Cv2.Circle(img, p, 5, new Scalar(255, 0, 0),-1);
}
Cv2.ImShow("img",img);

f:id:pfpfdev:20200716201004p:plain

それ以外

var img = Cv2.ImRead("feature.png");
var gray = new Mat(img.Rows, img.Cols, MatType.CV_8UC1);
gray = img.CvtColor(ColorConversion.RgbToGray);
//インターフェースが同じなのでどれでも同じようにつかえる
//var detector = new FastFeatureDetector();
//var detector = new MSER();
//var detector = new BRISK();
//var detector = new ORB();
//var detector = new StarDetector();
//var detector = new SIFT();
var detector = new SURF();
var kp = detector.Detect(gray);
Cv2.DrawKeypoints(gray, kp, img);
Cv2.ImShow("img",img);

f:id:pfpfdev:20200716201027p:plain

名前空間がちょっとだけ違うから見つけられない可能性はあるかもしれないけれど、 OpenCVのラッパーなので使い方自体はほかの言語と変わらないかなあ。

マッチング

こっちはOpenCVがサポートしているすべての方法が使える。

  • BruteForce
  • BruteForce-L1
  • BruteForce-Hamming
  • BruteForce-Hamming(2)
  • FlannBased

これをDescriptorMatcher.Create()にそのままstringとして渡せばよさそう。 もしくはBFMatcher,FlannBasedMatcherのコンストラクタをそのまま呼び出すか。

ここら辺もOpenCVの普通の使い方と同じ感じだから難しくないように思う。

var img = Cv2.ImRead("feature.png");
var gray = new Mat(img.Rows, img.Cols, MatType.CV_8UC1);
gray = img.CvtColor(ColorConversion.RgbToGray);
//さかさまにする回転行列
var rotateMatrix = Cv2.GetRotationMatrix2D(new Point2f(gray.Width / 2, gray.Height / 2), 180, 1);
//画像を回転
var rev = gray.WarpAffine(rotateMatrix, gray.Size());
//特徴点はSURF,ORBで検出する
//var detector = new ORB();
var detector = new SURF();
//正方向の画像について
var kpGray = detector.Detect(gray);
var dscGray = new Mat();
detector.Compute(gray, ref kpGray, dscGray);
//逆方向の画像について
var kpRev = detector.Detect(rev);
var dscRev = new Mat();
detector.Compute(rev, ref kpRev, dscRev);
//マッチング
//var matcher = DescriptorMatcher.Create("BruteForce-Hamming(2)");
//var matches = matcher.Match(dscGray, dscRev);
var matcher = new FlannBasedMatcher();
var matches = matcher.KnnMatch(dscGray, dscRev,1);
//出力
var output = new Mat();
Cv2.DrawMatches(gray, kpGray, rev, kpRev, matches, output);
Cv2.ImShow("output", output);

f:id:pfpfdev:20200716231814p:plain

ただし、Descriptorの内部形式がアルゴリズムによって違うので適切な組み合わせをしないといけない。 以下の記事で言及されているようにSIFT, SURFあたりはユークリッド距離を求めるFlannBasedと相性がよく、ORB, BRIEF, BRISK, FREAK, AKAZEはハフマン距離を求めるBF系と相性がいい。変換する方法もあるみたいだけどLshIndexParamとかはOpenCVSharpで見つけられなかったので、この組み合わせで使うようにすればよさげ。

stackoverflow.com

*1:detectインターフェースが実装されていないものは省略