読者です 読者をやめる 読者になる 読者になる

のどあめ

ググってもなかなか出てこなかった情報を垂れ流すブログ。

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にチェックアウトしてから使ってください.

今回のまとめ

  • OpenCV 3.0 alpha をメインプログラムに使うのはやめよう
  • Dense Samplingしたい人はOpenCV 2.4.9を使おう

今回参考にしたWebページ


次回は,FisherVectorか,Sparse Codingについて記事を書きたいと思います.