はじめに
OpenCV には DenseFeatureDetector
という便利なものがあります。
これは主に一般物体認識で Dense Sampling をしたいときに利用します。
通常、たとえば SIFT や SURF では特徴点を抽出してから特徴記述を行うわけですが、
特徴点の抽出をせず、画像の至る所をグリッドサンプリングしていきたいことがあります。
そういう時に使うのがこの DenseFeatureDetector
です。
ところがコイツはパラメータが多い上にいまいちわかりにくいので、引数の解説をしたいと思います。
class DenseFeatureDetector : public FeatureDetector
{
public:
DenseFeatureDetector( float initFeatureScale=1.f, int featureScaleLevels=1,
float featureScaleMul=0.1f,
int initXyStep=6, int initImgBound=0,
bool varyXyStepWithScale=true,
bool varyImgBoundWithScale=false );
protected:
...
};
各パラメータ解説
initFeatureScale
初期の特徴のサイズ(半径)です。float
ですが単位はピクセルです。
詳しくは後述。
featureScaleLevels
DenseFeatureDetector では特徴のサイズを変えて何回かサンプリングできます。
SIFT や SURF ではこの大きさを適当に決めてくれるのですが、今回は自分で指定するので大きさの変化に対してロバストにするために色々なサイズで特徴抽出をしたほうがよいわけです。
ここでは何段階特徴のサイズを変更するかを決めます。デフォルトは1。
詳しくは後述。
featureScaleMul
featureScaleLevels
が1増えるごとにどれくらい特徴のサイズを大きくするかを決めます。
つまり、最初は特徴のサイズ r
は initFeatureScale
で初期化され、そのあと featureScaleLevels
回、 featureScaleMul
を掛けた大きさでサンプリングします。
最終的な特徴のサイズは initFeatureScale * pow(featureScaleMul, featureScaleLevels)
px になります。
たとえば initFeatureScale = 5.0
, featureScaleLevels = 3
, featureScaleMul = 2.0
の場合だと
まずは特徴サイズが 5px でサンプリングされ、次に 5 * 3 = 15px でサンプリングされ、最後に 15 * 3 = 45px でサンプリングされます。
initXyStep
上記3つは特徴の大きさに関する値でしたが、これはどれくらいの密度でサンプリングするかの値です。デフォルトは1。単位はpx。
これは1pxごとに特徴抽出すると数が多すぎるので、数pxおきにしたいなどといった場合に指定します。たとえば5を指定すると5pxおきに特徴抽出してくれます。
なお名前からわかるように x と y で別々の値を指定することはできません。
initImgBound
画像の端っこは特徴抽出したくない時に指定します。
単位はpxで、1以上を指定するとその大きさぶん画像の端から離れた場所から特徴抽出します。
たとえば画像が黒く縁取られているのでそのあたりは無視したい、といった場合などに使うんだと思います。
varyXyStepWithScale
true
にすると XyStep
にも featureScaleMul
を乗算します。
デフォルトは false
です。一般物体認識タスクでは精度を高めたいなら密にサンプリングできる false
, リソースの消費が気になる場合は true
で良いと思います。
varyImgBoundWithScale
これは varyXyStepWithScale
の initImgBound
です。
基本的に false
のままで良いと思います。
サンプルコード
#include <iostream>
#include <fstream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.hpp>
int main(int argc, char* argv[])
{
cv::Mat img = cv::imread(argv[1]);
if (img.empty()) {
exit(EXIT_FAILURE);
}
cv::DenseFeatureDetector detector(
8.0f, //initFeatureScale: 初期の特徴のサイズ(直径)[px]
3, //featureScaleLevels: 何段階サイズ変更してしてサンプリングするか(>=1)
1.414f, //featureScaleMul: ScaleLevelごとにどれくらい拡大縮小するか(!=0)
4, //initXyStep: 特徴点をどれくらいの量ずらしながらサンプリングするか
0, //initImgBound: 画像の端からどれくらい離すか(>=0)
false, //varyXyStepWithScale: XyStepにもScaleMul を掛けるか
false //varyImgBoundWithScale: BoundにもScaleMul を掛けるか
);
std::vector<cv::KeyPoint> keypoints;
detector.detect(img, keypoints);
// 記述子として次のいずれかを指定する
// FAST, GFTT, SIFT (nonfree), SURF (nonfree),
// MSER, STAR, ORB, BRISK, FREAK, BRIEF
cv::Ptr<cv::DescriptorExtractor> extractor = cv::DescriptorExtractor::create("BRISK");
cv::Mat descriptors;
extractor->compute(img, keypoints, descriptors);
if(argc > 3) {
std::ofstream stream(argv[2]);
stream << cv::format(descriptors, "csv");
} else {
std::cout << cv::format(descriptors, "csv") << std::endl;
}
return 0;
}
各特徴記述子 (descriptor) についても何を選んだら良いのかよくわからないかもしれませんが、これらの違いについてはまた別の機会に。