modified FernClassifier::train(); remove old RTreeClassifier and added new implementa...
authorMaria Dimashova <no@email>
Mon, 26 Jul 2010 08:58:46 +0000 (08:58 +0000)
committerMaria Dimashova <no@email>
Mon, 26 Jul 2010 08:58:46 +0000 (08:58 +0000)
modules/features2d/include/opencv2/features2d/features2d.hpp
modules/features2d/src/calonder.cpp
modules/features2d/src/descriptors.cpp
modules/features2d/src/planardetect.cpp
samples/c/find_obj_calonder.cpp

index 108ee6d..5c0174a 100644 (file)
@@ -179,6 +179,17 @@ CVAPI(CvSeq*) cvGetStarKeypoints( const CvArr* img, CvMemStorage* storage,
 
 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.
 // An image patch is rotated on angle phi (in degrees), then scaled lambda1 times
@@ -395,10 +406,7 @@ public:
 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
 {
@@ -459,9 +467,9 @@ class CV_EXPORTS FernClassifier
 public:
     FernClassifier();
     FernClassifier(const FileNode& node);
-    FernClassifier(const vector<Point2f>& points,
-                   const vector<Ptr<Mat> >& refimgs,
-                   const vector<int>& labels=vector<int>(),
+    FernClassifier(const vector<vector<Point2f> >& points,
+                   const vector<Mat>& refimgs,
+                   const vector<vector<int> >& labels=vector<vector<int> >(),
                    int _nclasses=0, int _patchSize=PATCH_SIZE,
                    int _signatureSize=DEFAULT_SIGNATURE_SIZE,
                    int _nstructs=DEFAULT_STRUCTS,
@@ -481,9 +489,9 @@ public:
                                      int _nviews=DEFAULT_VIEWS,
                                      int _compressionMethod=COMPRESSION_NONE,
                                      const PatchGenerator& patchGenerator=PatchGenerator());
-    virtual void train(const vector<Point2f>& points,
-                       const vector<Ptr<Mat> >& refimgs,
-                       const vector<int>& labels=vector<int>(),
+    virtual void train(const vector<vector<Point2f> >& points,
+                       const vector<Mat>& refimgs,
+                       const vector<vector<int> >& labels=vector<vector<int> >(),
                        int _nclasses=0, int _patchSize=PATCH_SIZE,
                        int _signatureSize=DEFAULT_SIGNATURE_SIZE,
                        int _nstructs=DEFAULT_STRUCTS,
@@ -594,270 +602,127 @@ protected:
     FernClassifier fernClassifier;
 };
 
-
-
 /****************************************************************************************\
-*            Calonder Descriptor                                                         *
+*                                 Calonder Classifier                                    *
 \****************************************************************************************/
-
-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
+class CV_EXPORTS CalonderClassifier
 {
-    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:
-    typedef enum { PDT_GAUSS=1, PDT_BERNOULLI, PDT_DBFRIENDLY } PHI_DISTR_TYPE;
-    ~CSMatrixGenerator();
-    static float* getCSMatrix(int m, int n, PHI_DISTR_TYPE dt);     // do NOT free returned pointer
-
-private:
-    static float *cs_phi_;    // matrix for compressive sensing
-    static int cs_phi_m_, cs_phi_n_;
-};
-
-
-template< typename T >
-struct AlignedMemBlock
-{
-  AlignedMemBlock() : raw(NULL), data(NULL) { };
-
-  // 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;
+    CalonderClassifier();
+    CalonderClassifier( const vector<vector<Point2f> >& points, const vector<Mat>& refimgs,
+                        const vector<vector<int> >& labels=vector<vector<int> >(), int _numClasses=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() );
+
+    virtual ~CalonderClassifier();
+    virtual void clear();
 
-    RandomizedTree();
-    ~RandomizedTree();
+    void train( const vector<vector<Point2f> >& points, const vector<Mat>& refimgs,
+                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,
-        int depth, int views, size_t reduced_num_dim, int num_quant_bits);
+    virtual void operator()(const Mat& img, Point2f pt, vector<float>& signature, float thresh=0.f) const;
+    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,
-        PatchGenerator &make_patch, int depth, int views, size_t reduced_num_dim,
-        int num_quant_bits);
+    void read( const FileNode& fn );
+    void write( FileStorage& fs ) const;
 
-    // following two funcs are EXPERIMENTAL (do not use unless you know exactly what you do)
-    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);
+    bool empty() const;
 
-    // patch_data must be a 32x32 array (no row padding)
-    float* getPosterior(uchar* patch_data);
-    const float* getPosterior(uchar* patch_data) const;
-    uchar* getPosterior2(uchar* patch_data);
+    void setVerbose( bool _verbose );
 
-    void read(const char* file_name, int num_quant_bits);
-    void read(std::istream &is, int num_quant_bits);
-    void write(const char* file_name) const;
-    void write(std::ostream &os) const;
+    int getPatchSize() const;
+    int getNumTrees() const;
+    int getTreeDepth() 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
-    void savePosteriors(std::string url, bool append=false);
-    void savePosteriors2(std::string url, bool append=false);
+    static float GET_LOWER_QUANT_PERC() { return .03f; }
+    static float GET_UPPER_QUANT_PERC() { return .92f; }
 
+    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:
-    int classes_;
-    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]);
-};
+    void prepare( int _patchSize, int _signatureSize, int _numTrees, int _treeDepth, int _numViews );
 
-struct RTreeNode
-{
-    short offset1, offset2;
-
-    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];
-    }
-};
+    int getLeafIdx( int treeIdx, const Mat& patch ) const;
+    void finalize( int _compressedDim, int _compressType, int _numQuantBits,
+                   const vector<int>& leafSampleCounters);
 
+    void compressLeaves( int _compressedDim, int _compressType );
 
+    bool verbose;
 
-//} // namespace features
-//----------------------------
-//rtree_classifier.h
-//class RTTester;
+    int patchSize;
+    int signatureSize;
+    int numTrees;
+    int treeDepth;
+    int numViews;
 
-//namespace features {
+    int origNumClasses;
+    int compressType;
+    int numQuantBits;
 
-class CV_EXPORTS RTreeClassifier
-{
-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_;
+    int numLeavesPerTree;
+    int numNodesPerTree;
 
-private:
-    int classes_;
-    int num_quant_bits_;
-    uchar **posteriors_;
-    ushort *ptemp_;
-    int original_num_classes_;
-    bool keep_floats_;
+    struct Node
+    {
+        uchar x1, y1, x2, y2;
+        Node() : x1(0), y1(0), x2(0), y2(0) {}
+        Node( uchar _x1, uchar _y1, uchar _x2, uchar _y2 ) : x1(_x1), y1(_y1), x2(_x2), y2(_y2)
+        {}
+        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                                 *
 \****************************************************************************************/
@@ -1004,7 +869,7 @@ protected:
     CvAffinePose* m_affine_poses; // an array of 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)
 
     int m_pca_dim_high; // the number of descriptor pca components to use for generating affine poses
@@ -1275,7 +1140,8 @@ public:
      *
      * image        The image.
      * 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
     {
@@ -1430,8 +1296,8 @@ public:
      */
     virtual void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const = 0;
 
-    virtual void read (const FileNode&) {};
-    virtual void write (FileStorage&) const {};
+    virtual void read( const FileNode& ) {};
+    virtual void write( FileStorage& ) const {};
 
 protected:
     /*
@@ -1451,9 +1317,9 @@ public:
                              int firstOctave=SIFT::CommonParams::DEFAULT_FIRST_OCTAVE,
                              int angleMode=SIFT::CommonParams::FIRST_ANGLE );
 
-    virtual void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors) const;
-    virtual void read (const FileNode &fn);
-    virtual void write (FileStorage &fs) const;
+    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:
     SIFT sift;
@@ -1465,14 +1331,56 @@ public:
     SurfDescriptorExtractor( int nOctaves=4,
                              int nOctaveLayers=2, bool extended=false );
 
-    virtual void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors) const;
-    virtual void read (const FileNode &fn);
-    virtual void write (FileStorage &fs) const;
+    virtual void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const;
+    virtual void read( const FileNode &fn );
+    virtual void write( FileStorage &fs ) const;
 
 protected:
     SURF 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 );
 
 /****************************************************************************************\
@@ -1533,7 +1441,7 @@ struct CV_EXPORTS L1
 
 
 /****************************************************************************************\
-*                                  DMatch                                     *
+*                                      DMatch                                            *
 \****************************************************************************************/
 /*
  * Struct for matching: match index and distance between descriptors
@@ -1591,8 +1499,7 @@ public:
      * mask          Mask specifying permissible matches.
      * matches       Indices of the closest matches from the training set
      */
-    void match( const Mat& query, const Mat& mask,
-                vector<int>& matches ) const;
+    void match( const Mat& query, const Mat& mask, vector<int>& matches ) const;
 
     /*
      * Find the best match for each descriptor from a query set
@@ -1613,8 +1520,7 @@ public:
      * mask          Mask specifying permissible matches.
      * matches       DMatches of the closest matches from the training set
      */
-    void match( const Mat& query, const Mat& mask,
-                    vector<DMatch>& matches ) const;
+    void match( const Mat& query, const Mat& mask, vector<DMatch>& matches ) const;
 
     /*
      * Find many matches for each descriptor from a query set
@@ -1638,7 +1544,7 @@ public:
       * threshold     Distance threshold for descriptors matching
       */
     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
 
 template<>
 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 );
 
@@ -2036,10 +1942,10 @@ public:
     virtual void clear ();
 
     // 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
-    virtual void write (FileStorage& fs) const;
+    virtual void write( FileStorage& fs ) const;
 
 protected:
     Ptr<OneWayDescriptorBase> base;
@@ -2049,6 +1955,7 @@ protected:
 /*
  *  CalonderDescriptorMatch
  */
+#if 0
 class CV_EXPORTS CalonderDescriptorMatch : public GenericDescriptorMatch
 {
 public:
@@ -2113,6 +2020,7 @@ protected:
     Ptr<RTreeClassifier> classifier;
     Params params;
 };
+#endif
 
 /*
  *  FernDescriptorMatch
@@ -2178,6 +2086,7 @@ protected:
 };
 
 CV_EXPORTS Ptr<GenericDescriptorMatch> createGenericDescriptorMatch( const string& genericDescritptorMatchType, const string &paramsFilename = string () );
+
 /****************************************************************************************\
 *                                VectorDescriptorMatch                                   *
 \****************************************************************************************/
@@ -2199,63 +2108,27 @@ public:
     void index();
 
     // Calculates descriptors for a set of keypoints from a single image
-    virtual void add( const Mat& image, vector<KeyPoint>& keypoints )
-    {
-        Mat descriptors;
-        extractor->compute( image, keypoints, descriptors );
-        matcher->add( descriptors );
-
-        collection.add( Mat(), keypoints );
-    };
+    virtual void add( const Mat& image, vector<KeyPoint>& keypoints );
 
     // Matches a set of keypoints with the training set
-    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<int>& keypointIndices );
 
-    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 match( const Mat& image, vector<KeyPoint>& points, vector<DMatch>& matches );
 
-    virtual void clear()
-    {
-        GenericDescriptorMatch::clear();
-        matcher->clear();
-    }
+    virtual void match( const Mat& image, vector<KeyPoint>& points,
+                        vector<vector<DMatch> >& matches, float threshold );
 
-    virtual void read (const FileNode& fn)
-    {
-        GenericDescriptorMatch::read(fn);
-        extractor->read (fn);
-    }
+    virtual void clear();
+    virtual void read( const FileNode& fn );
+    virtual void write( FileStorage& fs ) const;
 
-    virtual void write (FileStorage& fs) const
-    {
-        GenericDescriptorMatch::write(fs);
-        extractor->write (fs);
-    }
 protected:
     Ptr<DescriptorExtractor> extractor;
     Ptr<DescriptorMatcher> matcher;
     //vector<int> classIds;
 };
 
+
 struct CV_EXPORTS DrawMatchesFlags
 {
     enum{ DEFAULT = 0, // Output image matrix will be created (Mat::create),
index 7c25d65..458f010 100644 (file)
 //
 //M*/
 #include "precomp.hpp"
-
-#include <opencv2/core/wimage.hpp>
-#include <vector>
+#include <cstdio>
 #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
 
-/****************************************************************************************\
-The code below is implementation of Calonder Descriptor and RTree Classifier
-originally introduced by Michael Calonder.
+using namespace std;
 
-The code was integrated into OpenCV by Alexey Latyshev
-\****************************************************************************************/
+const int progressBarSize = 50;
+namespace cv
+{
 
-namespace cv {
-
-
-    //----------------------------
-    //randomized_tree.cpp
-
-    inline uchar* getData(IplImage* image)
-    {
-        return reinterpret_cast<uchar*>(image->imageData);
-    }
+CalonderClassifier::CalonderClassifier()
+{
+    verbose = false;
+    clear();
+}
 
-    inline float* RandomizedTree::getPosteriorByIndex(int index)
-    {
-        return const_cast<float*>(const_cast<const RandomizedTree*>(this)->getPosteriorByIndex(index));
-    }
+CalonderClassifier::~CalonderClassifier()
+{}
+
+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
-    {
-        return posteriors_[index].p();
-    }
+int CalonderClassifier::getPatchSize() const
+{ return patchSize; }
 
-    inline uchar* RandomizedTree::getPosteriorByIndex2(int index)
-    {
-        return posteriors2_[index].p();
-    }
+int CalonderClassifier::getNumTrees() const
+{ return numTrees; }
 
+int CalonderClassifier::getTreeDepth() const
+{ return treeDepth; }
 
-    template < typename PointT >
-    cv::WImageView1_b extractPatch(cv::WImageView1_b const& image, PointT pt, int patch_sz = PATCH_SIZE)
-    {
-        const int offset = patch_sz / 2;
+int CalonderClassifier::getNumViews() const
+{ return numViews; }
 
-        // TODO: WImage{C}.View really should have const version
-        cv::WImageView1_b &img_ref = const_cast< cv::WImageView1_b& >(image);
-        return img_ref.View(pt.x - offset, pt.y - offset, patch_sz, patch_sz);
-    }
+int CalonderClassifier::getSignatureSize() const
+{ return signatureSize; }
 
-    template < typename PointT >
-    cv::WImageView3_b extractPatch3(cv::WImageView3_b const& image, PointT pt)
-    {
-        static const int offset = PATCH_SIZE / 2;
+int CalonderClassifier::getCompressType() const
+{ return compressType; }
 
-        // TODO: WImage{C}.View really should have const version
-        cv::WImageView3_b &img_ref = const_cast< cv::WImageView3_b& >(image);
-        return img_ref.View(pt.x - offset, pt.y - offset,
-            PATCH_SIZE, PATCH_SIZE);
-    }
+int CalonderClassifier::getNumQuantBits() const
+{ return numQuantBits; }
 
-    float *CSMatrixGenerator::cs_phi_   = NULL;
-    int    CSMatrixGenerator::cs_phi_m_ = 0;
-    int    CSMatrixGenerator::cs_phi_n_ = 0;
+int CalonderClassifier::getOrigNumClasses() const
+{ return origNumClasses; }
 
-    RandomizedTree::RandomizedTree()
-        : posteriors_(NULL), posteriors2_(NULL)
-    {
-    }
+void CalonderClassifier::setVerbose( bool _verbose )
+{
+    verbose = _verbose;
+}
 
-    RandomizedTree::~RandomizedTree()
-    {
-        freePosteriors(3);
-    }
+void CalonderClassifier::clear()
+{
+    patchSize = numTrees = origNumClasses = signatureSize = treeDepth = numViews = numQuantBits = 0;
+    compressType = COMPRESS_NONE;
 
-    void RandomizedTree::createNodes(int num_nodes, cv::RNG &rng)
-    {
-        nodes_.reserve(num_nodes);
-        for (int i = 0; i < num_nodes; ++i) {
-            nodes_.push_back( RTreeNode(rng(PATCH_SIZE),
-                rng(PATCH_SIZE),
-                rng(PATCH_SIZE),
-                rng(PATCH_SIZE)) );
-        }
-    }
+    nodes.clear();
+    posteriors.clear();
+#if QUANTIZATION_AVAILABLE
+    quantizedPosteriors.clear();
+#endif
+}
 
-    int RandomizedTree::getIndex(uchar* patch_data) const
-    {
-        int index = 0;
-        for (int d = 0; d < depth_; ++d) {
-            int child_offset = nodes_[index](patch_data);
-            index = 2*index + 1 + child_offset;
-        }
-        return index - nodes_.size();
-    }
+bool CalonderClassifier::empty() const
+{
+    return posteriors.empty() && quantizedPosteriors.empty();
+}
 
-    void RandomizedTree::train(std::vector<BaseKeypoint> const& base_set,
-        cv::RNG &rng, int depth, int views, size_t reduced_num_dim,
-        int num_quant_bits)
-    {
+void CalonderClassifier::prepare( int _patchSize, int _signatureSize, int _numTrees, int _treeDepth, int _numViews )
+{
+    clear();
 
-        //CalonderPatchGenerator make_patch(NULL, rng);
-        PatchGenerator make_patch = PatchGenerator();
-        train(base_set, rng, make_patch, depth, views, reduced_num_dim, num_quant_bits);
-    }
+    patchSize = _patchSize;
+    signatureSize = _signatureSize;
+    numTrees = _numTrees;
+    treeDepth = _treeDepth;
+    numViews = _numViews;
 
-    void RandomizedTree::train(std::vector<BaseKeypoint> const& base_set,
-        cv::RNG &rng, PatchGenerator &make_patch,
-        int depth, int views, size_t reduced_num_dim,
-        int num_quant_bits)
-    {
-        init(base_set.size(), depth, rng);
+    numLeavesPerTree = 1 << treeDepth;      // 2^d
+    numNodesPerTree = numLeavesPerTree - 1; // 2^d - 1
 
-        Mat patch;
+    nodes = vector<Node>( numTrees*numNodesPerTree );
+    posteriors = vector<float>( numTrees*numLeavesPerTree*signatureSize, 0.f );
+}
 
-        // Estimate posterior probabilities using random affine views
-        std::vector<BaseKeypoint>::const_iterator keypt_it;
-        int class_id = 0;
-        for (keypt_it = base_set.begin(); keypt_it != base_set.end(); ++keypt_it, ++class_id) {
-            for (int i = 0; i < views; ++i) {
+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 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;
-                addExample(class_id, getData(&_patch));
+                CV_Assert( classID >= 0 && classID < signatureSize );
+                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);
-
-    }
-
-    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;
+        if( verbose ) cout << endl << ">" << endl;
     }
 
-    void RandomizedTree::init(int num_classes, int depth, cv::RNG &rng)
-    {
-        depth_ = depth;
-        num_leaves_ = 1 << depth;        // 2**d
-        int num_nodes = num_leaves_ - 1; // 2**d - 1
-
-        // Initialize probabilities and counts to 0
-        allocPosteriorsAligned(num_leaves_, num_classes);      // will set classes_ correctly
-        for (int i = 0; i < num_leaves_; ++i)
-            memset((void*)posteriors_[i].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));
+    _compressedDim = std::max( 0, std::min(signatureSize, _compressedDim) );
+    _numQuantBits = std::max( 0, std::min((int)MAX_NUM_QUANT_BITS, _numQuantBits) );
+    finalize( _compressedDim, _compressType, _numQuantBits, leafSampleCounters );
 
-        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);
-        float* posterior = getPosteriorByIndex(index);
-        ++leaf_counts_[index];
-        ++posterior[class_id];
+        int offset = treeNodes[idx](patch);
+        idx = 2*idx + 1 + offset;
     }
+    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
-        for (int index = 0; index < num_leaves_; ++index) {
-            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)
+        for( int li = 0; li < numLeavesPerTree; li++ )
         {
-            //added (inside cycle)
-            //float *post = getPosteriorByIndex(i);
-            //   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++)
+            int sampleCount = treeLeafSampleCounters[li];
+            if( sampleCount != 0 )
             {
-                cs_posteriors[i*reduced_num_dim+idx] = 0.0f;
-                for (int col = 0; col < classes_; col++)
-                {
-                    cs_posteriors[i*reduced_num_dim+idx] += cs_phi[idx*reduced_num_dim + col] * post[col];
-                }
-            }
+                float normalizer = 1.0f / sampleCount;
+                int leafPosteriorIdx = li*signatureSize;
+                for( int ci = 0; ci < signatureSize; ci++ )
+                   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;
-
-        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);
+        if( verbose )
+            cout << endl << "[WARNING] NO compression to leaves applied, because _compressedDim=" << _compressedDim << endl;
     }
 
-
-    float* RandomizedTree::getPosterior(uchar* patch_data)
+    // convert float-posteriors to uchar-posteriors (quantization step)
+#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
-    {
-        return getPosteriorByIndex( getIndex(patch_data) );
-    }
+Mat createCompressionMatrix( int rows, int cols, int distrType )
+{
+    Mat mtr( rows, cols, CV_32FC1 );
+    assert( rows <= cols );
 
-    uchar* RandomizedTree::getPosterior2(uchar* patch_data)
-    {
-        return getPosteriorByIndex2( getIndex(patch_data) );
-    }
+    RNG rng(23);
 
-    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
-        for (int k=0; k<dim; ++k, ++vec) {
-            *vec = float(int((*vec - bnds[0])/(bnds[1] - bnds[0])*(map_bnd[1] - map_bnd[0]) + map_bnd[0]));
-            // 0: clamp both, lower and upper values
-            if (clamp_mode == 0)      *vec = (*vec<map_bnd[0]) ? map_bnd[0] : ((*vec>map_bnd[1]) ? map_bnd[1] : *vec);
-            // 1: clamp lower values only
-            else if (clamp_mode == 1) *vec = (*vec<map_bnd[0]) ? map_bnd[0] : *vec;
-            // 2: clamp upper values only
-            else if (clamp_mode == 2) *vec = (*vec>map_bnd[1]) ? map_bnd[1] : *vec;
-            // 4: no clamping
-            else if (clamp_mode == 4) ; // yep, nothing
-            else {
-                printf("clamp_mode == %i is not valid (%s:%i).\n", clamp_mode, __FILE__, __LINE__);
-                exit(1);
-            }
-        }
-
+        float sigma = 1./rows;
+        for( int y = 0; y < rows; y++ )
+            for( int x = 0; x < cols; x++ )
+                mtr.at<float>(y,x) = rng.gaussian( sigma );
     }
-
-    void RandomizedTree::quantizeVector(float *vec, int dim, int N, float bnds[2], uchar *dst)
+    else if( distrType == CalonderClassifier::COMPRESS_DISTR_BERNOULLI )
     {
-        int map_bnd[2] = {0, N};          // bounds of quantized target interval we're mapping to
-        int tmp;
-        for (int k=0; k<dim; ++k) {
-            tmp = int((*vec - bnds[0])/(bnds[1] - bnds[0])*(map_bnd[1] - map_bnd[0]) + map_bnd[0]);
-            *dst = (uchar)((tmp<0) ? 0 : ((tmp>N) ? N : tmp));
-            ++vec;
-            ++dst;
-        }
+        float par = (float)(1./sqrt(rows));
+        for( int y = 0; y < rows; y++ )
+            for( int x = 0; x < cols; x++ )
+                mtr.at<float>(y,x) = rng(2)==0 ? par : -par;
     }
-
-
-    void RandomizedTree::read(const char* file_name, int num_quant_bits)
+    else if( distrType == CalonderClassifier::COMPRESS_DISTR_DBFRIENDLY )
     {
-        std::ifstream file(file_name, std::ifstream::binary);
-        read(file, num_quant_bits);
-        file.close();
+        float par = (float)sqrt(3./rows);
+        for( int y = 0; y < rows; y++ )
+            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)
-    {
-        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]));
+    return mtr;
+}
 
-        //posteriors_.resize(classes_ * num_leaves_);
-        //freePosteriors(3);
-        //printf("[DEBUG] reading: %i leaves, %i classes\n", num_leaves_, classes_);
-        allocPosteriorsAligned(num_leaves_, classes_);
-        for (int i=0; i<num_leaves_; i++)
-            is.read((char*)posteriors_[i].p(), classes_ * sizeof(*posteriors_[0].p()));
+void CalonderClassifier::compressLeaves( int _compressedDim, int _compressType )
+{
+    if( verbose )
+        cout << endl << "[OK] compressing leaves with matrix " << _compressedDim << " x " << signatureSize << endl;
 
-        // make char-posteriors from float-posteriors
-        makePosteriors2(num_quant_bits);
-    }
+    Mat compressionMtrT = (createCompressionMatrix( _compressedDim, signatureSize, _compressType )).t();
 
-    void RandomizedTree::write(const char* file_name) const
-    {
-        std::ofstream file(file_name, std::ofstream::binary);
-        write(file);
-        file.close();
-    }
+    vector<float> comprPosteriors( numTrees*numLeavesPerTree*_compressedDim, 0);
+    Mat( numTrees*numLeavesPerTree, _compressedDim, CV_32FC1, &comprPosteriors[0] ) =
+            Mat( numTrees*numLeavesPerTree, signatureSize, CV_32FC1, &posteriors[0]) * compressionMtrT;
 
-    void RandomizedTree::write(std::ostream &os) const
-    {
-        if (!posteriors_) {
-            printf("WARNING: Cannot write float posteriors cause posteriors_ == NULL\n");
-            return;
-        }
+    posteriors.resize( comprPosteriors.size() );
+    copy( comprPosteriors.begin(), comprPosteriors.end(), posteriors.begin() );
 
-        os.write((char*)(&classes_), sizeof(classes_));
-        os.write((char*)(&depth_), sizeof(depth_));
+    signatureSize = _compressedDim;
+    compressType = _compressType;
+}
 
-        os.write((char*)(&nodes_[0]), nodes_.size() * sizeof(nodes_[0]));
-        for (int i=0; i<num_leaves_; i++) {
-            os.write((char*)posteriors_[i].p(), classes_ * sizeof(*posteriors_[0].p()));
-        }
-    }
+#if QUANTIZATION_AVAILABLE
+static float percentile( const float* data, int n, float 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)
-    {
-        std::ofstream file(url.c_str(), (append?std::ios::app:std::ios::out));
-        for (int i=0; i<num_leaves_; i++) {
-            float *post = posteriors_[i].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 quantizeVector( const float* src, int dim, float fbounds[2], uchar ubounds[2], uchar* dst )
+{
+    assert( fbounds[0] < fbounds[1] );
+    assert( ubounds[0] < ubounds[1] );
 
-    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));
-        for (int i=0; i<num_leaves_; i++) {
-            uchar *post = posteriors2_[i].p();
-            for (int i=0; i<classes_; i++)
-                file << int(*post++) << (i<classes_-1?" ":"");
-            file << std::endl;
-        }
-        file.close();
+        float part = (src[i] - fbounds[0]) * normFactor;
+        assert( 0 <= part && part <= 1 ) ;
+        uchar val = ubounds[0] +  (uchar)( part*ubounds[1] );
+        dst[i] = std::max( 0, (int)std::min(ubounds[1], val) );
     }
+}
 
-    // returns the p% percentile of data (length n vector)
-    static float percentile(float *data, int n, float p)
-    {
-        assert(n>0);
-        assert(p>=0 && p<=1);
-        std::vector<float> vec(data, data+n);
-        sort(vec.begin(), vec.end());
-        int ix = (int)(p*(n-1));
-        return vec[ix];
-    }
+void CalonderClassifier::quantizePosteriors( int _numQuantBits, bool isClearFloatPosteriors )
+{
+    uchar ubounds[] = { 0, (uchar)((1<<_numQuantBits)-1) };
+    float fbounds[] = { 0.f, 0.f };
 
-    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
-        // TODO: do this more accurately
-        assert(posteriors_ != NULL);
-        perc[0] = perc[1] = .0f;
-        for (int i=0; i<num_leaves_; i++) {
-            perc[0] += percentile(posteriors_[i].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] += percentile( &posteriors[li*signatureSize], signatureSize, GET_LOWER_QUANT_PERC() );
+        fbounds[1] += percentile( &posteriors[li*signatureSize], signatureSize, GET_UPPER_QUANT_PERC() );
     }
+    fbounds[0] /= totalLeavesCount;
+    fbounds[1] /= totalLeavesCount;
 
-    float* CSMatrixGenerator::getCSMatrix(int m, int n, PHI_DISTR_TYPE dt)
-    {
-        assert(m <= n);
+    quantizedPosteriors.resize( posteriors.size() );
+    quantizeVector( &posteriors[0], posteriors.size(), fbounds, ubounds, &quantizedPosteriors[0] );
 
-        if (cs_phi_m_!=m || cs_phi_n_!=n || cs_phi_==NULL) {
-            if (cs_phi_) delete [] cs_phi_;
-            cs_phi_ = new float[m*n];
-        }
+    if( isClearFloatPosteriors )
+        clearFloatPosteriors();
+}
 
-#if 0 // debug - load the random matrix from a file (for reproducability of results)
-        //assert(m == 176);
-        //assert(n == 500);
-        //const char *phi = "/u/calonder/temp/dim_red/kpca_phi.txt";
-        const char *phi = "/u/calonder/temp/dim_red/debug_phi.txt";
-        std::ifstream ifs(phi);
-        for (size_t i=0; i<m*n; ++i) {
-            if (!ifs.good()) {
-                printf("[ERROR] RandomizedTree::makeRandomMeasMatrix: problem reading '%s'\n", phi);
-                exit(0);
-            }
-            ifs >> cs_phi[i];
-        }
-        ifs.close();
+void CalonderClassifier::clearFloatPosteriors()
+{
+    quantizedPosteriors.clear();
+}
 
-        static bool warned=false;
-        if (!warned) {
-            printf("[NOTE] RT: reading %ix%i PHI matrix from '%s'...\n", m, n, phi);
-            warned=true;
-        }
+#endif
 
+void CalonderClassifier::operator()( const Mat& img, Point2f pt, vector<float>& signature, float thresh ) const
+{
+    if( img.empty() || img.type() != CV_8UC1 )
         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) {
-            // special case - set to 0 for safety
-            memset(cs_phi, 0, m*n*sizeof(float));
-            printf("[WARNING] %s:%i: square CS matrix (-> no reduction)\n", __FILE__, __LINE__);
-        }
-        else {
-            cv::RNG rng(23);
-
-            // par is distr param, cf 'Favorable JL Distributions' (Baraniuk et al, 2006)
-            if (dt == PDT_GAUSS) {
-                float par = (float)(1./m);
-                //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.");
-        }
+void CalonderClassifier::operator()( const Mat& patch, vector<float>& signature, float thresh ) const
+{
+    if( posteriors.empty() || patch.empty() || patch.type() != CV_8UC1 || patch.cols < patchSize || patch.rows < patchSize )
+        return;
 
-        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_;
-        cs_phi_ = NULL;
+        int leafIdx = getLeafIdx( ti, patch );
+        const float* post = &posteriors[ti*treePostSize + leafIdx*signatureSize];
+        for( int ci = 0; ci < signatureSize; ci++ )
+            sig[ci] += post[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)
+    float coef = 1.f/numTrees;
+    for( int ci = 0; ci < signatureSize; ci++ )
     {
-        assert(sig_len == 176);
-        void *p_sig;
-        //added
-        // 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);
+        sig[ci] *= coef;
+        if( sig[ci] < thresh )
+            sig[ci] = 0;
     }
+}
 
-    inline uchar* RTreeClassifier::safeSignatureAlloc(int num_sig, int sig_len)
-    {
-        uchar *sig;
-        safeSignatureAlloc(&sig, num_sig, sig_len);
-        return sig;
-    }
+#if QUANTIZATION_AVAILABLE
+void CalonderClassifier::operator()( const Mat& img, Point2f pt, vector<uchar>& signature, uchar thresh ) const
+{
+    if( img.empty() || img.type() != CV_8UC1 )
+        return;
 
-    inline void add(int size, const float* src1, const float* src2, float* dst)
-    {
-        while(--size >= 0) {
-            *dst = *src1 + *src2;
-            ++dst; ++src1; ++src2;
-        }
-    }
+    Mat patch;
+    getRectSubPix(img, Size(patchSize,patchSize), pt, patch, img.type());
+    (*this)(patch, signature, thresh );
+}
 
-    inline void add(int size, const ushort* src1, const uchar* src2, ushort* dst)
-    {
-        while(--size >= 0) {
-            *dst = *src1 + *src2;
-            ++dst; ++src1; ++src2;
-        }
-    }
+void CalonderClassifier::operator()( const Mat& patch, vector<uchar>& signature, uchar thresh ) const
+{
+    if( quantizedPosteriors.empty() || patch.empty() || patch.type() != CV_8UC1 || patch.cols > patchSize || patch.rows > patchSize )
+        return;
+
+    int treePostSize = numLeavesPerTree*signatureSize;
 
-    RTreeClassifier::RTreeClassifier()
-        : classes_(0)
+    vector<float> sum( signatureSize, 0.f );
+    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];
     }
-
-    void RTreeClassifier::train(std::vector<BaseKeypoint> const& base_set,
-        cv::RNG &rng, int num_trees, int depth,
-        int views, size_t reduced_num_dim,
-        int num_quant_bits, bool print_status)
+    float coef = 1.f/numTrees;
+    signature.resize( signatureSize );
+    uchar* sig = &signature[0];
+    for( int ci = 0; ci < signatureSize; ci++ )
     {
-        PatchGenerator make_patch = PatchGenerator();
-        train(base_set, rng, make_patch, num_trees, depth, views, reduced_num_dim, num_quant_bits, print_status);
+        sig[ci] = (uchar)(sum[ci]*coef);
+        if( sig[ci] < thresh )
+            sig[ci] = 0;
     }
+}
+#endif
 
-    // Single-threaded version of train(), with progress output
-    void RTreeClassifier::train(std::vector<BaseKeypoint> const& base_set,
-        cv::RNG &rng, PatchGenerator &make_patch, int num_trees,
-        int depth, int views, size_t reduced_num_dim,
-        int num_quant_bits, bool print_status)
-    {
-        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;
-        }
+void CalonderClassifier::read( const FileNode& fn )
+{
+    prepare( fn["patchSize"], fn["signatureSize"], fn["numTrees"], fn["treeDepth"], fn["numViews"] );
+    origNumClasses = fn["origNumClasses"];
+    compressType = fn["compressType"];
+    int _numQuantBits = fn["numQuantBits"];
 
-        num_quant_bits_ = num_quant_bits;
-        classes_ = reduced_num_dim; // base_set.size();
-        original_num_classes_ = base_set.size();
-        trees_.resize(num_trees);
-        if (print_status)
-        {
-            printf("[OK] Training trees: base size=%i, reduced size=%i\n", (int)base_set.size(), (int)reduced_num_dim);
-        }
+    for( int ti = 0; ti < numTrees; ti++ )
+    {
+        stringstream treeName;
+        treeName << "tree" << ti;
+        FileNode treeFN = fn["trees"][treeName.str()];
 
-        int count = 1;
-        if (print_status)
-        {
-            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++)
+        Node* treeNodes = &nodes[ti*numNodesPerTree];
+        FileNodeIterator nodesFNIter = treeFN["nodes"].begin();
+        for( int ni = 0; ni < numNodesPerTree; ni++ )
         {
-            trees_[i].train(base_set, rng, make_patch, depth, views, reduced_num_dim, num_quant_bits_);
-            if (print_status)
-            {
-                printf("\r[OK] Trained %i / %i trees", count++, num_trees);
-                fflush(stdout);
-            }
+            Node* node = treeNodes + ni;
+            nodesFNIter >> node->x1 >> node->y1 >> node->x2 >> node->y2;
         }
 
-        if (print_status)
+        FileNode posteriorsFN = treeFN["posteriors"];
+        for( int li = 0; li < numLeavesPerTree; li++ )
         {
-            printf("\n");
-            countZeroElements();
-            printf("\n\n");
+            stringstream leafName;
+            leafName << "leaf" << li;
+            float* post = &posteriors[ti*numLeavesPerTree*signatureSize + li*signatureSize];
+            FileNodeIterator leafFNIter = posteriorsFN[leafName.str()].begin();
+            for( int ci = 0; ci < signatureSize; ci++ )
+                leafFNIter >> post[ci];
         }
     }
-
-    void RTreeClassifier::getSignature(IplImage* patch, float *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);
-        }
-
-        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;
+#if QUANTIZATION_AVAILABLE
+    if( _numQuantBits )
+        quantizePosteriors(_numQuantBits);
 #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
-        if (posteriors_ == NULL)
+void CalonderClassifier::write( FileStorage& fs ) const
+{
+    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()];
-            //aadded
-            //  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_);
+            const Node* node = treeNodes + ni;
+            fs << node->x1 << node->y1 << node->x2 << node->y2;
         }
+        fs << "]"; // nodes
 
-        printf("[OK] Loaded RTC, quantization=%i bits\n", num_quant_bits_);
-
-        countZeroElements();
-    }
-
-    void RTreeClassifier::write(const char* file_name) const
-    {
-        std::ofstream file(file_name, std::ofstream::binary);
-        write(file);
-        file.close();
-    }
-
-    void RTreeClassifier::write(std::ostream &os) const
-    {
-        int num_trees = 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 << "posteriors" << "{";
+        for( int li = 0; li < numLeavesPerTree; li++ )
+        {
+            stringstream leafName;
+            leafName << "leaf" << li;
+            fs << leafName.str() << "[:";
+            const float* post = &posteriors[ti*numLeavesPerTree*signatureSize + li*signatureSize];
+            for( int ci = 0; ci < signatureSize; ci++ )
+            {
+                fs << post[ci];
             }
+            fs << "]"; // leaf
         }
-        classes_ = 176;
-
-        //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 << "}"; // posteriors
+        fs << "}"; // tree
     }
+    fs << "}"; // trees
+}
 
-
-
-    //} // namespace features
 }
index a68f008..64e0014 100644 (file)
@@ -144,7 +144,7 @@ void DescriptorExtractor::removeBorderKeypoints( vector<KeyPoint>& keypoints,
 }
 
 /****************************************************************************************\
-*                                SiftDescriptorExtractor                                  *
+*                                SiftDescriptorExtractor                                 *
 \****************************************************************************************/
 SiftDescriptorExtractor::SiftDescriptorExtractor( double magnification, bool isNormalize, bool recalculateAngles,
                                                   int nOctaves, int nOctaveLayers, int firstOctave, int angleMode )
@@ -188,7 +188,7 @@ void SiftDescriptorExtractor::write (FileStorage &fs) const
 }
 
 /****************************************************************************************\
-*                                SurfDescriptorExtractor                                  *
+*                                SurfDescriptorExtractor                                 *
 \****************************************************************************************/
 SurfDescriptorExtractor::SurfDescriptorExtractor( int nOctaves,
                                                   int nOctaveLayers, bool extended )
@@ -228,6 +228,10 @@ void SurfDescriptorExtractor::write( FileStorage &fs ) const
     fs << "extended" << surf.extended;
 }
 
+/****************************************************************************************\
+*           Factory functions for descriptor extractor and matcher creating              *
+\****************************************************************************************/
+
 Ptr<DescriptorExtractor> createDescriptorExtractor( const string& descriptorExtractorType )
 {
     DescriptorExtractor* de = 0;
@@ -270,7 +274,9 @@ Ptr<DescriptorMatcher> createDescriptorMatcher( const string& descriptorMatcherT
     return dm;
 }
 
-
+/****************************************************************************************\
+*                             BruteForceMatcher L2 specialization                        *
+\****************************************************************************************/
 template<>
 void BruteForceMatcher<L2<float> >::matchImpl( const Mat& descriptors_1, const Mat& descriptors_2,
                                              const Mat& /*mask*/, vector<int>& matches ) const
@@ -317,7 +323,6 @@ void BruteForceMatcher<L2<float> >::matchImpl( const Mat& descriptors_1, const M
 #endif
 }
 
-
 /****************************************************************************************\
 *                                GenericDescriptorMatch                                  *
 \****************************************************************************************/
@@ -394,6 +399,9 @@ void GenericDescriptorMatch::clear()
     collection.clear();
 }
 
+/*
+ * Factory function for GenericDescriptorMatch creating
+ */
 Ptr<GenericDescriptorMatch> createGenericDescriptorMatch( const string& genericDescritptorMatchType, const string &paramsFilename )
 {
     GenericDescriptorMatch *descriptorMatch = 0;
@@ -409,7 +417,7 @@ Ptr<GenericDescriptorMatch> createGenericDescriptorMatch( const string& genericD
     }
     else if( ! genericDescritptorMatchType.compare ("CALONDER") )
     {
-        descriptorMatch = new CalonderDescriptorMatch ();
+        //descriptorMatch = new CalonderDescriptorMatch ();
     }
 
     if( !paramsFilename.empty() && descriptorMatch != 0 )
@@ -626,6 +634,7 @@ void OneWayDescriptorMatch::clear ()
 /****************************************************************************************\
 *                                CalonderDescriptorMatch                                 *
 \****************************************************************************************/
+#if 0
 CalonderDescriptorMatch::Params::Params( const RNG& _rng, const PatchGenerator& _patchGen,
                                          int _numTrees, int _depth, int _views,
                                          size_t _reducedNumDim,
@@ -774,6 +783,7 @@ void CalonderDescriptorMatch::write( FileStorage& fs ) const
     fs << "numQuantBits" << params.numQuantBits;
     fs << "printStatus" << params.printStatus;
 }
+#endif
 
 /****************************************************************************************\
 *                                  FernDescriptorMatch                                   *
@@ -827,22 +837,13 @@ void FernDescriptorMatch::trainFernClassifier()
     {
         assert( params.filename.empty() );
 
-        vector<Point2f> points;
-        vector<Ptr<Mat> > refimgs;
-        vector<int> labels;
-        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);
-            }
-        }
+        vector<vector<Point2f> > points;
+        for( size_t imgIdx = 0; imgIdx < collection.images.size(); imgIdx++ )
+            KeyPoint::convert( collection.points[imgIdx], points[imgIdx] );
 
-        classifier = new FernClassifier( points, refimgs, labels, params.nclasses, params.patchSize,
-                                         params.signatureSize, params.nstructs, params.structSize, params.nviews,
-                                         params.compressionMethod, params.patchGenerator );
+        classifier = new FernClassifier( points, collection.images, vector<vector<int> >(), 0, // each points is a class
+                                         params.patchSize, params.signatureSize, params.nstructs, params.structSize,
+                                         params.nviews, params.compressionMethod, params.patchGenerator );
     }
 }
 
@@ -966,4 +967,59 @@ void FernDescriptorMatch::clear ()
     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);
+}
+
 }
index 0ee4d30..e1bdafb 100644 (file)
@@ -692,9 +692,9 @@ Size FernClassifier::getPatchSize() const
 }
 
 
-FernClassifier::FernClassifier(const vector<Point2f>& points,
-                               const vector<Ptr<Mat> >& refimgs,
-                               const vector<int>& labels,
+FernClassifier::FernClassifier(const vector<vector<Point2f> >& points,
+                               const vector<Mat>& refimgs,
+                               const vector<vector<int> >& labels,
                                int _nclasses, int _patchSize,
                                int _signatureSize, int _nstructs,
                                int _structSize, int _nviews, int _compressionMethod,
@@ -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,
-                           const vector<Ptr<Mat> >& refimgs,
-                           const vector<int>& labels,
+void FernClassifier::train(const vector<vector<Point2f> >& points,
+                           const vector<Mat>& refimgs,
+                           const vector<vector<int> >& labels,
                            int _nclasses, int _patchSize,
                            int _signatureSize, int _nstructs,
                            int _structSize, int _nviews, int _compressionMethod,
                            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() );
 
+
     prepare(_nclasses, _patchSize, _signatureSize, _nstructs,
             _structSize, _nviews, _compressionMethod);
 
     // pass all the views of all the samples through the generated trees and accumulate
     // the statistics (posterior probabilities) in leaves.
     Mat patch;
-    int i, j, nsamples = (int)points.size();
     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 Mat& src = *refimgs[i];
-        int classId = labels.empty() ? i : labels[i];
-        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++ )
+        const Point2f* imgPoints = &points[imgIdx][0];
+        const int* imgLabels = labels.empty() ? 0 : &labels[imgIdx][0];
+        for( size_t pointIdx = 0; pointIdx < points[imgIdx].size(); pointIdx++, globalPointIdx++ )
         {
-            patchGenerator(src, pt, patch, patchSize, rng);
-            for( int f = 0; f < nstructs; f++ )
-                posteriors[getLeaf(f, patch)*nclasses + classId]++;
+            Point2f pt = imgPoints[pointIdx];
+            const Mat& src = refimgs[imgIdx];
+            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 )
index 6b138a2..dfc5690 100644 (file)
-//Calonder descriptor sample
-#include <stdio.h>
-
-#if 0
-#include <cxcore.h>
-#include <cv.h>
-#include <cvaux.h>
 #include <highgui.h>
-#include <vector>
-using namespace std;
+#include <opencv2/core/core.hpp>
+#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)
-const int n_points = -1;
-
-//Draw the border of projection of train image calculed by averaging detected correspondences
-const bool draw_border = true;
+using namespace std;
+using namespace cv;
 
-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);
-    cvmSet(m, row, col + 1, val2);
-    cvmSet(m, row, col + 2, val3);
-    cvmSet(m, row, col + 3, val4);
-    cvmSet(m, row, col + 4, val5);
-    cvmSet(m, row, col + 5, val6);
+    H.create(3, 3, CV_32FC1);
+    H.at<float>(0,0) = rng.uniform( 0.8f, 1.2f);
+    H.at<float>(0,1) = rng.uniform(-0.1f, 0.1f);
+    H.at<float>(0,2) = rng.uniform(-0.1f, 0.1f)*src.cols;
+    H.at<float>(1,0) = rng.uniform(-0.1f, 0.1f);
+    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();
-    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++)
+    if( argc != 4 && argc != 3 )
     {
-        cvmSet6(A, 2*i, 0, p1[i].x, p1[i].y, 1, 0, 0, 0);
-        cvmSet6(A, 2*i + 1, 0, 0, 0, 0, p1[i].x, p1[i].y, 1);
-        cvmSet(B, 2*i, 0, p2[i].x);
-        cvmSet(B, 2*i + 1, 0, p2[i].y);
+        cout << "Format:" << endl <<
+                "   classifier(xml to write) test_image file_with_train_images_filenames(txt)" <<
+                "   or" << endl <<
+                "   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)
-{
-    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++)
+    CalonderClassifier classifier;
+    if( argc == 4 ) // Train
     {
-        float x = a*p1[i].x + b*p1[i].y + c;
-        float y = d*p1[i].x + e*p1[i].y + f;
-        p2.push_back(cvPoint(x, y));
+        // Read train images and test image
+        ifstream fst( argv[3], ifstream::in );
+        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 );
     }
-}
-
-
-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++)
+    else
     {
-        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
-    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);
-
-       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( classifier.empty() )
+    {
+        cout << "Calonder classifier is empty" << endl;
+        return -1;
+    }
 
-               if (draw_border)
-               {
-                       cvLine(result,cvPoint(mapped_corners[0].x, mapped_corners[0].y+train_image->height),
-                               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),
-                               cvPoint(mapped_corners[2].x, mapped_corners[2].y+train_image->height),cvScalar(150),3);
-                       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);
-                       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);
-               }
+    // Test Calonder classifier on test image and warped one
+    Mat testImg1 = imread( argv[2], CV_LOAD_IMAGE_GRAYSCALE ), testImg2, H12;
+    if( testImg1.empty() )
+    {
+        cout << "Test image can not be read." << endl;
+        return -1;
+    }
+    warpPerspectiveRand( testImg1, testImg2, H12, theRNG() );
 
-               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 );
 
-       
-       
-       if (signature)
-               delete[] signature;
-       if (best_corr)
-               delete[] best_corr;
-       cvReleaseMemStorage(&storage);
-       cvReleaseImage(&train_image);
-       cvReleaseImage(&test_image);
+    // Calculate Calonder descriptors
+    int signatureSize = classifier.getSignatureSize();
+    vector<float> r1(testPoints1.size()*signatureSize), r2(testPoints2.size()*signatureSize);
+    vector<float>::iterator rit = r1.begin();
+    for( size_t i = 0; i < testPoints1.size(); i++ )
+    {
+        vector<float> s;
+        classifier( testImg1, testPoints1[i], s );
+        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;
-#endif
+    Mat descriptors1(testPoints1.size(), classifier.getSignatureSize(), CV_32FC1, &r1[0] ),
+        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();
 }