OpenCV 3.0.0 alpha で Dense samplingする
さて,OpenCV 3.0.0 alphaがリリースされて結構たちましたね.
研究室のうちのグループでは調子に乗って一足先にグループ内ライブラリにOpenCV 3.0.0-alphaを取り入れましたが,あれがないこれがない,などと色々と困ったことになったりしました.
さて,その中のうちの一つとして「OpenCV3.0.0 alphaからDense Samplingがなくなった」というものがあります.
参考:OpenCV3.0.0-alphaの特徴抽出・マッチングまとめ - whoopsidaisies's diary
これは,Bag-of-featuresの時代は終わったということなのでしょうか・・・
まあ,Dense Samplingぐらいすぐ実装できる,と思っていたのですが,
いざやってみると結構躓いたので記事にしました.
Dense Samplingの実装
OpenCVのcv::FeatureDetectorの子クラスとして,
Dense Samplingを実装しました.
DenseFeatureDetector.h
#pragma once #include <opencv2/opencv.hpp> namespace cv { class DenseFeatureDetector : public cv::FeatureDetector { public: struct Param{ int xStep = 4; int yStep = 4; float initFeatureScale = 1.0f; int scaleLevels = 1; float featureScaleMul = 1.2f; int imgBoundX = 0; int imgBoundY = 0; bool varyXyStepWithScale = true; bool varyImgBoundWithScale = false; Param(){} }param; DenseFeatureDetector(){} DenseFeatureDetector(const Param& p) :param(p) { } static cv::AlgorithmInfo& _info(); virtual cv::AlgorithmInfo* info()const override; virtual void detectImpl(cv::InputArray image, std::vector<cv::KeyPoint>& keypoints, cv::InputArray mask = cv::noArray()) const override; static cv::Ptr<FeatureDetector> create(const Param& p=Param()){ return cv::Ptr<FeatureDetector>(new DenseFeatureDetector(p)); } }; }
DenseFeatureDetector.cpp
#include "DenseFeatureDetector.h" namespace cv{ #define CV_INIT_ALGORITHM(classname, algname, memberinit) \ static inline ::cv::Algorithm* create##classname##_hidden() \ { \ return new classname; \ } \ \ static inline ::cv::Ptr< ::cv::Algorithm> create##classname##_ptr_hidden() \ { \ return ::cv::makePtr<classname>(); \ } \ \ static inline ::cv::AlgorithmInfo& classname##_info() \ { \ static ::cv::AlgorithmInfo classname##_info_var(algname, create##classname##_hidden); \ return classname##_info_var; \ } \ \ static ::cv::AlgorithmInfo& classname##_info_auto = classname##_info(); \ \ ::cv::AlgorithmInfo* classname::info() const \ { \ static volatile bool initialized = false; \ \ if( !initialized ) \ { \ initialized = true; \ classname obj; \ memberinit; \ } \ return &classname##_info(); \ } CV_INIT_ALGORITHM(DenseFeatureDetector, "Dense",); void DenseFeatureDetector::detectImpl(cv::InputArray image, std::vector<cv::KeyPoint>& keypoints, cv::InputArray mask) const { cv::Mat maskMat = mask.empty() ? cv::Mat(image.size(), CV_8UC1, cv::Scalar(255)) : mask.getMat(); cv::Mat imageMat = image.getMat(); int width = imageMat.cols; int height = imageMat.rows; int nowBoundX = param.imgBoundX; int nowBoundY = param.imgBoundY; int nowXStep = param.xStep; int nowYStep = param.yStep; float nowFeatureSize = param.initFeatureScale; for (int s = 0; s < param.scaleLevels; s++){ for (int y = nowBoundY; y < height; y += nowYStep){ unsigned char* maskline = maskMat.ptr<unsigned char>(y); for (int x = nowBoundX; x < width; x += nowXStep){ if (maskline[x] > 128){ cv::KeyPoint kp; kp.pt = cv::Point(x, y); kp.size = nowFeatureSize; kp.response = 100.0f; keypoints.push_back(kp); } } } if (param.varyImgBoundWithScale){ nowBoundX *= param.featureScaleMul; nowBoundY *= param.featureScaleMul; } if (param.varyXyStepWithScale){ nowXStep *= param.featureScaleMul; nowYStep *= param.featureScaleMul; } nowFeatureSize *= param.featureScaleMul; } } }
以上です.
使い方としては,
#include "DenseFeatureDetector.h" int main(){ cv::Mat img = cv::imread("lena.jpg"); cv::DenseFeatureDetector::Param p; p.scaleLevels = 3; p.featureScaleMul = 1.5; p.xStep = 20; p.yStep = 20; cv::Ptr<cv::FeatureDetector> detector = cv::DenseFeatureDetector::create(p); std::vector<cv::KeyPoint> keypoints; detector->detect(img,keypoints); for(auto& k : keypoints){ std::cout << "Pt : "<< k.pt << " Size :" << k.size << std::endl; } return 0; }
出力
Pt : [0, 0] Size :1 Pt : [20, 0] Size :1 Pt : [40, 0] Size :1 Pt : [60, 0] Size :1 (中略) Pt : [180, 270] Size :2.25 Pt : [225, 270] Size :2.25 Pt : [270, 270] Size :2.25 Pt : [315, 270] Size :2.25 Pt : [360, 270] Size :2.25
こんなかんじです.
cv::Algorithmを継承したクラスは基本的にCV_INIT_ALGORITHMを使う必要があるみたいですね.
(´・ω・`)知らんがな.
これに気づくまでに結構時間がかかりました.
本当はcv::FeatureDetector::create("Dense")みたいなことがしたいのですが,これはOpenCVを再コンパイルする必要があると思うので,今回はとりあえず使えればOKというスタンスで実装しました.
何にせよ,これを使えばOpenCV 3.0 alphaでもDense Samplingが出来るようになりました!めでたしめでたし.
なお,この実装はOpenCVのgitリポジトリがHEADだと動かないみたいなので,
3.0.0 alphaにチェックアウトしてから使ってください.
今回参考にしたWebページ
- OpenCV3.0.0-alphaの特徴抽出・マッチングまとめ - whoopsidaisies's diary
- OpenCV - cv::DenseFeatureDetector の引数は何を意味しているのか - Qiita
次回は,FisherVectorか,Sparse Codingについて記事を書きたいと思います.