lot's of changes; nonfree & photo modules added; SIFT & SURF -> nonfree module; Inpainting -> photo; refactored features2d (ORB is still failing tests), optimized brute-force matcher and made it non-template.
This commit is contained in:
@@ -92,177 +92,6 @@ Finds keypoints in an image
|
||||
|
||||
:param params: The algorithm parameters stored in ``CvStarDetectorParams`` (OpenCV 1.x API only)
|
||||
|
||||
|
||||
SIFT
|
||||
----
|
||||
.. ocv:class:: SIFT
|
||||
|
||||
Class for extracting keypoints and computing descriptors using the Scale Invariant Feature Transform (SIFT) approach. ::
|
||||
|
||||
class CV_EXPORTS SIFT
|
||||
{
|
||||
public:
|
||||
struct CommonParams
|
||||
{
|
||||
static const int DEFAULT_NOCTAVES = 4;
|
||||
static const int DEFAULT_NOCTAVE_LAYERS = 3;
|
||||
static const int DEFAULT_FIRST_OCTAVE = -1;
|
||||
enum{ FIRST_ANGLE = 0, AVERAGE_ANGLE = 1 };
|
||||
|
||||
CommonParams();
|
||||
CommonParams( int _nOctaves, int _nOctaveLayers, int _firstOctave,
|
||||
int _angleMode );
|
||||
int nOctaves, nOctaveLayers, firstOctave;
|
||||
int angleMode;
|
||||
};
|
||||
|
||||
struct DetectorParams
|
||||
{
|
||||
static double GET_DEFAULT_THRESHOLD()
|
||||
{ return 0.04 / SIFT::CommonParams::DEFAULT_NOCTAVE_LAYERS / 2.0; }
|
||||
static double GET_DEFAULT_EDGE_THRESHOLD() { return 10.0; }
|
||||
|
||||
DetectorParams();
|
||||
DetectorParams( double _threshold, double _edgeThreshold );
|
||||
double threshold, edgeThreshold;
|
||||
};
|
||||
|
||||
struct DescriptorParams
|
||||
{
|
||||
static double GET_DEFAULT_MAGNIFICATION() { return 3.0; }
|
||||
static const bool DEFAULT_IS_NORMALIZE = true;
|
||||
static const int DESCRIPTOR_SIZE = 128;
|
||||
|
||||
DescriptorParams();
|
||||
DescriptorParams( double _magnification, bool _isNormalize,
|
||||
bool _recalculateAngles );
|
||||
double magnification;
|
||||
bool isNormalize;
|
||||
bool recalculateAngles;
|
||||
};
|
||||
|
||||
SIFT();
|
||||
//! sift-detector constructor
|
||||
SIFT( double _threshold, double _edgeThreshold,
|
||||
int _nOctaves=CommonParams::DEFAULT_NOCTAVES,
|
||||
int _nOctaveLayers=CommonParams::DEFAULT_NOCTAVE_LAYERS,
|
||||
int _firstOctave=CommonParams::DEFAULT_FIRST_OCTAVE,
|
||||
int _angleMode=CommonParams::FIRST_ANGLE );
|
||||
//! sift-descriptor constructor
|
||||
SIFT( double _magnification, bool _isNormalize=true,
|
||||
bool _recalculateAngles = true,
|
||||
int _nOctaves=CommonParams::DEFAULT_NOCTAVES,
|
||||
int _nOctaveLayers=CommonParams::DEFAULT_NOCTAVE_LAYERS,
|
||||
int _firstOctave=CommonParams::DEFAULT_FIRST_OCTAVE,
|
||||
int _angleMode=CommonParams::FIRST_ANGLE );
|
||||
SIFT( const CommonParams& _commParams,
|
||||
const DetectorParams& _detectorParams = DetectorParams(),
|
||||
const DescriptorParams& _descriptorParams = DescriptorParams() );
|
||||
|
||||
//! returns the descriptor size in floats (128)
|
||||
int descriptorSize() const { return DescriptorParams::DESCRIPTOR_SIZE; }
|
||||
//! finds the keypoints using the SIFT algorithm
|
||||
void operator()(const Mat& img, const Mat& mask,
|
||||
vector<KeyPoint>& keypoints) const;
|
||||
//! finds the keypoints and computes descriptors for them using SIFT algorithm.
|
||||
//! Optionally it can compute descriptors for the user-provided keypoints
|
||||
void operator()(const Mat& img, const Mat& mask,
|
||||
vector<KeyPoint>& keypoints,
|
||||
Mat& descriptors,
|
||||
bool useProvidedKeypoints=false) const;
|
||||
|
||||
CommonParams getCommonParams () const { return commParams; }
|
||||
DetectorParams getDetectorParams () const { return detectorParams; }
|
||||
DescriptorParams getDescriptorParams () const { return descriptorParams; }
|
||||
protected:
|
||||
...
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
SURF
|
||||
----
|
||||
.. ocv:class:: SURF
|
||||
|
||||
Class for extracting Speeded Up Robust Features from an image [Bay06]_. The class is derived from ``CvSURFParams`` structure, which specifies the algorithm parameters:
|
||||
|
||||
.. ocv:member:: int extended
|
||||
|
||||
* 0 means that the basic descriptors (64 elements each) shall be computed
|
||||
* 1 means that the extended descriptors (128 elements each) shall be computed
|
||||
|
||||
.. ocv:member:: int upright
|
||||
|
||||
* 0 means that detector computes orientation of each feature.
|
||||
* 1 means that the orientation is not computed (which is much, much faster). For example, if you match images from a stereo pair, or do image stitching, the matched features likely have very similar angles, and you can speed up feature extraction by setting ``upright=1``.
|
||||
|
||||
.. ocv:member:: double hessianThreshold
|
||||
|
||||
Threshold for the keypoint detector. Only features, whose hessian is larger than ``hessianThreshold`` are retained by the detector. Therefore, the larger the value, the less keypoints you will get. A good default value could be from 300 to 500, depending from the image contrast.
|
||||
|
||||
.. ocv:member:: int nOctaves
|
||||
|
||||
The number of a gaussian pyramid octaves that the detector uses. It is set to 4 by default. If you want to get very large features, use the larger value. If you want just small features, decrease it.
|
||||
|
||||
.. ocv:member:: int nOctaveLayers
|
||||
|
||||
The number of images within each octave of a gaussian pyramid. It is set to 2 by default.
|
||||
|
||||
|
||||
.. [Bay06] Bay, H. and Tuytelaars, T. and Van Gool, L. "SURF: Speeded Up Robust Features", 9th European Conference on Computer Vision, 2006
|
||||
|
||||
|
||||
SURF::SURF
|
||||
----------
|
||||
The SURF extractor constructors.
|
||||
|
||||
.. ocv:function:: SURF::SURF()
|
||||
|
||||
.. ocv:function:: SURF::SURF(double hessianThreshold, int nOctaves=4, int nOctaveLayers=2, bool extended=false, bool upright=false)
|
||||
|
||||
.. ocv:pyfunction:: cv2.SURF(_hessianThreshold[, _nOctaves[, _nOctaveLayers[, _extended[, _upright]]]]) -> <SURF object>
|
||||
|
||||
:param hessianThreshold: Threshold for hessian keypoint detector used in SURF.
|
||||
|
||||
:param nOctaves: Number of pyramid octaves the keypoint detector will use.
|
||||
|
||||
:param nOctaveLayers: Number of octave layers within each octave.
|
||||
|
||||
:param extended: Extended descriptor flag (true - use extended 128-element descriptors; false - use 64-element descriptors).
|
||||
|
||||
:param upright: Up-right or rotated features flag (true - do not compute orientation of features; false - compute orientation).
|
||||
|
||||
|
||||
SURF::operator()
|
||||
----------------
|
||||
Detects keypoints and computes SURF descriptors for them.
|
||||
|
||||
.. ocv:function:: void SURF::operator()(const Mat& image, const Mat& mask, vector<KeyPoint>& keypoints)
|
||||
.. ocv:function:: void SURF::operator()(const Mat& image, const Mat& mask, vector<KeyPoint>& keypoints, vector<float>& descriptors, bool useProvidedKeypoints=false)
|
||||
|
||||
.. ocv:pyfunction:: cv2.SURF.detect(img, mask) -> keypoints
|
||||
.. ocv:pyfunction:: cv2.SURF.detect(img, mask[, useProvidedKeypoints]) -> keypoints, descriptors
|
||||
|
||||
.. ocv:cfunction:: void cvExtractSURF( const CvArr* image, const CvArr* mask, CvSeq** keypoints, CvSeq** descriptors, CvMemStorage* storage, CvSURFParams params )
|
||||
|
||||
.. ocv:pyoldfunction:: cv.ExtractSURF(image, mask, storage, params)-> (keypoints, descriptors)
|
||||
|
||||
:param image: Input 8-bit grayscale image
|
||||
|
||||
:param mask: Optional input mask that marks the regions where we should detect features.
|
||||
|
||||
:param keypoints: The input/output vector of keypoints
|
||||
|
||||
:param descriptors: The output concatenated vectors of descriptors. Each descriptor is 64- or 128-element vector, as returned by ``SURF::descriptorSize()``. So the total size of ``descriptors`` will be ``keypoints.size()*descriptorSize()``.
|
||||
|
||||
:param useProvidedKeypoints: Boolean flag. If it is true, the keypoint detector is not run. Instead, the provided vector of keypoints is used and the algorithm just computes their descriptors.
|
||||
|
||||
:param storage: Memory storage for the output keypoints and descriptors in OpenCV 1.x API.
|
||||
|
||||
:param params: SURF algorithm parameters in OpenCV 1.x API.
|
||||
|
||||
|
||||
ORB
|
||||
----
|
||||
.. ocv:class:: ORB
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,7 @@ PERF_TEST_P(orb, detect, testing::Values(ORB_IMAGES))
|
||||
|
||||
Mat mask;
|
||||
declare.in(frame);
|
||||
ORB detector(1500, ORB::CommonParams(1.3f, 5));
|
||||
ORB detector(1500, 1.3f, 5);
|
||||
vector<KeyPoint> points;
|
||||
|
||||
TEST_CYCLE() detector(frame, mask, points);
|
||||
@@ -39,7 +39,7 @@ PERF_TEST_P(orb, extract, testing::Values(ORB_IMAGES))
|
||||
Mat mask;
|
||||
declare.in(frame);
|
||||
|
||||
ORB detector(1500, ORB::CommonParams(1.3f, 5));
|
||||
ORB detector(1500, 1.3f, 5);
|
||||
vector<KeyPoint> points;
|
||||
detector(frame, mask, points);
|
||||
|
||||
@@ -58,7 +58,7 @@ PERF_TEST_P(orb, full, testing::Values(ORB_IMAGES))
|
||||
|
||||
Mat mask;
|
||||
declare.in(frame);
|
||||
ORB detector(1500, ORB::CommonParams(1.3f, 5));
|
||||
ORB detector(1500, 1.3f, 5);
|
||||
|
||||
vector<KeyPoint> points;
|
||||
Mat descriptors;
|
||||
|
@@ -1,65 +0,0 @@
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
using namespace perf;
|
||||
using std::tr1::make_tuple;
|
||||
using std::tr1::get;
|
||||
|
||||
typedef perf::TestBaseWithParam<std::string> surf;
|
||||
|
||||
#define SURF_IMAGES \
|
||||
"cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\
|
||||
"stitching/a3.jpg"
|
||||
|
||||
PERF_TEST_P(surf, detect, testing::Values(SURF_IMAGES))
|
||||
{
|
||||
String filename = getDataPath(GetParam());
|
||||
Mat frame = imread(filename, IMREAD_GRAYSCALE);
|
||||
|
||||
if (frame.empty())
|
||||
FAIL() << "Unable to load source image " << filename;
|
||||
|
||||
Mat mask;
|
||||
declare.in(frame).time(90);
|
||||
SURF detector;
|
||||
vector<KeyPoint> points;
|
||||
|
||||
TEST_CYCLE() detector(frame, mask, points);
|
||||
}
|
||||
|
||||
PERF_TEST_P(surf, extract, testing::Values(SURF_IMAGES))
|
||||
{
|
||||
String filename = getDataPath(GetParam());
|
||||
Mat frame = imread(filename, IMREAD_GRAYSCALE);
|
||||
|
||||
if (frame.empty())
|
||||
FAIL() << "Unable to load source image " << filename;
|
||||
|
||||
Mat mask;
|
||||
declare.in(frame).time(90);
|
||||
|
||||
SURF detector;
|
||||
vector<KeyPoint> points;
|
||||
vector<float> descriptors;
|
||||
detector(frame, mask, points);
|
||||
|
||||
TEST_CYCLE() detector(frame, mask, points, descriptors, true);
|
||||
}
|
||||
|
||||
PERF_TEST_P(surf, full, testing::Values(SURF_IMAGES))
|
||||
{
|
||||
String filename = getDataPath(GetParam());
|
||||
Mat frame = imread(filename, IMREAD_GRAYSCALE);
|
||||
|
||||
if (frame.empty())
|
||||
FAIL() << "Unable to load source image " << filename;
|
||||
|
||||
Mat mask;
|
||||
declare.in(frame).time(90);
|
||||
SURF detector;
|
||||
vector<KeyPoint> points;
|
||||
vector<float> descriptors;
|
||||
|
||||
TEST_CYCLE() detector(frame, mask, points, descriptors, false);
|
||||
}
|
@@ -1,997 +0,0 @@
|
||||
//*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
#include "precomp.hpp"
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class CSMatrixGenerator {
|
||||
public:
|
||||
typedef enum { PDT_GAUSS=1, PDT_BERNOULLI, PDT_DBFRIENDLY } PHI_DISTR_TYPE;
|
||||
~CSMatrixGenerator();
|
||||
static float* getCSMatrix(int m, int n, PHI_DISTR_TYPE dt); // do NOT free returned pointer
|
||||
|
||||
|
||||
private:
|
||||
static float *cs_phi_; // matrix for compressive sensing
|
||||
static int cs_phi_m_, cs_phi_n_;
|
||||
};
|
||||
|
||||
float* CSMatrixGenerator::getCSMatrix(int m, int n, PHI_DISTR_TYPE dt)
|
||||
{
|
||||
assert(m <= n);
|
||||
|
||||
if (cs_phi_m_!=m || cs_phi_n_!=n || cs_phi_==NULL) {
|
||||
if (cs_phi_) delete [] cs_phi_;
|
||||
cs_phi_ = new float[m*n];
|
||||
}
|
||||
|
||||
#if 0 // debug - load the random matrix from a file (for reproducability of results)
|
||||
//assert(m == 176);
|
||||
//assert(n == 500);
|
||||
//const char *phi = "/u/calonder/temp/dim_red/kpca_phi.txt";
|
||||
const char *phi = "/u/calonder/temp/dim_red/debug_phi.txt";
|
||||
std::ifstream ifs(phi);
|
||||
for (size_t i=0; i<m*n; ++i) {
|
||||
if (!ifs.good()) {
|
||||
printf("[ERROR] RandomizedTree::makeRandomMeasMatrix: problem reading '%s'\n", phi);
|
||||
exit(0);
|
||||
}
|
||||
ifs >> cs_phi[i];
|
||||
}
|
||||
ifs.close();
|
||||
|
||||
static bool warned=false;
|
||||
if (!warned) {
|
||||
printf("[NOTE] RT: reading %ix%i PHI matrix from '%s'...\n", m, n, phi);
|
||||
warned=true;
|
||||
}
|
||||
|
||||
return;
|
||||
#endif
|
||||
|
||||
float *cs_phi = cs_phi_;
|
||||
|
||||
if (m == n) {
|
||||
// special case - set to 0 for safety
|
||||
memset(cs_phi, 0, m*n*sizeof(float));
|
||||
printf("[WARNING] %s:%i: square CS matrix (-> no reduction)\n", __FILE__, __LINE__);
|
||||
}
|
||||
else {
|
||||
cv::RNG rng(23);
|
||||
|
||||
// par is distr param, cf 'Favorable JL Distributions' (Baraniuk et al, 2006)
|
||||
if (dt == PDT_GAUSS) {
|
||||
float par = (float)(1./m);
|
||||
for (int i=0; i<m*n; ++i)
|
||||
*cs_phi++ = (float)rng.gaussian(par);
|
||||
}
|
||||
else if (dt == PDT_BERNOULLI) {
|
||||
float par = (float)(1./sqrt((float)m));
|
||||
for (int i=0; i<m*n; ++i)
|
||||
*cs_phi++ = (rng(2)==0 ? par : -par);
|
||||
}
|
||||
else if (dt == PDT_DBFRIENDLY) {
|
||||
float par = (float)sqrt(3./m);
|
||||
for (int i=0; i<m*n; ++i) {
|
||||
int r = rng(6);
|
||||
*cs_phi++ = (r==0 ? par : (r==1 ? -par : 0.f));
|
||||
}
|
||||
}
|
||||
else
|
||||
throw("PHI_DISTR_TYPE not implemented.");
|
||||
}
|
||||
|
||||
return cs_phi_;
|
||||
}
|
||||
|
||||
CSMatrixGenerator::~CSMatrixGenerator()
|
||||
{
|
||||
if (cs_phi_) delete [] cs_phi_;
|
||||
cs_phi_ = NULL;
|
||||
}
|
||||
|
||||
float *CSMatrixGenerator::cs_phi_ = NULL;
|
||||
int CSMatrixGenerator::cs_phi_m_ = 0;
|
||||
int CSMatrixGenerator::cs_phi_n_ = 0;
|
||||
|
||||
|
||||
inline void addVec(int size, const float* src1, const float* src2, float* dst)
|
||||
{
|
||||
while(--size >= 0) {
|
||||
*dst = *src1 + *src2;
|
||||
++dst; ++src1; ++src2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// sum up 50 byte vectors of length 176
|
||||
// assume 4 bits max for input vector values
|
||||
// final shift is 2 bits right
|
||||
// temp buffer should be twice as long as signature
|
||||
// sig and buffer need not be initialized
|
||||
inline void sum_50t_176c(uchar **pp, uchar *sig, unsigned short *temp)
|
||||
{
|
||||
#if CV_SSE2
|
||||
__m128i acc, *acc1, *acc2, *acc3, *acc4, tzero;
|
||||
__m128i *ssig, *ttemp;
|
||||
|
||||
ssig = (__m128i *)sig;
|
||||
ttemp = (__m128i *)temp;
|
||||
|
||||
// empty ttemp[]
|
||||
tzero = _mm_set_epi32(0, 0, 0, 0);
|
||||
for (int i=0; i<22; i++)
|
||||
ttemp[i] = tzero;
|
||||
|
||||
for (int j=0; j<48; j+=16)
|
||||
{
|
||||
// empty ssig[]
|
||||
for (int i=0; i<11; i++)
|
||||
ssig[i] = tzero;
|
||||
|
||||
for (int i=j; i<j+16; i+=4) // 4 columns at a time, to 16
|
||||
{
|
||||
acc1 = (__m128i *)pp[i];
|
||||
acc2 = (__m128i *)pp[i+1];
|
||||
acc3 = (__m128i *)pp[i+2];
|
||||
acc4 = (__m128i *)pp[i+3];
|
||||
|
||||
// add next four columns
|
||||
acc = _mm_adds_epu8(acc1[0],acc2[0]);
|
||||
acc = _mm_adds_epu8(acc,acc3[0]);
|
||||
acc = _mm_adds_epu8(acc,acc4[1]);
|
||||
ssig[0] = _mm_adds_epu8(acc,ssig[0]);
|
||||
// add four columns
|
||||
acc = _mm_adds_epu8(acc1[1],acc2[1]);
|
||||
acc = _mm_adds_epu8(acc,acc3[1]);
|
||||
acc = _mm_adds_epu8(acc,acc4[1]);
|
||||
ssig[1] = _mm_adds_epu8(acc,ssig[1]);
|
||||
// add four columns
|
||||
acc = _mm_adds_epu8(acc1[2],acc2[2]);
|
||||
acc = _mm_adds_epu8(acc,acc3[2]);
|
||||
acc = _mm_adds_epu8(acc,acc4[2]);
|
||||
ssig[2] = _mm_adds_epu8(acc,ssig[2]);
|
||||
// add four columns
|
||||
acc = _mm_adds_epu8(acc1[3],acc2[3]);
|
||||
acc = _mm_adds_epu8(acc,acc3[3]);
|
||||
acc = _mm_adds_epu8(acc,acc4[3]);
|
||||
ssig[3] = _mm_adds_epu8(acc,ssig[3]);
|
||||
// add four columns
|
||||
acc = _mm_adds_epu8(acc1[4],acc2[4]);
|
||||
acc = _mm_adds_epu8(acc,acc3[4]);
|
||||
acc = _mm_adds_epu8(acc,acc4[4]);
|
||||
ssig[4] = _mm_adds_epu8(acc,ssig[4]);
|
||||
// add four columns
|
||||
acc = _mm_adds_epu8(acc1[5],acc2[5]);
|
||||
acc = _mm_adds_epu8(acc,acc3[5]);
|
||||
acc = _mm_adds_epu8(acc,acc4[5]);
|
||||
ssig[5] = _mm_adds_epu8(acc,ssig[5]);
|
||||
// add four columns
|
||||
acc = _mm_adds_epu8(acc1[6],acc2[6]);
|
||||
acc = _mm_adds_epu8(acc,acc3[6]);
|
||||
acc = _mm_adds_epu8(acc,acc4[6]);
|
||||
ssig[6] = _mm_adds_epu8(acc,ssig[6]);
|
||||
// add four columns
|
||||
acc = _mm_adds_epu8(acc1[7],acc2[7]);
|
||||
acc = _mm_adds_epu8(acc,acc3[7]);
|
||||
acc = _mm_adds_epu8(acc,acc4[7]);
|
||||
ssig[7] = _mm_adds_epu8(acc,ssig[7]);
|
||||
// add four columns
|
||||
acc = _mm_adds_epu8(acc1[8],acc2[8]);
|
||||
acc = _mm_adds_epu8(acc,acc3[8]);
|
||||
acc = _mm_adds_epu8(acc,acc4[8]);
|
||||
ssig[8] = _mm_adds_epu8(acc,ssig[8]);
|
||||
// add four columns
|
||||
acc = _mm_adds_epu8(acc1[9],acc2[9]);
|
||||
acc = _mm_adds_epu8(acc,acc3[9]);
|
||||
acc = _mm_adds_epu8(acc,acc4[9]);
|
||||
ssig[9] = _mm_adds_epu8(acc,ssig[9]);
|
||||
// add four columns
|
||||
acc = _mm_adds_epu8(acc1[10],acc2[10]);
|
||||
acc = _mm_adds_epu8(acc,acc3[10]);
|
||||
acc = _mm_adds_epu8(acc,acc4[10]);
|
||||
ssig[10] = _mm_adds_epu8(acc,ssig[10]);
|
||||
}
|
||||
|
||||
// unpack to ttemp buffer and add
|
||||
ttemp[0] = _mm_add_epi16(_mm_unpacklo_epi8(ssig[0],tzero),ttemp[0]);
|
||||
ttemp[1] = _mm_add_epi16(_mm_unpackhi_epi8(ssig[0],tzero),ttemp[1]);
|
||||
ttemp[2] = _mm_add_epi16(_mm_unpacklo_epi8(ssig[1],tzero),ttemp[2]);
|
||||
ttemp[3] = _mm_add_epi16(_mm_unpackhi_epi8(ssig[1],tzero),ttemp[3]);
|
||||
ttemp[4] = _mm_add_epi16(_mm_unpacklo_epi8(ssig[2],tzero),ttemp[4]);
|
||||
ttemp[5] = _mm_add_epi16(_mm_unpackhi_epi8(ssig[2],tzero),ttemp[5]);
|
||||
ttemp[6] = _mm_add_epi16(_mm_unpacklo_epi8(ssig[3],tzero),ttemp[6]);
|
||||
ttemp[7] = _mm_add_epi16(_mm_unpackhi_epi8(ssig[3],tzero),ttemp[7]);
|
||||
ttemp[8] = _mm_add_epi16(_mm_unpacklo_epi8(ssig[4],tzero),ttemp[8]);
|
||||
ttemp[9] = _mm_add_epi16(_mm_unpackhi_epi8(ssig[4],tzero),ttemp[9]);
|
||||
ttemp[10] = _mm_add_epi16(_mm_unpacklo_epi8(ssig[5],tzero),ttemp[10]);
|
||||
ttemp[11] = _mm_add_epi16(_mm_unpackhi_epi8(ssig[5],tzero),ttemp[11]);
|
||||
ttemp[12] = _mm_add_epi16(_mm_unpacklo_epi8(ssig[6],tzero),ttemp[12]);
|
||||
ttemp[13] = _mm_add_epi16(_mm_unpackhi_epi8(ssig[6],tzero),ttemp[13]);
|
||||
ttemp[14] = _mm_add_epi16(_mm_unpacklo_epi8(ssig[7],tzero),ttemp[14]);
|
||||
ttemp[15] = _mm_add_epi16(_mm_unpackhi_epi8(ssig[7],tzero),ttemp[15]);
|
||||
ttemp[16] = _mm_add_epi16(_mm_unpacklo_epi8(ssig[8],tzero),ttemp[16]);
|
||||
ttemp[17] = _mm_add_epi16(_mm_unpackhi_epi8(ssig[8],tzero),ttemp[17]);
|
||||
ttemp[18] = _mm_add_epi16(_mm_unpacklo_epi8(ssig[9],tzero),ttemp[18]);
|
||||
ttemp[19] = _mm_add_epi16(_mm_unpackhi_epi8(ssig[9],tzero),ttemp[19]);
|
||||
ttemp[20] = _mm_add_epi16(_mm_unpacklo_epi8(ssig[10],tzero),ttemp[20]);
|
||||
ttemp[21] = _mm_add_epi16(_mm_unpackhi_epi8(ssig[10],tzero),ttemp[21]);
|
||||
}
|
||||
|
||||
// create ssignature from 16-bit result
|
||||
ssig[0] =_mm_packus_epi16(_mm_srai_epi16(ttemp[0],2),_mm_srai_epi16(ttemp[1],2));
|
||||
ssig[1] =_mm_packus_epi16(_mm_srai_epi16(ttemp[2],2),_mm_srai_epi16(ttemp[3],2));
|
||||
ssig[2] =_mm_packus_epi16(_mm_srai_epi16(ttemp[4],2),_mm_srai_epi16(ttemp[5],2));
|
||||
ssig[3] =_mm_packus_epi16(_mm_srai_epi16(ttemp[6],2),_mm_srai_epi16(ttemp[7],2));
|
||||
ssig[4] =_mm_packus_epi16(_mm_srai_epi16(ttemp[8],2),_mm_srai_epi16(ttemp[9],2));
|
||||
ssig[5] =_mm_packus_epi16(_mm_srai_epi16(ttemp[10],2),_mm_srai_epi16(ttemp[11],2));
|
||||
ssig[6] =_mm_packus_epi16(_mm_srai_epi16(ttemp[12],2),_mm_srai_epi16(ttemp[13],2));
|
||||
ssig[7] =_mm_packus_epi16(_mm_srai_epi16(ttemp[14],2),_mm_srai_epi16(ttemp[15],2));
|
||||
ssig[8] =_mm_packus_epi16(_mm_srai_epi16(ttemp[16],2),_mm_srai_epi16(ttemp[17],2));
|
||||
ssig[9] =_mm_packus_epi16(_mm_srai_epi16(ttemp[18],2),_mm_srai_epi16(ttemp[19],2));
|
||||
ssig[10] =_mm_packus_epi16(_mm_srai_epi16(ttemp[20],2),_mm_srai_epi16(ttemp[21],2));
|
||||
#else
|
||||
CV_Error( CV_StsNotImplemented, "Not supported without SSE2" );
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace cv
|
||||
{
|
||||
RandomizedTree::RandomizedTree()
|
||||
: posteriors_(NULL), posteriors2_(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
RandomizedTree::~RandomizedTree()
|
||||
{
|
||||
freePosteriors(3);
|
||||
}
|
||||
|
||||
void RandomizedTree::createNodes(int num_nodes, RNG &rng)
|
||||
{
|
||||
nodes_.reserve(num_nodes);
|
||||
for (int i = 0; i < num_nodes; ++i) {
|
||||
nodes_.push_back( RTreeNode((uchar)rng(RandomizedTree::PATCH_SIZE),
|
||||
(uchar)rng(RandomizedTree::PATCH_SIZE),
|
||||
(uchar)rng(RandomizedTree::PATCH_SIZE),
|
||||
(uchar)rng(RandomizedTree::PATCH_SIZE)) );
|
||||
}
|
||||
}
|
||||
|
||||
int RandomizedTree::getIndex(uchar* patch_data) const
|
||||
{
|
||||
int index = 0;
|
||||
for (int d = 0; d < depth_; ++d) {
|
||||
int child_offset = nodes_[index](patch_data);
|
||||
index = 2*index + 1 + child_offset;
|
||||
}
|
||||
return (int)(index - nodes_.size());
|
||||
}
|
||||
|
||||
void RandomizedTree::train(std::vector<BaseKeypoint> const& base_set,
|
||||
RNG &rng, int depth, int views, size_t reduced_num_dim,
|
||||
int num_quant_bits)
|
||||
{
|
||||
PatchGenerator make_patch;
|
||||
train(base_set, rng, make_patch, depth, views, reduced_num_dim, num_quant_bits);
|
||||
}
|
||||
|
||||
void RandomizedTree::train(std::vector<BaseKeypoint> const& base_set,
|
||||
RNG &rng, PatchGenerator &make_patch,
|
||||
int depth, int views, size_t reduced_num_dim,
|
||||
int num_quant_bits)
|
||||
{
|
||||
init((int)base_set.size(), depth, rng);
|
||||
|
||||
Mat patch;
|
||||
|
||||
// Estimate posterior probabilities using random affine views
|
||||
std::vector<BaseKeypoint>::const_iterator keypt_it;
|
||||
int class_id = 0;
|
||||
Size patchSize(PATCH_SIZE, PATCH_SIZE);
|
||||
for (keypt_it = base_set.begin(); keypt_it != base_set.end(); ++keypt_it, ++class_id) {
|
||||
for (int i = 0; i < views; ++i) {
|
||||
make_patch( Mat(keypt_it->image), Point(keypt_it->x, keypt_it->y ), patch, patchSize, rng );
|
||||
IplImage iplPatch = patch;
|
||||
addExample(class_id, getData(&iplPatch));
|
||||
}
|
||||
}
|
||||
|
||||
finalize(reduced_num_dim, num_quant_bits);
|
||||
}
|
||||
|
||||
void RandomizedTree::allocPosteriorsAligned(int num_leaves, int num_classes)
|
||||
{
|
||||
freePosteriors(3);
|
||||
|
||||
posteriors_ = new float*[num_leaves]; //(float**) malloc(num_leaves*sizeof(float*));
|
||||
for (int i=0; i<num_leaves; ++i) {
|
||||
posteriors_[i] = (float*)cvAlloc(num_classes*sizeof(posteriors_[i][0]));
|
||||
memset(posteriors_[i], 0, num_classes*sizeof(float));
|
||||
}
|
||||
|
||||
posteriors2_ = new uchar*[num_leaves];
|
||||
for (int i=0; i<num_leaves; ++i) {
|
||||
posteriors2_[i] = (uchar*)cvAlloc(num_classes*sizeof(posteriors2_[i][0]));
|
||||
memset(posteriors2_[i], 0, num_classes*sizeof(uchar));
|
||||
}
|
||||
|
||||
classes_ = num_classes;
|
||||
}
|
||||
|
||||
void RandomizedTree::freePosteriors(int which)
|
||||
{
|
||||
if (posteriors_ && (which&1)) {
|
||||
for (int i=0; i<num_leaves_; ++i)
|
||||
if (posteriors_[i])
|
||||
cvFree( &posteriors_[i] );
|
||||
delete [] posteriors_;
|
||||
posteriors_ = NULL;
|
||||
}
|
||||
|
||||
if (posteriors2_ && (which&2)) {
|
||||
for (int i=0; i<num_leaves_; ++i)
|
||||
cvFree( &posteriors2_[i] );
|
||||
delete [] posteriors2_;
|
||||
posteriors2_ = NULL;
|
||||
}
|
||||
|
||||
classes_ = -1;
|
||||
}
|
||||
|
||||
void RandomizedTree::init(int num_classes, int depth, RNG &rng)
|
||||
{
|
||||
depth_ = depth;
|
||||
num_leaves_ = 1 << depth; // 2**d
|
||||
int num_nodes = num_leaves_ - 1; // 2**d - 1
|
||||
|
||||
// Initialize probabilities and counts to 0
|
||||
allocPosteriorsAligned(num_leaves_, num_classes); // will set classes_ correctly
|
||||
for (int i = 0; i < num_leaves_; ++i)
|
||||
memset((void*)posteriors_[i], 0, num_classes*sizeof(float));
|
||||
leaf_counts_.resize(num_leaves_);
|
||||
|
||||
for (int i = 0; i < num_leaves_; ++i)
|
||||
memset((void*)posteriors2_[i], 0, num_classes*sizeof(uchar));
|
||||
|
||||
createNodes(num_nodes, rng);
|
||||
}
|
||||
|
||||
void RandomizedTree::addExample(int class_id, uchar* patch_data)
|
||||
{
|
||||
int index = getIndex(patch_data);
|
||||
float* posterior = getPosteriorByIndex(index);
|
||||
++leaf_counts_[index];
|
||||
++posterior[class_id];
|
||||
}
|
||||
|
||||
// returns the p% percentile of data (length n vector)
|
||||
static float percentile(float *data, int n, float p)
|
||||
{
|
||||
assert(n>0);
|
||||
assert(p>=0 && p<=1);
|
||||
std::vector<float> vec(data, data+n);
|
||||
std::sort(vec.begin(), vec.end());
|
||||
int ix = (int)(p*(n-1));
|
||||
return vec[ix];
|
||||
}
|
||||
|
||||
void RandomizedTree::finalize(size_t reduced_num_dim, int num_quant_bits)
|
||||
{
|
||||
// Normalize by number of patches to reach each leaf
|
||||
for (int index = 0; index < num_leaves_; ++index) {
|
||||
float* posterior = posteriors_[index];
|
||||
assert(posterior != NULL);
|
||||
int count = leaf_counts_[index];
|
||||
if (count != 0) {
|
||||
float normalizer = 1.0f / count;
|
||||
for (int c = 0; c < classes_; ++c) {
|
||||
*posterior *= normalizer;
|
||||
++posterior;
|
||||
}
|
||||
}
|
||||
}
|
||||
leaf_counts_.clear();
|
||||
|
||||
// apply compressive sensing
|
||||
if ((int)reduced_num_dim != classes_)
|
||||
compressLeaves(reduced_num_dim);
|
||||
else {
|
||||
static bool notified = false;
|
||||
if (!notified)
|
||||
printf("\n[OK] NO compression to leaves applied, dim=%i\n", (int)reduced_num_dim);
|
||||
notified = true;
|
||||
}
|
||||
|
||||
// convert float-posteriors to char-posteriors (quantization step)
|
||||
makePosteriors2(num_quant_bits);
|
||||
}
|
||||
|
||||
void RandomizedTree::compressLeaves(size_t reduced_num_dim)
|
||||
{
|
||||
static bool warned = false;
|
||||
if (!warned) {
|
||||
printf("\n[OK] compressing leaves with phi %i x %i\n", (int)reduced_num_dim, (int)classes_);
|
||||
warned = true;
|
||||
}
|
||||
|
||||
static bool warned2 = false;
|
||||
if ((int)reduced_num_dim == classes_) {
|
||||
if (!warned2)
|
||||
printf("[WARNING] RandomizedTree::compressLeaves: not compressing because reduced_dim == classes()\n");
|
||||
warned2 = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// DO NOT FREE RETURNED POINTER
|
||||
float *cs_phi = CSMatrixGenerator::getCSMatrix((int)reduced_num_dim, classes_, CSMatrixGenerator::PDT_BERNOULLI);
|
||||
|
||||
float *cs_posteriors = new float[num_leaves_ * reduced_num_dim]; // temp, num_leaves_ x reduced_num_dim
|
||||
for (int i=0; i<num_leaves_; ++i) {
|
||||
float *post = getPosteriorByIndex(i);
|
||||
float *prod = &cs_posteriors[i*reduced_num_dim];
|
||||
Mat A( (int)reduced_num_dim, classes_, CV_32FC1, cs_phi );
|
||||
Mat X( classes_, 1, CV_32FC1, post );
|
||||
Mat Y( (int)reduced_num_dim, 1, CV_32FC1, prod );
|
||||
Y = A*X;
|
||||
}
|
||||
|
||||
// copy new posteriors
|
||||
freePosteriors(3);
|
||||
allocPosteriorsAligned(num_leaves_, (int)reduced_num_dim);
|
||||
for (int i=0; i<num_leaves_; ++i)
|
||||
memcpy(posteriors_[i], &cs_posteriors[i*reduced_num_dim], reduced_num_dim*sizeof(float));
|
||||
classes_ = (int)reduced_num_dim;
|
||||
|
||||
delete [] cs_posteriors;
|
||||
}
|
||||
|
||||
void RandomizedTree::makePosteriors2(int num_quant_bits)
|
||||
{
|
||||
int N = (1<<num_quant_bits) - 1;
|
||||
|
||||
float perc[2];
|
||||
estimateQuantPercForPosteriors(perc);
|
||||
|
||||
assert(posteriors_ != NULL);
|
||||
for (int i=0; i<num_leaves_; ++i)
|
||||
quantizeVector(posteriors_[i], classes_, N, perc, posteriors2_[i]);
|
||||
|
||||
// printf("makePosteriors2 quantization bounds: %.3e, %.3e (num_leaves=%i, N=%i)\n",
|
||||
// perc[0], perc[1], num_leaves_, N);
|
||||
}
|
||||
|
||||
void RandomizedTree::estimateQuantPercForPosteriors(float perc[2])
|
||||
{
|
||||
// _estimate_ percentiles for this tree
|
||||
// TODO: do this more accurately
|
||||
assert(posteriors_ != NULL);
|
||||
perc[0] = perc[1] = .0f;
|
||||
for (int i=0; i<num_leaves_; i++) {
|
||||
perc[0] += percentile(posteriors_[i], classes_, GET_LOWER_QUANT_PERC());
|
||||
perc[1] += percentile(posteriors_[i], classes_, GET_UPPER_QUANT_PERC());
|
||||
}
|
||||
perc[0] /= num_leaves_;
|
||||
perc[1] /= num_leaves_;
|
||||
}
|
||||
|
||||
|
||||
float* RandomizedTree::getPosterior(uchar* patch_data)
|
||||
{
|
||||
return const_cast<float*>(const_cast<const RandomizedTree*>(this)->getPosterior(patch_data));
|
||||
}
|
||||
|
||||
const float* RandomizedTree::getPosterior(uchar* patch_data) const
|
||||
{
|
||||
return getPosteriorByIndex( getIndex(patch_data) );
|
||||
}
|
||||
|
||||
uchar* RandomizedTree::getPosterior2(uchar* patch_data)
|
||||
{
|
||||
return const_cast<uchar*>(const_cast<const RandomizedTree*>(this)->getPosterior2(patch_data));
|
||||
}
|
||||
|
||||
const uchar* RandomizedTree::getPosterior2(uchar* patch_data) const
|
||||
{
|
||||
return getPosteriorByIndex2( getIndex(patch_data) );
|
||||
}
|
||||
|
||||
void RandomizedTree::quantizeVector(float *vec, int dim, int N, float bnds[2], int clamp_mode)
|
||||
{
|
||||
float map_bnd[2] = {0.f,(float)N}; // bounds of quantized target interval we're mapping to
|
||||
for (int k=0; k<dim; ++k, ++vec) {
|
||||
*vec = float(int((*vec - bnds[0])/(bnds[1] - bnds[0])*(map_bnd[1] - map_bnd[0]) + map_bnd[0]));
|
||||
// 0: clamp both, lower and upper values
|
||||
if (clamp_mode == 0) *vec = (*vec<map_bnd[0]) ? map_bnd[0] : ((*vec>map_bnd[1]) ? map_bnd[1] : *vec);
|
||||
// 1: clamp lower values only
|
||||
else if (clamp_mode == 1) *vec = (*vec<map_bnd[0]) ? map_bnd[0] : *vec;
|
||||
// 2: clamp upper values only
|
||||
else if (clamp_mode == 2) *vec = (*vec>map_bnd[1]) ? map_bnd[1] : *vec;
|
||||
// 4: no clamping
|
||||
else if (clamp_mode == 4) ; // yep, nothing
|
||||
else {
|
||||
printf("clamp_mode == %i is not valid (%s:%i).\n", clamp_mode, __FILE__, __LINE__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RandomizedTree::quantizeVector(float *vec, int dim, int N, float bnds[2], uchar *dst)
|
||||
{
|
||||
int map_bnd[2] = {0, N}; // bounds of quantized target interval we're mapping to
|
||||
int tmp;
|
||||
for (int k=0; k<dim; ++k) {
|
||||
tmp = int((*vec - bnds[0])/(bnds[1] - bnds[0])*(map_bnd[1] - map_bnd[0]) + map_bnd[0]);
|
||||
*dst = (uchar)((tmp<0) ? 0 : ((tmp>N) ? N : tmp));
|
||||
++vec;
|
||||
++dst;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RandomizedTree::read(const char* file_name, int num_quant_bits)
|
||||
{
|
||||
std::ifstream file(file_name, std::ifstream::binary);
|
||||
read(file, num_quant_bits);
|
||||
file.close();
|
||||
}
|
||||
|
||||
void RandomizedTree::read(std::istream &is, int num_quant_bits)
|
||||
{
|
||||
is.read((char*)(&classes_), sizeof(classes_));
|
||||
is.read((char*)(&depth_), sizeof(depth_));
|
||||
|
||||
num_leaves_ = 1 << depth_;
|
||||
int num_nodes = num_leaves_ - 1;
|
||||
|
||||
nodes_.resize(num_nodes);
|
||||
is.read((char*)(&nodes_[0]), num_nodes * sizeof(nodes_[0]));
|
||||
|
||||
//posteriors_.resize(classes_ * num_leaves_);
|
||||
//freePosteriors(3);
|
||||
//printf("[DEBUG] reading: %i leaves, %i classes\n", num_leaves_, classes_);
|
||||
allocPosteriorsAligned(num_leaves_, classes_);
|
||||
for (int i=0; i<num_leaves_; i++)
|
||||
is.read((char*)posteriors_[i], classes_ * sizeof(*posteriors_[0]));
|
||||
|
||||
// make char-posteriors from float-posteriors
|
||||
makePosteriors2(num_quant_bits);
|
||||
}
|
||||
|
||||
void RandomizedTree::write(const char* file_name) const
|
||||
{
|
||||
std::ofstream file(file_name, std::ofstream::binary);
|
||||
write(file);
|
||||
file.close();
|
||||
}
|
||||
|
||||
void RandomizedTree::write(std::ostream &os) const
|
||||
{
|
||||
if (!posteriors_) {
|
||||
printf("WARNING: Cannot write float posteriors (posteriors_ = NULL).\n");
|
||||
return;
|
||||
}
|
||||
|
||||
os.write((char*)(&classes_), sizeof(classes_));
|
||||
os.write((char*)(&depth_), sizeof(depth_));
|
||||
|
||||
os.write((char*)(&nodes_[0]), (int)(nodes_.size() * sizeof(nodes_[0])));
|
||||
for (int i=0; i<num_leaves_; i++) {
|
||||
os.write((char*)posteriors_[i], classes_ * sizeof(*posteriors_[0]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RandomizedTree::savePosteriors(std::string url, bool append)
|
||||
{
|
||||
std::ofstream file(url.c_str(), (append?std::ios::app:std::ios::out));
|
||||
for (int i=0; i<num_leaves_; i++) {
|
||||
float *post = posteriors_[i];
|
||||
char buf[20];
|
||||
for (int i=0; i<classes_; i++) {
|
||||
sprintf(buf, "%.10e", *post++);
|
||||
file << buf << ((i<classes_-1) ? " " : "");
|
||||
}
|
||||
file << std::endl;
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
void RandomizedTree::savePosteriors2(std::string url, bool append)
|
||||
{
|
||||
std::ofstream file(url.c_str(), (append?std::ios::app:std::ios::out));
|
||||
for (int i=0; i<num_leaves_; i++) {
|
||||
uchar *post = posteriors2_[i];
|
||||
for (int i=0; i<classes_; i++)
|
||||
file << int(*post++) << (i<classes_-1?" ":"");
|
||||
file << std::endl;
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RTreeClassifier::RTreeClassifier()
|
||||
: classes_(0)
|
||||
{
|
||||
posteriors_ = NULL;
|
||||
}
|
||||
|
||||
void RTreeClassifier::train(std::vector<BaseKeypoint> const& base_set,
|
||||
RNG &rng, int num_trees, int depth,
|
||||
int views, size_t reduced_num_dim,
|
||||
int num_quant_bits)
|
||||
{
|
||||
PatchGenerator make_patch;
|
||||
train(base_set, rng, make_patch, num_trees, depth, views, reduced_num_dim, num_quant_bits);
|
||||
}
|
||||
|
||||
// Single-threaded version of train(), with progress output
|
||||
void RTreeClassifier::train(std::vector<BaseKeypoint> const& base_set,
|
||||
RNG &rng, PatchGenerator &make_patch, int num_trees,
|
||||
int depth, int views, size_t reduced_num_dim,
|
||||
int num_quant_bits)
|
||||
{
|
||||
if (reduced_num_dim > base_set.size()) {
|
||||
printf("INVALID PARAMS in RTreeClassifier::train: reduced_num_dim{%i} > base_set.size(){%i}\n",
|
||||
(int)reduced_num_dim, (int)base_set.size());
|
||||
return;
|
||||
}
|
||||
|
||||
num_quant_bits_ = num_quant_bits;
|
||||
classes_ = (int)reduced_num_dim; // base_set.size();
|
||||
original_num_classes_ = (int)base_set.size();
|
||||
trees_.resize(num_trees);
|
||||
|
||||
printf("[OK] Training trees: base size=%i, reduced size=%i\n", (int)base_set.size(), (int)reduced_num_dim);
|
||||
|
||||
int count = 1;
|
||||
printf("[OK] Trained 0 / %i trees", num_trees); fflush(stdout);
|
||||
for( int ti = 0; ti < num_trees; ti++ ) {
|
||||
trees_[ti].train(base_set, rng, make_patch, depth, views, reduced_num_dim, num_quant_bits_);
|
||||
printf("\r[OK] Trained %i / %i trees", count++, num_trees);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
countZeroElements();
|
||||
printf("\n\n");
|
||||
}
|
||||
|
||||
void RTreeClassifier::getSignature(IplImage* patch, float *sig) const
|
||||
{
|
||||
// Need pointer to 32x32 patch data
|
||||
uchar buffer[RandomizedTree::PATCH_SIZE * RandomizedTree::PATCH_SIZE];
|
||||
uchar* patch_data;
|
||||
if (patch->widthStep != RandomizedTree::PATCH_SIZE) {
|
||||
//printf("[INFO] patch is padded, data will be copied (%i/%i).\n",
|
||||
// patch->widthStep, RandomizedTree::PATCH_SIZE);
|
||||
uchar* data = getData(patch);
|
||||
patch_data = buffer;
|
||||
for (int i = 0; i < RandomizedTree::PATCH_SIZE; ++i) {
|
||||
memcpy((void*)patch_data, (void*)data, RandomizedTree::PATCH_SIZE);
|
||||
data += patch->widthStep;
|
||||
patch_data += RandomizedTree::PATCH_SIZE;
|
||||
}
|
||||
patch_data = buffer;
|
||||
}
|
||||
else {
|
||||
patch_data = getData(patch);
|
||||
}
|
||||
|
||||
memset((void*)sig, 0, classes_ * sizeof(float));
|
||||
std::vector<RandomizedTree>::const_iterator tree_it;
|
||||
|
||||
// get posteriors
|
||||
float **posteriors = new float*[trees_.size()]; // TODO: move alloc outside this func
|
||||
float **pp = posteriors;
|
||||
for (tree_it = trees_.begin(); tree_it != trees_.end(); ++tree_it, pp++) {
|
||||
*pp = const_cast<float*>(tree_it->getPosterior(patch_data));
|
||||
assert(*pp != NULL);
|
||||
}
|
||||
|
||||
// sum them up
|
||||
pp = posteriors;
|
||||
for (tree_it = trees_.begin(); tree_it != trees_.end(); ++tree_it, pp++)
|
||||
addVec(classes_, sig, *pp, sig);
|
||||
|
||||
delete [] posteriors;
|
||||
posteriors = NULL;
|
||||
|
||||
// full quantization (experimental)
|
||||
#if 0
|
||||
int n_max = 1<<8 - 1;
|
||||
int sum_max = (1<<4 - 1)*trees_.size();
|
||||
int shift = 0;
|
||||
while ((sum_max>>shift) > n_max) shift++;
|
||||
|
||||
for (int i = 0; i < classes_; ++i) {
|
||||
sig[i] = int(sig[i] + .5) >> shift;
|
||||
if (sig[i]>n_max) sig[i] = n_max;
|
||||
}
|
||||
|
||||
static bool warned = false;
|
||||
if (!warned) {
|
||||
printf("[WARNING] Using full quantization (RTreeClassifier::getSignature)! shift=%i\n", shift);
|
||||
warned = true;
|
||||
}
|
||||
#else
|
||||
// TODO: get rid of this multiply (-> number of trees is known at train
|
||||
// time, exploit it in RandomizedTree::finalize())
|
||||
float normalizer = 1.0f / trees_.size();
|
||||
for (int i = 0; i < classes_; ++i)
|
||||
sig[i] *= normalizer;
|
||||
#endif
|
||||
}
|
||||
|
||||
void RTreeClassifier::getSignature(IplImage* patch, uchar *sig) const
|
||||
{
|
||||
// Need pointer to 32x32 patch data
|
||||
uchar buffer[RandomizedTree::PATCH_SIZE * RandomizedTree::PATCH_SIZE];
|
||||
uchar* patch_data;
|
||||
if (patch->widthStep != RandomizedTree::PATCH_SIZE) {
|
||||
//printf("[INFO] patch is padded, data will be copied (%i/%i).\n",
|
||||
// patch->widthStep, RandomizedTree::PATCH_SIZE);
|
||||
uchar* data = getData(patch);
|
||||
patch_data = buffer;
|
||||
for (int i = 0; i < RandomizedTree::PATCH_SIZE; ++i) {
|
||||
memcpy((void*)patch_data, (void*)data, RandomizedTree::PATCH_SIZE);
|
||||
data += patch->widthStep;
|
||||
patch_data += RandomizedTree::PATCH_SIZE;
|
||||
}
|
||||
patch_data = buffer;
|
||||
} else {
|
||||
patch_data = getData(patch);
|
||||
}
|
||||
|
||||
std::vector<RandomizedTree>::const_iterator tree_it;
|
||||
|
||||
// get posteriors
|
||||
if (posteriors_ == NULL)
|
||||
{
|
||||
posteriors_ = (uchar**)cvAlloc( trees_.size()*sizeof(posteriors_[0]) );
|
||||
ptemp_ = (unsigned short*)cvAlloc( classes_*sizeof(ptemp_[0]) );
|
||||
}
|
||||
/// @todo What is going on in the next 4 lines?
|
||||
uchar **pp = posteriors_;
|
||||
for (tree_it = trees_.begin(); tree_it != trees_.end(); ++tree_it, pp++)
|
||||
*pp = const_cast<uchar*>(tree_it->getPosterior2(patch_data));
|
||||
pp = posteriors_;
|
||||
|
||||
#if 1
|
||||
// SSE2 optimized code
|
||||
sum_50t_176c(pp, sig, ptemp_); // sum them up
|
||||
#else
|
||||
static bool warned = false;
|
||||
|
||||
memset((void*)sig, 0, classes_ * sizeof(sig[0]));
|
||||
unsigned short *sig16 = new unsigned short[classes_]; // TODO: make member, no alloc here
|
||||
memset((void*)sig16, 0, classes_ * sizeof(sig16[0]));
|
||||
for (tree_it = trees_.begin(); tree_it != trees_.end(); ++tree_it, pp++)
|
||||
addVec(classes_, sig16, *pp, sig16);
|
||||
|
||||
// squeeze signatures into an uchar
|
||||
const bool full_shifting = true;
|
||||
int shift;
|
||||
if (full_shifting) {
|
||||
float num_add_bits_f = log((float)trees_.size())/log(2.f); // # additional bits required due to summation
|
||||
int num_add_bits = int(num_add_bits_f);
|
||||
if (num_add_bits_f != float(num_add_bits)) ++num_add_bits;
|
||||
shift = num_quant_bits_ + num_add_bits - 8*sizeof(uchar);
|
||||
//shift = num_quant_bits_ + num_add_bits - 2;
|
||||
//shift = 6;
|
||||
if (shift>0)
|
||||
for (int i = 0; i < classes_; ++i)
|
||||
sig[i] = (sig16[i] >> shift); // &3 cut off all but lowest 2 bits, 3(dec) = 11(bin)
|
||||
|
||||
if (!warned)
|
||||
printf("[OK] RTC: quantizing by FULL RIGHT SHIFT, shift = %i\n", shift);
|
||||
}
|
||||
else {
|
||||
printf("[ERROR] RTC: not implemented!\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!warned)
|
||||
printf("[WARNING] RTC: unoptimized signature computation\n");
|
||||
warned = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void RTreeClassifier::getSparseSignature(IplImage *patch, float *sig, float thresh) const
|
||||
{
|
||||
getFloatSignature(patch, sig);
|
||||
for (int i=0; i<classes_; ++i, sig++)
|
||||
if (*sig < thresh) *sig = 0.f;
|
||||
}
|
||||
|
||||
int RTreeClassifier::countNonZeroElements(float *vec, int n, double tol)
|
||||
{
|
||||
int res = 0;
|
||||
while (n-- > 0)
|
||||
res += (fabs(*vec++) > tol);
|
||||
return res;
|
||||
}
|
||||
|
||||
void RTreeClassifier::read(const char* file_name)
|
||||
{
|
||||
std::ifstream file(file_name, std::ifstream::binary);
|
||||
if( file.is_open() )
|
||||
{
|
||||
read(file);
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
void RTreeClassifier::read(std::istream &is)
|
||||
{
|
||||
int num_trees = 0;
|
||||
is.read((char*)(&num_trees), sizeof(num_trees));
|
||||
is.read((char*)(&classes_), sizeof(classes_));
|
||||
is.read((char*)(&original_num_classes_), sizeof(original_num_classes_));
|
||||
is.read((char*)(&num_quant_bits_), sizeof(num_quant_bits_));
|
||||
|
||||
if (num_quant_bits_<1 || num_quant_bits_>8) {
|
||||
printf("[WARNING] RTC: suspicious value num_quant_bits_=%i found; setting to %i.\n",
|
||||
num_quant_bits_, (int)DEFAULT_NUM_QUANT_BITS);
|
||||
num_quant_bits_ = DEFAULT_NUM_QUANT_BITS;
|
||||
}
|
||||
|
||||
trees_.resize(num_trees);
|
||||
std::vector<RandomizedTree>::iterator tree_it;
|
||||
|
||||
for (tree_it = trees_.begin(); tree_it != trees_.end(); ++tree_it) {
|
||||
tree_it->read(is, num_quant_bits_);
|
||||
}
|
||||
|
||||
printf("[OK] Loaded RTC, quantization=%i bits\n", num_quant_bits_);
|
||||
|
||||
countZeroElements();
|
||||
}
|
||||
|
||||
void RTreeClassifier::write(const char* file_name) const
|
||||
{
|
||||
std::ofstream file(file_name, std::ofstream::binary);
|
||||
write(file);
|
||||
file.close();
|
||||
}
|
||||
|
||||
void RTreeClassifier::write(std::ostream &os) const
|
||||
{
|
||||
int num_trees = (int)trees_.size();
|
||||
os.write((char*)(&num_trees), sizeof(num_trees));
|
||||
os.write((char*)(&classes_), sizeof(classes_));
|
||||
os.write((char*)(&original_num_classes_), sizeof(original_num_classes_));
|
||||
os.write((char*)(&num_quant_bits_), sizeof(num_quant_bits_));
|
||||
printf("RTreeClassifier::write: num_quant_bits_=%i\n", num_quant_bits_);
|
||||
|
||||
std::vector<RandomizedTree>::const_iterator tree_it;
|
||||
for (tree_it = trees_.begin(); tree_it != trees_.end(); ++tree_it)
|
||||
tree_it->write(os);
|
||||
}
|
||||
|
||||
void RTreeClassifier::saveAllFloatPosteriors(std::string url)
|
||||
{
|
||||
printf("[DEBUG] writing all float posteriors to %s...\n", url.c_str());
|
||||
for (int i=0; i<(int)trees_.size(); ++i)
|
||||
trees_[i].savePosteriors(url, (i==0 ? false : true));
|
||||
printf("[DEBUG] done\n");
|
||||
}
|
||||
|
||||
void RTreeClassifier::saveAllBytePosteriors(std::string url)
|
||||
{
|
||||
printf("[DEBUG] writing all byte posteriors to %s...\n", url.c_str());
|
||||
for (int i=0; i<(int)trees_.size(); ++i)
|
||||
trees_[i].savePosteriors2(url, (i==0 ? false : true));
|
||||
printf("[DEBUG] done\n");
|
||||
}
|
||||
|
||||
|
||||
void RTreeClassifier::setFloatPosteriorsFromTextfile_176(std::string url)
|
||||
{
|
||||
std::ifstream ifs(url.c_str());
|
||||
|
||||
for (int i=0; i<(int)trees_.size(); ++i) {
|
||||
int num_classes = trees_[i].classes_;
|
||||
assert(num_classes == 176); // TODO: remove this limitation (arose due to SSE2 optimizations)
|
||||
for (int k=0; k<trees_[i].num_leaves_; ++k) {
|
||||
float *post = trees_[i].getPosteriorByIndex(k);
|
||||
for (int j=0; j<num_classes; ++j, ++post)
|
||||
ifs >> *post;
|
||||
assert(ifs.good());
|
||||
}
|
||||
}
|
||||
classes_ = 176;
|
||||
|
||||
//setQuantization(num_quant_bits_);
|
||||
|
||||
ifs.close();
|
||||
printf("[EXPERIMENTAL] read entire tree from '%s'\n", url.c_str());
|
||||
}
|
||||
|
||||
|
||||
float RTreeClassifier::countZeroElements()
|
||||
{
|
||||
size_t flt_zeros = 0;
|
||||
size_t ui8_zeros = 0;
|
||||
size_t num_elem = trees_[0].classes();
|
||||
for (int i=0; i<(int)trees_.size(); ++i)
|
||||
for (int k=0; k<(int)trees_[i].num_leaves_; ++k) {
|
||||
float *p = trees_[i].getPosteriorByIndex(k);
|
||||
uchar *p2 = trees_[i].getPosteriorByIndex2(k);
|
||||
assert(p); assert(p2);
|
||||
for (int j=0; j<(int)num_elem; ++j, ++p, ++p2) {
|
||||
if (*p == 0.f) flt_zeros++;
|
||||
if (*p2 == 0) ui8_zeros++;
|
||||
}
|
||||
}
|
||||
num_elem = trees_.size()*trees_[0].num_leaves_*num_elem;
|
||||
float flt_perc = 100.f*flt_zeros/num_elem;
|
||||
float ui8_perc = 100.f*ui8_zeros/num_elem;
|
||||
printf("[OK] RTC: overall %i/%i (%.3f%%) zeros in float leaves\n", (int)flt_zeros, (int)num_elem, flt_perc);
|
||||
printf(" overall %i/%i (%.3f%%) zeros in uint8 leaves\n", (int)ui8_zeros, (int)num_elem, ui8_perc);
|
||||
|
||||
return flt_perc;
|
||||
}
|
||||
|
||||
void RTreeClassifier::setQuantization(int num_quant_bits)
|
||||
{
|
||||
for (int i=0; i<(int)trees_.size(); ++i)
|
||||
trees_[i].applyQuantization(num_quant_bits);
|
||||
|
||||
printf("[OK] signature quantization is now %i bits (before: %i)\n", num_quant_bits, num_quant_bits_);
|
||||
num_quant_bits_ = num_quant_bits;
|
||||
}
|
||||
|
||||
void RTreeClassifier::discardFloatPosteriors()
|
||||
{
|
||||
for (int i=0; i<(int)trees_.size(); ++i)
|
||||
trees_[i].discardFloatPosteriors();
|
||||
printf("[OK] RTC: discarded float posteriors of all trees\n");
|
||||
}
|
||||
|
||||
}
|
@@ -77,11 +77,11 @@ void DescriptorExtractor::compute( const vector<Mat>& imageCollection, vector<ve
|
||||
compute( imageCollection[i], pointCollection[i], descCollection[i] );
|
||||
}
|
||||
|
||||
void DescriptorExtractor::read( const FileNode& )
|
||||
/*void DescriptorExtractor::read( const FileNode& )
|
||||
{}
|
||||
|
||||
void DescriptorExtractor::write( FileStorage& ) const
|
||||
{}
|
||||
{}*/
|
||||
|
||||
bool DescriptorExtractor::empty() const
|
||||
{
|
||||
@@ -96,184 +96,16 @@ void DescriptorExtractor::removeBorderKeypoints( vector<KeyPoint>& keypoints,
|
||||
|
||||
Ptr<DescriptorExtractor> DescriptorExtractor::create(const string& descriptorExtractorType)
|
||||
{
|
||||
DescriptorExtractor* de = 0;
|
||||
|
||||
size_t pos = 0;
|
||||
if (!descriptorExtractorType.compare("SIFT"))
|
||||
if( descriptorExtractorType.find("Opponent") == 0)
|
||||
{
|
||||
de = new SiftDescriptorExtractor();
|
||||
size_t pos = string("Opponent").size();
|
||||
return DescriptorExtractor::create(descriptorExtractorType.substr(pos));
|
||||
}
|
||||
else if (!descriptorExtractorType.compare("SURF"))
|
||||
{
|
||||
de = new SurfDescriptorExtractor();
|
||||
}
|
||||
else if (!descriptorExtractorType.compare("ORB"))
|
||||
{
|
||||
de = new OrbDescriptorExtractor();
|
||||
}
|
||||
else if (!descriptorExtractorType.compare("BRIEF"))
|
||||
{
|
||||
de = new BriefDescriptorExtractor();
|
||||
}
|
||||
else if ( (pos=descriptorExtractorType.find("Opponent")) == 0)
|
||||
{
|
||||
pos += string("Opponent").size();
|
||||
de = new OpponentColorDescriptorExtractor( DescriptorExtractor::create(descriptorExtractorType.substr(pos)) );
|
||||
}
|
||||
return de;
|
||||
|
||||
return Algorithm::create<DescriptorExtractor>("Feature2D." + descriptorExtractorType);
|
||||
}
|
||||
|
||||
/****************************************************************************************\
|
||||
* SiftDescriptorExtractor *
|
||||
\****************************************************************************************/
|
||||
SiftDescriptorExtractor::SiftDescriptorExtractor(const SIFT::DescriptorParams& descriptorParams,
|
||||
const SIFT::CommonParams& commonParams)
|
||||
: sift( descriptorParams.magnification, descriptorParams.isNormalize, descriptorParams.recalculateAngles,
|
||||
commonParams.nOctaves, commonParams.nOctaveLayers, commonParams.firstOctave, commonParams.angleMode )
|
||||
{}
|
||||
|
||||
SiftDescriptorExtractor::SiftDescriptorExtractor( double magnification, bool isNormalize, bool recalculateAngles,
|
||||
int nOctaves, int nOctaveLayers, int firstOctave, int angleMode )
|
||||
: sift( magnification, isNormalize, recalculateAngles, nOctaves, nOctaveLayers, firstOctave, angleMode )
|
||||
{}
|
||||
|
||||
void SiftDescriptorExtractor::computeImpl( const Mat& image,
|
||||
vector<KeyPoint>& keypoints,
|
||||
Mat& descriptors) const
|
||||
{
|
||||
bool useProvidedKeypoints = true;
|
||||
Mat grayImage = image;
|
||||
if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY );
|
||||
|
||||
sift(grayImage, Mat(), keypoints, descriptors, useProvidedKeypoints);
|
||||
}
|
||||
|
||||
void SiftDescriptorExtractor::read (const FileNode &fn)
|
||||
{
|
||||
double magnification = fn["magnification"];
|
||||
bool isNormalize = (int)fn["isNormalize"] != 0;
|
||||
bool recalculateAngles = (int)fn["recalculateAngles"] != 0;
|
||||
int nOctaves = fn["nOctaves"];
|
||||
int nOctaveLayers = fn["nOctaveLayers"];
|
||||
int firstOctave = fn["firstOctave"];
|
||||
int angleMode = fn["angleMode"];
|
||||
|
||||
sift = SIFT( magnification, isNormalize, recalculateAngles, nOctaves, nOctaveLayers, firstOctave, angleMode );
|
||||
}
|
||||
|
||||
void SiftDescriptorExtractor::write (FileStorage &fs) const
|
||||
{
|
||||
// fs << "algorithm" << getAlgorithmName ();
|
||||
|
||||
SIFT::CommonParams commParams = sift.getCommonParams ();
|
||||
SIFT::DescriptorParams descriptorParams = sift.getDescriptorParams ();
|
||||
fs << "magnification" << descriptorParams.magnification;
|
||||
fs << "isNormalize" << descriptorParams.isNormalize;
|
||||
fs << "recalculateAngles" << descriptorParams.recalculateAngles;
|
||||
fs << "nOctaves" << commParams.nOctaves;
|
||||
fs << "nOctaveLayers" << commParams.nOctaveLayers;
|
||||
fs << "firstOctave" << commParams.firstOctave;
|
||||
fs << "angleMode" << commParams.angleMode;
|
||||
}
|
||||
|
||||
int SiftDescriptorExtractor::descriptorSize() const
|
||||
{
|
||||
return sift.descriptorSize();
|
||||
}
|
||||
|
||||
int SiftDescriptorExtractor::descriptorType() const
|
||||
{
|
||||
return CV_32FC1;
|
||||
}
|
||||
|
||||
/****************************************************************************************\
|
||||
* SurfDescriptorExtractor *
|
||||
\****************************************************************************************/
|
||||
SurfDescriptorExtractor::SurfDescriptorExtractor( int nOctaves,
|
||||
int nOctaveLayers, bool extended, bool upright )
|
||||
: surf( 0.0, nOctaves, nOctaveLayers, extended, upright )
|
||||
{}
|
||||
|
||||
void SurfDescriptorExtractor::computeImpl( const Mat& image,
|
||||
vector<KeyPoint>& keypoints,
|
||||
Mat& descriptors) const
|
||||
{
|
||||
// Compute descriptors for given keypoints
|
||||
vector<float> _descriptors;
|
||||
Mat mask;
|
||||
bool useProvidedKeypoints = true;
|
||||
Mat grayImage = image;
|
||||
if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY );
|
||||
|
||||
surf(grayImage, mask, keypoints, _descriptors, useProvidedKeypoints);
|
||||
|
||||
descriptors.create((int)keypoints.size(), (int)surf.descriptorSize(), CV_32FC1);
|
||||
assert( (int)_descriptors.size() == descriptors.rows * descriptors.cols );
|
||||
std::copy(_descriptors.begin(), _descriptors.end(), descriptors.begin<float>());
|
||||
}
|
||||
|
||||
void SurfDescriptorExtractor::read( const FileNode &fn )
|
||||
{
|
||||
int nOctaves = fn["nOctaves"];
|
||||
int nOctaveLayers = fn["nOctaveLayers"];
|
||||
bool extended = (int)fn["extended"] != 0;
|
||||
bool upright = (int)fn["upright"] != 0;
|
||||
|
||||
surf = SURF( 0.0, nOctaves, nOctaveLayers, extended, upright );
|
||||
}
|
||||
|
||||
void SurfDescriptorExtractor::write( FileStorage &fs ) const
|
||||
{
|
||||
// fs << "algorithm" << getAlgorithmName ();
|
||||
|
||||
fs << "nOctaves" << surf.nOctaves;
|
||||
fs << "nOctaveLayers" << surf.nOctaveLayers;
|
||||
fs << "extended" << surf.extended;
|
||||
fs << "upright" << surf.upright;
|
||||
}
|
||||
|
||||
int SurfDescriptorExtractor::descriptorSize() const
|
||||
{
|
||||
return surf.descriptorSize();
|
||||
}
|
||||
|
||||
int SurfDescriptorExtractor::descriptorType() const
|
||||
{
|
||||
return CV_32FC1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/** Default constructor */
|
||||
OrbDescriptorExtractor::OrbDescriptorExtractor(ORB::CommonParams params)
|
||||
{
|
||||
orb_ = ORB(0, params);
|
||||
}
|
||||
void OrbDescriptorExtractor::computeImpl(const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints,
|
||||
cv::Mat& descriptors) const
|
||||
{
|
||||
cv::Mat empty_mask;
|
||||
orb_(image, empty_mask, keypoints, descriptors, true);
|
||||
}
|
||||
void OrbDescriptorExtractor::read(const cv::FileNode& fn)
|
||||
{
|
||||
orb_.read(fn);
|
||||
}
|
||||
void OrbDescriptorExtractor::write(cv::FileStorage& fs) const
|
||||
{
|
||||
orb_.write(fs);
|
||||
}
|
||||
int OrbDescriptorExtractor::descriptorSize() const
|
||||
{
|
||||
return orb_.descriptorSize();
|
||||
}
|
||||
int OrbDescriptorExtractor::descriptorType() const
|
||||
{
|
||||
return CV_8UC1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/****************************************************************************************\
|
||||
* OpponentColorDescriptorExtractor *
|
||||
|
@@ -45,6 +45,7 @@ using namespace std;
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
/*
|
||||
* FeatureDetector
|
||||
*/
|
||||
@@ -71,11 +72,11 @@ void FeatureDetector::detect(const vector<Mat>& imageCollection, vector<vector<K
|
||||
detect( imageCollection[i], pointCollection[i], masks.empty() ? Mat() : masks[i] );
|
||||
}
|
||||
|
||||
void FeatureDetector::read( const FileNode& )
|
||||
/*void FeatureDetector::read( const FileNode& )
|
||||
{}
|
||||
|
||||
void FeatureDetector::write( FileStorage& ) const
|
||||
{}
|
||||
{}*/
|
||||
|
||||
bool FeatureDetector::empty() const
|
||||
{
|
||||
@@ -89,387 +90,91 @@ void FeatureDetector::removeInvalidPoints( const Mat& mask, vector<KeyPoint>& ke
|
||||
|
||||
Ptr<FeatureDetector> FeatureDetector::create( const string& detectorType )
|
||||
{
|
||||
FeatureDetector* fd = 0;
|
||||
size_t pos = 0;
|
||||
|
||||
if( !detectorType.compare( "FAST" ) )
|
||||
if( detectorType.find("Grid") == 0 )
|
||||
{
|
||||
fd = new FastFeatureDetector();
|
||||
return new GridAdaptedFeatureDetector(FeatureDetector::create(
|
||||
detectorType.substr(strlen("Grid"))));
|
||||
}
|
||||
else if( !detectorType.compare( "STAR" ) )
|
||||
|
||||
if( detectorType.find("Pyramid") == 0 )
|
||||
{
|
||||
fd = new StarFeatureDetector();
|
||||
return new PyramidAdaptedFeatureDetector(FeatureDetector::create(
|
||||
detectorType.substr(strlen("Pyramid"))));
|
||||
}
|
||||
else if( !detectorType.compare( "SIFT" ) )
|
||||
|
||||
if( detectorType.find("Dynamic") == 0 )
|
||||
{
|
||||
fd = new SiftFeatureDetector();
|
||||
return new DynamicAdaptedFeatureDetector(AdjusterAdapter::create(
|
||||
detectorType.substr(strlen("Dynamic"))));
|
||||
}
|
||||
else if( !detectorType.compare( "SURF" ) )
|
||||
|
||||
if( detectorType.compare( "HARRIS" ) == 0 )
|
||||
{
|
||||
fd = new SurfFeatureDetector();
|
||||
}
|
||||
else if( !detectorType.compare( "ORB" ) )
|
||||
{
|
||||
fd = new OrbFeatureDetector();
|
||||
}
|
||||
else if( !detectorType.compare( "MSER" ) )
|
||||
{
|
||||
fd = new MserFeatureDetector();
|
||||
}
|
||||
else if( !detectorType.compare( "GFTT" ) )
|
||||
{
|
||||
fd = new GoodFeaturesToTrackDetector();
|
||||
}
|
||||
else if( !detectorType.compare( "HARRIS" ) )
|
||||
{
|
||||
GoodFeaturesToTrackDetector::Params params;
|
||||
params.useHarrisDetector = true;
|
||||
fd = new GoodFeaturesToTrackDetector(params);
|
||||
}
|
||||
else if( !detectorType.compare( "Dense" ) )
|
||||
{
|
||||
fd = new DenseFeatureDetector();
|
||||
}
|
||||
else if( !detectorType.compare( "SimpleBlob" ) )
|
||||
{
|
||||
fd = new SimpleBlobDetector();
|
||||
}
|
||||
else if( (pos=detectorType.find("Grid")) == 0 )
|
||||
{
|
||||
pos += string("Grid").size();
|
||||
fd = new GridAdaptedFeatureDetector( FeatureDetector::create(detectorType.substr(pos)) );
|
||||
}
|
||||
else if( (pos=detectorType.find("Pyramid")) == 0 )
|
||||
{
|
||||
pos += string("Pyramid").size();
|
||||
fd = new PyramidAdaptedFeatureDetector( FeatureDetector::create(detectorType.substr(pos)) );
|
||||
}
|
||||
else if( (pos=detectorType.find("Dynamic")) == 0 )
|
||||
{
|
||||
pos += string("Dynamic").size();
|
||||
fd = new DynamicAdaptedFeatureDetector( AdjusterAdapter::create(detectorType.substr(pos)) );
|
||||
Ptr<FeatureDetector> fd = FeatureDetector::create("GFTT");
|
||||
fd->set("useHarrisDetector", true);
|
||||
return fd;
|
||||
}
|
||||
|
||||
return fd;
|
||||
return Algorithm::create<FeatureDetector>("Feature2D." + detectorType);
|
||||
}
|
||||
|
||||
/*
|
||||
* FastFeatureDetector
|
||||
*/
|
||||
FastFeatureDetector::FastFeatureDetector( int _threshold, bool _nonmaxSuppression )
|
||||
: threshold(_threshold), nonmaxSuppression(_nonmaxSuppression)
|
||||
{}
|
||||
|
||||
void FastFeatureDetector::read (const FileNode& fn)
|
||||
{
|
||||
threshold = fn["threshold"];
|
||||
nonmaxSuppression = (int)fn["nonmaxSuppression"] ? true : false;
|
||||
}
|
||||
|
||||
void FastFeatureDetector::write (FileStorage& fs) const
|
||||
{
|
||||
fs << "threshold" << threshold;
|
||||
fs << "nonmaxSuppression" << nonmaxSuppression;
|
||||
}
|
||||
|
||||
void FastFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
|
||||
{
|
||||
Mat grayImage = image;
|
||||
if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY );
|
||||
FAST( grayImage, keypoints, threshold, nonmaxSuppression );
|
||||
KeyPointsFilter::runByPixelsMask( keypoints, mask );
|
||||
}
|
||||
|
||||
/*
|
||||
* GoodFeaturesToTrackDetector
|
||||
*/
|
||||
GoodFeaturesToTrackDetector::Params::Params( int _maxCorners, double _qualityLevel, double _minDistance,
|
||||
int _blockSize, bool _useHarrisDetector, double _k ) :
|
||||
maxCorners(_maxCorners), qualityLevel(_qualityLevel), minDistance(_minDistance),
|
||||
GFTTDetector::GFTTDetector( int _nfeatures, double _qualityLevel,
|
||||
double _minDistance, int _blockSize,
|
||||
bool _useHarrisDetector, double _k )
|
||||
: nfeatures(_nfeatures), qualityLevel(_qualityLevel), minDistance(_minDistance),
|
||||
blockSize(_blockSize), useHarrisDetector(_useHarrisDetector), k(_k)
|
||||
{}
|
||||
|
||||
void GoodFeaturesToTrackDetector::Params::read (const FileNode& fn)
|
||||
{
|
||||
maxCorners = fn["maxCorners"];
|
||||
qualityLevel = fn["qualityLevel"];
|
||||
minDistance = fn["minDistance"];
|
||||
blockSize = fn["blockSize"];
|
||||
useHarrisDetector = (int)fn["useHarrisDetector"] != 0;
|
||||
k = fn["k"];
|
||||
}
|
||||
|
||||
void GoodFeaturesToTrackDetector::Params::write (FileStorage& fs) const
|
||||
{
|
||||
fs << "maxCorners" << maxCorners;
|
||||
fs << "qualityLevel" << qualityLevel;
|
||||
fs << "minDistance" << minDistance;
|
||||
fs << "blockSize" << blockSize;
|
||||
fs << "useHarrisDetector" << useHarrisDetector;
|
||||
fs << "k" << k;
|
||||
}
|
||||
|
||||
GoodFeaturesToTrackDetector::GoodFeaturesToTrackDetector( const Params& _params ) : params(_params)
|
||||
{}
|
||||
|
||||
GoodFeaturesToTrackDetector::GoodFeaturesToTrackDetector( int maxCorners, double qualityLevel,
|
||||
double minDistance, int blockSize,
|
||||
bool useHarrisDetector, double k )
|
||||
{
|
||||
params = Params( maxCorners, qualityLevel, minDistance, blockSize, useHarrisDetector, k );
|
||||
}
|
||||
|
||||
void GoodFeaturesToTrackDetector::read (const FileNode& fn)
|
||||
{
|
||||
params.read(fn);
|
||||
}
|
||||
|
||||
void GoodFeaturesToTrackDetector::write (FileStorage& fs) const
|
||||
{
|
||||
params.write(fs);
|
||||
}
|
||||
|
||||
void GoodFeaturesToTrackDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask) const
|
||||
void GFTTDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask) const
|
||||
{
|
||||
Mat grayImage = image;
|
||||
if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY );
|
||||
|
||||
vector<Point2f> corners;
|
||||
goodFeaturesToTrack( grayImage, corners, params.maxCorners, params.qualityLevel, params.minDistance, mask,
|
||||
params.blockSize, params.useHarrisDetector, params.k );
|
||||
goodFeaturesToTrack( grayImage, corners, nfeatures, qualityLevel, minDistance, mask,
|
||||
blockSize, useHarrisDetector, k );
|
||||
keypoints.resize(corners.size());
|
||||
vector<Point2f>::const_iterator corner_it = corners.begin();
|
||||
vector<KeyPoint>::iterator keypoint_it = keypoints.begin();
|
||||
for( ; corner_it != corners.end(); ++corner_it, ++keypoint_it )
|
||||
{
|
||||
*keypoint_it = KeyPoint( *corner_it, (float)params.blockSize );
|
||||
*keypoint_it = KeyPoint( *corner_it, (float)blockSize );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* MserFeatureDetector
|
||||
*/
|
||||
MserFeatureDetector::MserFeatureDetector( int delta, int minArea, int maxArea,
|
||||
double maxVariation, double minDiversity,
|
||||
int maxEvolution, double areaThreshold,
|
||||
double minMargin, int edgeBlurSize )
|
||||
: mser( delta, minArea, maxArea, maxVariation, minDiversity,
|
||||
maxEvolution, areaThreshold, minMargin, edgeBlurSize )
|
||||
{}
|
||||
|
||||
MserFeatureDetector::MserFeatureDetector( CvMSERParams params )
|
||||
: mser( params.delta, params.minArea, params.maxArea, params.maxVariation, params.minDiversity,
|
||||
params.maxEvolution, params.areaThreshold, params.minMargin, params.edgeBlurSize )
|
||||
{}
|
||||
|
||||
void MserFeatureDetector::read (const FileNode& fn)
|
||||
static Algorithm* createGFTT() { return new GFTTDetector; }
|
||||
static Algorithm* createHarris()
|
||||
{
|
||||
int delta = fn["delta"];
|
||||
int minArea = fn["minArea"];
|
||||
int maxArea = fn["maxArea"];
|
||||
float maxVariation = fn["maxVariation"];
|
||||
float minDiversity = fn["minDiversity"];
|
||||
int maxEvolution = fn["maxEvolution"];
|
||||
double areaThreshold = fn["areaThreshold"];
|
||||
double minMargin = fn["minMargin"];
|
||||
int edgeBlurSize = fn["edgeBlurSize"];
|
||||
|
||||
mser = MSER( delta, minArea, maxArea, maxVariation, minDiversity,
|
||||
maxEvolution, areaThreshold, minMargin, edgeBlurSize );
|
||||
GFTTDetector* d = new GFTTDetector;
|
||||
d->set("useHarris", true);
|
||||
return d;
|
||||
}
|
||||
|
||||
void MserFeatureDetector::write (FileStorage& fs) const
|
||||
static AlgorithmInfo gftt_info("Feature2D.GFTT", createGFTT);
|
||||
static AlgorithmInfo harris_info("Feature2D.HARRIS", createHarris);
|
||||
|
||||
AlgorithmInfo* GFTTDetector::info() const
|
||||
{
|
||||
//fs << "algorithm" << getAlgorithmName ();
|
||||
|
||||
fs << "delta" << mser.delta;
|
||||
fs << "minArea" << mser.minArea;
|
||||
fs << "maxArea" << mser.maxArea;
|
||||
fs << "maxVariation" << mser.maxVariation;
|
||||
fs << "minDiversity" << mser.minDiversity;
|
||||
fs << "maxEvolution" << mser.maxEvolution;
|
||||
fs << "areaThreshold" << mser.areaThreshold;
|
||||
fs << "minMargin" << mser.minMargin;
|
||||
fs << "edgeBlurSize" << mser.edgeBlurSize;
|
||||
}
|
||||
|
||||
|
||||
void MserFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
|
||||
{
|
||||
vector<vector<Point> > msers;
|
||||
|
||||
mser(image, msers, mask);
|
||||
|
||||
vector<vector<Point> >::const_iterator contour_it = msers.begin();
|
||||
for( ; contour_it != msers.end(); ++contour_it )
|
||||
static volatile bool initialized = false;
|
||||
if( !initialized )
|
||||
{
|
||||
// TODO check transformation from MSER region to KeyPoint
|
||||
RotatedRect rect = fitEllipse(Mat(*contour_it));
|
||||
float diam = sqrt(rect.size.height*rect.size.width);
|
||||
|
||||
if( diam > std::numeric_limits<float>::epsilon() )
|
||||
keypoints.push_back( KeyPoint( rect.center, diam, rect.angle) );
|
||||
gftt_info.addParam(this, "nfeatures", nfeatures);
|
||||
gftt_info.addParam(this, "qualityLevel", qualityLevel);
|
||||
gftt_info.addParam(this, "minDistance", minDistance);
|
||||
gftt_info.addParam(this, "useHarrisDetector", useHarrisDetector);
|
||||
gftt_info.addParam(this, "k", k);
|
||||
|
||||
harris_info.addParam(this, "nfeatures", nfeatures);
|
||||
harris_info.addParam(this, "qualityLevel", qualityLevel);
|
||||
harris_info.addParam(this, "minDistance", minDistance);
|
||||
harris_info.addParam(this, "useHarrisDetector", useHarrisDetector);
|
||||
harris_info.addParam(this, "k", k);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* StarFeatureDetector
|
||||
*/
|
||||
|
||||
StarFeatureDetector::StarFeatureDetector( const CvStarDetectorParams& params )
|
||||
: star( params.maxSize, params.responseThreshold, params.lineThresholdProjected,
|
||||
params.lineThresholdBinarized, params.suppressNonmaxSize)
|
||||
{}
|
||||
|
||||
StarFeatureDetector::StarFeatureDetector(int maxSize, int responseThreshold,
|
||||
int lineThresholdProjected,
|
||||
int lineThresholdBinarized,
|
||||
int suppressNonmaxSize)
|
||||
: star( maxSize, responseThreshold, lineThresholdProjected,
|
||||
lineThresholdBinarized, suppressNonmaxSize)
|
||||
{}
|
||||
|
||||
void StarFeatureDetector::read (const FileNode& fn)
|
||||
{
|
||||
int maxSize = fn["maxSize"];
|
||||
int responseThreshold = fn["responseThreshold"];
|
||||
int lineThresholdProjected = fn["lineThresholdProjected"];
|
||||
int lineThresholdBinarized = fn["lineThresholdBinarized"];
|
||||
int suppressNonmaxSize = fn["suppressNonmaxSize"];
|
||||
|
||||
star = StarDetector( maxSize, responseThreshold, lineThresholdProjected,
|
||||
lineThresholdBinarized, suppressNonmaxSize);
|
||||
}
|
||||
|
||||
void StarFeatureDetector::write (FileStorage& fs) const
|
||||
{
|
||||
//fs << "algorithm" << getAlgorithmName ();
|
||||
|
||||
fs << "maxSize" << star.maxSize;
|
||||
fs << "responseThreshold" << star.responseThreshold;
|
||||
fs << "lineThresholdProjected" << star.lineThresholdProjected;
|
||||
fs << "lineThresholdBinarized" << star.lineThresholdBinarized;
|
||||
fs << "suppressNonmaxSize" << star.suppressNonmaxSize;
|
||||
}
|
||||
|
||||
void StarFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
|
||||
{
|
||||
Mat grayImage = image;
|
||||
if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY );
|
||||
|
||||
star(grayImage, keypoints);
|
||||
KeyPointsFilter::runByPixelsMask( keypoints, mask );
|
||||
}
|
||||
|
||||
/*
|
||||
* SiftFeatureDetector
|
||||
*/
|
||||
SiftFeatureDetector::SiftFeatureDetector( const SIFT::DetectorParams &detectorParams,
|
||||
const SIFT::CommonParams &commonParams )
|
||||
: sift(detectorParams.threshold, detectorParams.edgeThreshold,
|
||||
commonParams.nOctaves, commonParams.nOctaveLayers, commonParams.firstOctave, commonParams.angleMode)
|
||||
{
|
||||
}
|
||||
|
||||
SiftFeatureDetector::SiftFeatureDetector( double threshold, double edgeThreshold,
|
||||
int nOctaves, int nOctaveLayers, int firstOctave, int angleMode ) :
|
||||
sift(threshold, edgeThreshold, nOctaves, nOctaveLayers, firstOctave, angleMode)
|
||||
{
|
||||
}
|
||||
|
||||
void SiftFeatureDetector::read( const FileNode& fn )
|
||||
{
|
||||
double threshold = fn["threshold"];
|
||||
double edgeThreshold = fn["edgeThreshold"];
|
||||
int nOctaves = fn["nOctaves"];
|
||||
int nOctaveLayers = fn["nOctaveLayers"];
|
||||
int firstOctave = fn["firstOctave"];
|
||||
int angleMode = fn["angleMode"];
|
||||
|
||||
sift = SIFT(threshold, edgeThreshold, nOctaves, nOctaveLayers, firstOctave, angleMode);
|
||||
}
|
||||
|
||||
void SiftFeatureDetector::write (FileStorage& fs) const
|
||||
{
|
||||
//fs << "algorithm" << getAlgorithmName ();
|
||||
|
||||
SIFT::CommonParams commParams = sift.getCommonParams ();
|
||||
SIFT::DetectorParams detectorParams = sift.getDetectorParams ();
|
||||
fs << "threshold" << detectorParams.threshold;
|
||||
fs << "edgeThreshold" << detectorParams.edgeThreshold;
|
||||
fs << "nOctaves" << commParams.nOctaves;
|
||||
fs << "nOctaveLayers" << commParams.nOctaveLayers;
|
||||
fs << "firstOctave" << commParams.firstOctave;
|
||||
fs << "angleMode" << commParams.angleMode;
|
||||
}
|
||||
|
||||
|
||||
void SiftFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
|
||||
{
|
||||
Mat grayImage = image;
|
||||
if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY );
|
||||
|
||||
sift(grayImage, mask, keypoints);
|
||||
}
|
||||
|
||||
/*
|
||||
* SurfFeatureDetector
|
||||
*/
|
||||
SurfFeatureDetector::SurfFeatureDetector( double hessianThreshold, int octaves, int octaveLayers, bool upright )
|
||||
: surf(hessianThreshold, octaves, octaveLayers, false, upright)
|
||||
{}
|
||||
|
||||
void SurfFeatureDetector::read (const FileNode& fn)
|
||||
{
|
||||
double hessianThreshold = fn["hessianThreshold"];
|
||||
int octaves = fn["octaves"];
|
||||
int octaveLayers = fn["octaveLayers"];
|
||||
bool upright = (int)fn["upright"] != 0;
|
||||
|
||||
surf = SURF( hessianThreshold, octaves, octaveLayers, false, upright );
|
||||
}
|
||||
|
||||
void SurfFeatureDetector::write (FileStorage& fs) const
|
||||
{
|
||||
//fs << "algorithm" << getAlgorithmName ();
|
||||
|
||||
fs << "hessianThreshold" << surf.hessianThreshold;
|
||||
fs << "octaves" << surf.nOctaves;
|
||||
fs << "octaveLayers" << surf.nOctaveLayers;
|
||||
fs << "upright" << surf.upright;
|
||||
}
|
||||
|
||||
void SurfFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
|
||||
{
|
||||
Mat grayImage = image;
|
||||
if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY );
|
||||
|
||||
surf(grayImage, mask, keypoints);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** Default constructor
|
||||
* @param n_features the number of desired features
|
||||
*/
|
||||
OrbFeatureDetector::OrbFeatureDetector(size_t n_features, ORB::CommonParams params)
|
||||
{
|
||||
orb_ = ORB(n_features, params);
|
||||
}
|
||||
|
||||
void OrbFeatureDetector::read(const FileNode& fn)
|
||||
{
|
||||
orb_.read(fn);
|
||||
}
|
||||
|
||||
void OrbFeatureDetector::write(FileStorage& fs) const
|
||||
{
|
||||
orb_.write(fs);
|
||||
}
|
||||
|
||||
void OrbFeatureDetector::detectImpl(const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints, const cv::Mat& mask) const
|
||||
{
|
||||
orb_(image, mask, keypoints);
|
||||
return &gftt_info;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -477,7 +182,7 @@ void OrbFeatureDetector::detectImpl(const cv::Mat& image, std::vector<cv::KeyPoi
|
||||
/*
|
||||
* DenseFeatureDetector
|
||||
*/
|
||||
DenseFeatureDetector::Params::Params( float _initFeatureScale, int _featureScaleLevels,
|
||||
DenseFeatureDetector::DenseFeatureDetector( float _initFeatureScale, int _featureScaleLevels,
|
||||
float _featureScaleMul, int _initXyStep,
|
||||
int _initImgBound, bool _varyXyStepWithScale,
|
||||
bool _varyImgBoundWithScale ) :
|
||||
@@ -486,51 +191,13 @@ DenseFeatureDetector::Params::Params( float _initFeatureScale, int _featureScale
|
||||
varyXyStepWithScale(_varyXyStepWithScale), varyImgBoundWithScale(_varyImgBoundWithScale)
|
||||
{}
|
||||
|
||||
void DenseFeatureDetector::Params::read( const FileNode& fn )
|
||||
{
|
||||
initFeatureScale = fn["initFeatureScale"];
|
||||
featureScaleLevels = fn["featureScaleLevels"];
|
||||
featureScaleMul = fn["featureScaleMul"];
|
||||
|
||||
initXyStep = fn["initXyStep"];
|
||||
initImgBound = fn["initImgBound"];
|
||||
|
||||
varyXyStepWithScale = (int)fn["varyXyStepWithScale"] != 0 ? true : false;
|
||||
varyImgBoundWithScale = (int)fn["varyImgBoundWithScale"] != 0 ? true : false;
|
||||
}
|
||||
|
||||
void DenseFeatureDetector::Params::write( FileStorage& fs ) const
|
||||
{
|
||||
fs << "initFeatureScale" << initFeatureScale;
|
||||
fs << "featureScaleLevels" << featureScaleLevels;
|
||||
fs << "featureScaleMul" << featureScaleMul;
|
||||
|
||||
fs << "initXyStep" << initXyStep;
|
||||
fs << "initImgBound" << initImgBound;
|
||||
|
||||
fs << "varyXyStepWithScale" << (int)varyXyStepWithScale;
|
||||
fs << "varyImgBoundWithScale" << (int)varyImgBoundWithScale;
|
||||
}
|
||||
|
||||
DenseFeatureDetector::DenseFeatureDetector(const DenseFeatureDetector::Params &_params) : params(_params)
|
||||
{}
|
||||
|
||||
void DenseFeatureDetector::read( const FileNode &fn )
|
||||
{
|
||||
params.read(fn);
|
||||
}
|
||||
|
||||
void DenseFeatureDetector::write( FileStorage &fs ) const
|
||||
{
|
||||
params.write(fs);
|
||||
}
|
||||
|
||||
void DenseFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
|
||||
{
|
||||
float curScale = params.initFeatureScale;
|
||||
int curStep = params.initXyStep;
|
||||
int curBound = params.initImgBound;
|
||||
for( int curLevel = 0; curLevel < params.featureScaleLevels; curLevel++ )
|
||||
float curScale = initFeatureScale;
|
||||
int curStep = initXyStep;
|
||||
int curBound = initImgBound;
|
||||
for( int curLevel = 0; curLevel < featureScaleLevels; curLevel++ )
|
||||
{
|
||||
for( int x = curBound; x < image.cols - curBound; x += curStep )
|
||||
{
|
||||
@@ -540,13 +207,35 @@ void DenseFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypo
|
||||
}
|
||||
}
|
||||
|
||||
curScale = curScale * params.featureScaleMul;
|
||||
if( params.varyXyStepWithScale ) curStep = static_cast<int>( curStep * params.featureScaleMul + 0.5f );
|
||||
if( params.varyImgBoundWithScale ) curBound = static_cast<int>( curBound * params.featureScaleMul + 0.5f );
|
||||
curScale = curScale * featureScaleMul;
|
||||
if( varyXyStepWithScale ) curStep = static_cast<int>( curStep * featureScaleMul + 0.5f );
|
||||
if( varyImgBoundWithScale ) curBound = static_cast<int>( curBound * featureScaleMul + 0.5f );
|
||||
}
|
||||
|
||||
KeyPointsFilter::runByPixelsMask( keypoints, mask );
|
||||
}
|
||||
|
||||
|
||||
static Algorithm* createDense() { return new DenseFeatureDetector; }
|
||||
|
||||
AlgorithmInfo* DenseFeatureDetector::info() const
|
||||
{
|
||||
static AlgorithmInfo info_("Feature2D.Dense", createDense);
|
||||
static volatile bool initialized = false;
|
||||
if( !initialized )
|
||||
{
|
||||
info_.addParam(this, "initFeatureScale", initFeatureScale);
|
||||
info_.addParam(this, "featureScaleLevels", featureScaleLevels);
|
||||
info_.addParam(this, "featureScaleMul", featureScaleMul);
|
||||
info_.addParam(this, "initXyStep", initXyStep);
|
||||
info_.addParam(this, "initImgBound", initImgBound);
|
||||
info_.addParam(this, "varyXyStepWithScale", varyXyStepWithScale);
|
||||
info_.addParam(this, "varyImgBoundWithScale", varyImgBoundWithScale);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
return &info_;
|
||||
}
|
||||
|
||||
/*
|
||||
* GridAdaptedFeatureDetector
|
||||
|
@@ -169,8 +169,9 @@ SurfAdjuster::SurfAdjuster( double initial_thresh, double min_thresh, double max
|
||||
|
||||
void SurfAdjuster::detectImpl(const Mat& image, vector<KeyPoint>& keypoints, const cv::Mat& mask) const
|
||||
{
|
||||
SurfFeatureDetector detector_tmp(thresh_);
|
||||
detector_tmp.detect(image, keypoints, mask);
|
||||
Ptr<FeatureDetector> surf = FeatureDetector::create("SURF");
|
||||
surf->set("hessianThreshold", thresh_);
|
||||
surf->detect(image, keypoints, mask);
|
||||
}
|
||||
|
||||
void SurfAdjuster::tooFew(int, int)
|
||||
|
@@ -185,11 +185,10 @@ static int cornerScore(const uchar* ptr, const int pixel[], int threshold)
|
||||
return threshold;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void cv::FAST(const Mat& img, std::vector<KeyPoint>& keypoints, int threshold, bool nonmax_suppression)
|
||||
void FAST(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool nonmax_suppression)
|
||||
{
|
||||
Mat img = _img.getMat();
|
||||
const int K = 8, N = 16 + K + 1;
|
||||
int i, j, k, pixel[N];
|
||||
makeOffsets(pixel, img.step);
|
||||
@@ -280,7 +279,7 @@ void cv::FAST(const Mat& img, std::vector<KeyPoint>& keypoints, int threshold, b
|
||||
{
|
||||
cornerpos[ncorners++] = j+k;
|
||||
if(nonmax_suppression)
|
||||
curr[j+k] = (uchar)cornerScore(ptr+k, pixel, threshold);
|
||||
curr[j+k] = cornerScore(ptr+k, pixel, threshold);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -318,7 +317,7 @@ void cv::FAST(const Mat& img, std::vector<KeyPoint>& keypoints, int threshold, b
|
||||
{
|
||||
cornerpos[ncorners++] = j;
|
||||
if(nonmax_suppression)
|
||||
curr[j] = (uchar)cornerScore(ptr, pixel, threshold);
|
||||
curr[j] = cornerScore(ptr, pixel, threshold);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -340,7 +339,7 @@ void cv::FAST(const Mat& img, std::vector<KeyPoint>& keypoints, int threshold, b
|
||||
{
|
||||
cornerpos[ncorners++] = j;
|
||||
if(nonmax_suppression)
|
||||
curr[j] = (uchar)cornerScore(ptr, pixel, threshold);
|
||||
curr[j] = cornerScore(ptr, pixel, threshold);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -375,3 +374,38 @@ void cv::FAST(const Mat& img, std::vector<KeyPoint>& keypoints, int threshold, b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* FastFeatureDetector
|
||||
*/
|
||||
FastFeatureDetector::FastFeatureDetector( int _threshold, bool _nonmaxSuppression )
|
||||
: threshold(_threshold), nonmaxSuppression(_nonmaxSuppression)
|
||||
{}
|
||||
|
||||
void FastFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
|
||||
{
|
||||
Mat grayImage = image;
|
||||
if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY );
|
||||
FAST( grayImage, keypoints, threshold, nonmaxSuppression );
|
||||
KeyPointsFilter::runByPixelsMask( keypoints, mask );
|
||||
}
|
||||
|
||||
|
||||
static Algorithm* createFAST() { return new FastFeatureDetector; }
|
||||
static AlgorithmInfo fast_info("Feature2D.FAST", createFAST);
|
||||
|
||||
AlgorithmInfo* FastFeatureDetector::info() const
|
||||
{
|
||||
static volatile bool initialized = false;
|
||||
if( !initialized )
|
||||
{
|
||||
fast_info.addParam(this, "threshold", threshold);
|
||||
fast_info.addParam(this, "nonmaxSuppression", nonmaxSuppression);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
return &fast_info;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -165,6 +165,52 @@ float KeyPoint::overlap( const KeyPoint& kp1, const KeyPoint& kp2 )
|
||||
|
||||
return ovrl;
|
||||
}
|
||||
|
||||
|
||||
struct KeypointResponseGreaterThanThreshold
|
||||
{
|
||||
KeypointResponseGreaterThanThreshold(float _value) :
|
||||
value(_value)
|
||||
{
|
||||
}
|
||||
inline bool operator()(const KeyPoint& kpt) const
|
||||
{
|
||||
return kpt.response >= value;
|
||||
}
|
||||
float value;
|
||||
};
|
||||
|
||||
struct KeypointResponseGreater
|
||||
{
|
||||
inline bool operator()(const KeyPoint& kp1, const KeyPoint& kp2) const
|
||||
{
|
||||
return kp1.response > kp2.response;
|
||||
}
|
||||
};
|
||||
|
||||
// takes keypoints and culls them by the response
|
||||
void KeyPointsFilter::retainBest(vector<KeyPoint>& keypoints, int n_points)
|
||||
{
|
||||
//this is only necessary if the keypoints size is greater than the number of desired points.
|
||||
if( n_points > 0 && keypoints.size() > (size_t)n_points )
|
||||
{
|
||||
if (n_points==0)
|
||||
{
|
||||
keypoints.clear();
|
||||
return;
|
||||
}
|
||||
//first use nth element to partition the keypoints into the best and worst.
|
||||
std::nth_element(keypoints.begin(), keypoints.begin() + n_points, keypoints.end(), KeypointResponseGreater());
|
||||
//this is the boundary response, and in the case of FAST may be ambigous
|
||||
float ambiguous_response = keypoints[n_points - 1].response;
|
||||
//use std::partition to grab all of the keypoints with the boundary response.
|
||||
vector<KeyPoint>::const_iterator new_end =
|
||||
std::partition(keypoints.begin() + n_points, keypoints.end(),
|
||||
KeypointResponseGreaterThanThreshold(ambiguous_response));
|
||||
//resize the keypoints, given this new end point. nth_element and partition reordered the points inplace
|
||||
keypoints.resize(new_end - keypoints.begin());
|
||||
}
|
||||
}
|
||||
|
||||
struct RoiPredicate
|
||||
{
|
||||
|
@@ -314,6 +314,144 @@ bool DescriptorMatcher::isMaskedOut( const vector<Mat>& masks, int queryIdx )
|
||||
return !masks.empty() && outCount == masks.size() ;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BFMatcher::BFMatcher( int _normType, bool _crossCheck )
|
||||
{
|
||||
normType = _normType;
|
||||
crossCheck = _crossCheck;
|
||||
}
|
||||
|
||||
Ptr<DescriptorMatcher> BFMatcher::clone( bool emptyTrainData ) const
|
||||
{
|
||||
BFMatcher* matcher = new BFMatcher(normType, crossCheck);
|
||||
if( !emptyTrainData )
|
||||
{
|
||||
matcher->trainDescCollection.resize(trainDescCollection.size());
|
||||
std::transform( trainDescCollection.begin(), trainDescCollection.end(),
|
||||
matcher->trainDescCollection.begin(), clone_op );
|
||||
}
|
||||
return matcher;
|
||||
}
|
||||
|
||||
|
||||
void BFMatcher::knnMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int knn,
|
||||
const vector<Mat>& masks, bool compactResult )
|
||||
{
|
||||
const int IMGIDX_SHIFT = 18;
|
||||
const int IMGIDX_ONE = (1 << IMGIDX_SHIFT);
|
||||
|
||||
if( queryDescriptors.empty() || trainDescCollection.empty() )
|
||||
{
|
||||
matches.clear();
|
||||
return;
|
||||
}
|
||||
CV_Assert( queryDescriptors.type() == trainDescCollection[0].type() );
|
||||
|
||||
matches.reserve(queryDescriptors.rows);
|
||||
|
||||
Mat dist, nidx;
|
||||
|
||||
int iIdx, imgCount = (int)trainDescCollection.size(), update = 0;
|
||||
int dtype = normType == NORM_HAMMING || normType == NORM_HAMMING2 ||
|
||||
(normType == NORM_L1 && queryDescriptors.type() == CV_8U) ? CV_32S : CV_32F;
|
||||
|
||||
CV_Assert( (int64)imgCount*IMGIDX_ONE < INT_MAX );
|
||||
|
||||
for( iIdx = 0; iIdx < imgCount; iIdx++ )
|
||||
{
|
||||
CV_Assert( trainDescCollection[iIdx].rows < IMGIDX_ONE );
|
||||
batchDistance(queryDescriptors, trainDescCollection[iIdx], dist, dtype, nidx,
|
||||
normType, knn, masks.empty() ? Mat() : masks[iIdx], update, crossCheck);
|
||||
update += IMGIDX_ONE;
|
||||
}
|
||||
|
||||
if( dtype == CV_32S )
|
||||
{
|
||||
Mat temp;
|
||||
dist.convertTo(temp, CV_32F);
|
||||
dist = temp;
|
||||
}
|
||||
|
||||
for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ )
|
||||
{
|
||||
const float* distptr = dist.ptr<float>(qIdx);
|
||||
const int* nidxptr = nidx.ptr<int>(qIdx);
|
||||
|
||||
matches.push_back( vector<DMatch>() );
|
||||
vector<DMatch>& mq = matches.back();
|
||||
mq.reserve(knn);
|
||||
|
||||
for( int k = 0; k < knn; k++ )
|
||||
{
|
||||
if( nidxptr[k] < 0 )
|
||||
break;
|
||||
mq.push_back( DMatch(qIdx, nidxptr[k] & (IMGIDX_ONE - 1),
|
||||
nidxptr[k] >> IMGIDX_SHIFT, distptr[k]) );
|
||||
}
|
||||
|
||||
if( mq.empty() && compactResult )
|
||||
matches.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BFMatcher::radiusMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches,
|
||||
float maxDistance, const vector<Mat>& masks, bool compactResult )
|
||||
{
|
||||
if( queryDescriptors.empty() || trainDescCollection.empty() )
|
||||
{
|
||||
matches.clear();
|
||||
return;
|
||||
}
|
||||
CV_Assert( queryDescriptors.type() == trainDescCollection[0].type() );
|
||||
|
||||
matches.resize(queryDescriptors.rows);
|
||||
Mat dist, distf;
|
||||
|
||||
int iIdx, imgCount = (int)trainDescCollection.size();
|
||||
int dtype = normType == NORM_HAMMING ||
|
||||
(normType == NORM_L1 && queryDescriptors.type() == CV_8U) ? CV_32S : CV_32F;
|
||||
|
||||
for( iIdx = 0; iIdx < imgCount; iIdx++ )
|
||||
{
|
||||
batchDistance(queryDescriptors, trainDescCollection[iIdx], dist, dtype, noArray(),
|
||||
normType, 0, masks.empty() ? Mat() : masks[iIdx], 0, false);
|
||||
if( dtype == CV_32S )
|
||||
dist.convertTo(distf, CV_32F);
|
||||
else
|
||||
distf = dist;
|
||||
|
||||
for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ )
|
||||
{
|
||||
const float* distptr = dist.ptr<float>(qIdx);
|
||||
|
||||
vector<DMatch>& mq = matches[qIdx];
|
||||
for( int k = 0; k < dist.cols; k++ )
|
||||
{
|
||||
if( distptr[k] <= maxDistance )
|
||||
mq.push_back( DMatch(qIdx, k, iIdx, distptr[k]) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int qIdx0 = 0;
|
||||
for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ )
|
||||
{
|
||||
if( matches[qIdx].empty() && compactResult )
|
||||
continue;
|
||||
|
||||
if( qIdx0 < qIdx )
|
||||
std::swap(matches[qIdx], matches[qIdx0]);
|
||||
|
||||
std::sort( matches[qIdx0].begin(), matches[qIdx0].end() );
|
||||
qIdx0++;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* Factory function for DescriptorMatcher creating
|
||||
*/
|
||||
@@ -326,31 +464,24 @@ Ptr<DescriptorMatcher> DescriptorMatcher::create( const string& descriptorMatche
|
||||
}
|
||||
else if( !descriptorMatcherType.compare( "BruteForce" ) ) // L2
|
||||
{
|
||||
dm = new BruteForceMatcher<L2<float> >();
|
||||
dm = new BFMatcher(NORM_L2);
|
||||
}
|
||||
else if( !descriptorMatcherType.compare( "BruteForce-SL2" ) ) // Squared L2
|
||||
{
|
||||
dm = new BruteForceMatcher<SL2<float> >();
|
||||
dm = new BFMatcher(NORM_L2SQR);
|
||||
}
|
||||
else if( !descriptorMatcherType.compare( "BruteForce-L1" ) )
|
||||
{
|
||||
dm = new BruteForceMatcher<L1<float> >();
|
||||
dm = new BFMatcher(NORM_L1);
|
||||
}
|
||||
else if( !descriptorMatcherType.compare("BruteForce-Hamming") )
|
||||
else if( !descriptorMatcherType.compare("BruteForce-Hamming") ||
|
||||
!descriptorMatcherType.compare("BruteForce-HammingLUT") )
|
||||
{
|
||||
dm = new BruteForceMatcher<Hamming>();
|
||||
}
|
||||
else if( !descriptorMatcherType.compare("BruteForce-HammingLUT") )
|
||||
{
|
||||
dm = new BruteForceMatcher<Hamming>();
|
||||
dm = new BFMatcher(NORM_HAMMING);
|
||||
}
|
||||
else if( !descriptorMatcherType.compare("BruteForce-Hamming(2)") )
|
||||
{
|
||||
dm = new BruteForceMatcher<HammingMultilevel<2> >();
|
||||
}
|
||||
else if( !descriptorMatcherType.compare("BruteForce-Hamming(4)") )
|
||||
{
|
||||
dm = new BruteForceMatcher<HammingMultilevel<4> >();
|
||||
dm = new BFMatcher(NORM_HAMMING2);
|
||||
}
|
||||
else
|
||||
CV_Error( CV_StsBadArg, "Unknown matcher name" );
|
||||
@@ -358,199 +489,6 @@ Ptr<DescriptorMatcher> DescriptorMatcher::create( const string& descriptorMatche
|
||||
return dm;
|
||||
}
|
||||
|
||||
/*
|
||||
* BruteForce SL2 and L2 specialization
|
||||
*/
|
||||
template<>
|
||||
void BruteForceMatcher<SL2<float> >::knnMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int knn,
|
||||
const vector<Mat>& masks, bool compactResult )
|
||||
{
|
||||
#ifndef HAVE_EIGEN
|
||||
commonKnnMatchImpl( *this, queryDescriptors, matches, knn, masks, compactResult );
|
||||
#else
|
||||
CV_Assert( queryDescriptors.type() == CV_32FC1 || queryDescriptors.empty() );
|
||||
CV_Assert( masks.empty() || masks.size() == trainDescCollection.size() );
|
||||
|
||||
matches.reserve(queryDescriptors.rows);
|
||||
size_t imgCount = trainDescCollection.size();
|
||||
|
||||
Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> e_query_t;
|
||||
vector<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> > e_trainCollection(trainDescCollection.size());
|
||||
vector<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> > e_trainNorms2(trainDescCollection.size());
|
||||
cv2eigen( queryDescriptors.t(), e_query_t);
|
||||
for( size_t i = 0; i < trainDescCollection.size(); i++ )
|
||||
{
|
||||
cv2eigen( trainDescCollection[i], e_trainCollection[i] );
|
||||
e_trainNorms2[i] = e_trainCollection[i].rowwise().squaredNorm() / 2;
|
||||
}
|
||||
|
||||
vector<Eigen::Matrix<float, Eigen::Dynamic, 1> > e_allDists( imgCount ); // distances between one query descriptor and all train descriptors
|
||||
|
||||
for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ )
|
||||
{
|
||||
if( isMaskedOut( masks, qIdx ) )
|
||||
{
|
||||
if( !compactResult ) // push empty vector
|
||||
matches.push_back( vector<DMatch>() );
|
||||
}
|
||||
else
|
||||
{
|
||||
float queryNorm2 = e_query_t.col(qIdx).squaredNorm();
|
||||
// 1. compute distances between i-th query descriptor and all train descriptors
|
||||
for( size_t iIdx = 0; iIdx < imgCount; iIdx++ )
|
||||
{
|
||||
CV_Assert( masks.empty() || masks[iIdx].empty() ||
|
||||
( masks[iIdx].rows == queryDescriptors.rows && masks[iIdx].cols == trainDescCollection[iIdx].rows &&
|
||||
masks[iIdx].type() == CV_8UC1 ) );
|
||||
CV_Assert( trainDescCollection[iIdx].type() == CV_32FC1 || trainDescCollection[iIdx].empty() );
|
||||
CV_Assert( queryDescriptors.cols == trainDescCollection[iIdx].cols );
|
||||
|
||||
e_allDists[iIdx] = e_trainCollection[iIdx] *e_query_t.col(qIdx);
|
||||
e_allDists[iIdx] -= e_trainNorms2[iIdx];
|
||||
|
||||
if( !masks.empty() && !masks[iIdx].empty() )
|
||||
{
|
||||
const uchar* maskPtr = (uchar*)masks[iIdx].ptr(qIdx);
|
||||
for( int c = 0; c < masks[iIdx].cols; c++ )
|
||||
{
|
||||
if( maskPtr[c] == 0 )
|
||||
e_allDists[iIdx](c) = -std::numeric_limits<float>::max();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. choose knn nearest matches for query[i]
|
||||
matches.push_back( vector<DMatch>() );
|
||||
vector<vector<DMatch> >::reverse_iterator curMatches = matches.rbegin();
|
||||
for( int k = 0; k < knn; k++ )
|
||||
{
|
||||
float totalMaxCoeff = -std::numeric_limits<float>::max();
|
||||
int bestTrainIdx = -1, bestImgIdx = -1;
|
||||
for( size_t iIdx = 0; iIdx < imgCount; iIdx++ )
|
||||
{
|
||||
int loc;
|
||||
float curMaxCoeff = e_allDists[iIdx].maxCoeff( &loc );
|
||||
if( curMaxCoeff > totalMaxCoeff )
|
||||
{
|
||||
totalMaxCoeff = curMaxCoeff;
|
||||
bestTrainIdx = loc;
|
||||
bestImgIdx = iIdx;
|
||||
}
|
||||
}
|
||||
if( bestTrainIdx == -1 )
|
||||
break;
|
||||
|
||||
e_allDists[bestImgIdx](bestTrainIdx) = -std::numeric_limits<float>::max();
|
||||
curMatches->push_back( DMatch(qIdx, bestTrainIdx, bestImgIdx, (-2)*totalMaxCoeff + queryNorm2) );
|
||||
}
|
||||
std::sort( curMatches->begin(), curMatches->end() );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template<>
|
||||
void BruteForceMatcher<SL2<float> >::radiusMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, float maxDistance,
|
||||
const vector<Mat>& masks, bool compactResult )
|
||||
{
|
||||
#ifndef HAVE_EIGEN
|
||||
commonRadiusMatchImpl( *this, queryDescriptors, matches, maxDistance, masks, compactResult );
|
||||
#else
|
||||
CV_Assert( queryDescriptors.type() == CV_32FC1 || queryDescriptors.empty() );
|
||||
CV_Assert( masks.empty() || masks.size() == trainDescCollection.size() );
|
||||
|
||||
matches.reserve(queryDescriptors.rows);
|
||||
size_t imgCount = trainDescCollection.size();
|
||||
|
||||
Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> e_query_t;
|
||||
vector<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> > e_trainCollection(trainDescCollection.size());
|
||||
vector<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> > e_trainNorms2(trainDescCollection.size());
|
||||
cv2eigen( queryDescriptors.t(), e_query_t);
|
||||
for( size_t i = 0; i < trainDescCollection.size(); i++ )
|
||||
{
|
||||
cv2eigen( trainDescCollection[i], e_trainCollection[i] );
|
||||
e_trainNorms2[i] = e_trainCollection[i].rowwise().squaredNorm() / 2;
|
||||
}
|
||||
|
||||
vector<Eigen::Matrix<float, Eigen::Dynamic, 1> > e_allDists( imgCount ); // distances between one query descriptor and all train descriptors
|
||||
|
||||
for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ )
|
||||
{
|
||||
if( isMaskedOut( masks, qIdx ) )
|
||||
{
|
||||
if( !compactResult ) // push empty vector
|
||||
matches.push_back( vector<DMatch>() );
|
||||
}
|
||||
else
|
||||
{
|
||||
float queryNorm2 = e_query_t.col(qIdx).squaredNorm();
|
||||
// 1. compute distances between i-th query descriptor and all train descriptors
|
||||
for( size_t iIdx = 0; iIdx < imgCount; iIdx++ )
|
||||
{
|
||||
CV_Assert( masks.empty() || masks[iIdx].empty() ||
|
||||
( masks[iIdx].rows == queryDescriptors.rows && masks[iIdx].cols == trainDescCollection[iIdx].rows &&
|
||||
masks[iIdx].type() == CV_8UC1 ) );
|
||||
CV_Assert( trainDescCollection[iIdx].type() == CV_32FC1 || trainDescCollection[iIdx].empty() );
|
||||
CV_Assert( queryDescriptors.cols == trainDescCollection[iIdx].cols );
|
||||
|
||||
e_allDists[iIdx] = e_trainCollection[iIdx] *e_query_t.col(qIdx);
|
||||
e_allDists[iIdx] -= e_trainNorms2[iIdx];
|
||||
}
|
||||
|
||||
matches.push_back( vector<DMatch>() );
|
||||
vector<vector<DMatch> >::reverse_iterator curMatches = matches.rbegin();
|
||||
for( size_t iIdx = 0; iIdx < imgCount; iIdx++ )
|
||||
{
|
||||
assert( e_allDists[iIdx].rows() == trainDescCollection[iIdx].rows );
|
||||
for( int tIdx = 0; tIdx < e_allDists[iIdx].rows(); tIdx++ )
|
||||
{
|
||||
if( masks.empty() || isPossibleMatch(masks[iIdx], qIdx, tIdx) )
|
||||
{
|
||||
float d = (-2)*e_allDists[iIdx](tIdx) + queryNorm2;
|
||||
if( d < maxDistance )
|
||||
curMatches->push_back( DMatch( qIdx, tIdx, iIdx, d ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
std::sort( curMatches->begin(), curMatches->end() );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void sqrtDistance( vector<vector<DMatch> >& matches )
|
||||
{
|
||||
for( size_t imgIdx = 0; imgIdx < matches.size(); imgIdx++ )
|
||||
{
|
||||
for( size_t matchIdx = 0; matchIdx < matches[imgIdx].size(); matchIdx++ )
|
||||
{
|
||||
matches[imgIdx][matchIdx].distance = std::sqrt( matches[imgIdx][matchIdx].distance );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void BruteForceMatcher<L2<float> >::knnMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int knn,
|
||||
const vector<Mat>& masks, bool compactResult )
|
||||
{
|
||||
BruteForceMatcher<SL2<float> > matcherSL2;
|
||||
matcherSL2.add( getTrainDescriptors() );
|
||||
matcherSL2.knnMatch( queryDescriptors, matches, knn, masks, compactResult );
|
||||
|
||||
sqrtDistance( matches );
|
||||
}
|
||||
|
||||
template<>
|
||||
void BruteForceMatcher<L2<float> >::radiusMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, float maxDistance,
|
||||
const vector<Mat>& masks, bool compactResult )
|
||||
{
|
||||
const float maxDistance2 = maxDistance * maxDistance;
|
||||
BruteForceMatcher<SL2<float> > matcherSL2;
|
||||
matcherSL2.add( getTrainDescriptors() );
|
||||
matcherSL2.radiusMatch( queryDescriptors, matches, maxDistance2, masks, compactResult );
|
||||
|
||||
sqrtDistance( matches );
|
||||
}
|
||||
|
||||
/*
|
||||
* Flann based matcher
|
||||
@@ -1124,16 +1062,9 @@ bool GenericDescriptorMatcher::empty() const
|
||||
Ptr<GenericDescriptorMatcher> GenericDescriptorMatcher::create( const string& genericDescritptorMatcherType,
|
||||
const string ¶msFilename )
|
||||
{
|
||||
Ptr<GenericDescriptorMatcher> descriptorMatcher;
|
||||
if( ! genericDescritptorMatcherType.compare("ONEWAY") )
|
||||
{
|
||||
descriptorMatcher = new OneWayDescriptorMatcher();
|
||||
}
|
||||
else if( ! genericDescritptorMatcherType.compare("FERN") )
|
||||
{
|
||||
descriptorMatcher = new FernDescriptorMatcher();
|
||||
}
|
||||
|
||||
Ptr<GenericDescriptorMatcher> descriptorMatcher =
|
||||
Algorithm::create<GenericDescriptorMatcher>("DescriptorMatcher." + genericDescritptorMatcherType);
|
||||
|
||||
if( !paramsFilename.empty() && !descriptorMatcher.empty() )
|
||||
{
|
||||
FileStorage fs = FileStorage( paramsFilename, FileStorage::READ );
|
||||
@@ -1146,331 +1077,6 @@ Ptr<GenericDescriptorMatcher> GenericDescriptorMatcher::create( const string& ge
|
||||
return descriptorMatcher;
|
||||
}
|
||||
|
||||
/****************************************************************************************\
|
||||
* OneWayDescriptorMatcher *
|
||||
\****************************************************************************************/
|
||||
|
||||
OneWayDescriptorMatcher::Params::Params( int _poseCount, Size _patchSize, string _pcaFilename,
|
||||
string _trainPath, string _trainImagesList,
|
||||
float _minScale, float _maxScale, float _stepScale ) :
|
||||
poseCount(_poseCount), patchSize(_patchSize), pcaFilename(_pcaFilename),
|
||||
trainPath(_trainPath), trainImagesList(_trainImagesList),
|
||||
minScale(_minScale), maxScale(_maxScale), stepScale(_stepScale)
|
||||
{}
|
||||
|
||||
|
||||
OneWayDescriptorMatcher::OneWayDescriptorMatcher( const Params& _params)
|
||||
{
|
||||
initialize(_params);
|
||||
}
|
||||
|
||||
OneWayDescriptorMatcher::~OneWayDescriptorMatcher()
|
||||
{}
|
||||
|
||||
void OneWayDescriptorMatcher::initialize( const Params& _params, const Ptr<OneWayDescriptorBase>& _base )
|
||||
{
|
||||
clear();
|
||||
|
||||
if( _base.empty() )
|
||||
base = _base;
|
||||
|
||||
params = _params;
|
||||
}
|
||||
|
||||
void OneWayDescriptorMatcher::clear()
|
||||
{
|
||||
GenericDescriptorMatcher::clear();
|
||||
|
||||
prevTrainCount = 0;
|
||||
if( !base.empty() )
|
||||
base->clear();
|
||||
}
|
||||
|
||||
void OneWayDescriptorMatcher::train()
|
||||
{
|
||||
if( base.empty() || prevTrainCount < (int)trainPointCollection.keypointCount() )
|
||||
{
|
||||
base = new OneWayDescriptorObject( params.patchSize, params.poseCount, params.pcaFilename,
|
||||
params.trainPath, params.trainImagesList, params.minScale, params.maxScale, params.stepScale );
|
||||
|
||||
base->Allocate( (int)trainPointCollection.keypointCount() );
|
||||
prevTrainCount = (int)trainPointCollection.keypointCount();
|
||||
|
||||
const vector<vector<KeyPoint> >& points = trainPointCollection.getKeypoints();
|
||||
int count = 0;
|
||||
for( size_t i = 0; i < points.size(); i++ )
|
||||
{
|
||||
IplImage _image = trainPointCollection.getImage((int)i);
|
||||
for( size_t j = 0; j < points[i].size(); j++ )
|
||||
base->InitializeDescriptor( count++, &_image, points[i][j], "" );
|
||||
}
|
||||
|
||||
#if defined(_KDTREE)
|
||||
base->ConvertDescriptorsArrayToTree();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool OneWayDescriptorMatcher::isMaskSupported()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void OneWayDescriptorMatcher::knnMatchImpl( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
|
||||
vector<vector<DMatch> >& matches, int knn,
|
||||
const vector<Mat>& /*masks*/, bool /*compactResult*/ )
|
||||
{
|
||||
train();
|
||||
|
||||
CV_Assert( knn == 1 ); // knn > 1 unsupported because of bug in OneWayDescriptorBase for this case
|
||||
|
||||
matches.resize( queryKeypoints.size() );
|
||||
IplImage _qimage = queryImage;
|
||||
for( size_t i = 0; i < queryKeypoints.size(); i++ )
|
||||
{
|
||||
int descIdx = -1, poseIdx = -1;
|
||||
float distance;
|
||||
base->FindDescriptor( &_qimage, queryKeypoints[i].pt, descIdx, poseIdx, distance );
|
||||
matches[i].push_back( DMatch((int)i, descIdx, distance) );
|
||||
}
|
||||
}
|
||||
|
||||
void OneWayDescriptorMatcher::radiusMatchImpl( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
|
||||
vector<vector<DMatch> >& matches, float maxDistance,
|
||||
const vector<Mat>& /*masks*/, bool /*compactResult*/ )
|
||||
{
|
||||
train();
|
||||
|
||||
matches.resize( queryKeypoints.size() );
|
||||
IplImage _qimage = queryImage;
|
||||
for( size_t i = 0; i < queryKeypoints.size(); i++ )
|
||||
{
|
||||
int descIdx = -1, poseIdx = -1;
|
||||
float distance;
|
||||
base->FindDescriptor( &_qimage, queryKeypoints[i].pt, descIdx, poseIdx, distance );
|
||||
if( distance < maxDistance )
|
||||
matches[i].push_back( DMatch((int)i, descIdx, distance) );
|
||||
}
|
||||
}
|
||||
|
||||
void OneWayDescriptorMatcher::read( const FileNode &fn )
|
||||
{
|
||||
base = new OneWayDescriptorObject( params.patchSize, params.poseCount, string (), string (), string (),
|
||||
params.minScale, params.maxScale, params.stepScale );
|
||||
base->Read (fn);
|
||||
}
|
||||
|
||||
void OneWayDescriptorMatcher::write( FileStorage& fs ) const
|
||||
{
|
||||
base->Write (fs);
|
||||
}
|
||||
|
||||
bool OneWayDescriptorMatcher::empty() const
|
||||
{
|
||||
return base.empty() || base->empty();
|
||||
}
|
||||
|
||||
Ptr<GenericDescriptorMatcher> OneWayDescriptorMatcher::clone( bool emptyTrainData ) const
|
||||
{
|
||||
OneWayDescriptorMatcher* matcher = new OneWayDescriptorMatcher( params );
|
||||
|
||||
if( !emptyTrainData )
|
||||
{
|
||||
CV_Error( CV_StsNotImplemented, "deep clone functionality is not implemented, because "
|
||||
"OneWayDescriptorBase has not copy constructor or clone method ");
|
||||
|
||||
//matcher->base;
|
||||
matcher->params = params;
|
||||
matcher->prevTrainCount = prevTrainCount;
|
||||
matcher->trainPointCollection = trainPointCollection;
|
||||
}
|
||||
return matcher;
|
||||
}
|
||||
|
||||
/****************************************************************************************\
|
||||
* FernDescriptorMatcher *
|
||||
\****************************************************************************************/
|
||||
FernDescriptorMatcher::Params::Params( int _nclasses, int _patchSize, int _signatureSize,
|
||||
int _nstructs, int _structSize, int _nviews, int _compressionMethod,
|
||||
const PatchGenerator& _patchGenerator ) :
|
||||
nclasses(_nclasses), patchSize(_patchSize), signatureSize(_signatureSize),
|
||||
nstructs(_nstructs), structSize(_structSize), nviews(_nviews),
|
||||
compressionMethod(_compressionMethod), patchGenerator(_patchGenerator)
|
||||
{}
|
||||
|
||||
FernDescriptorMatcher::Params::Params( const string& _filename )
|
||||
{
|
||||
filename = _filename;
|
||||
}
|
||||
|
||||
FernDescriptorMatcher::FernDescriptorMatcher( const Params& _params )
|
||||
{
|
||||
prevTrainCount = 0;
|
||||
params = _params;
|
||||
if( !params.filename.empty() )
|
||||
{
|
||||
classifier = new FernClassifier;
|
||||
FileStorage fs(params.filename, FileStorage::READ);
|
||||
if( fs.isOpened() )
|
||||
classifier->read( fs.getFirstTopLevelNode() );
|
||||
}
|
||||
}
|
||||
|
||||
FernDescriptorMatcher::~FernDescriptorMatcher()
|
||||
{}
|
||||
|
||||
void FernDescriptorMatcher::clear()
|
||||
{
|
||||
GenericDescriptorMatcher::clear();
|
||||
|
||||
classifier.release();
|
||||
prevTrainCount = 0;
|
||||
}
|
||||
|
||||
void FernDescriptorMatcher::train()
|
||||
{
|
||||
if( classifier.empty() || prevTrainCount < (int)trainPointCollection.keypointCount() )
|
||||
{
|
||||
assert( params.filename.empty() );
|
||||
|
||||
vector<vector<Point2f> > points( trainPointCollection.imageCount() );
|
||||
for( size_t imgIdx = 0; imgIdx < trainPointCollection.imageCount(); imgIdx++ )
|
||||
KeyPoint::convert( trainPointCollection.getKeypoints((int)imgIdx), points[imgIdx] );
|
||||
|
||||
classifier = new FernClassifier( points, trainPointCollection.getImages(), vector<vector<int> >(), 0, // each points is a class
|
||||
params.patchSize, params.signatureSize, params.nstructs, params.structSize,
|
||||
params.nviews, params.compressionMethod, params.patchGenerator );
|
||||
}
|
||||
}
|
||||
|
||||
bool FernDescriptorMatcher::isMaskSupported()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void FernDescriptorMatcher::calcBestProbAndMatchIdx( const Mat& image, const Point2f& pt,
|
||||
float& bestProb, int& bestMatchIdx, vector<float>& signature )
|
||||
{
|
||||
(*classifier)( image, pt, signature);
|
||||
|
||||
bestProb = -FLT_MAX;
|
||||
bestMatchIdx = -1;
|
||||
for( int ci = 0; ci < classifier->getClassCount(); ci++ )
|
||||
{
|
||||
if( signature[ci] > bestProb )
|
||||
{
|
||||
bestProb = signature[ci];
|
||||
bestMatchIdx = ci;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FernDescriptorMatcher::knnMatchImpl( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
|
||||
vector<vector<DMatch> >& matches, int knn,
|
||||
const vector<Mat>& /*masks*/, bool /*compactResult*/ )
|
||||
{
|
||||
train();
|
||||
|
||||
matches.resize( queryKeypoints.size() );
|
||||
vector<float> signature( (size_t)classifier->getClassCount() );
|
||||
|
||||
for( size_t queryIdx = 0; queryIdx < queryKeypoints.size(); queryIdx++ )
|
||||
{
|
||||
(*classifier)( queryImage, queryKeypoints[queryIdx].pt, signature);
|
||||
|
||||
for( int k = 0; k < knn; k++ )
|
||||
{
|
||||
DMatch bestMatch;
|
||||
size_t best_ci = 0;
|
||||
for( size_t ci = 0; ci < signature.size(); ci++ )
|
||||
{
|
||||
if( -signature[ci] < bestMatch.distance )
|
||||
{
|
||||
int imgIdx = -1, trainIdx = -1;
|
||||
trainPointCollection.getLocalIdx( (int)ci , imgIdx, trainIdx );
|
||||
bestMatch = DMatch( (int)queryIdx, trainIdx, imgIdx, -signature[ci] );
|
||||
best_ci = ci;
|
||||
}
|
||||
}
|
||||
|
||||
if( bestMatch.trainIdx == -1 )
|
||||
break;
|
||||
signature[best_ci] = -std::numeric_limits<float>::max();
|
||||
matches[queryIdx].push_back( bestMatch );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FernDescriptorMatcher::radiusMatchImpl( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
|
||||
vector<vector<DMatch> >& matches, float maxDistance,
|
||||
const vector<Mat>& /*masks*/, bool /*compactResult*/ )
|
||||
{
|
||||
train();
|
||||
matches.resize( queryKeypoints.size() );
|
||||
vector<float> signature( (size_t)classifier->getClassCount() );
|
||||
|
||||
for( size_t i = 0; i < queryKeypoints.size(); i++ )
|
||||
{
|
||||
(*classifier)( queryImage, queryKeypoints[i].pt, signature);
|
||||
|
||||
for( int ci = 0; ci < classifier->getClassCount(); ci++ )
|
||||
{
|
||||
if( -signature[ci] < maxDistance )
|
||||
{
|
||||
int imgIdx = -1, trainIdx = -1;
|
||||
trainPointCollection.getLocalIdx( ci , imgIdx, trainIdx );
|
||||
matches[i].push_back( DMatch( (int)i, trainIdx, imgIdx, -signature[ci] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FernDescriptorMatcher::read( const FileNode &fn )
|
||||
{
|
||||
params.nclasses = fn["nclasses"];
|
||||
params.patchSize = fn["patchSize"];
|
||||
params.signatureSize = fn["signatureSize"];
|
||||
params.nstructs = fn["nstructs"];
|
||||
params.structSize = fn["structSize"];
|
||||
params.nviews = fn["nviews"];
|
||||
params.compressionMethod = fn["compressionMethod"];
|
||||
|
||||
//classifier->read(fn);
|
||||
}
|
||||
|
||||
void FernDescriptorMatcher::write( FileStorage& fs ) const
|
||||
{
|
||||
fs << "nclasses" << params.nclasses;
|
||||
fs << "patchSize" << params.patchSize;
|
||||
fs << "signatureSize" << params.signatureSize;
|
||||
fs << "nstructs" << params.nstructs;
|
||||
fs << "structSize" << params.structSize;
|
||||
fs << "nviews" << params.nviews;
|
||||
fs << "compressionMethod" << params.compressionMethod;
|
||||
|
||||
// classifier->write(fs);
|
||||
}
|
||||
|
||||
bool FernDescriptorMatcher::empty() const
|
||||
{
|
||||
return classifier.empty() || classifier->empty();
|
||||
}
|
||||
|
||||
Ptr<GenericDescriptorMatcher> FernDescriptorMatcher::clone( bool emptyTrainData ) const
|
||||
{
|
||||
FernDescriptorMatcher* matcher = new FernDescriptorMatcher( params );
|
||||
if( !emptyTrainData )
|
||||
{
|
||||
CV_Error( CV_StsNotImplemented, "deep clone dunctionality is not implemented, because "
|
||||
"FernClassifier has not copy constructor or clone method ");
|
||||
|
||||
//matcher->classifier;
|
||||
matcher->params = params;
|
||||
matcher->prevTrainCount = prevTrainCount;
|
||||
matcher->trainPointCollection = trainPointCollection;
|
||||
}
|
||||
return matcher;
|
||||
}
|
||||
|
||||
/****************************************************************************************\
|
||||
* VectorDescriptorMatcher *
|
||||
|
@@ -41,7 +41,10 @@
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#define TABLE_SIZE 400
|
||||
namespace cv
|
||||
{
|
||||
|
||||
const int TABLE_SIZE = 400;
|
||||
|
||||
static double chitab3[]={0, 0.0150057, 0.0239478, 0.0315227,
|
||||
0.0383427, 0.0446605, 0.0506115, 0.0562786,
|
||||
@@ -144,36 +147,36 @@ static double chitab3[]={0, 0.0150057, 0.0239478, 0.0315227,
|
||||
3.37455, 3.48653, 3.61862, 3.77982,
|
||||
3.98692, 4.2776, 4.77167, 133.333 };
|
||||
|
||||
typedef struct CvLinkedPoint
|
||||
typedef struct LinkedPoint
|
||||
{
|
||||
struct CvLinkedPoint* prev;
|
||||
struct CvLinkedPoint* next;
|
||||
CvPoint pt;
|
||||
struct LinkedPoint* prev;
|
||||
struct LinkedPoint* next;
|
||||
Point pt;
|
||||
}
|
||||
CvLinkedPoint;
|
||||
LinkedPoint;
|
||||
|
||||
// the history of region grown
|
||||
typedef struct CvMSERGrowHistory
|
||||
typedef struct MSERGrowHistory
|
||||
{
|
||||
struct CvMSERGrowHistory* shortcut;
|
||||
struct CvMSERGrowHistory* child;
|
||||
struct MSERGrowHistory* shortcut;
|
||||
struct MSERGrowHistory* child;
|
||||
int stable; // when it ever stabled before, record the size
|
||||
int val;
|
||||
int size;
|
||||
}
|
||||
CvMSERGrowHistory;
|
||||
MSERGrowHistory;
|
||||
|
||||
typedef struct CvMSERConnectedComp
|
||||
typedef struct MSERConnectedComp
|
||||
{
|
||||
CvLinkedPoint* head;
|
||||
CvLinkedPoint* tail;
|
||||
CvMSERGrowHistory* history;
|
||||
LinkedPoint* head;
|
||||
LinkedPoint* tail;
|
||||
MSERGrowHistory* history;
|
||||
unsigned long grey_level;
|
||||
int size;
|
||||
int dvar; // the derivative of last var
|
||||
float var; // the current variation (most time is the variation of one-step back)
|
||||
}
|
||||
CvMSERConnectedComp;
|
||||
MSERConnectedComp;
|
||||
|
||||
// Linear Time MSER claims by using bsf can get performance gain, here is the implementation
|
||||
// however it seems that will not do any good in real world test
|
||||
@@ -186,24 +189,29 @@ inline void _bitreset(unsigned long * a, unsigned long b)
|
||||
*a &= ~(1<<b);
|
||||
}
|
||||
|
||||
CvMSERParams cvMSERParams( int delta, int minArea, int maxArea, float maxVariation, float minDiversity, int maxEvolution, double areaThreshold, double minMargin, int edgeBlurSize )
|
||||
struct MSERParams
|
||||
{
|
||||
CvMSERParams params;
|
||||
params.delta = delta;
|
||||
params.minArea = minArea;
|
||||
params.maxArea = maxArea;
|
||||
params.maxVariation = maxVariation;
|
||||
params.minDiversity = minDiversity;
|
||||
params.maxEvolution = maxEvolution;
|
||||
params.areaThreshold = areaThreshold;
|
||||
params.minMargin = minMargin;
|
||||
params.edgeBlurSize = edgeBlurSize;
|
||||
return params;
|
||||
}
|
||||
MSERParams( int _delta, int _minArea, int _maxArea, float _maxVariation,
|
||||
float _minDiversity, int _maxEvolution, double _areaThreshold,
|
||||
double _minMargin, int _edgeBlurSize )
|
||||
: delta(_delta), minArea(_minArea), maxArea(_maxArea), maxVariation(_maxVariation),
|
||||
minDiversity(_minDiversity), maxEvolution(_maxEvolution), areaThreshold(_areaThreshold),
|
||||
minMargin(_minMargin), edgeBlurSize(_edgeBlurSize)
|
||||
{}
|
||||
int delta;
|
||||
int minArea;
|
||||
int maxArea;
|
||||
float maxVariation;
|
||||
float minDiversity;
|
||||
int maxEvolution;
|
||||
double areaThreshold;
|
||||
double minMargin;
|
||||
int edgeBlurSize;
|
||||
};
|
||||
|
||||
// clear the connected component in stack
|
||||
CV_INLINE static void
|
||||
icvInitMSERComp( CvMSERConnectedComp* comp )
|
||||
static void
|
||||
initMSERComp( MSERConnectedComp* comp )
|
||||
{
|
||||
comp->size = 0;
|
||||
comp->var = 0;
|
||||
@@ -212,9 +220,8 @@ icvInitMSERComp( CvMSERConnectedComp* comp )
|
||||
}
|
||||
|
||||
// add history of size to a connected component
|
||||
CV_INLINE static void
|
||||
icvMSERNewHistory( CvMSERConnectedComp* comp,
|
||||
CvMSERGrowHistory* history )
|
||||
static void
|
||||
MSERNewHistory( MSERConnectedComp* comp, MSERGrowHistory* history )
|
||||
{
|
||||
history->child = history;
|
||||
if ( NULL == comp->history )
|
||||
@@ -232,14 +239,14 @@ icvMSERNewHistory( CvMSERConnectedComp* comp,
|
||||
}
|
||||
|
||||
// merging two connected component
|
||||
CV_INLINE static void
|
||||
icvMSERMergeComp( CvMSERConnectedComp* comp1,
|
||||
CvMSERConnectedComp* comp2,
|
||||
CvMSERConnectedComp* comp,
|
||||
CvMSERGrowHistory* history )
|
||||
static void
|
||||
MSERMergeComp( MSERConnectedComp* comp1,
|
||||
MSERConnectedComp* comp2,
|
||||
MSERConnectedComp* comp,
|
||||
MSERGrowHistory* history )
|
||||
{
|
||||
CvLinkedPoint* head;
|
||||
CvLinkedPoint* tail;
|
||||
LinkedPoint* head;
|
||||
LinkedPoint* tail;
|
||||
comp->grey_level = comp2->grey_level;
|
||||
history->child = history;
|
||||
// select the winner by size
|
||||
@@ -301,18 +308,17 @@ icvMSERMergeComp( CvMSERConnectedComp* comp1,
|
||||
comp->size = comp1->size + comp2->size;
|
||||
}
|
||||
|
||||
CV_INLINE static float
|
||||
icvMSERVariationCalc( CvMSERConnectedComp* comp,
|
||||
int delta )
|
||||
static float
|
||||
MSERVariationCalc( MSERConnectedComp* comp, int delta )
|
||||
{
|
||||
CvMSERGrowHistory* history = comp->history;
|
||||
MSERGrowHistory* history = comp->history;
|
||||
int val = comp->grey_level;
|
||||
if ( NULL != history )
|
||||
{
|
||||
CvMSERGrowHistory* shortcut = history->shortcut;
|
||||
MSERGrowHistory* shortcut = history->shortcut;
|
||||
while ( shortcut != shortcut->shortcut && shortcut->val + delta > val )
|
||||
shortcut = shortcut->shortcut;
|
||||
CvMSERGrowHistory* child = shortcut->child;
|
||||
MSERGrowHistory* child = shortcut->child;
|
||||
while ( child != child->child && child->val + delta <= val )
|
||||
{
|
||||
shortcut = child;
|
||||
@@ -328,15 +334,13 @@ icvMSERVariationCalc( CvMSERConnectedComp* comp,
|
||||
return 1.;
|
||||
}
|
||||
|
||||
CV_INLINE static bool
|
||||
icvMSERStableCheck( CvMSERConnectedComp* comp,
|
||||
CvMSERParams params )
|
||||
static bool MSERStableCheck( MSERConnectedComp* comp, MSERParams params )
|
||||
{
|
||||
// tricky part: it actually check the stablity of one-step back
|
||||
if ( comp->history == NULL || comp->history->size <= params.minArea || comp->history->size >= params.maxArea )
|
||||
return 0;
|
||||
float div = (float)(comp->history->size-comp->history->stable)/(float)comp->history->size;
|
||||
float var = icvMSERVariationCalc( comp, params.delta );
|
||||
float var = MSERVariationCalc( comp, params.delta );
|
||||
int dvar = ( comp->var < var || (unsigned long)(comp->history->val + 1) < comp->grey_level );
|
||||
int stable = ( dvar && !comp->dvar && comp->var < params.maxVariation && div > params.minDiversity );
|
||||
comp->var = var;
|
||||
@@ -347,9 +351,7 @@ icvMSERStableCheck( CvMSERConnectedComp* comp,
|
||||
}
|
||||
|
||||
// add a pixel to the pixel list
|
||||
CV_INLINE static void
|
||||
icvAccumulateMSERComp( CvMSERConnectedComp* comp,
|
||||
CvLinkedPoint* point )
|
||||
static void accumulateMSERComp( MSERConnectedComp* comp, LinkedPoint* point )
|
||||
{
|
||||
if ( comp->size > 0 )
|
||||
{
|
||||
@@ -366,14 +368,12 @@ icvAccumulateMSERComp( CvMSERConnectedComp* comp,
|
||||
}
|
||||
|
||||
// convert the point set to CvSeq
|
||||
CV_INLINE static CvContour*
|
||||
icvMSERToContour( CvMSERConnectedComp* comp,
|
||||
CvMemStorage* storage )
|
||||
static CvContour* MSERToContour( MSERConnectedComp* comp, CvMemStorage* storage )
|
||||
{
|
||||
CvSeq* _contour = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvContour), sizeof(CvPoint), storage );
|
||||
CvContour* contour = (CvContour*)_contour;
|
||||
cvSeqPushMulti( _contour, 0, comp->history->size );
|
||||
CvLinkedPoint* lpt = comp->head;
|
||||
LinkedPoint* lpt = comp->head;
|
||||
for ( int i = 0; i < comp->history->size; i++ )
|
||||
{
|
||||
CvPoint* pt = CV_GET_SEQ_ELEM( CvPoint, _contour, i );
|
||||
@@ -391,8 +391,7 @@ icvMSERToContour( CvMSERConnectedComp* comp,
|
||||
// 17~19 bits is the direction
|
||||
// 8~11 bits is the bucket it falls to (for BitScanForward)
|
||||
// 0~8 bits is the color
|
||||
static int*
|
||||
icvPreprocessMSER_8UC1( CvMat* img,
|
||||
static int* preprocessMSER_8UC1( CvMat* img,
|
||||
int*** heap_cur,
|
||||
CvMat* src,
|
||||
CvMat* mask )
|
||||
@@ -476,17 +475,16 @@ icvPreprocessMSER_8UC1( CvMat* img,
|
||||
return startptr;
|
||||
}
|
||||
|
||||
static void
|
||||
icvExtractMSER_8UC1_Pass( int* ioptr,
|
||||
static void extractMSER_8UC1_Pass( int* ioptr,
|
||||
int* imgptr,
|
||||
int*** heap_cur,
|
||||
CvLinkedPoint* ptsptr,
|
||||
CvMSERGrowHistory* histptr,
|
||||
CvMSERConnectedComp* comptr,
|
||||
LinkedPoint* ptsptr,
|
||||
MSERGrowHistory* histptr,
|
||||
MSERConnectedComp* comptr,
|
||||
int step,
|
||||
int stepmask,
|
||||
int stepgap,
|
||||
CvMSERParams params,
|
||||
MSERParams params,
|
||||
int color,
|
||||
CvSeq* contours,
|
||||
CvMemStorage* storage )
|
||||
@@ -494,7 +492,7 @@ icvExtractMSER_8UC1_Pass( int* ioptr,
|
||||
comptr->grey_level = 256;
|
||||
comptr++;
|
||||
comptr->grey_level = (*imgptr)&0xff;
|
||||
icvInitMSERComp( comptr );
|
||||
initMSERComp( comptr );
|
||||
*imgptr |= 0x80000000;
|
||||
heap_cur += (*imgptr)&0xff;
|
||||
int dir[] = { 1, step, -1, -step };
|
||||
@@ -527,7 +525,7 @@ icvExtractMSER_8UC1_Pass( int* ioptr,
|
||||
#endif
|
||||
imgptr = imgptr_nbr;
|
||||
comptr++;
|
||||
icvInitMSERComp( comptr );
|
||||
initMSERComp( comptr );
|
||||
comptr->grey_level = (*imgptr)&0xff;
|
||||
continue;
|
||||
} else {
|
||||
@@ -544,7 +542,7 @@ icvExtractMSER_8UC1_Pass( int* ioptr,
|
||||
int i = (int)(imgptr-ioptr);
|
||||
ptsptr->pt = cvPoint( i&stepmask, i>>stepgap );
|
||||
// get the current location
|
||||
icvAccumulateMSERComp( comptr, ptsptr );
|
||||
accumulateMSERComp( comptr, ptsptr );
|
||||
ptsptr++;
|
||||
// get the next pixel from boundary heap
|
||||
if ( **heap_cur )
|
||||
@@ -595,13 +593,13 @@ icvExtractMSER_8UC1_Pass( int* ioptr,
|
||||
if ( pixel_val < comptr[-1].grey_level )
|
||||
{
|
||||
// check the stablity and push a new history, increase the grey level
|
||||
if ( icvMSERStableCheck( comptr, params ) )
|
||||
if ( MSERStableCheck( comptr, params ) )
|
||||
{
|
||||
CvContour* contour = icvMSERToContour( comptr, storage );
|
||||
CvContour* contour = MSERToContour( comptr, storage );
|
||||
contour->color = color;
|
||||
cvSeqPush( contours, &contour );
|
||||
}
|
||||
icvMSERNewHistory( comptr, histptr );
|
||||
MSERNewHistory( comptr, histptr );
|
||||
comptr[0].grey_level = pixel_val;
|
||||
histptr++;
|
||||
} else {
|
||||
@@ -609,20 +607,20 @@ icvExtractMSER_8UC1_Pass( int* ioptr,
|
||||
for ( ; ; )
|
||||
{
|
||||
comptr--;
|
||||
icvMSERMergeComp( comptr+1, comptr, comptr, histptr );
|
||||
MSERMergeComp( comptr+1, comptr, comptr, histptr );
|
||||
histptr++;
|
||||
if ( pixel_val <= comptr[0].grey_level )
|
||||
break;
|
||||
if ( pixel_val < comptr[-1].grey_level )
|
||||
{
|
||||
// check the stablity here otherwise it wouldn't be an ER
|
||||
if ( icvMSERStableCheck( comptr, params ) )
|
||||
if ( MSERStableCheck( comptr, params ) )
|
||||
{
|
||||
CvContour* contour = icvMSERToContour( comptr, storage );
|
||||
CvContour* contour = MSERToContour( comptr, storage );
|
||||
contour->color = color;
|
||||
cvSeqPush( contours, &contour );
|
||||
}
|
||||
icvMSERNewHistory( comptr, histptr );
|
||||
MSERNewHistory( comptr, histptr );
|
||||
comptr[0].grey_level = pixel_val;
|
||||
histptr++;
|
||||
break;
|
||||
@@ -635,12 +633,11 @@ icvExtractMSER_8UC1_Pass( int* ioptr,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
icvExtractMSER_8UC1( CvMat* src,
|
||||
static void extractMSER_8UC1( CvMat* src,
|
||||
CvMat* mask,
|
||||
CvSeq* contours,
|
||||
CvMemStorage* storage,
|
||||
CvMSERParams params )
|
||||
MSERParams params )
|
||||
{
|
||||
int step = 8;
|
||||
int stepgap = 3;
|
||||
@@ -662,16 +659,16 @@ icvExtractMSER_8UC1( CvMat* src,
|
||||
heap_start[0] = heap;
|
||||
|
||||
// pre-allocate linked point and grow history
|
||||
CvLinkedPoint* pts = (CvLinkedPoint*)cvAlloc( src->rows*src->cols*sizeof(pts[0]) );
|
||||
CvMSERGrowHistory* history = (CvMSERGrowHistory*)cvAlloc( src->rows*src->cols*sizeof(history[0]) );
|
||||
CvMSERConnectedComp comp[257];
|
||||
LinkedPoint* pts = (LinkedPoint*)cvAlloc( src->rows*src->cols*sizeof(pts[0]) );
|
||||
MSERGrowHistory* history = (MSERGrowHistory*)cvAlloc( src->rows*src->cols*sizeof(history[0]) );
|
||||
MSERConnectedComp comp[257];
|
||||
|
||||
// darker to brighter (MSER-)
|
||||
imgptr = icvPreprocessMSER_8UC1( img, heap_start, src, mask );
|
||||
icvExtractMSER_8UC1_Pass( ioptr, imgptr, heap_start, pts, history, comp, step, stepmask, stepgap, params, -1, contours, storage );
|
||||
imgptr = preprocessMSER_8UC1( img, heap_start, src, mask );
|
||||
extractMSER_8UC1_Pass( ioptr, imgptr, heap_start, pts, history, comp, step, stepmask, stepgap, params, -1, contours, storage );
|
||||
// brighter to darker (MSER+)
|
||||
imgptr = icvPreprocessMSER_8UC1( img, heap_start, src, mask );
|
||||
icvExtractMSER_8UC1_Pass( ioptr, imgptr, heap_start, pts, history, comp, step, stepmask, stepgap, params, 1, contours, storage );
|
||||
imgptr = preprocessMSER_8UC1( img, heap_start, src, mask );
|
||||
extractMSER_8UC1_Pass( ioptr, imgptr, heap_start, pts, history, comp, step, stepmask, stepgap, params, 1, contours, storage );
|
||||
|
||||
// clean up
|
||||
cvFree( &history );
|
||||
@@ -680,26 +677,26 @@ icvExtractMSER_8UC1( CvMat* src,
|
||||
cvReleaseMat( &img );
|
||||
}
|
||||
|
||||
struct CvMSCRNode;
|
||||
struct MSCRNode;
|
||||
|
||||
typedef struct CvTempMSCR
|
||||
struct TempMSCR
|
||||
{
|
||||
CvMSCRNode* head;
|
||||
CvMSCRNode* tail;
|
||||
MSCRNode* head;
|
||||
MSCRNode* tail;
|
||||
double m; // the margin used to prune area later
|
||||
int size;
|
||||
} CvTempMSCR;
|
||||
};
|
||||
|
||||
typedef struct CvMSCRNode
|
||||
struct MSCRNode
|
||||
{
|
||||
CvMSCRNode* shortcut;
|
||||
MSCRNode* shortcut;
|
||||
// to make the finding of root less painful
|
||||
CvMSCRNode* prev;
|
||||
CvMSCRNode* next;
|
||||
MSCRNode* prev;
|
||||
MSCRNode* next;
|
||||
// a point double-linked list
|
||||
CvTempMSCR* tmsr;
|
||||
TempMSCR* tmsr;
|
||||
// the temporary msr (set to NULL at every re-initialise)
|
||||
CvTempMSCR* gmsr;
|
||||
TempMSCR* gmsr;
|
||||
// the global msr (once set, never to NULL)
|
||||
int index;
|
||||
// the index of the node, at this point, it should be x at the first 16-bits, and y at the last 16-bits.
|
||||
@@ -708,25 +705,23 @@ typedef struct CvMSCRNode
|
||||
int size, sizei;
|
||||
double dt, di;
|
||||
double s;
|
||||
} CvMSCRNode;
|
||||
};
|
||||
|
||||
typedef struct CvMSCREdge
|
||||
struct MSCREdge
|
||||
{
|
||||
double chi;
|
||||
CvMSCRNode* left;
|
||||
CvMSCRNode* right;
|
||||
} CvMSCREdge;
|
||||
MSCRNode* left;
|
||||
MSCRNode* right;
|
||||
};
|
||||
|
||||
CV_INLINE static double
|
||||
icvChisquaredDistance( uchar* x, uchar* y )
|
||||
static double ChiSquaredDistance( uchar* x, uchar* y )
|
||||
{
|
||||
return (double)((x[0]-y[0])*(x[0]-y[0]))/(double)(x[0]+y[0]+1e-10)+
|
||||
(double)((x[1]-y[1])*(x[1]-y[1]))/(double)(x[1]+y[1]+1e-10)+
|
||||
(double)((x[2]-y[2])*(x[2]-y[2]))/(double)(x[2]+y[2]+1e-10);
|
||||
}
|
||||
|
||||
CV_INLINE static void
|
||||
icvInitMSCRNode( CvMSCRNode* node )
|
||||
static void initMSCRNode( MSCRNode* node )
|
||||
{
|
||||
node->gmsr = node->tmsr = NULL;
|
||||
node->reinit = 0xffff;
|
||||
@@ -736,9 +731,8 @@ icvInitMSCRNode( CvMSCRNode* node )
|
||||
}
|
||||
|
||||
// the preprocess to get the edge list with proper gaussian blur
|
||||
static int
|
||||
icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
CvMSCREdge* edge,
|
||||
static int preprocessMSER_8UC3( MSCRNode* node,
|
||||
MSCREdge* edge,
|
||||
double* total,
|
||||
CvMat* src,
|
||||
CvMat* mask,
|
||||
@@ -755,7 +749,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
{
|
||||
for ( int j = 0; j < src->cols-1; j++ )
|
||||
{
|
||||
*dxptr = icvChisquaredDistance( srcptr, lastptr );
|
||||
*dxptr = ChiSquaredDistance( srcptr, lastptr );
|
||||
dxptr++;
|
||||
srcptr += 3;
|
||||
lastptr += 3;
|
||||
@@ -770,7 +764,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
{
|
||||
for ( int j = 0; j < src->cols; j++ )
|
||||
{
|
||||
*dyptr = icvChisquaredDistance( srcptr, lastptr );
|
||||
*dyptr = ChiSquaredDistance( srcptr, lastptr );
|
||||
dyptr++;
|
||||
srcptr += 3;
|
||||
lastptr += 3;
|
||||
@@ -793,8 +787,8 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
Ne = 0;
|
||||
int maskcpt = mask->step-mask->cols+1;
|
||||
uchar* maskptr = mask->data.ptr;
|
||||
CvMSCRNode* nodeptr = node;
|
||||
icvInitMSCRNode( nodeptr );
|
||||
MSCRNode* nodeptr = node;
|
||||
initMSCRNode( nodeptr );
|
||||
nodeptr->index = 0;
|
||||
*total += edge->chi = *dxptr;
|
||||
if ( maskptr[0] && maskptr[1] )
|
||||
@@ -809,7 +803,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
maskptr++;
|
||||
for ( int i = 1; i < src->cols-1; i++ )
|
||||
{
|
||||
icvInitMSCRNode( nodeptr );
|
||||
initMSCRNode( nodeptr );
|
||||
nodeptr->index = i;
|
||||
if ( maskptr[0] && maskptr[1] )
|
||||
{
|
||||
@@ -823,13 +817,13 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
nodeptr++;
|
||||
maskptr++;
|
||||
}
|
||||
icvInitMSCRNode( nodeptr );
|
||||
initMSCRNode( nodeptr );
|
||||
nodeptr->index = src->cols-1;
|
||||
nodeptr++;
|
||||
maskptr += maskcpt;
|
||||
for ( int i = 1; i < src->rows-1; i++ )
|
||||
{
|
||||
icvInitMSCRNode( nodeptr );
|
||||
initMSCRNode( nodeptr );
|
||||
nodeptr->index = i<<16;
|
||||
if ( maskptr[0] )
|
||||
{
|
||||
@@ -856,7 +850,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
maskptr++;
|
||||
for ( int j = 1; j < src->cols-1; j++ )
|
||||
{
|
||||
icvInitMSCRNode( nodeptr );
|
||||
initMSCRNode( nodeptr );
|
||||
nodeptr->index = (i<<16)|j;
|
||||
if ( maskptr[0] )
|
||||
{
|
||||
@@ -882,7 +876,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
nodeptr++;
|
||||
maskptr++;
|
||||
}
|
||||
icvInitMSCRNode( nodeptr );
|
||||
initMSCRNode( nodeptr );
|
||||
nodeptr->index = (i<<16)|(src->cols-1);
|
||||
if ( maskptr[0] && maskptr[-mask->step] )
|
||||
{
|
||||
@@ -896,7 +890,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
nodeptr++;
|
||||
maskptr += maskcpt;
|
||||
}
|
||||
icvInitMSCRNode( nodeptr );
|
||||
initMSCRNode( nodeptr );
|
||||
nodeptr->index = (src->rows-1)<<16;
|
||||
if ( maskptr[0] )
|
||||
{
|
||||
@@ -923,7 +917,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
maskptr++;
|
||||
for ( int i = 1; i < src->cols-1; i++ )
|
||||
{
|
||||
icvInitMSCRNode( nodeptr );
|
||||
initMSCRNode( nodeptr );
|
||||
nodeptr->index = ((src->rows-1)<<16)|i;
|
||||
if ( maskptr[0] )
|
||||
{
|
||||
@@ -949,7 +943,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
nodeptr++;
|
||||
maskptr++;
|
||||
}
|
||||
icvInitMSCRNode( nodeptr );
|
||||
initMSCRNode( nodeptr );
|
||||
nodeptr->index = ((src->rows-1)<<16)|(src->cols-1);
|
||||
if ( maskptr[0] && maskptr[-mask->step] )
|
||||
{
|
||||
@@ -959,8 +953,8 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
Ne++;
|
||||
}
|
||||
} else {
|
||||
CvMSCRNode* nodeptr = node;
|
||||
icvInitMSCRNode( nodeptr );
|
||||
MSCRNode* nodeptr = node;
|
||||
initMSCRNode( nodeptr );
|
||||
nodeptr->index = 0;
|
||||
*total += edge->chi = *dxptr;
|
||||
dxptr++;
|
||||
@@ -970,7 +964,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
nodeptr++;
|
||||
for ( int i = 1; i < src->cols-1; i++ )
|
||||
{
|
||||
icvInitMSCRNode( nodeptr );
|
||||
initMSCRNode( nodeptr );
|
||||
nodeptr->index = i;
|
||||
*total += edge->chi = *dxptr;
|
||||
dxptr++;
|
||||
@@ -979,12 +973,12 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
edge++;
|
||||
nodeptr++;
|
||||
}
|
||||
icvInitMSCRNode( nodeptr );
|
||||
initMSCRNode( nodeptr );
|
||||
nodeptr->index = src->cols-1;
|
||||
nodeptr++;
|
||||
for ( int i = 1; i < src->rows-1; i++ )
|
||||
{
|
||||
icvInitMSCRNode( nodeptr );
|
||||
initMSCRNode( nodeptr );
|
||||
nodeptr->index = i<<16;
|
||||
*total += edge->chi = *dyptr;
|
||||
dyptr++;
|
||||
@@ -999,7 +993,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
nodeptr++;
|
||||
for ( int j = 1; j < src->cols-1; j++ )
|
||||
{
|
||||
icvInitMSCRNode( nodeptr );
|
||||
initMSCRNode( nodeptr );
|
||||
nodeptr->index = (i<<16)|j;
|
||||
*total += edge->chi = *dyptr;
|
||||
dyptr++;
|
||||
@@ -1013,7 +1007,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
edge++;
|
||||
nodeptr++;
|
||||
}
|
||||
icvInitMSCRNode( nodeptr );
|
||||
initMSCRNode( nodeptr );
|
||||
nodeptr->index = (i<<16)|(src->cols-1);
|
||||
*total += edge->chi = *dyptr;
|
||||
dyptr++;
|
||||
@@ -1022,7 +1016,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
edge++;
|
||||
nodeptr++;
|
||||
}
|
||||
icvInitMSCRNode( nodeptr );
|
||||
initMSCRNode( nodeptr );
|
||||
nodeptr->index = (src->rows-1)<<16;
|
||||
*total += edge->chi = *dxptr;
|
||||
dxptr++;
|
||||
@@ -1037,7 +1031,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
nodeptr++;
|
||||
for ( int i = 1; i < src->cols-1; i++ )
|
||||
{
|
||||
icvInitMSCRNode( nodeptr );
|
||||
initMSCRNode( nodeptr );
|
||||
nodeptr->index = ((src->rows-1)<<16)|i;
|
||||
*total += edge->chi = *dxptr;
|
||||
dxptr++;
|
||||
@@ -1051,7 +1045,7 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
edge++;
|
||||
nodeptr++;
|
||||
}
|
||||
icvInitMSCRNode( nodeptr );
|
||||
initMSCRNode( nodeptr );
|
||||
nodeptr->index = ((src->rows-1)<<16)|(src->cols-1);
|
||||
*total += edge->chi = *dyptr;
|
||||
edge->left = nodeptr-src->cols;
|
||||
@@ -1063,14 +1057,13 @@ icvPreprocessMSER_8UC3( CvMSCRNode* node,
|
||||
#define cmp_mscr_edge(edge1, edge2) \
|
||||
((edge1).chi < (edge2).chi)
|
||||
|
||||
static CV_IMPLEMENT_QSORT( icvQuickSortMSCREdge, CvMSCREdge, cmp_mscr_edge )
|
||||
static CV_IMPLEMENT_QSORT( QuickSortMSCREdge, MSCREdge, cmp_mscr_edge )
|
||||
|
||||
// to find the root of one region
|
||||
CV_INLINE static CvMSCRNode*
|
||||
icvFindMSCR( CvMSCRNode* x )
|
||||
static MSCRNode* findMSCR( MSCRNode* x )
|
||||
{
|
||||
CvMSCRNode* prev = x;
|
||||
CvMSCRNode* next;
|
||||
MSCRNode* prev = x;
|
||||
MSCRNode* next;
|
||||
for ( ; ; )
|
||||
{
|
||||
next = x->shortcut;
|
||||
@@ -1079,7 +1072,7 @@ icvFindMSCR( CvMSCRNode* x )
|
||||
prev= x;
|
||||
x = next;
|
||||
}
|
||||
CvMSCRNode* root = x;
|
||||
MSCRNode* root = x;
|
||||
for ( ; ; )
|
||||
{
|
||||
prev = x->shortcut;
|
||||
@@ -1093,9 +1086,7 @@ icvFindMSCR( CvMSCRNode* x )
|
||||
// the stable mscr should be:
|
||||
// bigger than minArea and smaller than maxArea
|
||||
// differ from its ancestor more than minDiversity
|
||||
CV_INLINE static bool
|
||||
icvMSCRStableCheck( CvMSCRNode* x,
|
||||
CvMSERParams params )
|
||||
static bool MSCRStableCheck( MSCRNode* x, MSERParams params )
|
||||
{
|
||||
if ( x->size <= params.minArea || x->size >= params.maxArea )
|
||||
return 0;
|
||||
@@ -1106,25 +1097,25 @@ icvMSCRStableCheck( CvMSCRNode* x,
|
||||
}
|
||||
|
||||
static void
|
||||
icvExtractMSER_8UC3( CvMat* src,
|
||||
extractMSER_8UC3( CvMat* src,
|
||||
CvMat* mask,
|
||||
CvSeq* contours,
|
||||
CvMemStorage* storage,
|
||||
CvMSERParams params )
|
||||
MSERParams params )
|
||||
{
|
||||
CvMSCRNode* map = (CvMSCRNode*)cvAlloc( src->cols*src->rows*sizeof(map[0]) );
|
||||
MSCRNode* map = (MSCRNode*)cvAlloc( src->cols*src->rows*sizeof(map[0]) );
|
||||
int Ne = src->cols*src->rows*2-src->cols-src->rows;
|
||||
CvMSCREdge* edge = (CvMSCREdge*)cvAlloc( Ne*sizeof(edge[0]) );
|
||||
CvTempMSCR* mscr = (CvTempMSCR*)cvAlloc( src->cols*src->rows*sizeof(mscr[0]) );
|
||||
MSCREdge* edge = (MSCREdge*)cvAlloc( Ne*sizeof(edge[0]) );
|
||||
TempMSCR* mscr = (TempMSCR*)cvAlloc( src->cols*src->rows*sizeof(mscr[0]) );
|
||||
double emean = 0;
|
||||
CvMat* dx = cvCreateMat( src->rows, src->cols-1, CV_64FC1 );
|
||||
CvMat* dy = cvCreateMat( src->rows-1, src->cols, CV_64FC1 );
|
||||
Ne = icvPreprocessMSER_8UC3( map, edge, &emean, src, mask, dx, dy, Ne, params.edgeBlurSize );
|
||||
Ne = preprocessMSER_8UC3( map, edge, &emean, src, mask, dx, dy, Ne, params.edgeBlurSize );
|
||||
emean = emean / (double)Ne;
|
||||
icvQuickSortMSCREdge( edge, Ne, 0 );
|
||||
CvMSCREdge* edge_ub = edge+Ne;
|
||||
CvMSCREdge* edgeptr = edge;
|
||||
CvTempMSCR* mscrptr = mscr;
|
||||
QuickSortMSCREdge( edge, Ne, 0 );
|
||||
MSCREdge* edge_ub = edge+Ne;
|
||||
MSCREdge* edgeptr = edge;
|
||||
TempMSCR* mscrptr = mscr;
|
||||
// the evolution process
|
||||
for ( int i = 0; i < params.maxEvolution; i++ )
|
||||
{
|
||||
@@ -1135,21 +1126,21 @@ icvExtractMSER_8UC3( CvMat* src,
|
||||
// to process all the edges in the list that chi < thres
|
||||
while ( edgeptr < edge_ub && edgeptr->chi < thres )
|
||||
{
|
||||
CvMSCRNode* lr = icvFindMSCR( edgeptr->left );
|
||||
CvMSCRNode* rr = icvFindMSCR( edgeptr->right );
|
||||
MSCRNode* lr = findMSCR( edgeptr->left );
|
||||
MSCRNode* rr = findMSCR( edgeptr->right );
|
||||
// get the region root (who is responsible)
|
||||
if ( lr != rr )
|
||||
{
|
||||
// rank idea take from: N-tree Disjoint-Set Forests for Maximally Stable Extremal Regions
|
||||
if ( rr->rank > lr->rank )
|
||||
{
|
||||
CvMSCRNode* tmp;
|
||||
MSCRNode* tmp;
|
||||
CV_SWAP( lr, rr, tmp );
|
||||
} else if ( lr->rank == rr->rank ) {
|
||||
// at the same rank, we will compare the size
|
||||
if ( lr->size > rr->size )
|
||||
{
|
||||
CvMSCRNode* tmp;
|
||||
MSCRNode* tmp;
|
||||
CV_SWAP( lr, rr, tmp );
|
||||
}
|
||||
lr->rank++;
|
||||
@@ -1181,7 +1172,7 @@ icvExtractMSER_8UC3( CvMat* src,
|
||||
if ( s < lr->s )
|
||||
{
|
||||
// skip the first one and check stablity
|
||||
if ( i > lr->reinit+1 && icvMSCRStableCheck( lr, params ) )
|
||||
if ( i > lr->reinit+1 && MSCRStableCheck( lr, params ) )
|
||||
{
|
||||
if ( lr->tmsr == NULL )
|
||||
{
|
||||
@@ -1202,13 +1193,13 @@ icvExtractMSER_8UC3( CvMat* src,
|
||||
if ( edgeptr >= edge_ub )
|
||||
break;
|
||||
}
|
||||
for ( CvTempMSCR* ptr = mscr; ptr < mscrptr; ptr++ )
|
||||
for ( TempMSCR* ptr = mscr; ptr < mscrptr; ptr++ )
|
||||
// to prune area with margin less than minMargin
|
||||
if ( ptr->m > params.minMargin )
|
||||
{
|
||||
CvSeq* _contour = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvContour), sizeof(CvPoint), storage );
|
||||
cvSeqPushMulti( _contour, 0, ptr->size );
|
||||
CvMSCRNode* lpt = ptr->head;
|
||||
MSCRNode* lpt = ptr->head;
|
||||
for ( int i = 0; i < ptr->size; i++ )
|
||||
{
|
||||
CvPoint* pt = CV_GET_SEQ_ELEM( CvPoint, _contour, i );
|
||||
@@ -1228,12 +1219,12 @@ icvExtractMSER_8UC3( CvMat* src,
|
||||
cvFree( &map );
|
||||
}
|
||||
|
||||
void
|
||||
cvExtractMSER( CvArr* _img,
|
||||
static void
|
||||
extractMSER( CvArr* _img,
|
||||
CvArr* _mask,
|
||||
CvSeq** _contours,
|
||||
CvMemStorage* storage,
|
||||
CvMSERParams params )
|
||||
MSERParams params )
|
||||
{
|
||||
CvMat srchdr, *src = cvGetMat( _img, &srchdr );
|
||||
CvMat maskhdr, *mask = _mask ? cvGetMat( _mask, &maskhdr ) : 0;
|
||||
@@ -1252,30 +1243,24 @@ cvExtractMSER( CvArr* _img,
|
||||
switch ( CV_MAT_TYPE(src->type) )
|
||||
{
|
||||
case CV_8UC1:
|
||||
icvExtractMSER_8UC1( src, mask, contours, storage, params );
|
||||
extractMSER_8UC1( src, mask, contours, storage, params );
|
||||
break;
|
||||
case CV_8UC3:
|
||||
icvExtractMSER_8UC3( src, mask, contours, storage, params );
|
||||
extractMSER_8UC3( src, mask, contours, storage, params );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
MSER::MSER()
|
||||
{
|
||||
*(CvMSERParams*)this = cvMSERParams();
|
||||
}
|
||||
|
||||
MSER::MSER( int _delta, int _min_area, int _max_area,
|
||||
double _max_variation, double _min_diversity,
|
||||
int _max_evolution, double _area_threshold,
|
||||
double _min_margin, int _edge_blur_size )
|
||||
: delta(_delta), minArea(_min_area), maxArea(_max_area),
|
||||
maxVariation(_max_variation), minDiversity(_min_diversity),
|
||||
maxEvolution(_max_evolution), areaThreshold(_area_threshold),
|
||||
minMargin(_min_margin), edgeBlurSize(_edge_blur_size)
|
||||
{
|
||||
*(CvMSERParams*)this = cvMSERParams(_delta, _min_area, _max_area, (float)_max_variation,
|
||||
(float)_min_diversity, _max_evolution, _area_threshold, _min_margin, _edge_blur_size);
|
||||
}
|
||||
|
||||
void MSER::operator()( const Mat& image, vector<vector<Point> >& dstcontours, const Mat& mask ) const
|
||||
@@ -1285,12 +1270,56 @@ void MSER::operator()( const Mat& image, vector<vector<Point> >& dstcontours, co
|
||||
pmask = &(_mask = mask);
|
||||
MemStorage storage(cvCreateMemStorage(0));
|
||||
Seq<CvSeq*> contours;
|
||||
cvExtractMSER( &_image, pmask, &contours.seq, storage, *(const CvMSERParams*)this );
|
||||
extractMSER( &_image, pmask, &contours.seq, storage,
|
||||
MSERParams(delta, minArea, maxArea, maxVariation, minDiversity,
|
||||
maxEvolution, areaThreshold, minMargin, edgeBlurSize));
|
||||
SeqIterator<CvSeq*> it = contours.begin();
|
||||
size_t i, ncontours = contours.size();
|
||||
dstcontours.resize(ncontours);
|
||||
for( i = 0; i < ncontours; i++, ++it )
|
||||
Seq<Point>(*it).copyTo(dstcontours[i]);
|
||||
}
|
||||
|
||||
|
||||
void MserFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
|
||||
{
|
||||
vector<vector<Point> > msers;
|
||||
|
||||
(*this)(image, msers, mask);
|
||||
|
||||
vector<vector<Point> >::const_iterator contour_it = msers.begin();
|
||||
for( ; contour_it != msers.end(); ++contour_it )
|
||||
{
|
||||
// TODO check transformation from MSER region to KeyPoint
|
||||
RotatedRect rect = fitEllipse(Mat(*contour_it));
|
||||
float diam = sqrt(rect.size.height*rect.size.width);
|
||||
|
||||
if( diam > std::numeric_limits<float>::epsilon() )
|
||||
keypoints.push_back( KeyPoint( rect.center, diam, rect.angle) );
|
||||
}
|
||||
}
|
||||
|
||||
static Algorithm* createMSER() { return new MSER; }
|
||||
static AlgorithmInfo mser_info("Feature2D.MSER", createMSER);
|
||||
|
||||
AlgorithmInfo* MSER::info() const
|
||||
{
|
||||
static volatile bool initialized = false;
|
||||
if( !initialized )
|
||||
{
|
||||
mser_info.addParam(this, "delta", delta);
|
||||
mser_info.addParam(this, "minArea", minArea);
|
||||
mser_info.addParam(this, "maxArea", maxArea);
|
||||
mser_info.addParam(this, "maxVariation", maxVariation);
|
||||
mser_info.addParam(this, "minDiversity", minDiversity);
|
||||
mser_info.addParam(this, "maxEvolution", maxEvolution);
|
||||
mser_info.addParam(this, "areaThreshold", areaThreshold);
|
||||
mser_info.addParam(this, "minMargin", minMargin);
|
||||
mser_info.addParam(this, "edgeBlurSize", edgeBlurSize);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
return &mser_info;
|
||||
}
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -94,27 +94,6 @@ HarrisResponses(const Mat& img, vector<KeyPoint>& pts, int blockSize, float harr
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct KeypointResponseGreaterThanThreshold
|
||||
{
|
||||
KeypointResponseGreaterThanThreshold(float _value) :
|
||||
value(_value)
|
||||
{
|
||||
}
|
||||
inline bool operator()(const KeyPoint& kpt) const
|
||||
{
|
||||
return kpt.response >= value;
|
||||
}
|
||||
float value;
|
||||
};
|
||||
|
||||
struct KeypointResponseGreater
|
||||
{
|
||||
inline bool operator()(const KeyPoint& kp1, const KeyPoint& kp2) const
|
||||
{
|
||||
return kp1.response > kp2.response;
|
||||
}
|
||||
};
|
||||
|
||||
static float IC_Angle(const Mat& image, const int half_k, Point2f pt,
|
||||
const vector<int> & u_max)
|
||||
{
|
||||
@@ -224,37 +203,37 @@ static void computeOrbDescriptor(const KeyPoint& kpt,
|
||||
{
|
||||
for (int i = 0; i < dsize; ++i, pattern += 16)
|
||||
{
|
||||
int t0, t1, t2, t3, a, b, k, val;
|
||||
int t0, t1, t2, t3, u, v, k, val;
|
||||
t0 = GET_VALUE(0); t1 = GET_VALUE(1);
|
||||
t2 = GET_VALUE(2); t3 = GET_VALUE(3);
|
||||
a = 0, b = 2;
|
||||
if( t1 > t0 ) t0 = t1, a = 1;
|
||||
if( t3 > t2 ) t2 = t3, b = 3;
|
||||
k = t0 > t2 ? a : b;
|
||||
u = 0, v = 2;
|
||||
if( t1 > t0 ) t0 = t1, u = 1;
|
||||
if( t3 > t2 ) t2 = t3, v = 3;
|
||||
k = t0 > t2 ? u : v;
|
||||
val = k;
|
||||
|
||||
t0 = GET_VALUE(4); t1 = GET_VALUE(5);
|
||||
t2 = GET_VALUE(6); t3 = GET_VALUE(7);
|
||||
a = 0, b = 2;
|
||||
if( t1 > t0 ) t0 = t1, a = 1;
|
||||
if( t3 > t2 ) t2 = t3, b = 3;
|
||||
k = t0 > t2 ? a : b;
|
||||
u = 0, v = 2;
|
||||
if( t1 > t0 ) t0 = t1, u = 1;
|
||||
if( t3 > t2 ) t2 = t3, v = 3;
|
||||
k = t0 > t2 ? u : v;
|
||||
val |= k << 2;
|
||||
|
||||
t0 = GET_VALUE(8); t1 = GET_VALUE(9);
|
||||
t2 = GET_VALUE(10); t3 = GET_VALUE(11);
|
||||
a = 0, b = 2;
|
||||
if( t1 > t0 ) t0 = t1, a = 1;
|
||||
if( t3 > t2 ) t2 = t3, b = 3;
|
||||
k = t0 > t2 ? a : b;
|
||||
u = 0, v = 2;
|
||||
if( t1 > t0 ) t0 = t1, u = 1;
|
||||
if( t3 > t2 ) t2 = t3, v = 3;
|
||||
k = t0 > t2 ? u : v;
|
||||
val |= k << 4;
|
||||
|
||||
t0 = GET_VALUE(12); t1 = GET_VALUE(13);
|
||||
t2 = GET_VALUE(14); t3 = GET_VALUE(15);
|
||||
a = 0, b = 2;
|
||||
if( t1 > t0 ) t0 = t1, a = 1;
|
||||
if( t3 > t2 ) t2 = t3, b = 3;
|
||||
k = t0 > t2 ? a : b;
|
||||
u = 0, v = 2;
|
||||
if( t1 > t0 ) t0 = t1, u = 1;
|
||||
if( t3 > t2 ) t2 = t3, v = 3;
|
||||
k = t0 > t2 ? u : v;
|
||||
val |= k << 6;
|
||||
|
||||
desc[i] = (uchar)val;
|
||||
@@ -567,168 +546,195 @@ static void makeRandomPattern(int patchSize, Point* pattern, int npoints)
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ORB::CommonParams::read(const FileNode& fn)
|
||||
static Algorithm* createORB() { return new ORB; }
|
||||
static AlgorithmInfo orb_info("Feature2D.ORB", createORB);
|
||||
|
||||
AlgorithmInfo* ORB::info() const
|
||||
{
|
||||
scale_factor_ = fn["scaleFactor"];
|
||||
n_levels_ = int(fn["nLevels"]);
|
||||
first_level_ = int(fn["firstLevel"]);
|
||||
edge_threshold_ = fn["edgeThreshold"];
|
||||
patch_size_ = fn["patchSize"];
|
||||
WTA_K_ = fn["WTA_K"];
|
||||
if( WTA_K_ == 0 ) WTA_K_ = 2;
|
||||
score_type_ = fn["scoreType"];
|
||||
static volatile bool initialized = false;
|
||||
if( !initialized )
|
||||
{
|
||||
orb_info.addParam(this, "nFeatures", nfeatures);
|
||||
orb_info.addParam(this, "scaleFactor", scaleFactor);
|
||||
orb_info.addParam(this, "nLevels", nlevels);
|
||||
orb_info.addParam(this, "firstLevel", firstLevel);
|
||||
orb_info.addParam(this, "edgeThreshold", edgeThreshold);
|
||||
orb_info.addParam(this, "patchSize", patchSize);
|
||||
orb_info.addParam(this, "WTA_K", WTA_K);
|
||||
orb_info.addParam(this, "scoreType", scoreType);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
return &orb_info;
|
||||
}
|
||||
|
||||
void ORB::CommonParams::write(FileStorage& fs) const
|
||||
|
||||
static inline float getScale(int level, int firstLevel, double scaleFactor)
|
||||
{
|
||||
fs << "scaleFactor" << scale_factor_;
|
||||
fs << "nLevels" << int(n_levels_);
|
||||
fs << "firstLevel" << int(first_level_);
|
||||
fs << "edgeThreshold" << int(edge_threshold_);
|
||||
fs << "patchSize" << int(patch_size_);
|
||||
fs << "WTA_K" << WTA_K_;
|
||||
fs << "scoreType" << score_type_;
|
||||
}
|
||||
|
||||
void ORB::read(const FileNode& fn)
|
||||
{
|
||||
CommonParams params;
|
||||
params.read(fn);
|
||||
int n_features = int(fn["nFeatures"]);
|
||||
*this = ORB(n_features, params);
|
||||
}
|
||||
|
||||
void ORB::write(FileStorage& fs) const
|
||||
{
|
||||
params_.write(fs);
|
||||
fs << "nFeatures" << int(n_features_);
|
||||
}
|
||||
|
||||
static inline float get_scale(const ORB::CommonParams& params, int level)
|
||||
{
|
||||
return std::pow(params.scale_factor_, float(level) - float(params.first_level_));
|
||||
return (float)std::pow(scaleFactor, (double)(level - firstLevel));
|
||||
}
|
||||
|
||||
/** Constructor
|
||||
* @param detector_params parameters to use
|
||||
*/
|
||||
ORB::ORB(size_t n_features, const CommonParams & detector_params) :
|
||||
params_(detector_params), n_features_(n_features)
|
||||
{
|
||||
// fill the extractors and descriptors for the corresponding scales
|
||||
int n_levels = (int)params_.n_levels_;
|
||||
float factor = (float)(1.0 / params_.scale_factor_);
|
||||
float n_desired_features_per_scale = n_features_*(1 - factor)/(1 - (float)pow((double)factor, (double)n_levels));
|
||||
|
||||
n_features_per_level_.resize(n_levels);
|
||||
int sum_n_features = 0;
|
||||
for( int level = 0; level < n_levels-1; level++ )
|
||||
{
|
||||
n_features_per_level_[level] = cvRound(n_desired_features_per_scale);
|
||||
sum_n_features += n_features_per_level_[level];
|
||||
n_desired_features_per_scale *= factor;
|
||||
}
|
||||
n_features_per_level_[n_levels-1] = n_features - sum_n_features;
|
||||
|
||||
// Make sure we forget about what is too close to the boundary
|
||||
//params_.edge_threshold_ = std::max(params_.edge_threshold_, params_.patch_size_/2 + kKernelWidth / 2 + 2);
|
||||
|
||||
// pre-compute the end of a row in a circular patch
|
||||
int half_patch_size = params_.patch_size_ / 2;
|
||||
u_max_.resize(half_patch_size + 1);
|
||||
for (int v = 0; v <= half_patch_size * sqrt(2.f) / 2 + 1; ++v)
|
||||
u_max_[v] = cvRound(sqrt(float(half_patch_size * half_patch_size - v * v)));
|
||||
|
||||
// Make sure we are symmetric
|
||||
for (int v = half_patch_size, v_0 = 0; v >= half_patch_size * sqrt(2.f) / 2; --v)
|
||||
{
|
||||
while (u_max_[v_0] == u_max_[v_0 + 1])
|
||||
++v_0;
|
||||
u_max_[v] = v_0;
|
||||
++v_0;
|
||||
}
|
||||
|
||||
const int npoints = 512;
|
||||
Point pattern_buf[npoints];
|
||||
const Point* pattern0 = (const Point*)bit_pattern_31_;
|
||||
if( params_.patch_size_ != 31 )
|
||||
{
|
||||
pattern0 = pattern_buf;
|
||||
makeRandomPattern(params_.patch_size_, pattern_buf, npoints);
|
||||
}
|
||||
|
||||
CV_Assert( params_.WTA_K_ == 2 || params_.WTA_K_ == 3 || params_.WTA_K_ == 4 );
|
||||
|
||||
if( params_.WTA_K_ == 2 )
|
||||
std::copy(pattern0, pattern0 + npoints, back_inserter(pattern));
|
||||
else
|
||||
{
|
||||
int ntuples = descriptorSize()*4;
|
||||
initializeOrbPattern(pattern0, pattern, ntuples, params_.WTA_K_, npoints);
|
||||
}
|
||||
}
|
||||
ORB::ORB(int _nfeatures, float _scaleFactor, int _nlevels, int _edgeThreshold,
|
||||
int _firstLevel, int WTA_K, int _scoreType, int _patchSize) :
|
||||
nfeatures(_nfeatures), scaleFactor(_scaleFactor), nlevels(_nlevels),
|
||||
edgeThreshold(_edgeThreshold), firstLevel(_firstLevel), WTA_K(WTA_K),
|
||||
scoreType(_scoreType), patchSize(_patchSize)
|
||||
{}
|
||||
|
||||
|
||||
/** destructor to empty the patterns */
|
||||
ORB::~ORB()
|
||||
{
|
||||
}
|
||||
|
||||
/** returns the descriptor size in bytes */
|
||||
int ORB::descriptorSize() const
|
||||
{
|
||||
return kBytes;
|
||||
}
|
||||
|
||||
int ORB::descriptorType() const
|
||||
{
|
||||
return CV_8U;
|
||||
}
|
||||
|
||||
/** Compute the ORB features and descriptors on an image
|
||||
* @param img the image to compute the features and descriptors on
|
||||
* @param mask the mask to apply
|
||||
* @param keypoints the resulting keypoints
|
||||
*/
|
||||
void ORB::operator()(const Mat &image, const Mat &mask, vector<KeyPoint> & keypoints)
|
||||
void ORB::operator()(InputArray image, InputArray mask, vector<KeyPoint>& keypoints) const
|
||||
{
|
||||
Mat empty_descriptors;
|
||||
this->operator ()(image, mask, keypoints, empty_descriptors, true, false);
|
||||
(*this)(image, mask, keypoints, noArray(), false);
|
||||
}
|
||||
|
||||
/** Compute the ORB features and descriptors on an image
|
||||
* @param img the image to compute the features and descriptors on
|
||||
* @param mask the mask to apply
|
||||
|
||||
/** Compute the ORB keypoint orientations
|
||||
* @param image the image to compute the features and descriptors on
|
||||
* @param integral_image the integral image of the iamge (can be empty, but the computation will be slower)
|
||||
* @param scale the scale at which we compute the orientation
|
||||
* @param keypoints the resulting keypoints
|
||||
* @param descriptors the resulting descriptors
|
||||
* @param useProvidedKeypoints if true, the keypoints are used as an input
|
||||
*/
|
||||
void ORB::operator()(const Mat &image, const Mat &mask, vector<KeyPoint> & keypoints,
|
||||
Mat & descriptors, bool useProvidedKeypoints)
|
||||
static void computeOrientation(const Mat& image, vector<KeyPoint>& keypoints,
|
||||
int halfPatchSize, const vector<int>& umax)
|
||||
{
|
||||
this->operator ()(image, mask, keypoints, descriptors, !useProvidedKeypoints, true);
|
||||
}
|
||||
|
||||
//takes keypoints and culls them by the response
|
||||
static void cull(vector<KeyPoint>& keypoints, size_t n_points)
|
||||
{
|
||||
//this is only necessary if the keypoints size is greater than the number of desired points.
|
||||
if (keypoints.size() > n_points)
|
||||
// Process each keypoint
|
||||
for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),
|
||||
keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)
|
||||
{
|
||||
if (n_points==0) {
|
||||
keypoints.clear();
|
||||
return;
|
||||
}
|
||||
//first use nth element to partition the keypoints into the best and worst.
|
||||
std::nth_element(keypoints.begin(), keypoints.begin() + n_points, keypoints.end(), KeypointResponseGreater());
|
||||
//this is the boundary response, and in the case of FAST may be ambigous
|
||||
float ambiguous_response = keypoints[n_points - 1].response;
|
||||
//use std::partition to grab all of the keypoints with the boundary response.
|
||||
vector<KeyPoint>::const_iterator new_end =
|
||||
std::partition(keypoints.begin() + n_points, keypoints.end(),
|
||||
KeypointResponseGreaterThanThreshold(ambiguous_response));
|
||||
//resize the keypoints, given this new end point. nth_element and partition reordered the points inplace
|
||||
keypoints.resize(new_end - keypoints.begin());
|
||||
keypoint->angle = IC_Angle(image, halfPatchSize, keypoint->pt, umax);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Compute the ORB keypoints on an image
|
||||
* @param image_pyramid the image pyramid to compute the features and descriptors on
|
||||
* @param mask_pyramid the masks to apply at every level
|
||||
* @param keypoints the resulting keypoints, clustered per level
|
||||
*/
|
||||
static void computeKeyPoints(const vector<Mat>& imagePyramid,
|
||||
const vector<Mat>& maskPyramid,
|
||||
vector<vector<KeyPoint> >& allKeypoints,
|
||||
int nfeatures, int firstLevel, double scaleFactor,
|
||||
int edgeThreshold, int patchSize, int scoreType )
|
||||
{
|
||||
int nlevels = (int)imagePyramid.size();
|
||||
vector<int> nfeaturesPerLevel(nlevels);
|
||||
|
||||
// fill the extractors and descriptors for the corresponding scales
|
||||
float factor = (float)(1.0 / scaleFactor);
|
||||
float ndesiredFeaturesPerScale = nfeatures*(1 - factor)/(1 - (float)pow((double)factor, (double)nlevels));
|
||||
|
||||
int sumFeatures = 0;
|
||||
for( int level = 0; level < nlevels-1; level++ )
|
||||
{
|
||||
nfeaturesPerLevel[level] = cvRound(ndesiredFeaturesPerScale);
|
||||
sumFeatures += nfeaturesPerLevel[level];
|
||||
ndesiredFeaturesPerScale *= factor;
|
||||
}
|
||||
nfeaturesPerLevel[nlevels-1] = std::max(nfeatures - sumFeatures, 0);
|
||||
|
||||
// Make sure we forget about what is too close to the boundary
|
||||
//edge_threshold_ = std::max(edge_threshold_, patch_size_/2 + kKernelWidth / 2 + 2);
|
||||
|
||||
// pre-compute the end of a row in a circular patch
|
||||
int halfPatchSize = patchSize / 2;
|
||||
vector<int> umax(halfPatchSize + 1);
|
||||
|
||||
int v, v0, vmax = cvFloor(halfPatchSize * sqrt(2.f) / 2 + 1);
|
||||
int vmin = cvCeil(halfPatchSize * sqrt(2.f) / 2);
|
||||
for (v = 0; v <= vmax; ++v)
|
||||
umax[v] = cvRound(sqrt(halfPatchSize * halfPatchSize - v * v));
|
||||
|
||||
// Make sure we are symmetric
|
||||
for (v = halfPatchSize, v0 = 0; v >= vmin; --v)
|
||||
{
|
||||
while (umax[v0] == umax[v0 + 1])
|
||||
++v0;
|
||||
umax[v] = v0;
|
||||
++v0;
|
||||
}
|
||||
|
||||
allKeypoints.resize(nlevels);
|
||||
|
||||
for (int level = 0; level < nlevels; ++level)
|
||||
{
|
||||
int nfeatures = nfeaturesPerLevel[level];
|
||||
allKeypoints[level].reserve(nfeatures*2);
|
||||
|
||||
vector<KeyPoint> & keypoints = allKeypoints[level];
|
||||
|
||||
// Detect FAST features, 20 is a good threshold
|
||||
FastFeatureDetector fd(20, true);
|
||||
fd.detect(imagePyramid[level], keypoints, maskPyramid[level]);
|
||||
|
||||
// Remove keypoints very close to the border
|
||||
KeyPointsFilter::runByImageBorder(keypoints, imagePyramid[level].size(), edgeThreshold);
|
||||
|
||||
if( scoreType == ORB::HARRIS_SCORE )
|
||||
{
|
||||
// Keep more points than necessary as FAST does not give amazing corners
|
||||
KeyPointsFilter::retainBest(keypoints, 2 * nfeatures);
|
||||
|
||||
// Compute the Harris cornerness (better scoring than FAST)
|
||||
HarrisResponses(imagePyramid[level], keypoints, 7, HARRIS_K);
|
||||
}
|
||||
|
||||
//cull to the final desired level, using the new Harris scores or the original FAST scores.
|
||||
KeyPointsFilter::retainBest(keypoints, nfeatures);
|
||||
|
||||
float sf = getScale(level, firstLevel, scaleFactor);
|
||||
|
||||
// Set the level of the coordinates
|
||||
for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),
|
||||
keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)
|
||||
{
|
||||
keypoint->octave = level;
|
||||
keypoint->size = patchSize*sf;
|
||||
}
|
||||
|
||||
computeOrientation(imagePyramid[level], keypoints, halfPatchSize, umax);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Compute the ORB decriptors
|
||||
* @param image the image to compute the features and descriptors on
|
||||
* @param integral_image the integral image of the image (can be empty, but the computation will be slower)
|
||||
* @param level the scale at which we compute the orientation
|
||||
* @param keypoints the keypoints to use
|
||||
* @param descriptors the resulting descriptors
|
||||
*/
|
||||
static void computeDescriptors(const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors,
|
||||
const vector<Point>& pattern, int dsize, int WTA_K)
|
||||
{
|
||||
//convert to grayscale if more than one color
|
||||
CV_Assert(image.type() == CV_8UC1);
|
||||
//create the descriptor mat, keypoints.size() rows, BYTES cols
|
||||
descriptors = Mat::zeros((int)keypoints.size(), dsize, CV_8UC1);
|
||||
|
||||
for (size_t i = 0; i < keypoints.size(); i++)
|
||||
computeOrbDescriptor(keypoints[i], image, &pattern[0], descriptors.ptr((int)i), dsize, WTA_K);
|
||||
}
|
||||
|
||||
|
||||
/** Compute the ORB features and descriptors on an image
|
||||
* @param img the image to compute the features and descriptors on
|
||||
@@ -738,24 +744,25 @@ static void cull(vector<KeyPoint>& keypoints, size_t n_points)
|
||||
* @param do_keypoints if true, the keypoints are computed, otherwise used as an input
|
||||
* @param do_descriptors if true, also computes the descriptors
|
||||
*/
|
||||
void ORB::operator()(const Mat &image_in, const Mat &mask, vector<KeyPoint> & keypoints_in_out,
|
||||
Mat& descriptors, bool do_keypoints, bool do_descriptors)
|
||||
void ORB::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _keypoints,
|
||||
OutputArray _descriptors, bool useProvidedKeypoints) const
|
||||
{
|
||||
if (((!do_keypoints) && (!do_descriptors)) || (image_in.empty()))
|
||||
bool do_keypoints = !useProvidedKeypoints;
|
||||
bool do_descriptors = _descriptors.needed();
|
||||
|
||||
if( (!do_keypoints && !do_descriptors) || _image.empty() )
|
||||
return;
|
||||
|
||||
//ROI handling
|
||||
const int HARRIS_BLOCK_SIZE = 9;
|
||||
int half_patch_size = params_.patch_size_ / 2;
|
||||
int border = std::max(params_.edge_threshold_, std::max(half_patch_size, HARRIS_BLOCK_SIZE/2))+1;
|
||||
int halfPatchSize = patchSize / 2;
|
||||
int border = std::max(edgeThreshold, std::max(halfPatchSize, HARRIS_BLOCK_SIZE/2))+1;
|
||||
|
||||
Mat image;
|
||||
if (image_in.type() != CV_8UC1)
|
||||
cvtColor(image_in, image, CV_BGR2GRAY);
|
||||
else
|
||||
image = image_in;
|
||||
Mat image = _image.getMat(), mask = _mask.getMat();
|
||||
if( image.type() != CV_8UC1 )
|
||||
cvtColor(_image, image, CV_BGR2GRAY);
|
||||
|
||||
int n_levels = (int)params_.n_levels_;
|
||||
int nlevels = this->nlevels;
|
||||
|
||||
if( !do_keypoints )
|
||||
{
|
||||
@@ -768,46 +775,46 @@ void ORB::operator()(const Mat &image_in, const Mat &mask, vector<KeyPoint> & ke
|
||||
//
|
||||
// In short, ultimately the descriptor should
|
||||
// ignore octave parameter and deal only with the keypoint size.
|
||||
n_levels = 0;
|
||||
for( size_t i = 0; i < keypoints_in_out.size(); i++ )
|
||||
n_levels = std::max(n_levels, std::max(keypoints_in_out[i].octave, 0));
|
||||
n_levels++;
|
||||
nlevels = 0;
|
||||
for( size_t i = 0; i < _keypoints.size(); i++ )
|
||||
nlevels = std::max(nlevels, std::max(_keypoints[i].octave, 0));
|
||||
nlevels++;
|
||||
}
|
||||
|
||||
// Pre-compute the scale pyramids
|
||||
vector<Mat> image_pyramid(n_levels), mask_pyramid(n_levels);
|
||||
for (int level = 0; level < n_levels; ++level)
|
||||
vector<Mat> imagePyramid(nlevels), maskPyramid(nlevels);
|
||||
for (int level = 0; level < nlevels; ++level)
|
||||
{
|
||||
float scale = 1/get_scale(params_, level);
|
||||
float scale = 1/getScale(level, firstLevel, scale);
|
||||
Size sz(cvRound(image.cols*scale), cvRound(image.rows*scale));
|
||||
Size wholeSize(sz.width + border*2, sz.height + border*2);
|
||||
Mat temp(wholeSize, image.type()), masktemp;
|
||||
image_pyramid[level] = temp(Rect(border, border, sz.width, sz.height));
|
||||
imagePyramid[level] = temp(Rect(border, border, sz.width, sz.height));
|
||||
|
||||
if( !mask.empty() )
|
||||
{
|
||||
masktemp = Mat(wholeSize, mask.type());
|
||||
mask_pyramid[level] = masktemp(Rect(border, border, sz.width, sz.height));
|
||||
maskPyramid[level] = masktemp(Rect(border, border, sz.width, sz.height));
|
||||
}
|
||||
|
||||
// Compute the resized image
|
||||
if (level != (int)params_.first_level_)
|
||||
if( level != firstLevel )
|
||||
{
|
||||
if( level < (int)params_.first_level_ )
|
||||
if( level < firstLevel )
|
||||
{
|
||||
resize(image, image_pyramid[level], sz, scale, scale, INTER_LINEAR);
|
||||
resize(image, imagePyramid[level], sz, scale, scale, INTER_LINEAR);
|
||||
if (!mask.empty())
|
||||
resize(mask, mask_pyramid[level], sz, scale, scale, INTER_LINEAR);
|
||||
copyMakeBorder(image_pyramid[level], temp, border, border, border, border,
|
||||
resize(mask, maskPyramid[level], sz, scale, scale, INTER_LINEAR);
|
||||
copyMakeBorder(imagePyramid[level], temp, border, border, border, border,
|
||||
BORDER_REFLECT_101+BORDER_ISOLATED);
|
||||
}
|
||||
else
|
||||
{
|
||||
float sf = params_.scale_factor_;
|
||||
resize(image_pyramid[level-1], image_pyramid[level], sz, 1./sf, 1./sf, INTER_LINEAR);
|
||||
float sf = scaleFactor;
|
||||
resize(imagePyramid[level-1], imagePyramid[level], sz, 1./sf, 1./sf, INTER_LINEAR);
|
||||
if (!mask.empty())
|
||||
resize(mask_pyramid[level-1], mask_pyramid[level], sz, 1./sf, 1./sf, INTER_LINEAR);
|
||||
copyMakeBorder(image_pyramid[level], temp, border, border, border, border,
|
||||
resize(maskPyramid[level-1], maskPyramid[level], sz, 1./sf, 1./sf, INTER_LINEAR);
|
||||
copyMakeBorder(imagePyramid[level], temp, border, border, border, border,
|
||||
BORDER_REFLECT_101+BORDER_ISOLATED);
|
||||
}
|
||||
}
|
||||
@@ -815,22 +822,24 @@ void ORB::operator()(const Mat &image_in, const Mat &mask, vector<KeyPoint> & ke
|
||||
{
|
||||
copyMakeBorder(image, temp, border, border, border, border,
|
||||
BORDER_REFLECT_101);
|
||||
image.copyTo(image_pyramid[level]);
|
||||
image.copyTo(imagePyramid[level]);
|
||||
if( !mask.empty() )
|
||||
mask.copyTo(mask_pyramid[level]);
|
||||
mask.copyTo(maskPyramid[level]);
|
||||
}
|
||||
|
||||
if( !mask.empty() )
|
||||
copyMakeBorder(mask_pyramid[level], masktemp, border, border, border, border,
|
||||
copyMakeBorder(maskPyramid[level], masktemp, border, border, border, border,
|
||||
BORDER_CONSTANT+BORDER_ISOLATED);
|
||||
}
|
||||
|
||||
// Pre-compute the keypoints (we keep the best over all scales, so this has to be done beforehand
|
||||
vector < vector<KeyPoint> > all_keypoints;
|
||||
if (do_keypoints)
|
||||
vector < vector<KeyPoint> > allKeypoints;
|
||||
if( do_keypoints )
|
||||
{
|
||||
// Get keypoints, those will be far enough from the border that no check will be required for the descriptor
|
||||
computeKeyPoints(image_pyramid, mask_pyramid, all_keypoints);
|
||||
computeKeyPoints(imagePyramid, maskPyramid, allKeypoints,
|
||||
nfeatures, firstLevel, scaleFactor,
|
||||
edgeThreshold, patchSize, scoreType);
|
||||
|
||||
// make sure we have the right number of keypoints keypoints
|
||||
/*vector<KeyPoint> temp;
|
||||
@@ -842,7 +851,7 @@ void ORB::operator()(const Mat &image_in, const Mat &mask, vector<KeyPoint> & ke
|
||||
keypoints.clear();
|
||||
}
|
||||
|
||||
cull(temp, n_features_);
|
||||
KeyPoint::retainBest(temp, n_features_);
|
||||
|
||||
for (vector<KeyPoint>::iterator keypoint = temp.begin(),
|
||||
keypoint_end = temp.end(); keypoint != keypoint_end; ++keypoint)
|
||||
@@ -851,48 +860,72 @@ void ORB::operator()(const Mat &image_in, const Mat &mask, vector<KeyPoint> & ke
|
||||
else
|
||||
{
|
||||
// Remove keypoints very close to the border
|
||||
KeyPointsFilter::runByImageBorder(keypoints_in_out, image.size(), params_.edge_threshold_);
|
||||
KeyPointsFilter::runByImageBorder(_keypoints, image.size(), edgeThreshold);
|
||||
|
||||
// Cluster the input keypoints depending on the level they were computed at
|
||||
all_keypoints.resize(n_levels);
|
||||
for (vector<KeyPoint>::iterator keypoint = keypoints_in_out.begin(),
|
||||
keypoint_end = keypoints_in_out.end(); keypoint != keypoint_end; ++keypoint)
|
||||
all_keypoints[keypoint->octave].push_back(*keypoint);
|
||||
allKeypoints.resize(nlevels);
|
||||
for (vector<KeyPoint>::iterator keypoint = _keypoints.begin(),
|
||||
keypointEnd = _keypoints.end(); keypoint != keypointEnd; ++keypoint)
|
||||
allKeypoints[keypoint->octave].push_back(*keypoint);
|
||||
|
||||
// Make sure we rescale the coordinates
|
||||
for (int level = 0; level < n_levels; ++level)
|
||||
for (int level = 0; level < nlevels; ++level)
|
||||
{
|
||||
if (level == (int)params_.first_level_)
|
||||
if (level == firstLevel)
|
||||
continue;
|
||||
|
||||
vector<KeyPoint> & keypoints = all_keypoints[level];
|
||||
float scale = 1/get_scale(params_, level);
|
||||
vector<KeyPoint> & keypoints = allKeypoints[level];
|
||||
float scale = 1/getScale(level, firstLevel, scaleFactor);
|
||||
for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),
|
||||
keypoint_end = keypoints.end(); keypoint != keypoint_end; ++keypoint)
|
||||
keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)
|
||||
keypoint->pt *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
if (do_descriptors)
|
||||
Mat descriptors;
|
||||
vector<Point> pattern;
|
||||
|
||||
if( do_descriptors )
|
||||
{
|
||||
int nkeypoints = 0;
|
||||
for (int level = 0; level < n_levels; ++level)
|
||||
nkeypoints += (int)all_keypoints[level].size();
|
||||
for (int level = 0; level < nlevels; ++level)
|
||||
nkeypoints += (int)allKeypoints[level].size();
|
||||
if( nkeypoints == 0 )
|
||||
descriptors.release();
|
||||
_descriptors.release();
|
||||
else
|
||||
descriptors.create(nkeypoints, descriptorSize(), CV_8U);
|
||||
{
|
||||
_descriptors.create(nkeypoints, descriptorSize(), CV_8U);
|
||||
descriptors = _descriptors.getMat();
|
||||
}
|
||||
|
||||
const int npoints = 512;
|
||||
Point patternbuf[npoints];
|
||||
const Point* pattern0 = (const Point*)bit_pattern_31_;
|
||||
|
||||
if( patchSize != 31 )
|
||||
{
|
||||
pattern0 = patternbuf;
|
||||
makeRandomPattern(patchSize, patternbuf, npoints);
|
||||
}
|
||||
|
||||
CV_Assert( WTA_K == 2 || WTA_K == 3 || WTA_K == 4 );
|
||||
|
||||
if( WTA_K == 2 )
|
||||
std::copy(pattern0, pattern0 + npoints, std::back_inserter(pattern));
|
||||
else
|
||||
{
|
||||
int ntuples = descriptorSize()*4;
|
||||
initializeOrbPattern(pattern0, pattern, ntuples, WTA_K, npoints);
|
||||
}
|
||||
}
|
||||
|
||||
keypoints_in_out.clear();
|
||||
_keypoints.clear();
|
||||
int offset = 0;
|
||||
for (int level = 0; level < n_levels; ++level)
|
||||
for (int level = 0; level < nlevels; ++level)
|
||||
{
|
||||
// Get the features and compute their orientation
|
||||
vector<KeyPoint>& keypoints = all_keypoints[level];
|
||||
vector<KeyPoint>& keypoints = allKeypoints[level];
|
||||
int nkeypoints = (int)keypoints.size();
|
||||
if (nkeypoints==0)
|
||||
continue;
|
||||
|
||||
// Compute the descriptors
|
||||
if (do_descriptors)
|
||||
@@ -900,122 +933,33 @@ void ORB::operator()(const Mat &image_in, const Mat &mask, vector<KeyPoint> & ke
|
||||
Mat desc = descriptors.rowRange(offset, offset + nkeypoints);
|
||||
offset += nkeypoints;
|
||||
// preprocess the resized image
|
||||
Mat& working_mat = image_pyramid[level];
|
||||
Mat& workingMat = imagePyramid[level];
|
||||
//boxFilter(working_mat, working_mat, working_mat.depth(), Size(5,5), Point(-1,-1), true, BORDER_REFLECT_101);
|
||||
GaussianBlur(working_mat, working_mat, Size(7, 7), 2, 2, BORDER_REFLECT_101);
|
||||
computeDescriptors(working_mat, Mat(), level, keypoints, desc);
|
||||
GaussianBlur(workingMat, workingMat, Size(7, 7), 2, 2, BORDER_REFLECT_101);
|
||||
computeDescriptors(workingMat, keypoints, desc, pattern, descriptorSize(), WTA_K);
|
||||
}
|
||||
|
||||
// Copy to the output data
|
||||
if (level != (int)params_.first_level_)
|
||||
if (level != firstLevel)
|
||||
{
|
||||
float scale = get_scale(params_, level);
|
||||
float scale = getScale(level, firstLevel, scaleFactor);
|
||||
for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),
|
||||
keypoint_end = keypoints.end(); keypoint != keypoint_end; ++keypoint)
|
||||
keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)
|
||||
keypoint->pt *= scale;
|
||||
}
|
||||
// And add the keypoints to the output
|
||||
keypoints_in_out.insert(keypoints_in_out.end(), keypoints.begin(), keypoints.end());
|
||||
_keypoints.insert(_keypoints.end(), keypoints.begin(), keypoints.end());
|
||||
}
|
||||
}
|
||||
|
||||
/** Compute the ORB keypoints on an image
|
||||
* @param image_pyramid the image pyramid to compute the features and descriptors on
|
||||
* @param mask_pyramid the masks to apply at every level
|
||||
* @param keypoints the resulting keypoints, clustered per level
|
||||
*/
|
||||
void ORB::computeKeyPoints(const vector<Mat>& image_pyramid,
|
||||
const vector<Mat>& mask_pyramid,
|
||||
vector<vector<KeyPoint> >& all_keypoints_out) const
|
||||
{
|
||||
all_keypoints_out.resize(params_.n_levels_);
|
||||
|
||||
for (int level = 0; level < (int)params_.n_levels_; ++level)
|
||||
{
|
||||
int n_features = n_features_per_level_[level];
|
||||
all_keypoints_out[level].reserve(n_features*2);
|
||||
|
||||
vector<KeyPoint> & keypoints = all_keypoints_out[level];
|
||||
|
||||
// Detect FAST features, 20 is a good threshold
|
||||
FastFeatureDetector fd(20, true);
|
||||
fd.detect(image_pyramid[level], keypoints, mask_pyramid[level]);
|
||||
|
||||
// Remove keypoints very close to the border
|
||||
KeyPointsFilter::runByImageBorder(keypoints, image_pyramid[level].size(), params_.edge_threshold_);
|
||||
|
||||
if( params_.score_type_ == CommonParams::HARRIS_SCORE )
|
||||
{
|
||||
// Keep more points than necessary as FAST does not give amazing corners
|
||||
cull(keypoints, 2 * n_features);
|
||||
|
||||
// Compute the Harris cornerness (better scoring than FAST)
|
||||
HarrisResponses(image_pyramid[level], keypoints, 7, HARRIS_K);
|
||||
}
|
||||
|
||||
//cull to the final desired level, using the new Harris scores or the original FAST scores.
|
||||
cull(keypoints, n_features);
|
||||
|
||||
float sf = get_scale(params_, level);
|
||||
|
||||
// Set the level of the coordinates
|
||||
for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),
|
||||
keypoint_end = keypoints.end(); keypoint != keypoint_end; ++keypoint)
|
||||
{
|
||||
keypoint->octave = level;
|
||||
keypoint->size = params_.patch_size_*sf;
|
||||
}
|
||||
|
||||
computeOrientation(image_pyramid[level], Mat(), level, keypoints);
|
||||
}
|
||||
}
|
||||
|
||||
/** Compute the ORB keypoint orientations
|
||||
* @param image the image to compute the features and descriptors on
|
||||
* @param integral_image the integral image of the iamge (can be empty, but the computation will be slower)
|
||||
* @param scale the scale at which we compute the orientation
|
||||
* @param keypoints the resulting keypoints
|
||||
*/
|
||||
void ORB::computeOrientation(const Mat& image, const Mat&, unsigned int /*scale*/,
|
||||
vector<KeyPoint>& keypoints) const
|
||||
void ORB::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask) const
|
||||
{
|
||||
int half_patch_size = params_.patch_size_/2;
|
||||
|
||||
// Process each keypoint
|
||||
for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),
|
||||
keypoint_end = keypoints.end(); keypoint != keypoint_end; ++keypoint)
|
||||
{
|
||||
keypoint->angle = IC_Angle(image, half_patch_size, keypoint->pt, u_max_);
|
||||
}
|
||||
}
|
||||
(*this)(image, mask, keypoints, noArray(), false);
|
||||
}
|
||||
|
||||
/** Compute the integral image and upadte the cached values
|
||||
* @param image the image to compute the features and descriptors on
|
||||
* @param level the scale at which we compute the orientation
|
||||
* @param descriptors the resulting descriptors
|
||||
*/
|
||||
void ORB::computeIntegralImage(const Mat&, unsigned int, Mat&)
|
||||
void ORB::computeImpl( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors) const
|
||||
{
|
||||
}
|
||||
|
||||
/** Compute the ORB decriptors
|
||||
* @param image the image to compute the features and descriptors on
|
||||
* @param integral_image the integral image of the image (can be empty, but the computation will be slower)
|
||||
* @param level the scale at which we compute the orientation
|
||||
* @param keypoints the keypoints to use
|
||||
* @param descriptors the resulting descriptors
|
||||
*/
|
||||
void ORB::computeDescriptors(const Mat& image, const Mat& /*integral_image*/, unsigned int,
|
||||
vector<KeyPoint>& keypoints, Mat& descriptors) const
|
||||
{
|
||||
//convert to grayscale if more than one color
|
||||
CV_Assert(image.type() == CV_8UC1);
|
||||
//create the descriptor mat, keypoints.size() rows, BYTES cols
|
||||
int dsize = descriptorSize();
|
||||
descriptors = Mat::zeros((int)keypoints.size(), dsize, CV_8UC1);
|
||||
|
||||
for (size_t i = 0; i < keypoints.size(); i++)
|
||||
computeOrbDescriptor(keypoints[i], image, &pattern[0], descriptors.ptr((int)i), dsize, params_.WTA_K_);
|
||||
(*this)(image, Mat(), keypoints, descriptors, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2008, Willow Garage Inc., all rights reserved.
|
||||
// Copyright (C) 2008-2012, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
@@ -41,20 +41,23 @@
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
static void
|
||||
icvComputeIntegralImages( const CvMat* matI, CvMat* matS, CvMat* matT, CvMat* _FT )
|
||||
namespace cv
|
||||
{
|
||||
int x, y, rows = matI->rows, cols = matI->cols;
|
||||
const uchar* I = matI->data.ptr;
|
||||
int *S = matS->data.i, *T = matT->data.i, *FT = _FT->data.i;
|
||||
int istep = matI->step, step = matS->step/sizeof(S[0]);
|
||||
|
||||
static void
|
||||
computeIntegralImages( const Mat& matI, Mat& matS, Mat& matT, Mat& _FT )
|
||||
{
|
||||
CV_Assert( matI.type() == CV_8U );
|
||||
|
||||
assert( CV_MAT_TYPE(matI->type) == CV_8UC1 &&
|
||||
CV_MAT_TYPE(matS->type) == CV_32SC1 &&
|
||||
CV_ARE_TYPES_EQ(matS, matT) && CV_ARE_TYPES_EQ(matS, _FT) &&
|
||||
CV_ARE_SIZES_EQ(matS, matT) && CV_ARE_SIZES_EQ(matS, _FT) &&
|
||||
matS->step == matT->step && matS->step == _FT->step &&
|
||||
matI->rows+1 == matS->rows && matI->cols+1 == matS->cols );
|
||||
int x, y, rows = matI.rows, cols = matI.cols;
|
||||
|
||||
matS.create(rows + 1, cols + 1, CV_32S);
|
||||
matT.create(rows + 1, cols + 1, CV_32S);
|
||||
_FT.create(rows + 1, cols + 1, CV_32S);
|
||||
|
||||
const uchar* I = matI.ptr<uchar>();
|
||||
int *S = matS.ptr<int>(), *T = matT.ptr<int>(), *FT = _FT.ptr<int>();
|
||||
int istep = matI.step, step = matS.step/sizeof(S[0]);
|
||||
|
||||
for( x = 0; x <= cols; x++ )
|
||||
S[x] = T[x] = FT[x] = 0;
|
||||
@@ -92,16 +95,14 @@ icvComputeIntegralImages( const CvMat* matI, CvMat* matS, CvMat* matT, CvMat* _F
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct CvStarFeature
|
||||
struct StarFeature
|
||||
{
|
||||
int area;
|
||||
int* p[8];
|
||||
}
|
||||
CvStarFeature;
|
||||
};
|
||||
|
||||
static int
|
||||
icvStarDetectorComputeResponses( const CvMat* img, CvMat* responses, CvMat* sizes,
|
||||
const CvStarDetectorParams* params )
|
||||
StarDetectorComputeResponses( const Mat& img, Mat& responses, Mat& sizes, int maxSize )
|
||||
{
|
||||
const int MAX_PATTERN = 17;
|
||||
static const int sizes0[] = {1, 2, 3, 4, 6, 8, 11, 12, 16, 22, 23, 32, 45, 46, 64, 90, 128, -1};
|
||||
@@ -117,22 +118,19 @@ icvStarDetectorComputeResponses( const CvMat* img, CvMat* responses, CvMat* size
|
||||
absmask.i = 0x7fffffff;
|
||||
volatile bool useSIMD = cv::checkHardwareSupport(CV_CPU_SSE2);
|
||||
#endif
|
||||
CvStarFeature f[MAX_PATTERN];
|
||||
StarFeature f[MAX_PATTERN];
|
||||
|
||||
CvMat *sum = 0, *tilted = 0, *flatTilted = 0;
|
||||
int y, i=0, rows = img->rows, cols = img->cols, step;
|
||||
Mat sum, tilted, flatTilted;
|
||||
int y, i=0, rows = img.rows, cols = img.cols;
|
||||
int border, npatterns=0, maxIdx=0;
|
||||
#ifdef _OPENMP
|
||||
int nthreads = cvGetNumThreads();
|
||||
#endif
|
||||
|
||||
assert( CV_MAT_TYPE(img->type) == CV_8UC1 &&
|
||||
CV_MAT_TYPE(responses->type) == CV_32FC1 &&
|
||||
CV_MAT_TYPE(sizes->type) == CV_16SC1 &&
|
||||
CV_ARE_SIZES_EQ(responses, sizes) );
|
||||
CV_Assert( img.type() == CV_8UC1 );
|
||||
|
||||
responses.create( img.size(), CV_32F );
|
||||
sizes.create( img.size(), CV_16S );
|
||||
|
||||
while( pairs[i][0] >= 0 && !
|
||||
( sizes0[pairs[i][0]] >= params->maxSize
|
||||
( sizes0[pairs[i][0]] >= maxSize
|
||||
|| sizes0[pairs[i+1][0]] + sizes0[pairs[i+1][0]]/2 >= std::min(rows, cols) ) )
|
||||
{
|
||||
++i;
|
||||
@@ -141,13 +139,9 @@ icvStarDetectorComputeResponses( const CvMat* img, CvMat* responses, CvMat* size
|
||||
npatterns = i;
|
||||
npatterns += (pairs[npatterns-1][0] >= 0);
|
||||
maxIdx = pairs[npatterns-1][0];
|
||||
|
||||
sum = cvCreateMat( rows + 1, cols + 1, CV_32SC1 );
|
||||
tilted = cvCreateMat( rows + 1, cols + 1, CV_32SC1 );
|
||||
flatTilted = cvCreateMat( rows + 1, cols + 1, CV_32SC1 );
|
||||
step = sum->step/CV_ELEM_SIZE(sum->type);
|
||||
|
||||
icvComputeIntegralImages( img, sum, tilted, flatTilted );
|
||||
|
||||
computeIntegralImages( img, sum, tilted, flatTilted );
|
||||
int step = (int)(sum.step/sum.elemSize());
|
||||
|
||||
for( i = 0; i <= maxIdx; i++ )
|
||||
{
|
||||
@@ -155,15 +149,15 @@ icvStarDetectorComputeResponses( const CvMat* img, CvMat* responses, CvMat* size
|
||||
int ur_area = (2*ur_size + 1)*(2*ur_size + 1);
|
||||
int t_area = t_size*t_size + (t_size + 1)*(t_size + 1);
|
||||
|
||||
f[i].p[0] = sum->data.i + (ur_size + 1)*step + ur_size + 1;
|
||||
f[i].p[1] = sum->data.i - ur_size*step + ur_size + 1;
|
||||
f[i].p[2] = sum->data.i + (ur_size + 1)*step - ur_size;
|
||||
f[i].p[3] = sum->data.i - ur_size*step - ur_size;
|
||||
f[i].p[0] = sum.ptr<int>() + (ur_size + 1)*step + ur_size + 1;
|
||||
f[i].p[1] = sum.ptr<int>() - ur_size*step + ur_size + 1;
|
||||
f[i].p[2] = sum.ptr<int>() + (ur_size + 1)*step - ur_size;
|
||||
f[i].p[3] = sum.ptr<int>() - ur_size*step - ur_size;
|
||||
|
||||
f[i].p[4] = tilted->data.i + (t_size + 1)*step + 1;
|
||||
f[i].p[5] = flatTilted->data.i - t_size;
|
||||
f[i].p[6] = flatTilted->data.i + t_size + 1;
|
||||
f[i].p[7] = tilted->data.i - t_size*step + 1;
|
||||
f[i].p[4] = tilted.ptr<int>() + (t_size + 1)*step + 1;
|
||||
f[i].p[5] = flatTilted.ptr<int>() - t_size;
|
||||
f[i].p[6] = flatTilted.ptr<int>() + t_size + 1;
|
||||
f[i].p[7] = tilted.ptr<int>() - t_size*step + 1;
|
||||
|
||||
f[i].area = ur_area + t_area;
|
||||
sizes1[i] = sizes0[i];
|
||||
@@ -199,10 +193,10 @@ icvStarDetectorComputeResponses( const CvMat* img, CvMat* responses, CvMat* size
|
||||
|
||||
for( y = 0; y < border; y++ )
|
||||
{
|
||||
float* r_ptr = (float*)(responses->data.ptr + responses->step*y);
|
||||
float* r_ptr2 = (float*)(responses->data.ptr + responses->step*(rows - 1 - y));
|
||||
short* s_ptr = (short*)(sizes->data.ptr + sizes->step*y);
|
||||
short* s_ptr2 = (short*)(sizes->data.ptr + sizes->step*(rows - 1 - y));
|
||||
float* r_ptr = responses.ptr<float>(y);
|
||||
float* r_ptr2 = responses.ptr<float>(rows - 1 - y);
|
||||
short* s_ptr = sizes.ptr<short>(y);
|
||||
short* s_ptr2 = sizes.ptr<short>(rows - 1 - y);
|
||||
|
||||
memset( r_ptr, 0, cols*sizeof(r_ptr[0]));
|
||||
memset( r_ptr2, 0, cols*sizeof(r_ptr2[0]));
|
||||
@@ -210,14 +204,11 @@ icvStarDetectorComputeResponses( const CvMat* img, CvMat* responses, CvMat* size
|
||||
memset( s_ptr2, 0, cols*sizeof(s_ptr2[0]));
|
||||
}
|
||||
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for num_threads(nthreads) schedule(static)
|
||||
#endif
|
||||
for( y = border; y < rows - border; y++ )
|
||||
{
|
||||
int x = border, i;
|
||||
float* r_ptr = (float*)(responses->data.ptr + responses->step*y);
|
||||
short* s_ptr = (short*)(sizes->data.ptr + sizes->step*y);
|
||||
float* r_ptr = responses.ptr<float>(y);
|
||||
short* s_ptr = sizes.ptr<short>(y);
|
||||
|
||||
memset( r_ptr, 0, border*sizeof(r_ptr[0]));
|
||||
memset( s_ptr, 0, border*sizeof(s_ptr[0]));
|
||||
@@ -300,22 +291,17 @@ icvStarDetectorComputeResponses( const CvMat* img, CvMat* responses, CvMat* size
|
||||
}
|
||||
}
|
||||
|
||||
cvReleaseMat(&sum);
|
||||
cvReleaseMat(&tilted);
|
||||
cvReleaseMat(&flatTilted);
|
||||
|
||||
return border;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
icvStarDetectorSuppressLines( const CvMat* responses, const CvMat* sizes, CvPoint pt,
|
||||
const CvStarDetectorParams* params )
|
||||
static bool StarDetectorSuppressLines( const Mat& responses, const Mat& sizes, Point pt,
|
||||
int lineThresholdProjected, int lineThresholdBinarized )
|
||||
{
|
||||
const float* r_ptr = responses->data.fl;
|
||||
int rstep = responses->step/sizeof(r_ptr[0]);
|
||||
const short* s_ptr = sizes->data.s;
|
||||
int sstep = sizes->step/sizeof(s_ptr[0]);
|
||||
const float* r_ptr = responses.ptr<float>();
|
||||
int rstep = (int)(responses.step/sizeof(r_ptr[0]));
|
||||
const short* s_ptr = sizes.ptr<short>();
|
||||
int sstep = (int)(sizes.step/sizeof(s_ptr[0]));
|
||||
int sz = s_ptr[pt.y*sstep + pt.x];
|
||||
int x, y, delta = sz/4, radius = delta*4;
|
||||
float Lxx = 0, Lyy = 0, Lxy = 0;
|
||||
@@ -329,7 +315,7 @@ icvStarDetectorSuppressLines( const CvMat* responses, const CvMat* sizes, CvPoin
|
||||
Lxx += Lx*Lx; Lyy += Ly*Ly; Lxy += Lx*Ly;
|
||||
}
|
||||
|
||||
if( (Lxx + Lyy)*(Lxx + Lyy) >= params->lineThresholdProjected*(Lxx*Lyy - Lxy*Lxy) )
|
||||
if( (Lxx + Lyy)*(Lxx + Lyy) >= lineThresholdProjected*(Lxx*Lyy - Lxy*Lxy) )
|
||||
return true;
|
||||
|
||||
for( y = pt.y - radius; y <= pt.y + radius; y += delta )
|
||||
@@ -340,7 +326,7 @@ icvStarDetectorSuppressLines( const CvMat* responses, const CvMat* sizes, CvPoin
|
||||
Lxxb += Lxb * Lxb; Lyyb += Lyb * Lyb; Lxyb += Lxb * Lyb;
|
||||
}
|
||||
|
||||
if( (Lxxb + Lyyb)*(Lxxb + Lyyb) >= params->lineThresholdBinarized*(Lxxb*Lyyb - Lxyb*Lxyb) )
|
||||
if( (Lxxb + Lyyb)*(Lxxb + Lyyb) >= lineThresholdBinarized*(Lxxb*Lyyb - Lxyb*Lxyb) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -348,24 +334,27 @@ icvStarDetectorSuppressLines( const CvMat* responses, const CvMat* sizes, CvPoin
|
||||
|
||||
|
||||
static void
|
||||
icvStarDetectorSuppressNonmax( const CvMat* responses, const CvMat* sizes,
|
||||
CvSeq* keypoints, int border,
|
||||
const CvStarDetectorParams* params )
|
||||
StarDetectorSuppressNonmax( const Mat& responses, const Mat& sizes,
|
||||
vector<KeyPoint>& keypoints, int border,
|
||||
int responseThreshold,
|
||||
int lineThresholdProjected,
|
||||
int lineThresholdBinarized,
|
||||
int suppressNonmaxSize )
|
||||
{
|
||||
int x, y, x1, y1, delta = params->suppressNonmaxSize/2;
|
||||
int rows = responses->rows, cols = responses->cols;
|
||||
const float* r_ptr = responses->data.fl;
|
||||
int rstep = responses->step/sizeof(r_ptr[0]);
|
||||
const short* s_ptr = sizes->data.s;
|
||||
int sstep = sizes->step/sizeof(s_ptr[0]);
|
||||
int x, y, x1, y1, delta = suppressNonmaxSize/2;
|
||||
int rows = responses.rows, cols = responses.cols;
|
||||
const float* r_ptr = responses.ptr<float>();
|
||||
int rstep = (int)(responses.step/sizeof(r_ptr[0]));
|
||||
const short* s_ptr = sizes.ptr<short>();
|
||||
int sstep = (int)(sizes.step/sizeof(s_ptr[0]));
|
||||
short featureSize = 0;
|
||||
|
||||
for( y = border; y < rows - border; y += delta+1 )
|
||||
for( x = border; x < cols - border; x += delta+1 )
|
||||
{
|
||||
float maxResponse = (float)params->responseThreshold;
|
||||
float minResponse = (float)-params->responseThreshold;
|
||||
CvPoint maxPt = {-1,-1}, minPt = {-1,-1};
|
||||
float maxResponse = (float)responseThreshold;
|
||||
float minResponse = (float)-responseThreshold;
|
||||
Point maxPt(-1, -1), minPt(-1, -1);
|
||||
int tileEndY = MIN(y + delta, rows - border - 1);
|
||||
int tileEndX = MIN(x + delta, cols - border - 1);
|
||||
|
||||
@@ -376,12 +365,12 @@ icvStarDetectorSuppressNonmax( const CvMat* responses, const CvMat* sizes,
|
||||
if( maxResponse < val )
|
||||
{
|
||||
maxResponse = val;
|
||||
maxPt = cvPoint(x1, y1);
|
||||
maxPt = Point(x1, y1);
|
||||
}
|
||||
else if( minResponse > val )
|
||||
{
|
||||
minResponse = val;
|
||||
minPt = cvPoint(x1, y1);
|
||||
minPt = Point(x1, y1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,10 +385,11 @@ icvStarDetectorSuppressNonmax( const CvMat* responses, const CvMat* sizes,
|
||||
}
|
||||
|
||||
if( (featureSize = s_ptr[maxPt.y*sstep + maxPt.x]) >= 4 &&
|
||||
!icvStarDetectorSuppressLines( responses, sizes, maxPt, params ))
|
||||
!StarDetectorSuppressLines( responses, sizes, maxPt, lineThresholdProjected,
|
||||
lineThresholdBinarized ))
|
||||
{
|
||||
CvStarKeypoint kpt = cvStarKeypoint( maxPt, featureSize, maxResponse );
|
||||
cvSeqPush( keypoints, &kpt );
|
||||
KeyPoint kpt((float)maxPt.x, (float)maxPt.y, featureSize, -1, maxResponse);
|
||||
keypoints.push_back(kpt);
|
||||
}
|
||||
}
|
||||
skip_max:
|
||||
@@ -414,66 +404,67 @@ icvStarDetectorSuppressNonmax( const CvMat* responses, const CvMat* sizes,
|
||||
}
|
||||
|
||||
if( (featureSize = s_ptr[minPt.y*sstep + minPt.x]) >= 4 &&
|
||||
!icvStarDetectorSuppressLines( responses, sizes, minPt, params ))
|
||||
!StarDetectorSuppressLines( responses, sizes, minPt,
|
||||
lineThresholdProjected, lineThresholdBinarized))
|
||||
{
|
||||
CvStarKeypoint kpt = cvStarKeypoint( minPt, featureSize, minResponse );
|
||||
cvSeqPush( keypoints, &kpt );
|
||||
KeyPoint kpt((float)minPt.x, (float)minPt.y, featureSize, -1, maxResponse);
|
||||
keypoints.push_back(kpt);
|
||||
}
|
||||
}
|
||||
skip_min:
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
CV_IMPL CvSeq*
|
||||
cvGetStarKeypoints( const CvArr* _img, CvMemStorage* storage,
|
||||
CvStarDetectorParams params )
|
||||
{
|
||||
CvMat stub, *img = cvGetMat(_img, &stub);
|
||||
CvSeq* keypoints = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvStarKeypoint), storage );
|
||||
CvMat* responses = cvCreateMat( img->rows, img->cols, CV_32FC1 );
|
||||
CvMat* sizes = cvCreateMat( img->rows, img->cols, CV_16SC1 );
|
||||
|
||||
int border = icvStarDetectorComputeResponses( img, responses, sizes, ¶ms );
|
||||
if( border >= 0 )
|
||||
icvStarDetectorSuppressNonmax( responses, sizes, keypoints, border, ¶ms );
|
||||
|
||||
cvReleaseMat( &responses );
|
||||
cvReleaseMat( &sizes );
|
||||
|
||||
return border >= 0 ? keypoints : 0;
|
||||
}
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
StarDetector::StarDetector()
|
||||
{
|
||||
*(CvStarDetectorParams*)this = cvStarDetectorParams();
|
||||
}
|
||||
|
||||
|
||||
StarDetector::StarDetector(int _maxSize, int _responseThreshold,
|
||||
int _lineThresholdProjected,
|
||||
int _lineThresholdBinarized,
|
||||
int _suppressNonmaxSize)
|
||||
{
|
||||
*(CvStarDetectorParams*)this = cvStarDetectorParams(_maxSize, _responseThreshold,
|
||||
_lineThresholdProjected, _lineThresholdBinarized, _suppressNonmaxSize);
|
||||
}
|
||||
: maxSize(_maxSize), responseThreshold(_responseThreshold),
|
||||
lineThresholdProjected(_lineThresholdProjected),
|
||||
lineThresholdBinarized(_lineThresholdBinarized),
|
||||
suppressNonmaxSize(_suppressNonmaxSize)
|
||||
{}
|
||||
|
||||
void StarDetector::operator()(const Mat& image, vector<KeyPoint>& keypoints) const
|
||||
|
||||
void StarDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
|
||||
{
|
||||
CvMat _image = image;
|
||||
MemStorage storage(cvCreateMemStorage(0));
|
||||
Seq<CvStarKeypoint> kp = cvGetStarKeypoints( &_image, storage, *(const CvStarDetectorParams*)this);
|
||||
Seq<CvStarKeypoint>::iterator it = kp.begin();
|
||||
keypoints.resize(kp.size());
|
||||
size_t i, n = kp.size();
|
||||
for( i = 0; i < n; i++, ++it )
|
||||
Mat grayImage = image;
|
||||
if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY );
|
||||
|
||||
(*this)(grayImage, keypoints);
|
||||
KeyPointsFilter::runByPixelsMask( keypoints, mask );
|
||||
}
|
||||
|
||||
void StarDetector::operator()(const Mat& img, vector<KeyPoint>& keypoints) const
|
||||
{
|
||||
Mat responses, sizes;
|
||||
int border = StarDetectorComputeResponses( img, responses, sizes, maxSize );
|
||||
keypoints.clear();
|
||||
if( border >= 0 )
|
||||
StarDetectorSuppressNonmax( responses, sizes, keypoints, border,
|
||||
responseThreshold, lineThresholdProjected,
|
||||
lineThresholdBinarized, suppressNonmaxSize );
|
||||
}
|
||||
|
||||
|
||||
static Algorithm* createStarDetector() { return new StarDetector; }
|
||||
static AlgorithmInfo star_info("Feature2D.STAR", createStarDetector);
|
||||
|
||||
AlgorithmInfo* StarDetector::info() const
|
||||
{
|
||||
static volatile bool initialized = false;
|
||||
if( !initialized )
|
||||
{
|
||||
const CvStarKeypoint& kpt = *it;
|
||||
keypoints[i] = KeyPoint(kpt.pt, (float)kpt.size, -1.f, kpt.response, 0);
|
||||
star_info.addParam(this, "maxSize", maxSize);
|
||||
star_info.addParam(this, "responseThreshold", responseThreshold);
|
||||
star_info.addParam(this, "lineThresholdProjected", lineThresholdProjected);
|
||||
star_info.addParam(this, "lineThresholdBinarized", lineThresholdBinarized);
|
||||
star_info.addParam(this, "suppressNonmaxSize", suppressNonmaxSize);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
return &star_info;
|
||||
}
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,513 +0,0 @@
|
||||
# x1 y1 x2 y2
|
||||
-1 -2 -1 7
|
||||
-1 -14 3 -3
|
||||
-2 1 2 11
|
||||
6 1 -7 -10
|
||||
2 13 0 -1
|
||||
5 -14 -3 5
|
||||
8 -2 4 2
|
||||
8 -11 5 -15
|
||||
-23 -6 -9 8
|
||||
6 -12 8 -10
|
||||
-1 -3 1 8
|
||||
6 3 6 5
|
||||
-6 -7 -5 5
|
||||
-2 22 -8 -11
|
||||
7 14 5 8
|
||||
14 -1 -14 -5
|
||||
9 -14 0 2
|
||||
-3 7 6 22
|
||||
6 -6 -5 -8
|
||||
9 -5 -1 7
|
||||
-7 -3 -18 -10
|
||||
-5 4 11 0
|
||||
3 2 10 9
|
||||
3 -10 9 4
|
||||
12 0 19 -3
|
||||
15 1 -5 -11
|
||||
-1 14 8 7
|
||||
-23 7 5 -5
|
||||
-6 0 17 -10
|
||||
-4 13 -4 -3
|
||||
1 -12 2 -12
|
||||
8 0 22 3
|
||||
13 -13 -1 3
|
||||
17 -16 10 6
|
||||
15 7 0 -5
|
||||
-12 2 -2 19
|
||||
-6 3 -15 -4
|
||||
3 8 14 0
|
||||
-11 4 5 5
|
||||
-7 11 1 7
|
||||
12 6 3 21
|
||||
2 -3 1 14
|
||||
1 5 11 -5
|
||||
-17 3 2 -6
|
||||
8 6 -10 5
|
||||
-2 -14 4 0
|
||||
-7 5 5 -6
|
||||
4 10 -7 4
|
||||
0 22 -18 7
|
||||
-3 -1 18 0
|
||||
22 -4 3 -5
|
||||
-7 1 -3 2
|
||||
-20 19 -2 17
|
||||
-10 3 24 -8
|
||||
-14 -5 5 7
|
||||
12 -2 -15 -4
|
||||
12 4 -19 0
|
||||
13 20 5 3
|
||||
-12 -8 0 5
|
||||
6 -5 -11 -7
|
||||
-11 6 -22 -3
|
||||
4 15 1 10
|
||||
-4 -7 -6 15
|
||||
10 5 24 0
|
||||
6 3 -2 22
|
||||
14 -13 -4 4
|
||||
8 -13 -22 -18
|
||||
-1 -1 3 -7
|
||||
-12 -19 3 4
|
||||
10 8 -2 13
|
||||
-1 -6 -5 -6
|
||||
-21 2 2 -3
|
||||
-7 4 16 0
|
||||
-5 -6 -1 -12
|
||||
-1 1 18 9
|
||||
10 -7 6 -11
|
||||
3 4 -7 19
|
||||
5 -18 5 -4
|
||||
0 4 4 -20
|
||||
-11 7 12 18
|
||||
17 -20 7 -18
|
||||
15 2 -11 19
|
||||
6 -18 3 -7
|
||||
1 -4 13 -14
|
||||
3 17 -8 2
|
||||
2 -7 6 1
|
||||
-9 17 8 -2
|
||||
-6 -8 12 -1
|
||||
4 -2 6 -1
|
||||
7 -2 8 6
|
||||
-1 -8 -9 -7
|
||||
-9 8 0 15
|
||||
22 0 -15 -4
|
||||
-1 -14 -2 3
|
||||
-4 -7 -7 17
|
||||
-2 -8 -4 9
|
||||
-7 5 7 7
|
||||
13 -5 11 -8
|
||||
-4 11 8 0
|
||||
-11 5 -6 -9
|
||||
-6 2 -20 3
|
||||
2 -6 10 6
|
||||
-6 -6 7 -15
|
||||
-3 -6 1 2
|
||||
0 11 2 -3
|
||||
-12 7 5 14
|
||||
-7 0 -1 -1
|
||||
0 -16 8 6
|
||||
11 22 -3 0
|
||||
0 19 -17 5
|
||||
-14 -23 -19 -13
|
||||
10 -8 -2 -11
|
||||
6 -11 13 -10
|
||||
-7 1 0 14
|
||||
1 -12 -5 -5
|
||||
7 4 -1 8
|
||||
-5 -1 2 15
|
||||
-1 -3 -10 7
|
||||
-6 3 -18 10
|
||||
-13 -7 10 -13
|
||||
-1 1 -10 13
|
||||
14 -19 -14 8
|
||||
-13 -4 1 7
|
||||
-2 1 -7 12
|
||||
-5 3 -5 1
|
||||
-2 -2 -10 8
|
||||
14 2 7 8
|
||||
9 3 2 8
|
||||
1 -9 0 -18
|
||||
0 4 12 1
|
||||
9 0 -10 -14
|
||||
-9 -13 6 -2
|
||||
5 1 10 10
|
||||
-6 -3 -5 -16
|
||||
6 11 0 -5
|
||||
10 -23 2 1
|
||||
-5 13 9 -3
|
||||
-1 -4 -5 -13
|
||||
13 10 8 -11
|
||||
20 19 2 -9
|
||||
-8 4 -9 0
|
||||
10 -14 19 15
|
||||
-12 -14 -3 -10
|
||||
-3 -23 -2 17
|
||||
-11 -3 -14 6
|
||||
-2 19 2 -4
|
||||
5 -5 -13 3
|
||||
-2 2 4 -5
|
||||
4 17 -11 17
|
||||
-2 -7 23 1
|
||||
13 8 -16 1
|
||||
-5 -13 -17 1
|
||||
6 4 -3 -8
|
||||
-9 -5 -10 -2
|
||||
0 -9 -2 -7
|
||||
0 5 2 5
|
||||
-16 -4 3 6
|
||||
-15 2 12 -2
|
||||
-1 4 2 6
|
||||
1 1 -8 -2
|
||||
12 -2 -2 -5
|
||||
8 -8 9 -9
|
||||
-10 2 1 3
|
||||
10 -4 4 -9
|
||||
12 6 5 2
|
||||
-8 -3 5 0
|
||||
1 -13 2 -7
|
||||
-10 -1 -18 7
|
||||
8 -1 -10 -9
|
||||
-1 -23 2 6
|
||||
-3 -5 2 3
|
||||
11 0 -7 -4
|
||||
2 15 -3 -10
|
||||
-8 -20 3 -13
|
||||
-12 -19 -11 5
|
||||
-13 -17 2 -3
|
||||
4 7 0 -12
|
||||
-1 5 -6 -14
|
||||
11 -4 -4 0
|
||||
10 3 -3 7
|
||||
21 13 6 -11
|
||||
24 -12 -4 -7
|
||||
16 4 -14 3
|
||||
5 -3 -12 -7
|
||||
-4 0 -5 7
|
||||
-9 -17 -7 13
|
||||
-6 22 5 -11
|
||||
-8 2 -11 23
|
||||
-10 7 14 -1
|
||||
-10 -3 3 8
|
||||
1 -13 0 -6
|
||||
-21 -7 -14 6
|
||||
19 18 -6 -4
|
||||
7 10 -4 -1
|
||||
21 -1 -5 1
|
||||
6 -10 -2 -11
|
||||
-3 18 7 -1
|
||||
-9 -3 10 -5
|
||||
14 -13 -3 17
|
||||
-19 11 -18 -1
|
||||
-2 8 -23 -18
|
||||
-5 0 -9 -2
|
||||
-11 -4 -8 2
|
||||
6 14 -6 -3
|
||||
0 -3 0 -15
|
||||
4 -9 -9 -15
|
||||
11 -1 11 3
|
||||
-16 -10 7 -7
|
||||
-10 -2 -2 -10
|
||||
-3 -5 -23 5
|
||||
-8 13 -11 -15
|
||||
11 -15 -6 6
|
||||
-3 -16 2 -2
|
||||
12 6 24 -16
|
||||
0 -10 11 8
|
||||
7 -7 -7 -19
|
||||
16 5 -3 9
|
||||
7 9 -16 -7
|
||||
2 3 9 -10
|
||||
1 21 7 8
|
||||
0 7 17 1
|
||||
12 -8 6 9
|
||||
-7 11 -6 -8
|
||||
0 19 3 9
|
||||
-7 1 -11 -5
|
||||
8 0 14 -2
|
||||
-2 12 -6 -15
|
||||
12 4 -21 0
|
||||
-4 17 -7 -6
|
||||
-9 -10 -7 -14
|
||||
-10 -15 -14 -15
|
||||
-5 -7 -12 5
|
||||
0 -4 -4 15
|
||||
2 5 -23 -6
|
||||
-21 -4 4 -6
|
||||
5 -10 6 -15
|
||||
-3 4 5 -1
|
||||
19 -4 -4 -23
|
||||
17 -4 -11 13
|
||||
12 1 -14 4
|
||||
-6 -11 10 -20
|
||||
5 4 20 3
|
||||
-20 -8 1 3
|
||||
9 -19 -3 9
|
||||
15 18 -4 11
|
||||
16 12 7 8
|
||||
-8 -14 9 -3
|
||||
0 -6 -4 2
|
||||
-10 1 2 -1
|
||||
-7 8 18 -6
|
||||
12 9 -23 -7
|
||||
-6 8 2 5
|
||||
6 -9 -7 -12
|
||||
-2 -1 2 -7
|
||||
9 9 15 7
|
||||
2 6 6 -6
|
||||
12 16 19 0
|
||||
3 4 0 6
|
||||
-1 -2 17 2
|
||||
1 8 1 3
|
||||
-1 -12 0 -11
|
||||
2 -11 9 7
|
||||
3 -1 4 -19
|
||||
-11 -1 3 -1
|
||||
-10 1 -4 -10
|
||||
3 -2 11 6
|
||||
7 3 -8 -9
|
||||
-14 24 -10 -2
|
||||
-3 -3 -6 -18
|
||||
-10 -13 -1 -7
|
||||
-7 2 -6 9
|
||||
-4 2 -13 6
|
||||
-4 4 3 -2
|
||||
2 -4 13 9
|
||||
5 -11 -11 -6
|
||||
-2 4 -9 11
|
||||
0 -19 -5 -23
|
||||
-7 -5 -6 -3
|
||||
-4 -6 14 12
|
||||
-11 12 -16 -8
|
||||
15 -21 6 -12
|
||||
-1 -2 16 -8
|
||||
-1 6 -2 -8
|
||||
-1 1 8 -9
|
||||
-4 3 -2 -2
|
||||
0 -7 -8 4
|
||||
-11 11 2 -12
|
||||
3 2 7 11
|
||||
-4 -7 -6 -9
|
||||
-7 3 0 -5
|
||||
-7 3 -5 -10
|
||||
-1 -3 -10 8
|
||||
8 0 1 5
|
||||
0 9 16 1
|
||||
4 8 -3 -11
|
||||
9 -15 17 8
|
||||
2 0 17 -9
|
||||
-11 -6 -3 -10
|
||||
1 1 -8 15
|
||||
-13 -12 4 -2
|
||||
4 -6 -10 -6
|
||||
-7 5 -5 7
|
||||
6 10 9 8
|
||||
7 -5 -3 -18
|
||||
3 -6 4 5
|
||||
-13 -10 -3 -5
|
||||
2 -11 0 -16
|
||||
-21 7 -13 -5
|
||||
-14 -14 -4 -4
|
||||
9 4 -3 7
|
||||
11 4 -4 10
|
||||
17 6 17 9
|
||||
8 -10 -11 0
|
||||
-16 -6 8 -6
|
||||
5 -13 -5 10
|
||||
2 3 16 12
|
||||
-8 13 -6 0
|
||||
0 10 -11 4
|
||||
5 8 -2 10
|
||||
-7 11 3 -13
|
||||
4 2 -3 -7
|
||||
-2 -14 16 -11
|
||||
-6 11 6 7
|
||||
15 -3 -10 8
|
||||
8 -3 -12 12
|
||||
6 -13 7 -14
|
||||
-5 -11 -6 -8
|
||||
-6 7 3 6
|
||||
10 -4 1 5
|
||||
16 9 13 10
|
||||
10 -17 8 2
|
||||
1 -5 -4 4
|
||||
8 -14 2 -5
|
||||
-9 4 -3 -6
|
||||
-7 3 0 -10
|
||||
-8 -2 4 -10
|
||||
5 -8 24 -9
|
||||
-8 2 -9 8
|
||||
17 -4 2 -5
|
||||
0 14 9 -9
|
||||
15 11 5 -6
|
||||
1 -8 4 -3
|
||||
-21 9 2 10
|
||||
-1 2 11 4
|
||||
3 24 -2 2
|
||||
17 -8 -10 -14
|
||||
5 6 7 -13
|
||||
10 11 -1 0
|
||||
6 4 6 -10
|
||||
-2 -12 6 5
|
||||
-1 3 -15 8
|
||||
-4 1 11 -7
|
||||
11 1 0 5
|
||||
-12 6 1 10
|
||||
-2 -3 4 -1
|
||||
-11 -2 12 -1
|
||||
-8 7 -18 -20
|
||||
0 2 2 -9
|
||||
-1 -13 2 -16
|
||||
-1 3 -17 -5
|
||||
8 15 -14 3
|
||||
-12 -13 15 6
|
||||
-8 2 6 2
|
||||
22 6 -23 -3
|
||||
-7 -2 0 -6
|
||||
-10 13 6 -6
|
||||
7 6 12 -10
|
||||
7 -6 11 -2
|
||||
-22 0 -17 -2
|
||||
-1 -4 -14 -11
|
||||
-8 -2 12 7
|
||||
-5 12 -13 7
|
||||
-2 2 6 -7
|
||||
8 0 23 -3
|
||||
12 6 -11 13
|
||||
-10 -21 8 10
|
||||
0 -3 15 7
|
||||
-6 7 -12 -5
|
||||
-10 -21 -11 12
|
||||
-11 -5 -11 8
|
||||
0 5 -1 -11
|
||||
-9 8 -1 7
|
||||
-23 11 -5 21
|
||||
-5 0 6 -8
|
||||
8 -6 12 8
|
||||
5 -7 -2 3
|
||||
-20 -5 9 -12
|
||||
12 -6 3 -11
|
||||
5 4 11 13
|
||||
12 2 -12 13
|
||||
-13 -4 7 4
|
||||
15 0 -16 -3
|
||||
2 -3 14 -2
|
||||
-14 4 -11 16
|
||||
3 -13 10 23
|
||||
-19 9 5 2
|
||||
3 5 -7 14
|
||||
-13 19 15 -11
|
||||
0 14 -5 -2
|
||||
-4 11 -6 0
|
||||
5 -2 -8 -13
|
||||
-15 -11 -17 -7
|
||||
3 1 -8 -10
|
||||
-10 -13 -12 7
|
||||
-13 0 -6 23
|
||||
-17 2 -3 -7
|
||||
3 1 -10 4
|
||||
4 13 -6 14
|
||||
-2 -19 5 -1
|
||||
-8 9 -5 10
|
||||
-1 7 7 5
|
||||
-10 9 0 19
|
||||
5 7 -7 -4
|
||||
1 -11 -11 -1
|
||||
-1 2 11 -4
|
||||
7 -1 -2 2
|
||||
-20 1 -6 -9
|
||||
-18 -4 -18 8
|
||||
-2 -16 -6 7
|
||||
-6 -3 -4 -1
|
||||
-16 0 -5 24
|
||||
-2 -4 9 -1
|
||||
2 -8 15 -6
|
||||
4 11 -3 0
|
||||
6 7 -10 2
|
||||
-9 -7 -6 12
|
||||
15 24 -1 -8
|
||||
-9 15 -15 -3
|
||||
-5 17 -10 11
|
||||
13 -2 4 -15
|
||||
-1 -2 -23 4
|
||||
3 -16 -14 -7
|
||||
-5 -3 -9 -10
|
||||
3 -5 -1 -2
|
||||
4 -1 8 1
|
||||
9 12 -14 9
|
||||
17 -9 0 -3
|
||||
4 5 -6 13
|
||||
-8 -1 10 19
|
||||
-5 8 2 -15
|
||||
-9 -12 -5 -4
|
||||
0 12 4 24
|
||||
-2 8 4 14
|
||||
-4 8 16 -7
|
||||
-1 5 -4 -8
|
||||
18 -2 17 -5
|
||||
-2 8 -2 -9
|
||||
-7 3 -6 1
|
||||
-22 -5 -2 -5
|
||||
-10 -8 1 14
|
||||
-13 -3 9 3
|
||||
-1 -4 0 -1
|
||||
-21 -7 -19 12
|
||||
8 -8 8 24
|
||||
-6 12 3 -2
|
||||
-11 -5 -4 -22
|
||||
5 -3 4 -4
|
||||
24 -16 -9 7
|
||||
23 -10 18 -9
|
||||
12 1 21 17
|
||||
-6 24 -11 -3
|
||||
17 -7 -6 1
|
||||
4 4 -7 2
|
||||
6 14 3 -12
|
||||
0 -6 13 -16
|
||||
5 -10 12 7
|
||||
2 5 -3 6
|
||||
0 7 1 -23
|
||||
-5 15 14 1
|
||||
-1 -3 6 6
|
||||
-9 6 12 -9
|
||||
-2 4 7 -4
|
||||
-5 -4 4 4
|
||||
0 -13 -10 6
|
||||
-12 2 -3 -6
|
||||
0 16 3 -3
|
||||
-14 5 11 6
|
||||
11 5 -13 0
|
||||
5 7 -5 -1
|
||||
4 12 10 6
|
||||
4 -10 -11 -1
|
||||
10 4 5 -14
|
||||
-14 11 0 -13
|
||||
8 2 24 12
|
||||
3 -1 2 -1
|
||||
-14 9 3 -23
|
||||
-6 -8 9 0
|
||||
14 -15 -10 10
|
||||
-6 -10 -5 -7
|
||||
5 11 -15 -3
|
||||
0 1 8 1
|
||||
-6 -11 -18 -4
|
||||
0 9 -4 22
|
||||
-1 -5 4 -9
|
||||
2 -20 6 1
|
||||
2 1 -12 -9
|
||||
15 5 -6 4
|
||||
4 19 11 4
|
||||
-4 17 -1 -8
|
||||
-12 -8 -3 7
|
||||
9 11 1 8
|
||||
22 9 15 -15
|
||||
-7 -7 -23 1
|
||||
13 -5 2 -8
|
||||
-5 3 -11 11
|
||||
-18 3 -5 14
|
||||
7 -20 -23 -10
|
||||
-5 -2 0 6
|
||||
-13 -17 2 -3
|
||||
-1 -6 -2 14
|
||||
-16 -12 6 15
|
||||
-2 -12 -19 3
|
@@ -1,317 +0,0 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <fstream>
|
||||
#include <numeric>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
class CV_DetectorsTest : public cvtest::BaseTest
|
||||
{
|
||||
public:
|
||||
CV_DetectorsTest();
|
||||
~CV_DetectorsTest();
|
||||
protected:
|
||||
void run(int);
|
||||
template <class T> bool testDetector(const Mat& img, const T& detector, vector<KeyPoint>& expected);
|
||||
|
||||
void LoadExpected(const string& file, vector<KeyPoint>& out);
|
||||
};
|
||||
|
||||
CV_DetectorsTest::CV_DetectorsTest()
|
||||
{
|
||||
}
|
||||
CV_DetectorsTest::~CV_DetectorsTest() {}
|
||||
|
||||
void getRotation(const Mat& img, Mat& aff, Mat& out)
|
||||
{
|
||||
Point center(img.cols/2, img.rows/2);
|
||||
aff = getRotationMatrix2D(center, 30, 1);
|
||||
warpAffine( img, out, aff, img.size());
|
||||
}
|
||||
|
||||
void getZoom(const Mat& img, Mat& aff, Mat& out)
|
||||
{
|
||||
const double mult = 1.2;
|
||||
|
||||
aff.create(2, 3, CV_64F);
|
||||
double *data = aff.ptr<double>();
|
||||
data[0] = mult; data[1] = 0; data[2] = 0;
|
||||
data[3] = 0; data[4] = mult; data[5] = 0;
|
||||
|
||||
warpAffine( img, out, aff, img.size());
|
||||
}
|
||||
|
||||
void getBlur(const Mat& img, Mat& aff, Mat& out)
|
||||
{
|
||||
aff.create(2, 3, CV_64F);
|
||||
double *data = aff.ptr<double>();
|
||||
data[0] = 1; data[1] = 0; data[2] = 0;
|
||||
data[3] = 0; data[4] = 1; data[5] = 0;
|
||||
|
||||
GaussianBlur(img, out, Size(5, 5), 2);
|
||||
}
|
||||
|
||||
void getBrightness(const Mat& img, Mat& aff, Mat& out)
|
||||
{
|
||||
aff.create(2, 3, CV_64F);
|
||||
double *data = aff.ptr<double>();
|
||||
data[0] = 1; data[1] = 0; data[2] = 0;
|
||||
data[3] = 0; data[4] = 1; data[5] = 0;
|
||||
|
||||
add(img, Mat(img.size(), img.type(), Scalar(15)), out);
|
||||
}
|
||||
|
||||
void showOrig(const Mat& img, const vector<KeyPoint>& orig_pts)
|
||||
{
|
||||
|
||||
Mat img_color;
|
||||
cvtColor(img, img_color, CV_GRAY2BGR);
|
||||
|
||||
for(size_t i = 0; i < orig_pts.size(); ++i)
|
||||
circle(img_color, orig_pts[i].pt, (int)orig_pts[i].size/2, CV_RGB(0, 255, 0));
|
||||
|
||||
namedWindow("O"); imshow("O", img_color);
|
||||
}
|
||||
|
||||
void show(const string& name, const Mat& new_img, const vector<KeyPoint>& new_pts, const vector<KeyPoint>& transf_pts)
|
||||
{
|
||||
|
||||
Mat new_img_color;
|
||||
cvtColor(new_img, new_img_color, CV_GRAY2BGR);
|
||||
|
||||
for(size_t i = 0; i < transf_pts.size(); ++i)
|
||||
circle(new_img_color, transf_pts[i].pt, (int)transf_pts[i].size/2, CV_RGB(255, 0, 0));
|
||||
|
||||
for(size_t i = 0; i < new_pts.size(); ++i)
|
||||
circle(new_img_color, new_pts[i].pt, (int)new_pts[i].size/2, CV_RGB(0, 0, 255));
|
||||
|
||||
namedWindow(name + "_T"); imshow(name + "_T", new_img_color);
|
||||
}
|
||||
|
||||
struct WrapPoint
|
||||
{
|
||||
const double* R;
|
||||
WrapPoint(const Mat& rmat) : R(rmat.ptr<double>()) { };
|
||||
|
||||
KeyPoint operator()(const KeyPoint& kp) const
|
||||
{
|
||||
KeyPoint res = kp;
|
||||
res.pt.x = static_cast<float>(kp.pt.x * R[0] + kp.pt.y * R[1] + R[2]);
|
||||
res.pt.y = static_cast<float>(kp.pt.x * R[3] + kp.pt.y * R[4] + R[5]);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
struct sortByR { bool operator()(const KeyPoint& kp1, const KeyPoint& kp2) { return norm(kp1.pt) < norm(kp2.pt); } };
|
||||
|
||||
template <class T> bool CV_DetectorsTest::testDetector(const Mat& img, const T& detector, vector<KeyPoint>& exp)
|
||||
{
|
||||
vector<KeyPoint> orig_kpts;
|
||||
detector(img, orig_kpts);
|
||||
|
||||
typedef void (*TransfFunc )(const Mat&, Mat&, Mat& FransfFunc);
|
||||
const TransfFunc transfFunc[] = { getRotation, getZoom, getBlur, getBrightness };
|
||||
//const string names[] = { "Rotation", "Zoom", "Blur", "Brightness" };
|
||||
const size_t case_num = sizeof(transfFunc)/sizeof(transfFunc[0]);
|
||||
|
||||
vector<Mat> affs(case_num);
|
||||
vector<Mat> new_imgs(case_num);
|
||||
|
||||
vector< vector<KeyPoint> > new_kpts(case_num);
|
||||
vector< vector<KeyPoint> > transf_kpts(case_num);
|
||||
|
||||
//showOrig(img, orig_kpts);
|
||||
for(size_t i = 0; i < case_num; ++i)
|
||||
{
|
||||
transfFunc[i](img, affs[i], new_imgs[i]);
|
||||
detector(new_imgs[i], new_kpts[i]);
|
||||
transform(orig_kpts.begin(), orig_kpts.end(), back_inserter(transf_kpts[i]), WrapPoint(affs[i]));
|
||||
//show(names[i], new_imgs[i], new_kpts[i], transf_kpts[i]);
|
||||
}
|
||||
|
||||
const float thres = 3;
|
||||
const float nthres = 3;
|
||||
|
||||
vector<KeyPoint> result;
|
||||
for(size_t i = 0; i < orig_kpts.size(); ++i)
|
||||
{
|
||||
const KeyPoint& okp = orig_kpts[i];
|
||||
int foundCounter = 0;
|
||||
for(size_t j = 0; j < case_num; ++j)
|
||||
{
|
||||
const KeyPoint& tkp = transf_kpts[j][i];
|
||||
|
||||
size_t k = 0;
|
||||
|
||||
for(; k < new_kpts[j].size(); ++k)
|
||||
if (norm(new_kpts[j][k].pt - tkp.pt) < nthres && fabs(new_kpts[j][k].size - tkp.size) < thres)
|
||||
break;
|
||||
|
||||
if (k != new_kpts[j].size())
|
||||
++foundCounter;
|
||||
|
||||
}
|
||||
if (foundCounter == (int)case_num)
|
||||
result.push_back(okp);
|
||||
}
|
||||
|
||||
sort(result.begin(), result.end(), sortByR());
|
||||
sort(exp.begin(), exp.end(), sortByR());
|
||||
|
||||
if (result.size() != exp.size())
|
||||
{
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
|
||||
return false;
|
||||
}
|
||||
|
||||
int foundCounter1 = 0;
|
||||
for(size_t i = 0; i < exp.size(); ++i)
|
||||
{
|
||||
const KeyPoint& e = exp[i];
|
||||
size_t j = 0;
|
||||
for(; j < result.size(); ++j)
|
||||
{
|
||||
const KeyPoint& r = result[i];
|
||||
if (norm(r.pt-e.pt) < nthres && fabs(r.size - e.size) < thres)
|
||||
break;
|
||||
}
|
||||
if (j != result.size())
|
||||
++foundCounter1;
|
||||
}
|
||||
|
||||
int foundCounter2 = 0;
|
||||
for(size_t i = 0; i < result.size(); ++i)
|
||||
{
|
||||
const KeyPoint& r = result[i];
|
||||
size_t j = 0;
|
||||
for(; j < exp.size(); ++j)
|
||||
{
|
||||
const KeyPoint& e = exp[i];
|
||||
if (norm(r.pt-e.pt) < nthres && fabs(r.size - e.size) < thres)
|
||||
break;
|
||||
}
|
||||
if (j != exp.size())
|
||||
++foundCounter2;
|
||||
}
|
||||
//showOrig(img, result); waitKey();
|
||||
|
||||
const float errorRate = 0.9f;
|
||||
if (float(foundCounter1)/exp.size() < errorRate || float(foundCounter2)/result.size() < errorRate)
|
||||
{
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct SurfNoMaskWrap
|
||||
{
|
||||
const SURF& detector;
|
||||
SurfNoMaskWrap(const SURF& surf) : detector(surf) {}
|
||||
SurfNoMaskWrap& operator=(const SurfNoMaskWrap&);
|
||||
void operator()(const Mat& img, vector<KeyPoint>& kpts) const { detector(img, Mat(), kpts); }
|
||||
};
|
||||
|
||||
void CV_DetectorsTest::LoadExpected(const string& file, vector<KeyPoint>& out)
|
||||
{
|
||||
Mat mat_exp;
|
||||
FileStorage fs(file, FileStorage::READ);
|
||||
if (fs.isOpened())
|
||||
{
|
||||
read( fs["ResultVectorData"], mat_exp, Mat() );
|
||||
out.resize(mat_exp.cols / sizeof(KeyPoint));
|
||||
copy(mat_exp.ptr<KeyPoint>(), mat_exp.ptr<KeyPoint>() + out.size(), out.begin());
|
||||
}
|
||||
else
|
||||
{
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA);
|
||||
out.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void CV_DetectorsTest::run( int /*start_from*/ )
|
||||
{
|
||||
Mat img = imread(string(ts->get_data_path()) + "shared/graffiti.png", 0);
|
||||
|
||||
if (img.empty())
|
||||
{
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
|
||||
return;
|
||||
}
|
||||
|
||||
Mat to_test(img.size() * 2, img.type(), Scalar(0));
|
||||
Mat roi = to_test(Rect(img.rows/2, img.cols/2, img.cols, img.rows));
|
||||
img.copyTo(roi);
|
||||
GaussianBlur(to_test, to_test, Size(3, 3), 1.5);
|
||||
|
||||
vector<KeyPoint> exp;
|
||||
LoadExpected(string(ts->get_data_path()) + "detectors/surf.xml", exp);
|
||||
if (exp.empty())
|
||||
return;
|
||||
|
||||
if (!testDetector(to_test, SurfNoMaskWrap(SURF(1536+512+512, 2)), exp))
|
||||
return;
|
||||
|
||||
LoadExpected(string(ts->get_data_path()) + "detectors/star.xml", exp);
|
||||
if (exp.empty())
|
||||
return;
|
||||
|
||||
if (!testDetector(to_test, StarDetector(45, 30, 10, 8, 5), exp))
|
||||
return;
|
||||
|
||||
ts->set_failed_test_info( cvtest::TS::OK);
|
||||
}
|
||||
|
||||
|
||||
TEST(Features2d_Detectors, regression) { CV_DetectorsTest test; test.safe_run(); }
|
||||
|
||||
|
||||
|
@@ -440,7 +440,7 @@ protected:
|
||||
fs.open( string(ts->get_data_path()) + FEATURES2D_DIR + "/keypoints.xml.gz", FileStorage::WRITE );
|
||||
if( fs.isOpened() )
|
||||
{
|
||||
SurfFeatureDetector fd;
|
||||
ORB fd;
|
||||
fd.detect(img, keypoints);
|
||||
write( fs, "keypoints", keypoints );
|
||||
}
|
||||
@@ -491,7 +491,7 @@ private:
|
||||
CV_DescriptorExtractorTest& operator=(const CV_DescriptorExtractorTest&) { return *this; }
|
||||
};
|
||||
|
||||
template<typename T, typename Distance>
|
||||
/*template<typename T, typename Distance>
|
||||
class CV_CalonderDescriptorExtractorTest : public CV_DescriptorExtractorTest<Distance>
|
||||
{
|
||||
public:
|
||||
@@ -506,7 +506,7 @@ protected:
|
||||
new CalonderDescriptorExtractor<T>( string(CV_DescriptorExtractorTest<Distance>::ts->get_data_path()) +
|
||||
FEATURES2D_DIR + "/calonder_classifier.rtc");
|
||||
}
|
||||
};
|
||||
};*/
|
||||
|
||||
/****************************************************************************************\
|
||||
* Algorithmic tests for descriptor matchers *
|
||||
@@ -991,24 +991,12 @@ TEST( Features2d_Detector_MSER, regression )
|
||||
test.safe_run();
|
||||
}
|
||||
|
||||
TEST( Features2d_Detector_SIFT, regression )
|
||||
{
|
||||
CV_FeatureDetectorTest test( "detector-sift", FeatureDetector::create("SIFT") );
|
||||
test.safe_run();
|
||||
}
|
||||
|
||||
TEST( Features2d_Detector_STAR, regression )
|
||||
{
|
||||
CV_FeatureDetectorTest test( "detector-star", FeatureDetector::create("STAR") );
|
||||
test.safe_run();
|
||||
}
|
||||
|
||||
TEST( Features2d_Detector_SURF, regression )
|
||||
{
|
||||
CV_FeatureDetectorTest test( "detector-surf", FeatureDetector::create("SURF") );
|
||||
test.safe_run();
|
||||
}
|
||||
|
||||
TEST( Features2d_Detector_ORB, regression )
|
||||
{
|
||||
CV_FeatureDetectorTest test( "detector-orb", FeatureDetector::create("ORB") );
|
||||
@@ -1027,23 +1015,6 @@ TEST( Features2d_Detector_PyramidFAST, regression )
|
||||
test.safe_run();
|
||||
}
|
||||
|
||||
/*
|
||||
* Descriptors
|
||||
*/
|
||||
TEST( Features2d_DescriptorExtractor_SIFT, regression )
|
||||
{
|
||||
CV_DescriptorExtractorTest<L2<float> > test( "descriptor-sift", 0.03f,
|
||||
DescriptorExtractor::create("SIFT"), 8.06652f );
|
||||
test.safe_run();
|
||||
}
|
||||
|
||||
TEST( Features2d_DescriptorExtractor_SURF, regression )
|
||||
{
|
||||
CV_DescriptorExtractorTest<L2<float> > test( "descriptor-surf", 0.035f,
|
||||
DescriptorExtractor::create("SURF"), 0.147372f );
|
||||
test.safe_run();
|
||||
}
|
||||
|
||||
TEST( Features2d_DescriptorExtractor_ORB, regression )
|
||||
{
|
||||
// TODO adjust the parameters below
|
||||
@@ -1066,13 +1037,6 @@ TEST( Features2d_DescriptorExtractor_BRIEF, regression )
|
||||
test.safe_run();
|
||||
}*/
|
||||
|
||||
TEST( Features2d_DescriptorExtractor_OpponentSURF, regression )
|
||||
{
|
||||
CV_DescriptorExtractorTest<L2<float> > test( "descriptor-opponent-surf", 0.18f,
|
||||
DescriptorExtractor::create("OpponentSURF"), 0.147372f );
|
||||
test.safe_run();
|
||||
}
|
||||
|
||||
#if CV_SSE2
|
||||
TEST( Features2d_DescriptorExtractor_Calonder_uchar, regression )
|
||||
{
|
||||
@@ -1096,7 +1060,7 @@ TEST( Features2d_DescriptorExtractor_Calonder_float, regression )
|
||||
*/
|
||||
TEST( Features2d_DescriptorMatcher_BruteForce, regression )
|
||||
{
|
||||
CV_DescriptorMatcherTest test( "descriptor-matcher-brute-force", new BruteForceMatcher<L2<float> >, 0.01f );
|
||||
CV_DescriptorMatcherTest test( "descriptor-matcher-brute-force", new BFMatcher(NORM_L2), 0.01f );
|
||||
test.safe_run();
|
||||
}
|
||||
|
||||
|
@@ -155,27 +155,24 @@ void CV_MserTest::run(int)
|
||||
{
|
||||
string image_path = string(ts->get_data_path()) + "mser/puzzle.png";
|
||||
|
||||
IplImage* img = cvLoadImage( image_path.c_str());
|
||||
if (!img)
|
||||
Mat img = imread( image_path );
|
||||
if (img.empty())
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "Unable to open image mser/puzzle.png\n");
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_MISSING_TEST_DATA);
|
||||
return;
|
||||
}
|
||||
|
||||
CvSeq* contours;
|
||||
CvMemStorage* storage= cvCreateMemStorage();
|
||||
IplImage* hsv = cvCreateImage( cvGetSize( img ), IPL_DEPTH_8U, 3 );
|
||||
cvCvtColor( img, hsv, CV_BGR2YCrCb );
|
||||
CvMSERParams params = cvMSERParams();//cvMSERParams( 5, 60, cvRound(.2*img->width*img->height), .25, .2 );
|
||||
cvExtractMSER( hsv, NULL, &contours, storage, params );
|
||||
Mat yuv;
|
||||
cvtColor(img, yuv, COLOR_BGR2YCrCb);
|
||||
vector<vector<Point> > msers;
|
||||
MSER()(yuv, msers);
|
||||
|
||||
vector<CvBox2D> boxes;
|
||||
vector<CvBox2D> boxes_orig;
|
||||
for ( int i = 0; i < contours->total; i++ )
|
||||
for ( size_t i = 0; i < msers.size(); i++ )
|
||||
{
|
||||
CvContour* r = *(CvContour**)cvGetSeqElem( contours, i );
|
||||
CvBox2D box = cvFitEllipse2( r );
|
||||
RotatedRect box = fitEllipse(msers[i]);
|
||||
box.angle=(float)CV_PI/2-box.angle;
|
||||
boxes.push_back(box);
|
||||
}
|
||||
@@ -203,10 +200,6 @@ void CV_MserTest::run(int)
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
|
||||
ts->printf( cvtest::TS::LOG, "Incorrect correspondence in box %d\n",n_box);
|
||||
}
|
||||
|
||||
cvReleaseMemStorage(&storage);
|
||||
cvReleaseImage(&hsv);
|
||||
cvReleaseImage(&img);
|
||||
}
|
||||
|
||||
TEST(Features2d_MSER, regression) { CV_MserTest test; test.safe_run(); }
|
||||
|
Reference in New Issue
Block a user