Commit b5a71db7 authored by Maria Dimashova's avatar Maria Dimashova

modified FernClassifier::train(); remove old RTreeClassifier and added new…

modified FernClassifier::train(); remove old RTreeClassifier and added new implementation CalonderClassifier; removed old find_obj_calonder and added new one
parent 1135bc24
...@@ -179,6 +179,17 @@ CVAPI(CvSeq*) cvGetStarKeypoints( const CvArr* img, CvMemStorage* storage, ...@@ -179,6 +179,17 @@ CVAPI(CvSeq*) cvGetStarKeypoints( const CvArr* img, CvMemStorage* storage,
namespace cv namespace cv
{ {
struct CV_EXPORTS DefaultRngAuto
{
const static uint64 def_state = (uint64)-1;
const uint64 old_state;
DefaultRngAuto() : old_state(theRNG().state) { theRNG().state = def_state; }
~DefaultRngAuto() { theRNG().state = old_state; }
DefaultRngAuto& operator=(const DefaultRngAuto&);
};
// CvAffinePose: defines a parameterized affine transformation of an image patch. // CvAffinePose: defines a parameterized affine transformation of an image patch.
// An image patch is rotated on angle phi (in degrees), then scaled lambda1 times // An image patch is rotated on angle phi (in degrees), then scaled lambda1 times
...@@ -395,10 +406,7 @@ public: ...@@ -395,10 +406,7 @@ public:
CV_EXPORTS void FAST( const Mat& image, vector<KeyPoint>& keypoints, int threshold, bool nonmaxSupression=true ); CV_EXPORTS void FAST( const Mat& image, vector<KeyPoint>& keypoints, int threshold, bool nonmaxSupression=true );
/*! /*!
The Patch Generator class The Patch Generator class
*/ */
class CV_EXPORTS PatchGenerator class CV_EXPORTS PatchGenerator
{ {
...@@ -459,9 +467,9 @@ class CV_EXPORTS FernClassifier ...@@ -459,9 +467,9 @@ class CV_EXPORTS FernClassifier
public: public:
FernClassifier(); FernClassifier();
FernClassifier(const FileNode& node); FernClassifier(const FileNode& node);
FernClassifier(const vector<Point2f>& points, FernClassifier(const vector<vector<Point2f> >& points,
const vector<Ptr<Mat> >& refimgs, const vector<Mat>& refimgs,
const vector<int>& labels=vector<int>(), const vector<vector<int> >& labels=vector<vector<int> >(),
int _nclasses=0, int _patchSize=PATCH_SIZE, int _nclasses=0, int _patchSize=PATCH_SIZE,
int _signatureSize=DEFAULT_SIGNATURE_SIZE, int _signatureSize=DEFAULT_SIGNATURE_SIZE,
int _nstructs=DEFAULT_STRUCTS, int _nstructs=DEFAULT_STRUCTS,
...@@ -481,9 +489,9 @@ public: ...@@ -481,9 +489,9 @@ public:
int _nviews=DEFAULT_VIEWS, int _nviews=DEFAULT_VIEWS,
int _compressionMethod=COMPRESSION_NONE, int _compressionMethod=COMPRESSION_NONE,
const PatchGenerator& patchGenerator=PatchGenerator()); const PatchGenerator& patchGenerator=PatchGenerator());
virtual void train(const vector<Point2f>& points, virtual void train(const vector<vector<Point2f> >& points,
const vector<Ptr<Mat> >& refimgs, const vector<Mat>& refimgs,
const vector<int>& labels=vector<int>(), const vector<vector<int> >& labels=vector<vector<int> >(),
int _nclasses=0, int _patchSize=PATCH_SIZE, int _nclasses=0, int _patchSize=PATCH_SIZE,
int _signatureSize=DEFAULT_SIGNATURE_SIZE, int _signatureSize=DEFAULT_SIGNATURE_SIZE,
int _nstructs=DEFAULT_STRUCTS, int _nstructs=DEFAULT_STRUCTS,
...@@ -594,270 +602,127 @@ protected: ...@@ -594,270 +602,127 @@ protected:
FernClassifier fernClassifier; FernClassifier fernClassifier;
}; };
/****************************************************************************************\ /****************************************************************************************\
* Calonder Descriptor * * Calonder Classifier *
\****************************************************************************************/ \****************************************************************************************/
class CV_EXPORTS CalonderClassifier
struct CV_EXPORTS DefaultRngAuto
{
const static uint64 def_state = (uint64)-1;
const uint64 old_state;
DefaultRngAuto() : old_state(theRNG().state) { theRNG().state = def_state; }
~DefaultRngAuto() { theRNG().state = old_state; }
DefaultRngAuto& operator=(const DefaultRngAuto&);
};
/*
A pseudo-random number generator usable with std::random_shuffle.
*/
typedef cv::RNG CalonderRng;
typedef unsigned int int_type;
//----------------------------
//randomized_tree.h
//class RTTester;
//namespace features {
static const size_t DEFAULT_REDUCED_NUM_DIM = 176;
static const float LOWER_QUANT_PERC = .03f;
static const float UPPER_QUANT_PERC = .92f;
static const int PATCH_SIZE = 32;
static const int DEFAULT_DEPTH = 9;
static const int DEFAULT_VIEWS = 5000;
struct RTreeNode;
struct BaseKeypoint
{ {
int x;
int y;
IplImage* image;
BaseKeypoint()
: x(0), y(0), image(NULL)
{}
BaseKeypoint(int x, int y, IplImage* image)
: x(x), y(y), image(image)
{}
};
class CSMatrixGenerator {
public: public:
typedef enum { PDT_GAUSS=1, PDT_BERNOULLI, PDT_DBFRIENDLY } PHI_DISTR_TYPE; CalonderClassifier();
~CSMatrixGenerator(); CalonderClassifier( const vector<vector<Point2f> >& points, const vector<Mat>& refimgs,
static float* getCSMatrix(int m, int n, PHI_DISTR_TYPE dt); // do NOT free returned pointer const vector<vector<int> >& labels=vector<vector<int> >(), int _numClasses=0,
int _pathSize=DEFAULT_PATCH_SIZE,
private: int _numTrees=DEFAULT_NUM_TREES,
static float *cs_phi_; // matrix for compressive sensing int _treeDepth=DEFAULT_TREE_DEPTH,
static int cs_phi_m_, cs_phi_n_; int _numViews=DEFAULT_NUM_VIEWS,
}; int _compressedDim=DEFAULT_COMPRESSED_DIM,
int _compressType=DEFAULT_COMPRESS_TYPE,
int _numQuantBits=DEFAULT_NUM_QUANT_BITS,
template< typename T > const PatchGenerator& patchGenerator=PatchGenerator() );
struct AlignedMemBlock
{ virtual ~CalonderClassifier();
AlignedMemBlock() : raw(NULL), data(NULL) { }; virtual void clear();
// Alloc's an `a` bytes-aligned block good to hold `sz` elements of class T
AlignedMemBlock(const int n, const int a)
{
alloc(n, a);
}
~AlignedMemBlock()
{
free(raw);
}
void alloc(const int n, const int a)
{
uchar* raw = (uchar*)malloc(n*sizeof(T) + a);
int delta = (a - uint64(raw)%a)%a; // # bytes required for padding s.t. we get `a`-aligned
data = reinterpret_cast<T*>(raw + delta);
}
// Methods to access the aligned data. NEVER EVER FREE A RETURNED POINTER!
inline T* p() { return data; }
inline T* operator()() { return data; }
private:
T *raw; // raw block, probably not aligned
T *data; // exposed data, aligned, DO NOT FREE
};
typedef AlignedMemBlock<float> FloatSignature;
typedef AlignedMemBlock<uchar> Signature;
class CV_EXPORTS RandomizedTree
{
public:
friend class RTreeClassifier;
//friend class ::RTTester;
RandomizedTree(); void train( const vector<vector<Point2f> >& points, const vector<Mat>& refimgs,
~RandomizedTree(); const vector<vector<int> >& labels=vector<vector<int> >(), int _nclasses=0,
int _pathSize=DEFAULT_PATCH_SIZE,
int _numTrees=DEFAULT_NUM_TREES,
int _treeDepth=DEFAULT_TREE_DEPTH,
int _numViews=DEFAULT_NUM_VIEWS,
int _compressedDim=DEFAULT_COMPRESSED_DIM,
int _compressType=DEFAULT_COMPRESS_TYPE,
int _numQuantBits=DEFAULT_NUM_QUANT_BITS,
const PatchGenerator& patchGenerator=PatchGenerator() );
void train(std::vector<BaseKeypoint> const& base_set, cv::RNG &rng, virtual void operator()(const Mat& img, Point2f pt, vector<float>& signature, float thresh=0.f) const;
int depth, int views, size_t reduced_num_dim, int num_quant_bits); virtual void operator()(const Mat& patch, vector<float>& signature, float thresh=-1.f) const;
#define QUANTIZATION_AVAILABLE 1
#if QUANTIZATION_AVAILABLE
void quantizePosteriors( int _numQuantBits, bool isClearFloatPosteriors=false );
void clearFloatPosteriors();
virtual void operator()(const Mat& img, Point2f pt, vector<uchar>& signature, uchar thresh=-1.f) const;
virtual void operator()(const Mat& patch, vector<uchar>& signature, uchar thresh=-1.f) const;
#endif
void train(std::vector<BaseKeypoint> const& base_set, cv::RNG &rng, void read( const FileNode& fn );
PatchGenerator &make_patch, int depth, int views, size_t reduced_num_dim, void write( FileStorage& fs ) const;
int num_quant_bits);
// following two funcs are EXPERIMENTAL (do not use unless you know exactly what you do) bool empty() const;
static void quantizeVector(float *vec, int dim, int N, float bnds[2], int clamp_mode=0);
static void quantizeVector(float *src, int dim, int N, float bnds[2], uchar *dst);
// patch_data must be a 32x32 array (no row padding) void setVerbose( bool _verbose );
float* getPosterior(uchar* patch_data);
const float* getPosterior(uchar* patch_data) const;
uchar* getPosterior2(uchar* patch_data);
void read(const char* file_name, int num_quant_bits); int getPatchSize() const;
void read(std::istream &is, int num_quant_bits); int getNumTrees() const;
void write(const char* file_name) const; int getTreeDepth() const;
void write(std::ostream &os) const; int getNumViews() const;
int getSignatureSize() const;
int getCompressType() const;
int getNumQuantBits() const;
int getOrigNumClasses() const;
inline int classes() { return classes_; }
inline int depth() { return depth_; }
inline void applyQuantization(int num_quant_bits) { makePosteriors2(num_quant_bits); } enum
{
COMPRESS_NONE = -1,
COMPRESS_DISTR_GAUSS = 0,
COMPRESS_DISTR_BERNOULLI = 1,
COMPRESS_DISTR_DBFRIENDLY = 2,
};
// debug static float GET_LOWER_QUANT_PERC() { return .03f; }
void savePosteriors(std::string url, bool append=false); static float GET_UPPER_QUANT_PERC() { return .92f; }
void savePosteriors2(std::string url, bool append=false);
enum
{
MAX_NUM_QUANT_BITS = 8,
DEFAULT_PATCH_SIZE = 32,
DEFAULT_NUM_TREES = 48,
DEFAULT_TREE_DEPTH = 9,
DEFAULT_NUM_VIEWS = 500,
DEFAULT_COMPRESSED_DIM = 176,
DEFAULT_COMPRESS_TYPE = COMPRESS_DISTR_BERNOULLI,
DEFAULT_NUM_QUANT_BITS = -1,
};
private: private:
int classes_; void prepare( int _patchSize, int _signatureSize, int _numTrees, int _treeDepth, int _numViews );
int depth_;
int num_leaves_;
std::vector<RTreeNode> nodes_;
//float **posteriors_; // 16-bytes aligned posteriors
//uchar **posteriors2_; // 16-bytes aligned posteriors
FloatSignature *posteriors_;
Signature *posteriors2_;
std::vector<int> leaf_counts_;
void createNodes(int num_nodes, cv::RNG &rng);
void allocPosteriorsAligned(int num_leaves, int num_classes);
void freePosteriors(int which); // which: 1=posteriors_, 2=posteriors2_, 3=both
void init(int classes, int depth, cv::RNG &rng);
void addExample(int class_id, uchar* patch_data);
void finalize(size_t reduced_num_dim, int num_quant_bits);
int getIndex(uchar* patch_data) const;
inline float* getPosteriorByIndex(int index);
inline uchar* getPosteriorByIndex2(int index);
inline const float* getPosteriorByIndex(int index) const;
//void makeRandomMeasMatrix(float *cs_phi, PHI_DISTR_TYPE dt, size_t reduced_num_dim);
void convertPosteriorsToChar();
void makePosteriors2(int num_quant_bits);
void compressLeaves(size_t reduced_num_dim);
void estimateQuantPercForPosteriors(float perc[2]);
};
struct RTreeNode int getLeafIdx( int treeIdx, const Mat& patch ) const;
{ void finalize( int _compressedDim, int _compressType, int _numQuantBits,
short offset1, offset2; const vector<int>& leafSampleCounters);
RTreeNode() {}
RTreeNode(uchar x1, uchar y1, uchar x2, uchar y2)
: offset1(y1*PATCH_SIZE + x1),
offset2(y2*PATCH_SIZE + x2)
{}
//! Left child on 0, right child on 1
inline bool operator() (uchar* patch_data) const
{
return patch_data[offset1] > patch_data[offset2];
}
};
void compressLeaves( int _compressedDim, int _compressType );
bool verbose;
//} // namespace features int patchSize;
//---------------------------- int signatureSize;
//rtree_classifier.h int numTrees;
//class RTTester; int treeDepth;
int numViews;
//namespace features { int origNumClasses;
int compressType;
int numQuantBits;
class CV_EXPORTS RTreeClassifier int numLeavesPerTree;
{ int numNodesPerTree;
public:
//friend class ::RTTester;
static const int DEFAULT_TREES = 80;
static const size_t DEFAULT_NUM_QUANT_BITS = 4;
//static const int SIG_LEN = 176;
RTreeClassifier();
//modified
void train(std::vector<BaseKeypoint> const& base_set,
cv::RNG &rng,
int num_trees = RTreeClassifier::DEFAULT_TREES,
int depth = DEFAULT_DEPTH,
int views = DEFAULT_VIEWS,
size_t reduced_num_dim = DEFAULT_REDUCED_NUM_DIM,
int num_quant_bits = DEFAULT_NUM_QUANT_BITS,
bool print_status = true);
void train(std::vector<BaseKeypoint> const& base_set,
cv::RNG &rng,
PatchGenerator &make_patch,
int num_trees = DEFAULT_TREES,
int depth = DEFAULT_DEPTH,
int views = DEFAULT_VIEWS,
size_t reduced_num_dim = DEFAULT_REDUCED_NUM_DIM,
int num_quant_bits = DEFAULT_NUM_QUANT_BITS,
bool print_status = true);
// sig must point to a memory block of at least classes()*sizeof(float|uchar) bytes
void getSignature(IplImage *patch, uchar *sig);
void getSignature(IplImage *patch, float *sig);
void getSparseSignature(IplImage *patch, float *sig, float thresh);
// TODO: deprecated in favor of getSignature overload, remove
void getFloatSignature(IplImage *patch, float *sig) { getSignature(patch, sig); }
static int countNonZeroElements(float *vec, int n, double tol=1e-10);
static inline void safeSignatureAlloc(uchar **sig, int num_sig=1, int sig_len=176);
static inline uchar* safeSignatureAlloc(int num_sig=1, int sig_len=176);
inline int classes() const { return classes_; }
inline int original_num_classes() { return original_num_classes_; }
void setQuantization(int num_quant_bits);
void discardFloatPosteriors();
void read(const char* file_name);
void read(std::istream &is);
void write(const char* file_name) const;
void write(std::ostream &os) const;
// experimental and debug
void saveAllFloatPosteriors(std::string file_url);
void saveAllBytePosteriors(std::string file_url);
void setFloatPosteriorsFromTextfile_176(std::string url);
float countZeroElements();
std::vector<RandomizedTree> trees_;
private: struct Node
int classes_; {
int num_quant_bits_; uchar x1, y1, x2, y2;
uchar **posteriors_; Node() : x1(0), y1(0), x2(0), y2(0) {}
ushort *ptemp_; Node( uchar _x1, uchar _y1, uchar _x2, uchar _y2 ) : x1(_x1), y1(_y1), x2(_x2), y2(_y2)
int original_num_classes_; {}
bool keep_floats_; int operator() (const Mat_<uchar>& patch) const
{ return patch(y1,x1) > patch(y2, x2) ? 1 : 0; }
};
vector<Node> nodes;
vector<float> posteriors;
#if QUANTIZATION_AVAILABLE
vector<uchar> quantizedPosteriors;
#endif
}; };
/****************************************************************************************\ /****************************************************************************************\
* One-Way Descriptor * * One-Way Descriptor *
\****************************************************************************************/ \****************************************************************************************/
...@@ -1004,7 +869,7 @@ protected: ...@@ -1004,7 +869,7 @@ protected:
CvAffinePose* m_affine_poses; // an array of poses CvAffinePose* m_affine_poses; // an array of poses
CvMat** m_transforms; // an array of affine transforms corresponding to poses CvMat** m_transforms; // an array of affine transforms corresponding to poses
std::string m_feature_name; // the name of the feature associated with the descriptor string m_feature_name; // the name of the feature associated with the descriptor
CvPoint m_center; // the coordinates of the feature (the center of the input image ROI) CvPoint m_center; // the coordinates of the feature (the center of the input image ROI)
int m_pca_dim_high; // the number of descriptor pca components to use for generating affine poses int m_pca_dim_high; // the number of descriptor pca components to use for generating affine poses
...@@ -1275,7 +1140,8 @@ public: ...@@ -1275,7 +1140,8 @@ public:
* *
* image The image. * image The image.
* keypoints The detected keypoints. * keypoints The detected keypoints.
* mask Mask specifying where to look for keypoints (optional). Must be a char matrix with non-zero values in the region of interest. * mask Mask specifying where to look for keypoints (optional). Must be a char
* matrix with non-zero values in the region of interest.
*/ */
void detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const void detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const
{ {
...@@ -1430,8 +1296,8 @@ public: ...@@ -1430,8 +1296,8 @@ public:
*/ */
virtual void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const = 0; virtual void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const = 0;
virtual void read (const FileNode&) {}; virtual void read( const FileNode& ) {};
virtual void write (FileStorage&) const {}; virtual void write( FileStorage& ) const {};
protected: protected:
/* /*
...@@ -1451,9 +1317,9 @@ public: ...@@ -1451,9 +1317,9 @@ public:
int firstOctave=SIFT::CommonParams::DEFAULT_FIRST_OCTAVE, int firstOctave=SIFT::CommonParams::DEFAULT_FIRST_OCTAVE,
int angleMode=SIFT::CommonParams::FIRST_ANGLE ); int angleMode=SIFT::CommonParams::FIRST_ANGLE );
virtual void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors) const; virtual void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const;
virtual void read (const FileNode &fn); virtual void read( const FileNode &fn );
virtual void write (FileStorage &fs) const; virtual void write( FileStorage &fs ) const;
protected: protected:
SIFT sift; SIFT sift;
...@@ -1465,14 +1331,56 @@ public: ...@@ -1465,14 +1331,56 @@ public:
SurfDescriptorExtractor( int nOctaves=4, SurfDescriptorExtractor( int nOctaves=4,
int nOctaveLayers=2, bool extended=false ); int nOctaveLayers=2, bool extended=false );
virtual void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors) const; virtual void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const;
virtual void read (const FileNode &fn); virtual void read( const FileNode &fn );
virtual void write (FileStorage &fs) const; virtual void write( FileStorage &fs ) const;
protected: protected:
SURF surf; SURF surf;
}; };
#if 0
template<typename T>
class CalonderDescriptorExtractor : public DescriptorExtractor
{
public:
CalonderDescriptorExtractor( const string& classifierFile );
virtual void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const;
virtual void read( const FileNode &fn );
virtual void write( FileStorage &fs ) const;
protected:
RTreeClassifier classifier_;
static const int BORDER_SIZE = 16;
};
template<typename T>
CalonderDescriptorExtractor<T>::CalonderDescriptorExtractor(const std::string& classifier_file)
{
classifier_.read( classifier_file.c_str() );
}
template<typename T>
void CalonderDescriptorExtractor<T>::compute( const cv::Mat& image,
std::vector<cv::KeyPoint>& keypoints,
cv::Mat& descriptors) const
{
// Cannot compute descriptors for keypoints on the image border.
removeBorderKeypoints(keypoints, image.size(), BORDER_SIZE);
/// @todo Check 16-byte aligned
descriptors.create(keypoints.size(), classifier_.classes(), cv::DataType<T>::type);
IplImage ipl = (IplImage)image;
for (size_t i = 0; i < keypoints.size(); ++i) {
cv::Point2f keypt = keypoints[i].pt;
cv::WImageView1_b patch = features::extractPatch(&ipl, keypt);
classifier_.getSignature(patch.Ipl(), descriptors.ptr<T>(i));
}
}
#endif
CV_EXPORTS Ptr<DescriptorExtractor> createDescriptorExtractor( const string& descriptorExtractorType ); CV_EXPORTS Ptr<DescriptorExtractor> createDescriptorExtractor( const string& descriptorExtractorType );
/****************************************************************************************\ /****************************************************************************************\
...@@ -1533,7 +1441,7 @@ struct CV_EXPORTS L1 ...@@ -1533,7 +1441,7 @@ struct CV_EXPORTS L1
/****************************************************************************************\ /****************************************************************************************\
* DMatch * * DMatch *
\****************************************************************************************/ \****************************************************************************************/
/* /*
* Struct for matching: match index and distance between descriptors * Struct for matching: match index and distance between descriptors
...@@ -1591,8 +1499,7 @@ public: ...@@ -1591,8 +1499,7 @@ public:
* mask Mask specifying permissible matches. * mask Mask specifying permissible matches.
* matches Indices of the closest matches from the training set * matches Indices of the closest matches from the training set
*/ */
void match( const Mat& query, const Mat& mask, void match( const Mat& query, const Mat& mask, vector<int>& matches ) const;
vector<int>& matches ) const;
/* /*
* Find the best match for each descriptor from a query set * Find the best match for each descriptor from a query set
...@@ -1613,8 +1520,7 @@ public: ...@@ -1613,8 +1520,7 @@ public:
* mask Mask specifying permissible matches. * mask Mask specifying permissible matches.
* matches DMatches of the closest matches from the training set * matches DMatches of the closest matches from the training set
*/ */
void match( const Mat& query, const Mat& mask, void match( const Mat& query, const Mat& mask, vector<DMatch>& matches ) const;
vector<DMatch>& matches ) const;
/* /*
* Find many matches for each descriptor from a query set * Find many matches for each descriptor from a query set
...@@ -1638,7 +1544,7 @@ public: ...@@ -1638,7 +1544,7 @@ public:
* threshold Distance threshold for descriptors matching * threshold Distance threshold for descriptors matching
*/ */
void match( const Mat& query, const Mat& mask, void match( const Mat& query, const Mat& mask,
vector<vector<DMatch> >& matches, float threshold ) const; vector<vector<DMatch> >& matches, float threshold ) const;
...@@ -1878,7 +1784,7 @@ void BruteForceMatcher<Distance>::matchImpl( const Mat& descriptors_1, const Mat ...@@ -1878,7 +1784,7 @@ void BruteForceMatcher<Distance>::matchImpl( const Mat& descriptors_1, const Mat
template<> template<>
void BruteForceMatcher<L2<float> >::matchImpl( const Mat& descriptors_1, const Mat& descriptors_2, void BruteForceMatcher<L2<float> >::matchImpl( const Mat& descriptors_1, const Mat& descriptors_2,
const Mat& mask, vector<int>& matches ) const; const Mat& mask, vector<int>& matches ) const;
CV_EXPORTS Ptr<DescriptorMatcher> createDescriptorMatcher( const string& descriptorMatcherType ); CV_EXPORTS Ptr<DescriptorMatcher> createDescriptorMatcher( const string& descriptorMatcherType );
...@@ -2036,10 +1942,10 @@ public: ...@@ -2036,10 +1942,10 @@ public:
virtual void clear (); virtual void clear ();
// Reads match object from a file node // Reads match object from a file node
virtual void read (const FileNode &fn); virtual void read( const FileNode &fn );
// Writes match object to a file storage // Writes match object to a file storage
virtual void write (FileStorage& fs) const; virtual void write( FileStorage& fs ) const;
protected: protected:
Ptr<OneWayDescriptorBase> base; Ptr<OneWayDescriptorBase> base;
...@@ -2049,6 +1955,7 @@ protected: ...@@ -2049,6 +1955,7 @@ protected:
/* /*
* CalonderDescriptorMatch * CalonderDescriptorMatch
*/ */
#if 0
class CV_EXPORTS CalonderDescriptorMatch : public GenericDescriptorMatch class CV_EXPORTS CalonderDescriptorMatch : public GenericDescriptorMatch
{ {
public: public:
...@@ -2113,6 +2020,7 @@ protected: ...@@ -2113,6 +2020,7 @@ protected:
Ptr<RTreeClassifier> classifier; Ptr<RTreeClassifier> classifier;
Params params; Params params;
}; };
#endif
/* /*
* FernDescriptorMatch * FernDescriptorMatch
...@@ -2178,6 +2086,7 @@ protected: ...@@ -2178,6 +2086,7 @@ protected:
}; };
CV_EXPORTS Ptr<GenericDescriptorMatch> createGenericDescriptorMatch( const string& genericDescritptorMatchType, const string &paramsFilename = string () ); CV_EXPORTS Ptr<GenericDescriptorMatch> createGenericDescriptorMatch( const string& genericDescritptorMatchType, const string &paramsFilename = string () );
/****************************************************************************************\ /****************************************************************************************\
* VectorDescriptorMatch * * VectorDescriptorMatch *
\****************************************************************************************/ \****************************************************************************************/
...@@ -2199,63 +2108,27 @@ public: ...@@ -2199,63 +2108,27 @@ public:
void index(); void index();
// Calculates descriptors for a set of keypoints from a single image // Calculates descriptors for a set of keypoints from a single image
virtual void add( const Mat& image, vector<KeyPoint>& keypoints ) virtual void add( const Mat& image, vector<KeyPoint>& keypoints );
{
Mat descriptors;
extractor->compute( image, keypoints, descriptors );
matcher->add( descriptors );
collection.add( Mat(), keypoints );
};
// Matches a set of keypoints with the training set // Matches a set of keypoints with the training set
virtual void match( const Mat& image, vector<KeyPoint>& points, vector<int>& keypointIndices ) virtual void match( const Mat& image, vector<KeyPoint>& points, vector<int>& keypointIndices );
{
Mat descriptors;
extractor->compute( image, points, descriptors );
matcher->match( descriptors, keypointIndices );
};
virtual void match( const Mat& image, vector<KeyPoint>& points, vector<DMatch>& matches ) virtual void match( const Mat& image, vector<KeyPoint>& points, vector<DMatch>& matches );
{
Mat descriptors;
extractor->compute( image, points, descriptors );
matcher->match( descriptors, matches );
}
virtual void match( const Mat& image, vector<KeyPoint>& points, vector<vector<DMatch> >& matches, float threshold )
{
Mat descriptors;
extractor->compute( image, points, descriptors );
matcher->match( descriptors, matches, threshold );
}
virtual void clear() virtual void match( const Mat& image, vector<KeyPoint>& points,
{ vector<vector<DMatch> >& matches, float threshold );
GenericDescriptorMatch::clear();
matcher->clear();
}
virtual void read (const FileNode& fn) virtual void clear();
{ virtual void read( const FileNode& fn );
GenericDescriptorMatch::read(fn); virtual void write( FileStorage& fs ) const;
extractor->read (fn);
}
virtual void write (FileStorage& fs) const
{
GenericDescriptorMatch::write(fs);
extractor->write (fs);
}
protected: protected:
Ptr<DescriptorExtractor> extractor; Ptr<DescriptorExtractor> extractor;
Ptr<DescriptorMatcher> matcher; Ptr<DescriptorMatcher> matcher;
//vector<int> classIds; //vector<int> classIds;
}; };
struct CV_EXPORTS DrawMatchesFlags struct CV_EXPORTS DrawMatchesFlags
{ {
enum{ DEFAULT = 0, // Output image matrix will be created (Mat::create), enum{ DEFAULT = 0, // Output image matrix will be created (Mat::create),
......
...@@ -40,967 +40,525 @@ ...@@ -40,967 +40,525 @@
// //
//M*/ //M*/
#include "precomp.hpp" #include "precomp.hpp"
#include <cstdio>
#include <opencv2/core/wimage.hpp>
#include <vector>
#include <iostream> #include <iostream>
#include <cmath>
#include <cassert>
#include <fstream>
#include <cstring>
using namespace cv;
#if defined _MSC_VER && _MSC_VER >= 1400
#pragma warning(disable: 4244 4267)
#endif
/****************************************************************************************\ using namespace std;
The code below is implementation of Calonder Descriptor and RTree Classifier
originally introduced by Michael Calonder.
The code was integrated into OpenCV by Alexey Latyshev const int progressBarSize = 50;
\****************************************************************************************/ namespace cv
{
namespace cv { CalonderClassifier::CalonderClassifier()
{
verbose = false;
//---------------------------- clear();
//randomized_tree.cpp }
inline uchar* getData(IplImage* image)
{
return reinterpret_cast<uchar*>(image->imageData);
}
inline float* RandomizedTree::getPosteriorByIndex(int index) CalonderClassifier::~CalonderClassifier()
{ {}
return const_cast<float*>(const_cast<const RandomizedTree*>(this)->getPosteriorByIndex(index));
} CalonderClassifier::CalonderClassifier( const vector<vector<Point2f> >& points, const vector<Mat>& refimgs,
const vector<vector<int> >& labels, int _numClasses,
int _pathSize, int _numTrees, int _treeDepth,
int _numViews, int _compressedDim, int _compressType, int _numQuantBits,
const PatchGenerator &patchGenerator )
{
verbose = false;
train( points, refimgs, labels, _numClasses, _pathSize, _numTrees, _treeDepth, _numViews,
_compressedDim, _compressType, _numQuantBits, patchGenerator );
}
inline const float* RandomizedTree::getPosteriorByIndex(int index) const int CalonderClassifier::getPatchSize() const
{ { return patchSize; }
return posteriors_[index].p();
}
inline uchar* RandomizedTree::getPosteriorByIndex2(int index) int CalonderClassifier::getNumTrees() const
{ { return numTrees; }
return posteriors2_[index].p();
}
int CalonderClassifier::getTreeDepth() const
{ return treeDepth; }
template < typename PointT > int CalonderClassifier::getNumViews() const
cv::WImageView1_b extractPatch(cv::WImageView1_b const& image, PointT pt, int patch_sz = PATCH_SIZE) { return numViews; }
{
const int offset = patch_sz / 2;
// TODO: WImage{C}.View really should have const version int CalonderClassifier::getSignatureSize() const
cv::WImageView1_b &img_ref = const_cast< cv::WImageView1_b& >(image); { return signatureSize; }
return img_ref.View(pt.x - offset, pt.y - offset, patch_sz, patch_sz);
}
template < typename PointT > int CalonderClassifier::getCompressType() const
cv::WImageView3_b extractPatch3(cv::WImageView3_b const& image, PointT pt) { return compressType; }
{
static const int offset = PATCH_SIZE / 2;
// TODO: WImage{C}.View really should have const version int CalonderClassifier::getNumQuantBits() const
cv::WImageView3_b &img_ref = const_cast< cv::WImageView3_b& >(image); { return numQuantBits; }
return img_ref.View(pt.x - offset, pt.y - offset,
PATCH_SIZE, PATCH_SIZE);
}
float *CSMatrixGenerator::cs_phi_ = NULL; int CalonderClassifier::getOrigNumClasses() const
int CSMatrixGenerator::cs_phi_m_ = 0; { return origNumClasses; }
int CSMatrixGenerator::cs_phi_n_ = 0;
RandomizedTree::RandomizedTree() void CalonderClassifier::setVerbose( bool _verbose )
: posteriors_(NULL), posteriors2_(NULL) {
{ verbose = _verbose;
} }
RandomizedTree::~RandomizedTree() void CalonderClassifier::clear()
{ {
freePosteriors(3); patchSize = numTrees = origNumClasses = signatureSize = treeDepth = numViews = numQuantBits = 0;
} compressType = COMPRESS_NONE;
void RandomizedTree::createNodes(int num_nodes, cv::RNG &rng) nodes.clear();
{ posteriors.clear();
nodes_.reserve(num_nodes); #if QUANTIZATION_AVAILABLE
for (int i = 0; i < num_nodes; ++i) { quantizedPosteriors.clear();
nodes_.push_back( RTreeNode(rng(PATCH_SIZE), #endif
rng(PATCH_SIZE), }
rng(PATCH_SIZE),
rng(PATCH_SIZE)) );
}
}
int RandomizedTree::getIndex(uchar* patch_data) const bool CalonderClassifier::empty() const
{ {
int index = 0; return posteriors.empty() && quantizedPosteriors.empty();
for (int d = 0; d < depth_; ++d) { }
int child_offset = nodes_[index](patch_data);
index = 2*index + 1 + child_offset;
}
return index - nodes_.size();
}
void RandomizedTree::train(std::vector<BaseKeypoint> const& base_set, void CalonderClassifier::prepare( int _patchSize, int _signatureSize, int _numTrees, int _treeDepth, int _numViews )
cv::RNG &rng, int depth, int views, size_t reduced_num_dim, {
int num_quant_bits) clear();
{
//CalonderPatchGenerator make_patch(NULL, rng); patchSize = _patchSize;
PatchGenerator make_patch = PatchGenerator(); signatureSize = _signatureSize;
train(base_set, rng, make_patch, depth, views, reduced_num_dim, num_quant_bits); numTrees = _numTrees;
} treeDepth = _treeDepth;
numViews = _numViews;
void RandomizedTree::train(std::vector<BaseKeypoint> const& base_set, numLeavesPerTree = 1 << treeDepth; // 2^d
cv::RNG &rng, PatchGenerator &make_patch, numNodesPerTree = numLeavesPerTree - 1; // 2^d - 1
int depth, int views, size_t reduced_num_dim,
int num_quant_bits)
{
init(base_set.size(), depth, rng);
Mat patch; nodes = vector<Node>( numTrees*numNodesPerTree );
posteriors = vector<float>( numTrees*numLeavesPerTree*signatureSize, 0.f );
}
// Estimate posterior probabilities using random affine views static int calcNumPoints( const vector<vector<Point2f> >& points )
std::vector<BaseKeypoint>::const_iterator keypt_it; {
int class_id = 0; int count = 0;
for (keypt_it = base_set.begin(); keypt_it != base_set.end(); ++keypt_it, ++class_id) { for( size_t i = 0; i < points.size(); i++ )
for (int i = 0; i < views; ++i) { count += points[i].size();
return count;
}
void CalonderClassifier::train( const vector<vector<Point2f> >& points, const vector<Mat>& refimgs,
const vector<vector<int> >& labels, int _numClasses,
int _patchSize, int _numTrees, int _treeDepth, int _numViews,
int _compressedDim, int _compressType, int _numQuantBits,
const PatchGenerator &patchGenerator )
{
if( points.empty() || refimgs.size() != points.size() )
CV_Error( CV_StsBadSize, "points vector must be no empty and refimgs must have the same size as points" );
if( _patchSize < 5 || _patchSize >= 256 )
CV_Error( CV_StsBadArg, "patchSize must be in [5, 255]");
if( _numTrees <= 0 || _treeDepth <= 0 )
CV_Error( CV_StsBadArg, "numTrees, treeDepth, numViews must be positive");
int numPoints = calcNumPoints( points );
if( !labels.empty() && ( labels.size() != points.size() || _numClasses <=0 || _numClasses > numPoints ) )
CV_Error( CV_StsBadArg, "labels has incorrect size or _numClasses is not in [1, numPoints]");
_numViews = std::max( 1, _numViews );
int _origNumClasses = labels.empty() ? numPoints : _numClasses;
if( verbose )
{
cout << "Using train parameters:" << endl;
cout << " patchSize=" << _patchSize << endl;
cout << " numTrees=" << _numTrees << endl;
cout << " treeDepth=" << _treeDepth << endl;
cout << " numViews=" << _numViews << endl;
cout << " compressedDim=" << _compressedDim << endl;
cout << " compressType=" << _compressType << endl;
cout << " numQuantBits=" << _numQuantBits << endl;
cout << endl
<< " numPoints=" << numPoints << endl;
cout << " origNumClasses=" << _origNumClasses << endl;
}
prepare( _patchSize, _origNumClasses, _numTrees, _treeDepth, _numViews );
origNumClasses = _origNumClasses;
vector<int> leafSampleCounters = vector<int>( numTrees*numLeavesPerTree, 0 );
// generate nodes
RNG rng = theRNG();
for( int i = 0; i < numTrees*numNodesPerTree; i++ )
{
uchar x1 = rng(_patchSize);
uchar y1 = rng(_patchSize);
uchar x2 = rng(_patchSize);
uchar y2 = rng(_patchSize);
nodes[i] = Node(x1, y1, x2, y2);
}
Size size( patchSize, patchSize );
Mat patch;
if( verbose ) cout << "START training..." << endl;
for( size_t treeIdx = 0; treeIdx < (size_t)numTrees; treeIdx++ )
{
if( verbose ) cout << "< tree " << treeIdx << endl;
int globalPointIdx = 0;
int* treeLeafSampleCounters = &leafSampleCounters[treeIdx*numLeavesPerTree];
float* treePosteriors = &posteriors[treeIdx*numLeavesPerTree*signatureSize];
for( size_t imgIdx = 0; imgIdx < points.size(); imgIdx++ )
{
const Point2f* imgPoints = &points[imgIdx][0];
const int* imgLabels = labels.empty() ? 0 : &labels[imgIdx][0];
int last = -1, cur;
for( size_t pointIdx = 0; pointIdx < points[imgIdx].size(); pointIdx++, globalPointIdx++ )
{
int classID = imgLabels==0 ? globalPointIdx : imgLabels[pointIdx];
Point2f pt = imgPoints[pointIdx];
const Mat& src = refimgs[imgIdx];
make_patch(keypt_it->image, Point2f(keypt_it->x,keypt_it->y) ,patch, Size(PATCH_SIZE,PATCH_SIZE),rng); if( verbose && (cur = (int)((float)globalPointIdx/numPoints*progressBarSize)) != last )
{
last = cur;
cout << ".";
cout.flush();
}
IplImage _patch = patch; CV_Assert( classID >= 0 && classID < signatureSize );
addExample(class_id, getData(&_patch)); for( int v = 0; v < numViews; v++ )
{
patchGenerator( src, pt, patch, size, rng );
// add sample
int leafIdx = getLeafIdx( treeIdx, patch );
treeLeafSampleCounters[leafIdx]++;
treePosteriors[leafIdx*signatureSize + classID]++;
}
} }
} }
finalize(reduced_num_dim, num_quant_bits); if( verbose ) cout << endl << ">" << endl;
}
void RandomizedTree::allocPosteriorsAligned(int num_leaves, int num_classes)
{
printf("alloc posteriors aligned\n");
freePosteriors(3);
posteriors_ = new FloatSignature[num_leaves];
for (int i=0; i<num_leaves; ++i)
posteriors_[i].alloc(num_classes, 16);
//(float**) malloc(num_leaves*sizeof(float*));
//for (int i=0; i<num_leaves; ++i) {
// //added
// /* err_cnt += posix_memalign((void**)&posteriors_[i], 16, num_classes*sizeof(float));*/
// posteriors_[i] = (float*)malloc(num_classes*sizeof(float));
// memset(posteriors_[i], 0, num_classes*sizeof(float));
//}
posteriors2_ = new Signature[num_leaves];
for (int i=0; i<num_leaves; ++i)
posteriors2_[i].alloc(num_classes, 16);
//for (int i=0; i<num_leaves; ++i) {
// //added
// /* err_cnt += posix_memalign((void**)&posteriors2_[i], 16, num_classes*sizeof(uchar)); */
// posteriors2_[i] = (uchar*)malloc(num_classes*sizeof(uchar));
// memset(posteriors2_[i], 0, num_classes*sizeof(uchar));
//}
//if (err_cnt) {
// printf("Something went wrong in posix_memalign()! err_cnt=%i\n", err_cnt);
// exit(0);
//}
classes_ = num_classes;
}
void RandomizedTree::freePosteriors(int which)
{
if (posteriors_ && (which&1)) {
//for (int i=0; i<num_leaves_; ++i) {
// if (posteriors_[i]) {
// free(posteriors_[i]); //delete [] posteriors_[i];
// posteriors_[i] = NULL;
// }
//}
delete [] posteriors_;
posteriors_ = NULL;
}
if (posteriors2_ && (which&2)) {
//for (int i=0; i<num_leaves_; ++i)
// free(posteriors2_[i]);
delete [] posteriors2_;
posteriors2_ = NULL;
}
classes_ = -1;
} }
void RandomizedTree::init(int num_classes, int depth, cv::RNG &rng) _compressedDim = std::max( 0, std::min(signatureSize, _compressedDim) );
{ _numQuantBits = std::max( 0, std::min((int)MAX_NUM_QUANT_BITS, _numQuantBits) );
depth_ = depth; finalize( _compressedDim, _compressType, _numQuantBits, leafSampleCounters );
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].p(), 0, num_classes*sizeof(float));
leaf_counts_.resize(num_leaves_);
for (int i = 0; i < num_leaves_; ++i)
memset((void*)posteriors2_[i].p(), 0, num_classes*sizeof(uchar));
createNodes(num_nodes, rng); if( verbose ) cout << "END training." << endl;
} }
void RandomizedTree::addExample(int class_id, uchar* patch_data) int CalonderClassifier::getLeafIdx( int treeIdx, const Mat& patch ) const
{
const Node* treeNodes = &nodes[treeIdx*numNodesPerTree];
int idx = 0;
for( int d = 0; d < treeDepth-1; d++ )
{ {
int index = getIndex(patch_data); int offset = treeNodes[idx](patch);
float* posterior = getPosteriorByIndex(index); idx = 2*idx + 1 + offset;
++leaf_counts_[index];
++posterior[class_id];
} }
return idx;
}
void RandomizedTree::finalize(size_t reduced_num_dim, int num_quant_bits) void CalonderClassifier::finalize( int _compressedDim, int _compressType, int _numQuantBits,
const vector<int>& leafSampleCounters )
{
for( int ti = 0; ti < numTrees; ti++ )
{ {
const int* treeLeafSampleCounters = &leafSampleCounters[ti*numLeavesPerTree];
float* treePosteriors = &posteriors[ti*numLeavesPerTree*signatureSize];
// Normalize by number of patches to reach each leaf // Normalize by number of patches to reach each leaf
for (int index = 0; index < num_leaves_; ++index) { for( int li = 0; li < numLeavesPerTree; li++ )
float* posterior = posteriors_[index].p();
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", 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, 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(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)
{ {
//added (inside cycle) int sampleCount = treeLeafSampleCounters[li];
//float *post = getPosteriorByIndex(i); if( sampleCount != 0 )
// float *prod = &cs_posteriors[i*reduced_num_dim];
// cblas_sgemv(CblasRowMajor, CblasNoTrans, reduced_num_dim, classes_, 1.f, cs_phi,
// classes_, post, 1, 0.f, prod, 1);
float *post = getPosteriorByIndex(i);
//Matrix multiplication
for (int idx = 0; idx < (int)reduced_num_dim; idx++)
{ {
cs_posteriors[i*reduced_num_dim+idx] = 0.0f; float normalizer = 1.0f / sampleCount;
for (int col = 0; col < classes_; col++) int leafPosteriorIdx = li*signatureSize;
{ for( int ci = 0; ci < signatureSize; ci++ )
cs_posteriors[i*reduced_num_dim+idx] += cs_phi[idx*reduced_num_dim + col] * post[col]; treePosteriors[leafPosteriorIdx + ci] *= normalizer;
} }
}
} }
// copy new posteriors
freePosteriors(3);
allocPosteriorsAligned(num_leaves_, reduced_num_dim);
for (int i=0; i<num_leaves_; ++i)
memcpy(posteriors_[i].p(), &cs_posteriors[i*reduced_num_dim], reduced_num_dim*sizeof(float));
classes_ = reduced_num_dim;
delete [] cs_posteriors;
} }
void RandomizedTree::makePosteriors2(int num_quant_bits) // apply compressive sensing
if( _compressedDim > 0 && _compressedDim < signatureSize )
compressLeaves( _compressedDim, _compressType );
else
{ {
int N = (1<<num_quant_bits) - 1; if( verbose )
cout << endl << "[WARNING] NO compression to leaves applied, because _compressedDim=" << _compressedDim << endl;
float perc[2];
estimateQuantPercForPosteriors(perc);
assert(posteriors_ != NULL);
for (int i=0; i<num_leaves_; ++i)
quantizeVector(posteriors_[i].p(), classes_, N, perc, posteriors2_[i].p());
// printf("makePosteriors2 quantization bounds: %.3e, %.3e (num_leaves=%i, N=%i)\n",
// perc[0], perc[1], num_leaves_, N);
} }
// convert float-posteriors to uchar-posteriors (quantization step)
float* RandomizedTree::getPosterior(uchar* patch_data) #if QUANTIZATION_AVAILABLE
if( _numQuantBits > 0 )
quantizePosteriors( _numQuantBits );
else
{ {
return const_cast<float*>(const_cast<const RandomizedTree*>(this)->getPosterior(patch_data)); if( verbose )
cout << endl << "[WARNING] NO quantization to posteriors, because _numQuantBits=" << _numQuantBits << endl;
} }
#endif
}
const float* RandomizedTree::getPosterior(uchar* patch_data) const Mat createCompressionMatrix( int rows, int cols, int distrType )
{ {
return getPosteriorByIndex( getIndex(patch_data) ); Mat mtr( rows, cols, CV_32FC1 );
} assert( rows <= cols );
uchar* RandomizedTree::getPosterior2(uchar* patch_data) RNG rng(23);
{
return getPosteriorByIndex2( getIndex(patch_data) );
}
void RandomizedTree::quantizeVector(float *vec, int dim, int N, float bnds[2], int clamp_mode) if( distrType == CalonderClassifier::COMPRESS_DISTR_GAUSS )
{ {
float map_bnd[2] = {0.f,(float)N}; // bounds of quantized target interval we're mapping to float sigma = 1./rows;
for (int k=0; k<dim; ++k, ++vec) { for( int y = 0; y < rows; y++ )
*vec = float(int((*vec - bnds[0])/(bnds[1] - bnds[0])*(map_bnd[1] - map_bnd[0]) + map_bnd[0])); for( int x = 0; x < cols; x++ )
// 0: clamp both, lower and upper values mtr.at<float>(y,x) = rng.gaussian( sigma );
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);
}
}
} }
else if( distrType == CalonderClassifier::COMPRESS_DISTR_BERNOULLI )
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 float par = (float)(1./sqrt(rows));
int tmp; for( int y = 0; y < rows; y++ )
for (int k=0; k<dim; ++k) { for( int x = 0; x < cols; x++ )
tmp = int((*vec - bnds[0])/(bnds[1] - bnds[0])*(map_bnd[1] - map_bnd[0]) + map_bnd[0]); mtr.at<float>(y,x) = rng(2)==0 ? par : -par;
*dst = (uchar)((tmp<0) ? 0 : ((tmp>N) ? N : tmp));
++vec;
++dst;
}
} }
else if( distrType == CalonderClassifier::COMPRESS_DISTR_DBFRIENDLY )
void RandomizedTree::read(const char* file_name, int num_quant_bits)
{ {
std::ifstream file(file_name, std::ifstream::binary); float par = (float)sqrt(3./rows);
read(file, num_quant_bits); for( int y = 0; y < rows; y++ )
file.close(); for( int x = 0; x < cols; x++ )
{
int rng6 = rng(6);
mtr.at<float>(y,x) = rng6==0 ? par : (rng6==1 ? -par : 0.f);
}
} }
else
CV_Assert( 0 );
void RandomizedTree::read(std::istream &is, int num_quant_bits) return mtr;
{ }
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_); void CalonderClassifier::compressLeaves( int _compressedDim, int _compressType )
//freePosteriors(3); {
//printf("[DEBUG] reading: %i leaves, %i classes\n", num_leaves_, classes_); if( verbose )
allocPosteriorsAligned(num_leaves_, classes_); cout << endl << "[OK] compressing leaves with matrix " << _compressedDim << " x " << signatureSize << endl;
for (int i=0; i<num_leaves_; i++)
is.read((char*)posteriors_[i].p(), classes_ * sizeof(*posteriors_[0].p()));
// make char-posteriors from float-posteriors Mat compressionMtrT = (createCompressionMatrix( _compressedDim, signatureSize, _compressType )).t();
makePosteriors2(num_quant_bits);
}
void RandomizedTree::write(const char* file_name) const vector<float> comprPosteriors( numTrees*numLeavesPerTree*_compressedDim, 0);
{ Mat( numTrees*numLeavesPerTree, _compressedDim, CV_32FC1, &comprPosteriors[0] ) =
std::ofstream file(file_name, std::ofstream::binary); Mat( numTrees*numLeavesPerTree, signatureSize, CV_32FC1, &posteriors[0]) * compressionMtrT;
write(file);
file.close();
}
void RandomizedTree::write(std::ostream &os) const posteriors.resize( comprPosteriors.size() );
{ copy( comprPosteriors.begin(), comprPosteriors.end(), posteriors.begin() );
if (!posteriors_) {
printf("WARNING: Cannot write float posteriors cause posteriors_ == NULL\n");
return;
}
os.write((char*)(&classes_), sizeof(classes_)); signatureSize = _compressedDim;
os.write((char*)(&depth_), sizeof(depth_)); compressType = _compressType;
}
os.write((char*)(&nodes_[0]), nodes_.size() * sizeof(nodes_[0])); #if QUANTIZATION_AVAILABLE
for (int i=0; i<num_leaves_; i++) { static float percentile( const float* data, int n, float p )
os.write((char*)posteriors_[i].p(), classes_ * sizeof(*posteriors_[0].p())); {
} assert( n>0 );
} assert( p>=0 && p<=1 );
vector<float> vec( data, data+n );
sort(vec.begin(), vec.end());
int ix = (int)(p*(n-1));
return vec[ix];
}
void RandomizedTree::savePosteriors(std::string url, bool append) void quantizeVector( const float* src, int dim, float fbounds[2], uchar ubounds[2], uchar* dst )
{ {
std::ofstream file(url.c_str(), (append?std::ios::app:std::ios::out)); assert( fbounds[0] < fbounds[1] );
for (int i=0; i<num_leaves_; i++) { assert( ubounds[0] < ubounds[1] );
float *post = posteriors_[i].p();
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) float normFactor = 1.f/(fbounds[1] - fbounds[0]);
for( int i = 0; i < dim; i++ )
{ {
std::ofstream file(url.c_str(), (append?std::ios::app:std::ios::out)); float part = (src[i] - fbounds[0]) * normFactor;
for (int i=0; i<num_leaves_; i++) { assert( 0 <= part && part <= 1 ) ;
uchar *post = posteriors2_[i].p(); uchar val = ubounds[0] + (uchar)( part*ubounds[1] );
for (int i=0; i<classes_; i++) dst[i] = std::max( 0, (int)std::min(ubounds[1], val) );
file << int(*post++) << (i<classes_-1?" ":"");
file << std::endl;
}
file.close();
} }
}
// returns the p% percentile of data (length n vector) void CalonderClassifier::quantizePosteriors( int _numQuantBits, bool isClearFloatPosteriors )
static float percentile(float *data, int n, float p) {
{ uchar ubounds[] = { 0, (uchar)((1<<_numQuantBits)-1) };
assert(n>0); float fbounds[] = { 0.f, 0.f };
assert(p>=0 && p<=1);
std::vector<float> vec(data, data+n);
sort(vec.begin(), vec.end());
int ix = (int)(p*(n-1));
return vec[ix];
}
void RandomizedTree::estimateQuantPercForPosteriors(float perc[2]) int totalLeavesCount = numTrees*numLeavesPerTree;
for( int li = 0; li < totalLeavesCount; li++ ) // TODO for some random choosen leaves !
{ {
// _estimate_ percentiles for this tree fbounds[0] += percentile( &posteriors[li*signatureSize], signatureSize, GET_LOWER_QUANT_PERC() );
// TODO: do this more accurately fbounds[1] += percentile( &posteriors[li*signatureSize], signatureSize, GET_UPPER_QUANT_PERC() );
assert(posteriors_ != NULL);
perc[0] = perc[1] = .0f;
for (int i=0; i<num_leaves_; i++) {
perc[0] += percentile(posteriors_[i].p(), classes_, LOWER_QUANT_PERC);
perc[1] += percentile(posteriors_[i].p(), classes_, UPPER_QUANT_PERC);
}
perc[0] /= num_leaves_;
perc[1] /= num_leaves_;
} }
fbounds[0] /= totalLeavesCount;
fbounds[1] /= totalLeavesCount;
float* CSMatrixGenerator::getCSMatrix(int m, int n, PHI_DISTR_TYPE dt) quantizedPosteriors.resize( posteriors.size() );
{ quantizeVector( &posteriors[0], posteriors.size(), fbounds, ubounds, &quantizedPosteriors[0] );
assert(m <= n);
if (cs_phi_m_!=m || cs_phi_n_!=n || cs_phi_==NULL) { if( isClearFloatPosteriors )
if (cs_phi_) delete [] cs_phi_; clearFloatPosteriors();
cs_phi_ = new float[m*n]; }
}
#if 0 // debug - load the random matrix from a file (for reproducability of results) void CalonderClassifier::clearFloatPosteriors()
//assert(m == 176); {
//assert(n == 500); quantizedPosteriors.clear();
//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; #endif
if (!warned) {
printf("[NOTE] RT: reading %ix%i PHI matrix from '%s'...\n", m, n, phi);
warned=true;
}
void CalonderClassifier::operator()( const Mat& img, Point2f pt, vector<float>& signature, float thresh ) const
{
if( img.empty() || img.type() != CV_8UC1 )
return; return;
#endif
float *cs_phi = cs_phi_; Mat patch;
getRectSubPix(img, Size(patchSize,patchSize), pt, patch, img.type());
(*this)( patch, signature, thresh );
}
if (m == n) { void CalonderClassifier::operator()( const Mat& patch, vector<float>& signature, float thresh ) const
// special case - set to 0 for safety {
memset(cs_phi, 0, m*n*sizeof(float)); if( posteriors.empty() || patch.empty() || patch.type() != CV_8UC1 || patch.cols < patchSize || patch.rows < patchSize )
printf("[WARNING] %s:%i: square CS matrix (-> no reduction)\n", __FILE__, __LINE__); return;
}
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);
//modified
cv::RNG _rng;
for (int i=0; i<m*n; ++i)
{
*cs_phi++ = (float)_rng.gaussian((double)par);//sample_normal<float>(0., 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) {
//added
int _i = rng(6);
*cs_phi++ = (_i==0 ? par : (_i==1 ? -par : 0.f));
}
}
else
throw("PHI_DISTR_TYPE not implemented.");
}
return cs_phi_; int treePostSize = numLeavesPerTree*signatureSize;
}
CSMatrixGenerator::~CSMatrixGenerator() signature.resize( signatureSize, 0.f );
float* sig = &signature[0];
for( int ti = 0; ti < numTrees; ti++ )
{ {
if (cs_phi_) delete [] cs_phi_; int leafIdx = getLeafIdx( ti, patch );
cs_phi_ = NULL; const float* post = &posteriors[ti*treePostSize + leafIdx*signatureSize];
for( int ci = 0; ci < signatureSize; ci++ )
sig[ci] += post[ci];
} }
float coef = 1.f/numTrees;
for( int ci = 0; ci < signatureSize; ci++ )
//} // namespace features
//----------------------------
//rtree_classifier.cpp
//namespace features {
// Returns 16-byte aligned signatures that can be passed to getSignature().
// Release by calling free() - NOT delete!
//
// note: 1) for num_sig>1 all signatures will still be 16-byte aligned, as
// long as sig_len%16 == 0 holds.
// 2) casting necessary, otherwise it breaks gcc's strict aliasing rules
inline void RTreeClassifier::safeSignatureAlloc(uchar **sig, int num_sig, int sig_len)
{ {
assert(sig_len == 176); sig[ci] *= coef;
void *p_sig; if( sig[ci] < thresh )
//added sig[ci] = 0;
// posix_memalign(&p_sig, 16, num_sig*sig_len*sizeof(uchar));
p_sig = malloc(num_sig*sig_len*sizeof(uchar));
*sig = reinterpret_cast<uchar*>(p_sig);
} }
}
inline uchar* RTreeClassifier::safeSignatureAlloc(int num_sig, int sig_len) #if QUANTIZATION_AVAILABLE
{ void CalonderClassifier::operator()( const Mat& img, Point2f pt, vector<uchar>& signature, uchar thresh ) const
uchar *sig; {
safeSignatureAlloc(&sig, num_sig, sig_len); if( img.empty() || img.type() != CV_8UC1 )
return sig; return;
}
inline void add(int size, const float* src1, const float* src2, float* dst) Mat patch;
{ getRectSubPix(img, Size(patchSize,patchSize), pt, patch, img.type());
while(--size >= 0) { (*this)(patch, signature, thresh );
*dst = *src1 + *src2; }
++dst; ++src1; ++src2;
}
}
inline void add(int size, const ushort* src1, const uchar* src2, ushort* dst) void CalonderClassifier::operator()( const Mat& patch, vector<uchar>& signature, uchar thresh ) const
{ {
while(--size >= 0) { if( quantizedPosteriors.empty() || patch.empty() || patch.type() != CV_8UC1 || patch.cols > patchSize || patch.rows > patchSize )
*dst = *src1 + *src2; return;
++dst; ++src1; ++src2;
} int treePostSize = numLeavesPerTree*signatureSize;
}
RTreeClassifier::RTreeClassifier() vector<float> sum( signatureSize, 0.f );
: classes_(0) for( int ti = 0; ti < numTrees; ti++ )
{ {
posteriors_ = NULL; int leafIdx = getLeafIdx( ti, patch );
const uchar* post = &quantizedPosteriors[ti*treePostSize + leafIdx*signatureSize];
for( int ci = 0; ci < signatureSize; ci++ )
sum[ci] += post[ci];
} }
float coef = 1.f/numTrees;
void RTreeClassifier::train(std::vector<BaseKeypoint> const& base_set, signature.resize( signatureSize );
cv::RNG &rng, int num_trees, int depth, uchar* sig = &signature[0];
int views, size_t reduced_num_dim, for( int ci = 0; ci < signatureSize; ci++ )
int num_quant_bits, bool print_status)
{ {
PatchGenerator make_patch = PatchGenerator(); sig[ci] = (uchar)(sum[ci]*coef);
train(base_set, rng, make_patch, num_trees, depth, views, reduced_num_dim, num_quant_bits, print_status); if( sig[ci] < thresh )
sig[ci] = 0;
} }
}
#endif
// Single-threaded version of train(), with progress output void CalonderClassifier::read( const FileNode& fn )
void RTreeClassifier::train(std::vector<BaseKeypoint> const& base_set, {
cv::RNG &rng, PatchGenerator &make_patch, int num_trees, prepare( fn["patchSize"], fn["signatureSize"], fn["numTrees"], fn["treeDepth"], fn["numViews"] );
int depth, int views, size_t reduced_num_dim, origNumClasses = fn["origNumClasses"];
int num_quant_bits, bool print_status) compressType = fn["compressType"];
{ int _numQuantBits = fn["numQuantBits"];
if (reduced_num_dim > base_set.size()) {
if (print_status)
{
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; for( int ti = 0; ti < numTrees; ti++ )
classes_ = reduced_num_dim; // base_set.size(); {
original_num_classes_ = base_set.size(); stringstream treeName;
trees_.resize(num_trees); treeName << "tree" << ti;
if (print_status) FileNode treeFN = fn["trees"][treeName.str()];
{
printf("[OK] Training trees: base size=%i, reduced size=%i\n", (int)base_set.size(), (int)reduced_num_dim);
}
int count = 1; Node* treeNodes = &nodes[ti*numNodesPerTree];
if (print_status) FileNodeIterator nodesFNIter = treeFN["nodes"].begin();
{ for( int ni = 0; ni < numNodesPerTree; ni++ )
printf("[OK] Trained 0 / %i trees", num_trees); fflush(stdout);
}
//added
//BOOST_FOREACH( RandomizedTree &tree, trees_ ) {
//tree.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);
for (int i=0; i<(int)trees_.size(); i++)
{ {
trees_[i].train(base_set, rng, make_patch, depth, views, reduced_num_dim, num_quant_bits_); Node* node = treeNodes + ni;
if (print_status) nodesFNIter >> node->x1 >> node->y1 >> node->x2 >> node->y2;
{
printf("\r[OK] Trained %i / %i trees", count++, num_trees);
fflush(stdout);
}
} }
if (print_status) FileNode posteriorsFN = treeFN["posteriors"];
for( int li = 0; li < numLeavesPerTree; li++ )
{ {
printf("\n"); stringstream leafName;
countZeroElements(); leafName << "leaf" << li;
printf("\n\n"); float* post = &posteriors[ti*numLeavesPerTree*signatureSize + li*signatureSize];
FileNodeIterator leafFNIter = posteriorsFN[leafName.str()].begin();
for( int ci = 0; ci < signatureSize; ci++ )
leafFNIter >> post[ci];
} }
} }
#if QUANTIZATION_AVAILABLE
void RTreeClassifier::getSignature(IplImage* patch, float *sig) if( _numQuantBits )
{ quantizePosteriors(_numQuantBits);
// Need pointer to 32x32 patch data
uchar buffer[PATCH_SIZE * PATCH_SIZE];
uchar* patch_data;
if (patch->widthStep != PATCH_SIZE) {
//printf("[INFO] patch is padded, data will be copied (%i/%i).\n",
// patch->widthStep, PATCH_SIZE);
uchar* data = getData(patch);
patch_data = buffer;
for (int i = 0; i < PATCH_SIZE; ++i) {
memcpy((void*)patch_data, (void*)data, PATCH_SIZE);
data += patch->widthStep;
patch_data += PATCH_SIZE;
}
patch_data = buffer;
}
else {
patch_data = getData(patch);
}
memset((void*)sig, 0, classes_ * sizeof(float));
std::vector<RandomizedTree>::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 = 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++)
add(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 #endif
} }
// sum up 50 byte vectors of length 176
// assume 5 bits max for input vector values
// final shift is 3 bits right
//void sum_50c_176c(uchar **pp, uchar *sig)
//{
//}
void RTreeClassifier::getSignature(IplImage* patch, uchar *sig)
{
// Need pointer to 32x32 patch data
uchar buffer[PATCH_SIZE * PATCH_SIZE];
uchar* patch_data;
if (patch->widthStep != PATCH_SIZE) {
//printf("[INFO] patch is padded, data will be copied (%i/%i).\n",
// patch->widthStep, PATCH_SIZE);
uchar* data = getData(patch);
patch_data = buffer;
for (int i = 0; i < PATCH_SIZE; ++i) {
memcpy((void*)patch_data, (void*)data, PATCH_SIZE);
data += patch->widthStep;
patch_data += PATCH_SIZE;
}
patch_data = buffer;
} else {
patch_data = getData(patch);
}
std::vector<RandomizedTree>::iterator tree_it;
// get posteriors void CalonderClassifier::write( FileStorage& fs ) const
if (posteriors_ == NULL) {
if( !fs.isOpened() )
return;
fs << "patchSize" << patchSize;
fs << "numTrees" << numTrees;
fs << "treeDepth" << treeDepth;
fs << "numViews" << numViews;
fs << "origNumClasses" << origNumClasses;
fs << "signatureSize" << signatureSize;
fs << "compressType" << compressType;
fs << "numQuantBits" << numQuantBits;
fs << "trees" << "{";
for( int ti = 0; ti < numTrees; ti++ )
{
stringstream treeName;
treeName << "tree" << ti;
fs << treeName.str() << "{";
fs << "nodes" << "[:";
const Node* treeNodes = &nodes[ti*numNodesPerTree];
for( int ni = 0; ni < numNodesPerTree; ni++ )
{ {
posteriors_ = new uchar*[trees_.size()]; const Node* node = treeNodes + ni;
//aadded fs << node->x1 << node->y1 << node->x2 << node->y2;
// posix_memalign((void **)&ptemp_, 16, classes_*sizeof(ushort));
ptemp_ = (ushort*)malloc(classes_*sizeof(ushort));
}
uchar **pp = posteriors_;
for (tree_it = trees_.begin(); tree_it != trees_.end(); ++tree_it, pp++)
*pp = tree_it->getPosterior2(patch_data);
pp = posteriors_;
#if 0 // 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]));
ushort *sig16 = new ushort[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++)
add(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)
{
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);
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_);
} }
fs << "]"; // nodes
printf("[OK] Loaded RTC, quantization=%i bits\n", num_quant_bits_); fs << "posteriors" << "{";
for( int li = 0; li < numLeavesPerTree; li++ )
countZeroElements(); {
} stringstream leafName;
leafName << "leaf" << li;
void RTreeClassifier::write(const char* file_name) const fs << leafName.str() << "[:";
{ const float* post = &posteriors[ti*numLeavesPerTree*signatureSize + li*signatureSize];
std::ofstream file(file_name, std::ofstream::binary); for( int ci = 0; ci < signatureSize; ci++ )
write(file); {
file.close(); fs << post[ci];
}
void RTreeClassifier::write(std::ostream &os) const
{
int num_trees = 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());
} }
fs << "]"; // leaf
} }
classes_ = 176; fs << "}"; // posteriors
fs << "}"; // tree
//setQuantization(num_quant_bits_);
ifs.close();
printf("[EXPERIMENTAL] read entire tree from '%s'\n", url.c_str());
}
float RTreeClassifier::countZeroElements()
{
int flt_zeros = 0;
int ui8_zeros = 0;
int 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<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.*flt_zeros/num_elem;
float ui8_perc = 100.*ui8_zeros/num_elem;
printf("[OK] RTC: overall %i/%i (%.3f%%) zeros in float leaves\n", flt_zeros, num_elem, flt_perc);
printf(" overall %i/%i (%.3f%%) zeros in uint8 leaves\n", ui8_zeros, 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;
} }
fs << "}"; // trees
}
//} // namespace features
} }
...@@ -144,7 +144,7 @@ void DescriptorExtractor::removeBorderKeypoints( vector<KeyPoint>& keypoints, ...@@ -144,7 +144,7 @@ void DescriptorExtractor::removeBorderKeypoints( vector<KeyPoint>& keypoints,
} }
/****************************************************************************************\ /****************************************************************************************\
* SiftDescriptorExtractor * * SiftDescriptorExtractor *
\****************************************************************************************/ \****************************************************************************************/
SiftDescriptorExtractor::SiftDescriptorExtractor( double magnification, bool isNormalize, bool recalculateAngles, SiftDescriptorExtractor::SiftDescriptorExtractor( double magnification, bool isNormalize, bool recalculateAngles,
int nOctaves, int nOctaveLayers, int firstOctave, int angleMode ) int nOctaves, int nOctaveLayers, int firstOctave, int angleMode )
...@@ -188,7 +188,7 @@ void SiftDescriptorExtractor::write (FileStorage &fs) const ...@@ -188,7 +188,7 @@ void SiftDescriptorExtractor::write (FileStorage &fs) const
} }
/****************************************************************************************\ /****************************************************************************************\
* SurfDescriptorExtractor * * SurfDescriptorExtractor *
\****************************************************************************************/ \****************************************************************************************/
SurfDescriptorExtractor::SurfDescriptorExtractor( int nOctaves, SurfDescriptorExtractor::SurfDescriptorExtractor( int nOctaves,
int nOctaveLayers, bool extended ) int nOctaveLayers, bool extended )
...@@ -228,6 +228,10 @@ void SurfDescriptorExtractor::write( FileStorage &fs ) const ...@@ -228,6 +228,10 @@ void SurfDescriptorExtractor::write( FileStorage &fs ) const
fs << "extended" << surf.extended; fs << "extended" << surf.extended;
} }
/****************************************************************************************\
* Factory functions for descriptor extractor and matcher creating *
\****************************************************************************************/
Ptr<DescriptorExtractor> createDescriptorExtractor( const string& descriptorExtractorType ) Ptr<DescriptorExtractor> createDescriptorExtractor( const string& descriptorExtractorType )
{ {
DescriptorExtractor* de = 0; DescriptorExtractor* de = 0;
...@@ -270,7 +274,9 @@ Ptr<DescriptorMatcher> createDescriptorMatcher( const string& descriptorMatcherT ...@@ -270,7 +274,9 @@ Ptr<DescriptorMatcher> createDescriptorMatcher( const string& descriptorMatcherT
return dm; return dm;
} }
/****************************************************************************************\
* BruteForceMatcher L2 specialization *
\****************************************************************************************/
template<> template<>
void BruteForceMatcher<L2<float> >::matchImpl( const Mat& descriptors_1, const Mat& descriptors_2, void BruteForceMatcher<L2<float> >::matchImpl( const Mat& descriptors_1, const Mat& descriptors_2,
const Mat& /*mask*/, vector<int>& matches ) const const Mat& /*mask*/, vector<int>& matches ) const
...@@ -317,7 +323,6 @@ void BruteForceMatcher<L2<float> >::matchImpl( const Mat& descriptors_1, const M ...@@ -317,7 +323,6 @@ void BruteForceMatcher<L2<float> >::matchImpl( const Mat& descriptors_1, const M
#endif #endif
} }
/****************************************************************************************\ /****************************************************************************************\
* GenericDescriptorMatch * * GenericDescriptorMatch *
\****************************************************************************************/ \****************************************************************************************/
...@@ -394,6 +399,9 @@ void GenericDescriptorMatch::clear() ...@@ -394,6 +399,9 @@ void GenericDescriptorMatch::clear()
collection.clear(); collection.clear();
} }
/*
* Factory function for GenericDescriptorMatch creating
*/
Ptr<GenericDescriptorMatch> createGenericDescriptorMatch( const string& genericDescritptorMatchType, const string &paramsFilename ) Ptr<GenericDescriptorMatch> createGenericDescriptorMatch( const string& genericDescritptorMatchType, const string &paramsFilename )
{ {
GenericDescriptorMatch *descriptorMatch = 0; GenericDescriptorMatch *descriptorMatch = 0;
...@@ -409,7 +417,7 @@ Ptr<GenericDescriptorMatch> createGenericDescriptorMatch( const string& genericD ...@@ -409,7 +417,7 @@ Ptr<GenericDescriptorMatch> createGenericDescriptorMatch( const string& genericD
} }
else if( ! genericDescritptorMatchType.compare ("CALONDER") ) else if( ! genericDescritptorMatchType.compare ("CALONDER") )
{ {
descriptorMatch = new CalonderDescriptorMatch (); //descriptorMatch = new CalonderDescriptorMatch ();
} }
if( !paramsFilename.empty() && descriptorMatch != 0 ) if( !paramsFilename.empty() && descriptorMatch != 0 )
...@@ -626,6 +634,7 @@ void OneWayDescriptorMatch::clear () ...@@ -626,6 +634,7 @@ void OneWayDescriptorMatch::clear ()
/****************************************************************************************\ /****************************************************************************************\
* CalonderDescriptorMatch * * CalonderDescriptorMatch *
\****************************************************************************************/ \****************************************************************************************/
#if 0
CalonderDescriptorMatch::Params::Params( const RNG& _rng, const PatchGenerator& _patchGen, CalonderDescriptorMatch::Params::Params( const RNG& _rng, const PatchGenerator& _patchGen,
int _numTrees, int _depth, int _views, int _numTrees, int _depth, int _views,
size_t _reducedNumDim, size_t _reducedNumDim,
...@@ -774,6 +783,7 @@ void CalonderDescriptorMatch::write( FileStorage& fs ) const ...@@ -774,6 +783,7 @@ void CalonderDescriptorMatch::write( FileStorage& fs ) const
fs << "numQuantBits" << params.numQuantBits; fs << "numQuantBits" << params.numQuantBits;
fs << "printStatus" << params.printStatus; fs << "printStatus" << params.printStatus;
} }
#endif
/****************************************************************************************\ /****************************************************************************************\
* FernDescriptorMatch * * FernDescriptorMatch *
...@@ -827,22 +837,13 @@ void FernDescriptorMatch::trainFernClassifier() ...@@ -827,22 +837,13 @@ void FernDescriptorMatch::trainFernClassifier()
{ {
assert( params.filename.empty() ); assert( params.filename.empty() );
vector<Point2f> points; vector<vector<Point2f> > points;
vector<Ptr<Mat> > refimgs; for( size_t imgIdx = 0; imgIdx < collection.images.size(); imgIdx++ )
vector<int> labels; KeyPoint::convert( collection.points[imgIdx], points[imgIdx] );
for( size_t imageIdx = 0; imageIdx < collection.images.size(); imageIdx++ )
{
for( size_t pointIdx = 0; pointIdx < collection.points[imageIdx].size(); pointIdx++ )
{
refimgs.push_back(new Mat (collection.images[imageIdx]));
points.push_back(collection.points[imageIdx][pointIdx].pt);
labels.push_back((int)pointIdx);
}
}
classifier = new FernClassifier( points, refimgs, labels, params.nclasses, params.patchSize, classifier = new FernClassifier( points, collection.images, vector<vector<int> >(), 0, // each points is a class
params.signatureSize, params.nstructs, params.structSize, params.nviews, params.patchSize, params.signatureSize, params.nstructs, params.structSize,
params.compressionMethod, params.patchGenerator ); params.nviews, params.compressionMethod, params.patchGenerator );
} }
} }
...@@ -966,4 +967,59 @@ void FernDescriptorMatch::clear () ...@@ -966,4 +967,59 @@ void FernDescriptorMatch::clear ()
classifier.release(); classifier.release();
} }
/****************************************************************************************\
* VectorDescriptorMatch *
\****************************************************************************************/
void VectorDescriptorMatch::add( const Mat& image, vector<KeyPoint>& keypoints )
{
Mat descriptors;
extractor->compute( image, keypoints, descriptors );
matcher->add( descriptors );
collection.add( Mat(), keypoints );
};
void VectorDescriptorMatch::match( const Mat& image, vector<KeyPoint>& points, vector<int>& keypointIndices )
{
Mat descriptors;
extractor->compute( image, points, descriptors );
matcher->match( descriptors, keypointIndices );
};
void VectorDescriptorMatch::match( const Mat& image, vector<KeyPoint>& points, vector<DMatch>& matches )
{
Mat descriptors;
extractor->compute( image, points, descriptors );
matcher->match( descriptors, matches );
}
void VectorDescriptorMatch::match( const Mat& image, vector<KeyPoint>& points,
vector<vector<DMatch> >& matches, float threshold )
{
Mat descriptors;
extractor->compute( image, points, descriptors );
matcher->match( descriptors, matches, threshold );
}
void VectorDescriptorMatch::clear()
{
GenericDescriptorMatch::clear();
matcher->clear();
}
void VectorDescriptorMatch::read( const FileNode& fn )
{
GenericDescriptorMatch::read(fn);
extractor->read (fn);
}
void VectorDescriptorMatch::write (FileStorage& fs) const
{
GenericDescriptorMatch::write(fs);
extractor->write (fs);
}
} }
...@@ -692,9 +692,9 @@ Size FernClassifier::getPatchSize() const ...@@ -692,9 +692,9 @@ Size FernClassifier::getPatchSize() const
} }
FernClassifier::FernClassifier(const vector<Point2f>& points, FernClassifier::FernClassifier(const vector<vector<Point2f> >& points,
const vector<Ptr<Mat> >& refimgs, const vector<Mat>& refimgs,
const vector<int>& labels, const vector<vector<int> >& labels,
int _nclasses, int _patchSize, int _nclasses, int _patchSize,
int _signatureSize, int _nstructs, int _signatureSize, int _nstructs,
int _structSize, int _nviews, int _compressionMethod, int _structSize, int _nviews, int _compressionMethod,
...@@ -829,41 +829,56 @@ void FernClassifier::prepare(int _nclasses, int _patchSize, int _signatureSize, ...@@ -829,41 +829,56 @@ void FernClassifier::prepare(int _nclasses, int _patchSize, int _signatureSize,
} }
} }
static int calcNumPoints( const vector<vector<Point2f> >& points )
{
int count = 0;
for( size_t i = 0; i < points.size(); i++ )
count += points[i].size();
return count;
}
void FernClassifier::train(const vector<Point2f>& points, void FernClassifier::train(const vector<vector<Point2f> >& points,
const vector<Ptr<Mat> >& refimgs, const vector<Mat>& refimgs,
const vector<int>& labels, const vector<vector<int> >& labels,
int _nclasses, int _patchSize, int _nclasses, int _patchSize,
int _signatureSize, int _nstructs, int _signatureSize, int _nstructs,
int _structSize, int _nviews, int _compressionMethod, int _structSize, int _nviews, int _compressionMethod,
const PatchGenerator& patchGenerator) const PatchGenerator& patchGenerator)
{ {
_nclasses = _nclasses > 0 ? _nclasses : (int)points.size(); CV_Assert( points.size() == refimgs.size() );
int numPoints = calcNumPoints( points );
_nclasses = (!labels.empty() && _nclasses>0) ? _nclasses : numPoints;
CV_Assert( labels.empty() || labels.size() == points.size() ); CV_Assert( labels.empty() || labels.size() == points.size() );
prepare(_nclasses, _patchSize, _signatureSize, _nstructs, prepare(_nclasses, _patchSize, _signatureSize, _nstructs,
_structSize, _nviews, _compressionMethod); _structSize, _nviews, _compressionMethod);
// pass all the views of all the samples through the generated trees and accumulate // pass all the views of all the samples through the generated trees and accumulate
// the statistics (posterior probabilities) in leaves. // the statistics (posterior probabilities) in leaves.
Mat patch; Mat patch;
int i, j, nsamples = (int)points.size();
RNG& rng = theRNG(); RNG& rng = theRNG();
for( i = 0; i < nsamples; i++ ) int globalPointIdx = 0;
for( size_t imgIdx = 0; imgIdx < points.size(); imgIdx++ )
{ {
Point2f pt = points[i]; const Point2f* imgPoints = &points[imgIdx][0];
const Mat& src = *refimgs[i]; const int* imgLabels = labels.empty() ? 0 : &labels[imgIdx][0];
int classId = labels.empty() ? i : labels[i]; for( size_t pointIdx = 0; pointIdx < points[imgIdx].size(); pointIdx++, globalPointIdx++ )
if( verbose && (i+1)*progressBarSize/nsamples != i*progressBarSize/nsamples )
putchar('.');
CV_Assert( 0 <= classId && classId < nclasses );
classCounters[classId] += _nviews;
for( j = 0; j < _nviews; j++ )
{ {
patchGenerator(src, pt, patch, patchSize, rng); Point2f pt = imgPoints[pointIdx];
for( int f = 0; f < nstructs; f++ ) const Mat& src = refimgs[imgIdx];
posteriors[getLeaf(f, patch)*nclasses + classId]++; int classId = imgLabels==0 ? globalPointIdx : imgLabels[pointIdx];
if( verbose && (globalPointIdx+1)*progressBarSize/numPoints != globalPointIdx*progressBarSize/numPoints )
putchar('.');
CV_Assert( 0 <= classId && classId < nclasses );
classCounters[classId] += _nviews;
for( int v = 0; v < _nviews; v++ )
{
patchGenerator(src, pt, patch, patchSize, rng);
for( int f = 0; f < nstructs; f++ )
posteriors[getLeaf(f, patch)*nclasses + classId]++;
}
} }
} }
if( verbose ) if( verbose )
......
//Calonder descriptor sample
#include <stdio.h>
#if 0
#include <cxcore.h>
#include <cv.h>
#include <cvaux.h>
#include <highgui.h> #include <highgui.h>
#include <vector> #include <opencv2/core/core.hpp>
using namespace std; #include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <iostream>
#include <fstream>
// Number of training points (set to -1 to use all points) using namespace std;
const int n_points = -1; using namespace cv;
//Draw the border of projection of train image calculed by averaging detected correspondences
const bool draw_border = true;
void cvmSet6(CvMat* m, int row, int col, float val1, float val2, float val3, float val4, float val5, float val6) void warpPerspectiveRand( const Mat& src, Mat& dst, Mat& H, RNG& rng )
{ {
cvmSet(m, row, col, val1); H.create(3, 3, CV_32FC1);
cvmSet(m, row, col + 1, val2); H.at<float>(0,0) = rng.uniform( 0.8f, 1.2f);
cvmSet(m, row, col + 2, val3); H.at<float>(0,1) = rng.uniform(-0.1f, 0.1f);
cvmSet(m, row, col + 3, val4); H.at<float>(0,2) = rng.uniform(-0.1f, 0.1f)*src.cols;
cvmSet(m, row, col + 4, val5); H.at<float>(1,0) = rng.uniform(-0.1f, 0.1f);
cvmSet(m, row, col + 5, val6); H.at<float>(1,1) = rng.uniform( 0.8f, 1.2f);
H.at<float>(1,2) = rng.uniform(-0.1f, 0.1f)*src.rows;
H.at<float>(2,0) = rng.uniform( -1e-4f, 1e-4f);
H.at<float>(2,1) = rng.uniform( -1e-4f, 1e-4f);
H.at<float>(2,2) = rng.uniform( 0.8f, 1.2f);
warpPerspective( src, dst, H, src.size() );
} }
void FindAffineTransform(const vector<CvPoint>& p1, const vector<CvPoint>& p2, CvMat* affine) int main( int argc, char **argv )
{ {
int eq_num = 2*(int)p1.size(); if( argc != 4 && argc != 3 )
CvMat* A = cvCreateMat(eq_num, 6, CV_32FC1);
CvMat* B = cvCreateMat(eq_num, 1, CV_32FC1);
CvMat* X = cvCreateMat(6, 1, CV_32FC1);
for(int i = 0; i < (int)p1.size(); i++)
{ {
cvmSet6(A, 2*i, 0, p1[i].x, p1[i].y, 1, 0, 0, 0); cout << "Format:" << endl <<
cvmSet6(A, 2*i + 1, 0, 0, 0, 0, p1[i].x, p1[i].y, 1); " classifier(xml to write) test_image file_with_train_images_filenames(txt)" <<
cvmSet(B, 2*i, 0, p2[i].x); " or" << endl <<
cvmSet(B, 2*i + 1, 0, p2[i].y); " classifier(xml to read) test_image" << endl;
return -1;
} }
cvSolve(A, B, X, CV_SVD);
cvmSet(affine, 0, 0, cvmGet(X, 0, 0));
cvmSet(affine, 0, 1, cvmGet(X, 1, 0));
cvmSet(affine, 0, 2, cvmGet(X, 2, 0));
cvmSet(affine, 1, 0, cvmGet(X, 3, 0));
cvmSet(affine, 1, 1, cvmGet(X, 4, 0));
cvmSet(affine, 1, 2, cvmGet(X, 5, 0));
cvReleaseMat(&A);
cvReleaseMat(&B);
cvReleaseMat(&X);
}
void MapVectorAffine(const vector<CvPoint>& p1, vector<CvPoint>& p2, CvMat* transform) CalonderClassifier classifier;
{ if( argc == 4 ) // Train
float a = cvmGet(transform, 0, 0);
float b = cvmGet(transform, 0, 1);
float c = cvmGet(transform, 0, 2);
float d = cvmGet(transform, 1, 0);
float e = cvmGet(transform, 1, 1);
float f = cvmGet(transform, 1, 2);
for(int i = 0; i < (int)p1.size(); i++)
{ {
float x = a*p1[i].x + b*p1[i].y + c; // Read train images and test image
float y = d*p1[i].x + e*p1[i].y + f; ifstream fst( argv[3], ifstream::in );
p2.push_back(cvPoint(x, y)); vector<Mat> trainImgs;
while( !fst.eof() )
{
string str;
getline( fst, str );
if (str.empty()) break;
Mat img = imread( str, CV_LOAD_IMAGE_GRAYSCALE );
if( !img.empty() )
trainImgs.push_back( img );
}
if( trainImgs.empty() )
{
cout << "All train images can not be read." << endl;
return -1;
}
cout << trainImgs.size() << " train images were read." << endl;
// Extract keypoints from train images
SurfFeatureDetector detector;
vector<vector<Point2f> > trainPoints( trainImgs.size() );
for( size_t i = 0; i < trainImgs.size(); i++ )
{
vector<KeyPoint> kps;
detector.detect( trainImgs[i], kps );
KeyPoint::convert( kps, trainPoints[i] );
}
// Train Calonder classifier on extracted points
classifier.setVerbose( true);
classifier.train( trainPoints, trainImgs );
// Write Calonder classifier
FileStorage fs( argv[1], FileStorage::WRITE );
classifier.write( fs );
} }
} else
float CalcAffineReprojectionError(const vector<CvPoint>& p1, const vector<CvPoint>& p2, CvMat* transform)
{
vector<CvPoint> mapped_p1;
MapVectorAffine(p1, mapped_p1, transform);
float error = 0;
for(int i = 0; i < (int)p2.size(); i++)
{ {
error += ((p2[i].x - mapped_p1[i].x)*(p2[i].x - mapped_p1[i].x)+(p2[i].y - mapped_p1[i].y)*(p2[i].y - mapped_p1[i].y)); // Read Calonder classifier
FileStorage fs( argv[1], FileStorage::READ );
classifier.read( fs.root() );
} }
error /= p2.size();
return error;
}
#endif
int main( int, char** )
{
printf("calonder_sample is under construction\n");
return 0;
#if 0
IplImage* test_image;
IplImage* train_image;
if (argc < 3)
{
test_image = cvLoadImage("box_in_scene.png",0);
train_image = cvLoadImage("box.png ",0);
if (!test_image || !train_image)
{
printf("Usage: calonder_sample <train_image> <test_image>");
return 0;
}
}
else
{
test_image = cvLoadImage(argv[2],0);
train_image = cvLoadImage(argv[1],0);
}
if (!train_image)
{
printf("Unable to load train image\n");
return 0;
}
if (!test_image)
{
printf("Unable to load test image\n");
return 0;
}
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq *objectKeypoints = 0, *objectDescriptors = 0;
CvSeq *imageKeypoints = 0, *imageDescriptors = 0;
CvSURFParams params = cvSURFParams(500, 1);
cvExtractSURF( test_image, 0, &imageKeypoints, &imageDescriptors, storage, params );
cvExtractSURF( train_image, 0, &objectKeypoints, &objectDescriptors, storage, params );
cv::RTreeClassifier detector;
int patch_width = cv::PATCH_SIZE;
int patch_height = cv::PATCH_SIZE;
vector<cv::BaseKeypoint> base_set;
int i=0;
CvSURFPoint* point;
for (i=0;i<(n_points > 0 ? n_points : objectKeypoints->total);i++)
{
point=(CvSURFPoint*)cvGetSeqElem(objectKeypoints,i);
base_set.push_back(cv::BaseKeypoint(point->pt.x,point->pt.y,train_image));
}
//Detector training if( classifier.empty() )
cv::RNG rng( cvGetTickCount() ); {
cv::PatchGenerator gen(0,255,2,false,0.7,1.3,-CV_PI/3,CV_PI/3,-CV_PI/3,CV_PI/3); cout << "Calonder classifier is empty" << endl;
return -1;
printf("RTree Classifier training...\n"); }
detector.train(base_set,rng,gen,24,cv::DEFAULT_DEPTH,2000,(int)base_set.size(),detector.DEFAULT_NUM_QUANT_BITS);
printf("Done\n");
float* signature = new float[detector.original_num_classes()];
float* best_corr;
int* best_corr_idx;
if (imageKeypoints->total > 0)
{
best_corr = new float[imageKeypoints->total];
best_corr_idx = new int[imageKeypoints->total];
}
for(i=0; i < imageKeypoints->total; i++)
{
point=(CvSURFPoint*)cvGetSeqElem(imageKeypoints,i);
int part_idx = -1;
float prob = 0.0f;
CvRect roi = cvRect((int)(point->pt.x) - patch_width/2,(int)(point->pt.y) - patch_height/2, patch_width, patch_height);
cvSetImageROI(test_image, roi);
roi = cvGetImageROI(test_image);
if(roi.width != patch_width || roi.height != patch_height)
{
best_corr_idx[i] = part_idx;
best_corr[i] = prob;
}
else
{
cvSetImageROI(test_image, roi);
IplImage* roi_image = cvCreateImage(cvSize(roi.width, roi.height), test_image->depth, test_image->nChannels);
cvCopy(test_image,roi_image);
detector.getSignature(roi_image, signature);
for (int j = 0; j< detector.original_num_classes();j++)
{
if (prob < signature[j])
{
part_idx = j;
prob = signature[j];
}
}
best_corr_idx[i] = part_idx;
best_corr[i] = prob;
if (roi_image)
cvReleaseImage(&roi_image);
}
cvResetImageROI(test_image);
}
float min_prob = 0.0f;
vector<CvPoint> object;
vector<CvPoint> features;
for (int j=0;j<objectKeypoints->total;j++)
{
float prob = 0.0f;
int idx = -1;
for (i = 0; i<imageKeypoints->total;i++)
{
if ((best_corr_idx[i]!=j)||(best_corr[i] < min_prob))
continue;
if (best_corr[i] > prob)
{
prob = best_corr[i];
idx = i;
}
}
if (idx >=0)
{
point=(CvSURFPoint*)cvGetSeqElem(objectKeypoints,j);
object.push_back(cvPoint((int)point->pt.x,(int)point->pt.y));
point=(CvSURFPoint*)cvGetSeqElem(imageKeypoints,idx);
features.push_back(cvPoint((int)point->pt.x,(int)point->pt.y));
}
}
if ((int)object.size() > 3)
{
CvMat* affine = cvCreateMat(2, 3, CV_32FC1);
FindAffineTransform(object,features,affine);
vector<CvPoint> corners;
vector<CvPoint> mapped_corners;
corners.push_back(cvPoint(0,0));
corners.push_back(cvPoint(0,train_image->height));
corners.push_back(cvPoint(train_image->width,0));
corners.push_back(cvPoint(train_image->width,train_image->height));
MapVectorAffine(corners,mapped_corners,affine);
//Drawing the result
IplImage* result = cvCreateImage(cvSize(test_image->width > train_image->width ? test_image->width : train_image->width,
train_image->height + test_image->height),
test_image->depth, test_image->nChannels);
cvSetImageROI(result,cvRect(0,0,train_image->width, train_image->height));
cvCopy(train_image,result);
cvResetImageROI(result);
cvSetImageROI(result,cvRect(0,train_image->height,test_image->width, test_image->height));
cvCopy(test_image,result);
cvResetImageROI(result);
for (int i=0;i<(int)features.size();i++)
{
cvLine(result,object[i],cvPoint(features[i].x,features[i].y+train_image->height),cvScalar(255));
}
if (draw_border) // Test Calonder classifier on test image and warped one
{ Mat testImg1 = imread( argv[2], CV_LOAD_IMAGE_GRAYSCALE ), testImg2, H12;
cvLine(result,cvPoint(mapped_corners[0].x, mapped_corners[0].y+train_image->height), if( testImg1.empty() )
cvPoint(mapped_corners[1].x, mapped_corners[1].y+train_image->height),cvScalar(150),3); {
cvLine(result,cvPoint(mapped_corners[0].x, mapped_corners[0].y+train_image->height), cout << "Test image can not be read." << endl;
cvPoint(mapped_corners[2].x, mapped_corners[2].y+train_image->height),cvScalar(150),3); return -1;
cvLine(result,cvPoint(mapped_corners[1].x, mapped_corners[1].y+train_image->height), }
cvPoint(mapped_corners[3].x, mapped_corners[3].y+train_image->height),cvScalar(150),3); warpPerspectiveRand( testImg1, testImg2, H12, theRNG() );
cvLine(result,cvPoint(mapped_corners[2].x, mapped_corners[2].y+train_image->height),
cvPoint(mapped_corners[3].x, mapped_corners[3].y+train_image->height),cvScalar(150),3);
}
cvSaveImage("Result.jpg",result);
cvNamedWindow("Result",0);
cvShowImage("Result",result);
cvWaitKey();
cvReleaseMat(&affine);
cvReleaseImage(&result);
}
else
{
printf("Unable to find correspondence\n");
}
// Exstract keypoints from test images
SurfFeatureDetector detector;
vector<KeyPoint> testKeypoints1; detector.detect( testImg1, testKeypoints1 );
vector<KeyPoint> testKeypoints2; detector.detect( testImg2, testKeypoints2 );
vector<Point2f> testPoints1; KeyPoint::convert( testKeypoints1, testPoints1 );
vector<Point2f> testPoints2; KeyPoint::convert( testKeypoints2, testPoints2 );
// Calculate Calonder descriptors
int signatureSize = classifier.getSignatureSize();
if (signature) vector<float> r1(testPoints1.size()*signatureSize), r2(testPoints2.size()*signatureSize);
delete[] signature; vector<float>::iterator rit = r1.begin();
if (best_corr) for( size_t i = 0; i < testPoints1.size(); i++ )
delete[] best_corr; {
cvReleaseMemStorage(&storage); vector<float> s;
cvReleaseImage(&train_image); classifier( testImg1, testPoints1[i], s );
cvReleaseImage(&test_image); copy( s.begin(), s.end(), rit );
rit += s.size();
}
rit = r2.begin();
for( size_t i = 0; i < testPoints2.size(); i++ )
{
vector<float> s;
classifier( testImg2, testPoints2[i], s );
copy( s.begin(), s.end(), rit );
rit += s.size();
}
return 0; Mat descriptors1(testPoints1.size(), classifier.getSignatureSize(), CV_32FC1, &r1[0] ),
#endif descriptors2(testPoints2.size(), classifier.getSignatureSize(), CV_32FC1, &r2[0] );
// Match descriptors
BruteForceMatcher<L1<float> > matcher;
matcher.add( descriptors2 );
vector<int> matches;
matcher.match( descriptors1, matches );
// Draw results
// Prepare inlier mask
vector<char> matchesMask( matches.size(), 0 );
Mat points1t; perspectiveTransform(Mat(testPoints1), points1t, H12);
vector<int>::const_iterator mit = matches.begin();
for( size_t mi = 0; mi < matches.size(); mi++ )
{
if( norm(testPoints2[matches[mi]] - points1t.at<Point2f>(mi,0)) < 4 ) // inlier
matchesMask[mi] = 1;
}
// Draw
Mat drawImg;
drawMatches( testImg1, testKeypoints1, testImg2, testKeypoints2, matches, drawImg, CV_RGB(0, 255, 0), CV_RGB(0, 0, 255), matchesMask );
string winName = "Matches";
namedWindow( winName, WINDOW_AUTOSIZE );
imshow( winName, drawImg );
waitKey();
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment