OpenCVの検出器とディスクリプタは両方共,共通のインタフェースでラッピングされていますので, 同じ問題を解く別のアルゴリズムに簡単に切り替えることができます. キーポイント検出器を実装するすべてのオブジェクトは,FeatureDetector インタフェースを継承しています. また,多次元ベクトルとして表現されるディスクリプタは,DescriptorExtractor インタフェースを用いて求められます. DescriptorMatcher インタフェースは,ディスクリプタの2つの集合同士をマッチングさせるために利用されます. GenericDescriptorMatch は,ディスクリプタのより一般的なインタフェースを提供します. これは,ディスクリプタの表現方法に関係なく利用できます. また,DescriptorExtractor インタフェースを持つすべてのディスクリプタには, GenericDescriptorMatch インタフェースのラッパーが存在します(VectorDescriptorMatchを参照してください). 逆に,GenericDescriptorMatch インタフェースの実装となっていても,DescriptorExtractor をサポートしない, one way ディスクリプタや ferns ディスクリプタのようなディスクリプタも存在します.
2次元画像特徴検出器の基底クラス.
class FeatureDetector
{
public:
void detect( const Mat& image, vector<KeyPoint>& keypoints,
const Mat& mask=Mat() ) const;
virtual void read( const FileNode& fn ) {};
virtual void write( FileStorage& fs ) const {};
protected:
...
};
ファイルストレージに特徴検出器を書き出します.
パラメタ: |
|
---|
FAST() メソッドを利用した特徴検出器に対する,ラッパークラス.
class FastFeatureDetector : public FeatureDetector
{
public:
FastFeatureDetector( int _threshold = 1, bool _nonmaxSuppression = true );
virtual void read (const FileNode& fn);
virtual void write (FileStorage& fs) const;
protected:
...
};
goodFeaturesToTrack() メソッドを利用した特徴検出器に対する,ラッパークラス.
class GoodFeaturesToTrackDetector : public FeatureDetector
{
public:
GoodFeaturesToTrackDetector( int _maxCorners, double _qualityLevel,
double _minDistance, int _blockSize=3,
bool _useHarrisDetector=false, double _k=0.04 );
virtual void read (const FileNode& fn);
virtual void write (FileStorage& fs) const;
protected:
...
}
MSER() クラスを利用した特徴検出器に対する,ラッパークラス.
class MserFeatureDetector : public FeatureDetector
{
public:
MserFeatureDetector( CvMSERParams params = cvMSERParams () );
MserFeatureDetector( int delta, int minArea, int maxArea, float maxVariation,
float minDiversity, int maxEvolution, double areaThreshold,
double minMargin, int edgeBlurSize );
virtual void read (const FileNode& fn);
virtual void write (FileStorage& fs) const;
protected:
...
}
StarDetector() クラスを利用した特徴検出器に対する,ラッパークラス.
class StarFeatureDetector : public FeatureDetector
{
public:
StarFeatureDetector( int maxSize=16, int responseThreshold=30,
int lineThresholdProjected = 10,
int lineThresholdBinarized=8, int suppressNonmaxSize=5 );
virtual void read (const FileNode& fn);
virtual void write (FileStorage& fs) const;
protected:
...
}
SIFT() クラスを利用した特徴検出器に対する,ラッパークラス.
class SiftFeatureDetector : public FeatureDetector
{
public:
SiftFeatureDetector( double threshold=SIFT::DetectorParams::GET_DEFAULT_THRESHOLD(),
double edgeThreshold=SIFT::DetectorParams::GET_DEFAULT_EDGE_THRESHOLD(),
int nOctaves=SIFT::CommonParams::DEFAULT_NOCTAVES,
int nOctaveLayers=SIFT::CommonParams::DEFAULT_NOCTAVE_LAYERS,
int firstOctave=SIFT::CommonParams::DEFAULT_FIRST_OCTAVE,
int angleMode=SIFT::CommonParams::FIRST_ANGLE );
virtual void read (const FileNode& fn);
virtual void write (FileStorage& fs) const;
protected:
...
}
SURF() クラスを利用した特徴検出器に対する,ラッパークラス.
class SurfFeatureDetector : public FeatureDetector
{
public:
SurfFeatureDetector( double hessianThreshold = 400., int octaves = 3,
int octaveLayers = 4 );
virtual void read (const FileNode& fn);
virtual void write (FileStorage& fs) const;
protected:
...
}
画像のキーポイントに対するディスクリプタを計算するための抽象基底クラス.
class DescriptorExtractor
{
public:
virtual void compute( const Mat& image, vector<KeyPoint>& keypoints,
Mat& descriptors ) const = 0;
virtual void read (const FileNode &fn) {};
virtual void write (FileStorage &fs) const {};
protected:
...
};
このインタフェースでは,キーポイントディスクリプタが密に表現可能, つまり,ある基本型の特定次元のベクトルであることを仮定しています. 実際に利用されるディスクリプタの大部分はこのパターンに当てはまるので, ディスクリプタ間の距離を簡単に計算することができます. その結果,ディスクリプタのコレクションは, 1つの行が1つのキーポイントディスクリプタとなる Mat() として表現されます.
ファイルストレージにディスクリプタ抽出器を書き出します.
パラメタ: |
|
---|
SIFT() クラスを利用したディスクリプタ抽出器に対する,ラッパークラス.
class SiftDescriptorExtractor : public DescriptorExtractor
{
public:
SiftDescriptorExtractor(
double magnification=SIFT::DescriptorParams::GET_DEFAULT_MAGNIFICATION(),
bool isNormalize=true, bool recalculateAngles=true,
int nOctaves=SIFT::CommonParams::DEFAULT_NOCTAVES,
int nOctaveLayers=SIFT::CommonParams::DEFAULT_NOCTAVE_LAYERS,
int firstOctave=SIFT::CommonParams::DEFAULT_FIRST_OCTAVE,
int angleMode=SIFT::CommonParams::FIRST_ANGLE );
virtual void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors) const;
virtual void read (const FileNode &fn);
virtual void write (FileStorage &fs) const;
protected:
...
}
SURF() クラスを利用したディスクリプタ抽出器に対する,ラッパークラス.
class SurfDescriptorExtractor : public DescriptorExtractor
{
public:
SurfDescriptorExtractor( int nOctaves=4,
int nOctaveLayers=2, bool extended=false );
virtual void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors) const;
virtual void read (const FileNode &fn);
virtual void write (FileStorage &fs) const;
protected:
...
}
ディスクリプタの2つの集合同士を比較する抽象基底クラス.
class DescriptorMatcher
{
public:
void add( const Mat& descriptors );
// ディスクリプタ学習集合のインデックスを構築します.
void index();
void match( const Mat& query, vector<int>& matches ) const;
void match( const Mat& query, const Mat& mask,
vector<int>& matches ) const;
virtual void clear();
protected:
...
};
総当たり式で,マッチするディスクリプタを求めます. 1番目の集合の各ディスクリプタに対して,2番目の集合を1つ1つ試すことで,最も近いディスクリプタを見つけます.
template<class Distance>
class BruteForceMatcher : public DescriptorMatcher
{
public:
BruteForceMatcher( Distance d = Distance() ) : distance(d) {}
protected:
...
}
効率化のために,BruteForceMatcher は,距離測度でテンプレート化されています. float 型のディスクリプタに対しては,普通は L2<float> が使われます. L2 クラスは,次の用に定義されます:
template<typename T>
struct Accumulator
{
typedef T Type;
};
template<> struct Accumulator<unsigned char> { typedef unsigned int Type; };
template<> struct Accumulator<unsigned short> { typedef unsigned int Type; };
template<> struct Accumulator<char> { typedef int Type; };
template<> struct Accumulator<short> { typedef int Type; };
/*
* ユークリッド二乗距離ファンクタ
*/
template<class T>
struct L2
{
typedef T ValueType;
typedef typename Accumulator<T>::Type ResultType;
ResultType operator()( const T* a, const T* b, int size ) const;
{
ResultType result = ResultType();
for( int i = 0; i < size; i++ )
{
ResultType diff = a[i] - b[i];
result += diff*diff;
}
return sqrt(result);
}
};
対応する画像とクラスIDとを一緒にした,キーポイントの集合ストレージ.
class KeyPointCollection
{
public:
// ある1つの画像から得られたキーポイント群を,ストレージに追加します.
// image 入力画像
// points キーポイントのベクトル
void add( const Mat& _image, const vector<KeyPoint>& _points );
// コレクション中のキーポイントの総数を返します.
size_t calcKeypointCount() const;
// グローバルインデックスで指定されたキーポイントを返します.
KeyPoint getKeyPoint( int index ) const;
// 画像,キーポイント,startIndices をクリアします.
void clear();
vector<Mat> images;
vector<vector<KeyPoint> > points;
// 各画像の最初の点のグローバルインデックス,
// startIndices.size() = points.size()
vector<int> startIndices;
};
キーポイントディスクリプタの抽象クラス.
class GenericDescriptorMatch
{
public:
enum IndexType
{
NoIndex,
KDTreeIndex
};
GenericDescriptorMatch() {}
virtual ~GenericDescriptorMatch() {}
virtual void add( KeyPointCollection& keypoints );
virtual void add( const Mat& image, vector<KeyPoint>& points ) = 0;
virtual void classify( const Mat& image, vector<KeyPoint>& points );
virtual void match( const Mat& image, vector<KeyPoint>& points,
vector<int>& indices ) = 0;
virtual void clear();
virtual void read( const FileNode& fn );
virtual void write( FileStorage& fs ) const;
protected:
KeyPointCollection collection;
};
KeyPointCollection() KeyPoint()
学習集合(ここでは,ディスクリプタを計算可能な集合とします)にキーポイントを追加します.(とそれに対応する画像),または1つの画像から得られる のベクトルとして,キーポイントを渡すことができます.
パラメタ: |
|
---|
ファイルストレージにマッチオブジェクトを書き出します.
有限次元空間のベクトルとして記述可能なマッチングディスクリプタで利用されるクラス.
template<class Extractor, class Matcher>
class VectorDescriptorMatch : public GenericDescriptorMatch
{
public:
VectorDescriptorMatch( const Extractor& _extractor = Extractor(),
const Matcher& _matcher = Matcher() );
~VectorDescriptorMatch();
// flann インデックスを構築します.
void index();
// 1つの画像から得られるキーポイント集合に対するディスクリプタを計算します.
virtual void add( const Mat& image, vector<KeyPoint>& keypoints );
// キーポイント集合と学習集合とのマッチングを行います.
virtual void match( const Mat& image, vector<KeyPoint>& points,
vector<int>& keypointIndices );
// オブジェクト(つまり,格納されたキーポイント群)をクリアします
virtual void clear();
// ファイルノードからオブジェクトを読み込みます.
virtual void read (const FileNode& fn);
// ファイルストレージにオブジェクトを書き出します.
virtual void write (FileStorage& fs) const;
protected:
Extractor extractor;
Matcher matcher;
};
OneWayDescriptorBase() クラスを用いた,ディスクリプタの計算,マッチング,分類に対するラッパークラス.
class OneWayDescriptorMatch : public GenericDescriptorMatch
{
public:
class Params
{
public:
static const int POSE_COUNT = 500;
static const int PATCH_WIDTH = 24;
static const int PATCH_HEIGHT = 24;
static float GET_MIN_SCALE() { return 0.7f; }
static float GET_MAX_SCALE() { return 1.5f; }
static float GET_STEP_SCALE() { return 1.2f; }
Params( int _poseCount = POSE_COUNT,
Size _patchSize = Size(PATCH_WIDTH, PATCH_HEIGHT),
string _pcaFilename = string (),
string _trainPath = string(),
string _trainImagesList = string(),
float _minScale = GET_MIN_SCALE(), float _maxScale = GET_MAX_SCALE(),
float _stepScale = GET_STEP_SCALE() );
int poseCount;
Size patchSize;
string pcaFilename;
string trainPath;
string trainImagesList;
float minScale, maxScale, stepScale;
};
OneWayDescriptorMatch();
// OneWayDescriptorMatch() の後に Initalize(_params) を呼び出すのと等価です.
OneWayDescriptorMatch( const Params& _params );
virtual ~OneWayDescriptorMatch();
// one way ディスクリプタのパラメータを設定します.
void initialize( const Params& _params, OneWayDescriptorBase *_base = 0 );
// キーポイント集合に対する one way ディスクリプタを求めます.
virtual void add( const Mat& image, vector<KeyPoint>& keypoints );
// キーポイント集合に対する one way ディスクリプタを求めます.
virtual void add( KeyPointCollection& keypoints );
// 1つの画像から得られたキーポイント集合と,学習集合とのマッチングを行います.
// キーポイントを中心とするサイズ(patch_width/2*scale, patch_height/2*scale)の矩形が,
// 各キーポイント毎に,入力画像から切り取られます.
// スケールは, DescriptorOneWayParams::min_scale から DescriptorOneWayParams::max_scale
// まで繰り返し変化します.
// 各学習パッチとそれをアフィン変換したものまでの最小距離が,
// 全てのスケールに対して求められます.
// 各キーポイント毎にマッチしたのクラスIDが返されます.
// DescriptorOneWay::Initialize で読み込まれた PCA の主成分に対する距離が計算され,
// 最小距離を求めるために,kd-木が利用されます.
virtual void match( const Mat& image, vector<KeyPoint>& points,
vector<int>& indices );
// キーポイント集合を分類します.
// これはマッチングと同様ですが,インデックスではなくポイントクラスを返します.
virtual void classify( const Mat& image, vector<KeyPoint>& points );
// コレクションと OneWayDescriptorBase のキーポイントをクリアします.
virtual void clear ();
// ファイルノードからマッチングオブジェクトを読み込みます.
virtual void read (const FileNode &fn);
// ファイルストレージにマッチングオブジェクトを書き出します.
virtual void write (FileStorage& fs) const;
protected:
Ptr<OneWayDescriptorBase> base;
Params params;
};
RTreeClassifier() クラスを用いた,ディスクリプタの計算,マッチング,分類に対するラッパークラス.
class CV_EXPORTS CalonderDescriptorMatch : public GenericDescriptorMatch
{
public:
class Params
{
public:
static const int DEFAULT_NUM_TREES = 80;
static const int DEFAULT_DEPTH = 9;
static const int DEFAULT_VIEWS = 5000;
static const size_t DEFAULT_REDUCED_NUM_DIM = 176;
static const size_t DEFAULT_NUM_QUANT_BITS = 4;
static const int DEFAULT_PATCH_SIZE = PATCH_SIZE;
Params( const RNG& _rng = RNG(),
const PatchGenerator& _patchGen = PatchGenerator(),
int _numTrees=DEFAULT_NUM_TREES,
int _depth=DEFAULT_DEPTH,
int _views=DEFAULT_VIEWS,
size_t _reducedNumDim=DEFAULT_REDUCED_NUM_DIM,
int _numQuantBits=DEFAULT_NUM_QUANT_BITS,
bool _printStatus=true,
int _patchSize=DEFAULT_PATCH_SIZE );
Params( const string& _filename );
RNG rng;
PatchGenerator patchGen;
int numTrees;
int depth;
int views;
int patchSize;
size_t reducedNumDim;
int numQuantBits;
bool printStatus;
string filename;
};
CalonderDescriptorMatch();
CalonderDescriptorMatch( const Params& _params );
virtual ~CalonderDescriptorMatch();
void initialize( const Params& _params );
virtual void add( const Mat& image, vector<KeyPoint>& keypoints );
virtual void match( const Mat& image, vector<KeyPoint>& keypoints,
vector<int>& indices );
virtual void classify( const Mat& image, vector<KeyPoint>& keypoints );
virtual void clear ();
virtual void read( const FileNode &fn );
virtual void write( FileStorage& fs ) const;
protected:
...
};
FernClassifier() クラスを用いた,ディスクリプタの計算,マッチング,分類に対するラッパークラス.
class FernDescriptorMatch : public GenericDescriptorMatch
{
public:
class Params
{
public:
Params( int _nclasses=0,
int _patchSize=FernClassifier::PATCH_SIZE,
int _signatureSize=FernClassifier::DEFAULT_SIGNATURE_SIZE,
int _nstructs=FernClassifier::DEFAULT_STRUCTS,
int _structSize=FernClassifier::DEFAULT_STRUCT_SIZE,
int _nviews=FernClassifier::DEFAULT_VIEWS,
int _compressionMethod=FernClassifier::COMPRESSION_NONE,
const PatchGenerator& patchGenerator=PatchGenerator() );
Params( const string& _filename );
int nclasses;
int patchSize;
int signatureSize;
int nstructs;
int structSize;
int nviews;
int compressionMethod;
PatchGenerator patchGenerator;
string filename;
};
FernDescriptorMatch();
FernDescriptorMatch( const Params& _params );
virtual ~FernDescriptorMatch();
void initialize( const Params& _params );
virtual void add( const Mat& image, vector<KeyPoint>& keypoints );
virtual void match( const Mat& image, vector<KeyPoint>& keypoints,
vector<int>& indices );
virtual void classify( const Mat& image, vector<KeyPoint>& keypoints );
virtual void clear ();
virtual void read( const FileNode &fn );
virtual void write( FileStorage& fs ) const;
protected:
...
};
この関数は,2つの画像のキーポイント同士の対応関係を出力画像に書き出します.
この対応関係は,2つのキーポイント(円)を線分で繋げることで表されます.
param img1: 1番目の入力画像
- keypoints1 1番目の入力画像のキーポイント
- img1 2番目の入力画像
- keypoints2 2番目の入力画像のキーポイント
- matches 1番目の画像の2番目の画像に対する対応関係.つまり,keypoints1[i] は,対応する keypoints2[matches[i]] を持っています
- outImg 出力画像.これは,出力画像に何が書かれるかを表す flags に依存します.以下の flags ビット値を参照してください
- matchColor 対応関係(線分と,それで接続されたキーポイント)の色. matchColor ==Scalar::all(-1) の場合,色はランダムに生成されます
- singlePointColor キーポイント単体(円),つまり,対応関係を持たないキーポイントの色. matchColor ==Scalar::all(-1) の場合,色はランダムに生成されます
- matchesMask どの対応関係を描画するかを表すマスク.マスクが空の場合は,すべての対応関係が描画されます
- flags flags の各ビットは,描画のプロパティを設定します.取りうる flags ビット値は,DrawMatchesFlags によって定義されます.以下を参照してください
struct DrawMatchesFlags
{
enum{ DEFAULT = 0, // 出力画像行列が作成(Mat::create)されます.
// つまり,出力画像用の既存のメモリ領域が再利用される場合があります.
// 2つの入力画像,対応関係,そして対応関係を持たないキーポイント,が描画されます.
DRAW_OVER_OUTIMG = 1, // 出力画像行列が作成(Mat::create)されません.
// 対応関係は,既存の出力画像に上書きされます.
NOT_DRAW_SINGLE_POINTS = 2 // 対応関係を持たないキーポイントは描画されません.
};
};