Warning fixes continued
[platform/upstream/opencv.git] / modules / features2d / src / descriptors.cpp
index 78aaef9..2fb43b7 100644 (file)
 
 #include "precomp.hpp"
 
-#ifdef HAVE_EIGEN2
-#include <Eigen/Array>
-#endif
-
-//#define _KDTREE
-
 using namespace std;
-namespace cv
-{
 
-void drawMatches( const Mat& img1, const vector<KeyPoint>& keypoints1,
-                  const Mat& img2,const vector<KeyPoint>& keypoints2,
-                  const vector<int>& matches, Mat& outImg,
-                  const Scalar& matchColor, const Scalar& singlePointColor,
-                  const vector<char>& matchesMask, int flags )
+namespace cv
 {
-    Size size( img1.cols + img2.cols, MAX(img1.rows, img2.rows) );
-    if( flags & DrawMatchesFlags::DRAW_OVER_OUTIMG )
-    {
-        if( size.width > outImg.cols || size.height > outImg.rows )
-            CV_Error( CV_StsBadSize, "outImg has size less than need to draw img1 and img2 together" );
-    }
-    else
-    {
-        outImg.create( size, CV_MAKETYPE(img1.depth(), 3) );
-        Mat outImg1 = outImg( Rect(0, 0, img1.cols, img1.rows) );
-        cvtColor( img1, outImg1, CV_GRAY2RGB );
-        Mat outImg2 = outImg( Rect(img1.cols, 0, img2.cols, img2.rows) );
-        cvtColor( img2, outImg2, CV_GRAY2RGB );
-    }
-
-    RNG rng;
-    // draw keypoints
-    if( !(flags & DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS) )
-    {
-        bool isRandSinglePointColor = singlePointColor == Scalar::all(-1);
-        for( vector<KeyPoint>::const_iterator it = keypoints1.begin(); it < keypoints1.end(); ++it )
-        {
-            circle( outImg, it->pt, 3, isRandSinglePointColor ?
-                    Scalar(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256)) : singlePointColor );
-        }
-        for( vector<KeyPoint>::const_iterator it = keypoints2.begin(); it < keypoints2.end(); ++it )
-        {
-            Point p = it->pt;
-            circle( outImg, Point2f(p.x+img1.cols, p.y), 3, isRandSinglePointColor ?
-                    Scalar(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256)) : singlePointColor );
-        }
-     }
-
-    // draw matches
-    bool isRandMatchColor = matchColor == Scalar::all(-1);
-    if( matches.size() != keypoints1.size() )
-        CV_Error( CV_StsBadSize, "matches must have the same size as keypoints1" );
-    if( !matchesMask.empty() && matchesMask.size() != keypoints1.size() )
-        CV_Error( CV_StsBadSize, "mask must have the same size as keypoints1" );
-    vector<int>::const_iterator mit = matches.begin();
-    for( int i1 = 0; mit != matches.end(); ++mit, i1++ )
-    {
-        if( (matchesMask.empty() || matchesMask[i1] ) && *mit >= 0 )
-        {
-            Point2f pt1 = keypoints1[i1].pt,
-                    pt2 = keypoints2[*mit].pt,
-                    dpt2 = Point2f( std::min(pt2.x+img1.cols, float(outImg.cols-1)), pt2.y );
-            Scalar randColor( rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256) );
-            circle( outImg, pt1, 3, isRandMatchColor ? randColor : matchColor );
-            circle( outImg, dpt2, 3, isRandMatchColor ? randColor : matchColor );
-            line( outImg, pt1, dpt2, isRandMatchColor ? randColor : matchColor );
-        }
-    }
-}
 
 /****************************************************************************************\
 *                                 DescriptorExtractor                                    *
@@ -118,855 +52,238 @@ void drawMatches( const Mat& img1, const vector<KeyPoint>& keypoints1,
 /*
  *   DescriptorExtractor
  */
-struct RoiPredicate
-{
-    RoiPredicate(float _minX, float _minY, float _maxX, float _maxY)
-        : minX(_minX), minY(_minY), maxX(_maxX), maxY(_maxY)
-    {}
+DescriptorExtractor::~DescriptorExtractor()
+{}
 
-    bool operator()( const KeyPoint& keyPt) const
+void DescriptorExtractor::compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const
+{
+    if( image.empty() || keypoints.empty() )
     {
-        Point2f pt = keyPt.pt;
-        return (pt.x < minX) || (pt.x >= maxX) || (pt.y < minY) || (pt.y >= maxY);
+        descriptors.release();
+        return;
     }
 
-    float minX, minY, maxX, maxY;
-};
+    KeyPointsFilter::runByImageBorder( keypoints, image.size(), 0 );
+    KeyPointsFilter::runByKeypointSize( keypoints, std::numeric_limits<float>::epsilon() );
 
-void DescriptorExtractor::removeBorderKeypoints( vector<KeyPoint>& keypoints,
-                                                 Size imageSize, int borderPixels )
-{
-    keypoints.erase( remove_if(keypoints.begin(), keypoints.end(),
-                               RoiPredicate((float)borderPixels, (float)borderPixels,
-                                            (float)(imageSize.width - borderPixels),
-                                            (float)(imageSize.height - borderPixels))),
-                     keypoints.end());
+    computeImpl( image, keypoints, descriptors );
 }
 
-/****************************************************************************************\
-*                                SiftDescriptorExtractor                                  *
-\****************************************************************************************/
-SiftDescriptorExtractor::SiftDescriptorExtractor( double magnification, bool isNormalize, bool recalculateAngles,
-                                                  int nOctaves, int nOctaveLayers, int firstOctave, int angleMode )
-    : sift( magnification, isNormalize, recalculateAngles, nOctaves, nOctaveLayers, firstOctave, angleMode )
-{}
-
-void SiftDescriptorExtractor::compute( const Mat& image,
-                                       vector<KeyPoint>& keypoints,
-                                       Mat& descriptors) const
+void DescriptorExtractor::compute( const vector<Mat>& imageCollection, vector<vector<KeyPoint> >& pointCollection, vector<Mat>& descCollection ) const
 {
-    bool useProvidedKeypoints = true;
-    sift(image, Mat(), keypoints, descriptors, useProvidedKeypoints);
+    CV_Assert( imageCollection.size() == pointCollection.size() );
+    descCollection.resize( imageCollection.size() );
+    for( size_t i = 0; i < imageCollection.size(); i++ )
+        compute( imageCollection[i], pointCollection[i], descCollection[i] );
 }
 
-void SiftDescriptorExtractor::read (const FileNode &fn)
-{
-    double magnification = fn["magnification"];
-    bool isNormalize = (int)fn["isNormalize"] != 0;
-    bool recalculateAngles = (int)fn["recalculateAngles"] != 0;
-    int nOctaves = fn["nOctaves"];
-    int nOctaveLayers = fn["nOctaveLayers"];
-    int firstOctave = fn["firstOctave"];
-    int angleMode = fn["angleMode"];
-
-    sift = SIFT( magnification, isNormalize, recalculateAngles, nOctaves, nOctaveLayers, firstOctave, angleMode );
-}
-
-void SiftDescriptorExtractor::write (FileStorage &fs) const
-{
-//    fs << "algorithm" << getAlgorithmName ();
-
-    SIFT::CommonParams commParams = sift.getCommonParams ();
-    SIFT::DescriptorParams descriptorParams = sift.getDescriptorParams ();
-    fs << "magnification" << descriptorParams.magnification;
-    fs << "isNormalize" << descriptorParams.isNormalize;
-    fs << "recalculateAngles" << descriptorParams.recalculateAngles;
-    fs << "nOctaves" << commParams.nOctaves;
-    fs << "nOctaveLayers" << commParams.nOctaveLayers;
-    fs << "firstOctave" << commParams.firstOctave;
-    fs << "angleMode" << commParams.angleMode;
-}
-
-/****************************************************************************************\
-*                                SurfDescriptorExtractor                                  *
-\****************************************************************************************/
-SurfDescriptorExtractor::SurfDescriptorExtractor( int nOctaves,
-                                                  int nOctaveLayers, bool extended )
-    : surf( 0.0, nOctaves, nOctaveLayers, extended )
+/*void DescriptorExtractor::read( const FileNode& )
 {}
 
-void SurfDescriptorExtractor::compute( const Mat& image,
-                                       vector<KeyPoint>& keypoints,
-                                       Mat& descriptors) const
-{
-    // Compute descriptors for given keypoints
-    vector<float> _descriptors;
-    Mat mask;
-    bool useProvidedKeypoints = true;
-    surf(image, mask, keypoints, _descriptors, useProvidedKeypoints);
-
-    descriptors.create(keypoints.size(), surf.descriptorSize(), CV_32FC1);
-    assert( (int)_descriptors.size() == descriptors.rows * descriptors.cols );
-    std::copy(_descriptors.begin(), _descriptors.end(), descriptors.begin<float>());
-}
+void DescriptorExtractor::write( FileStorage& ) const
+{}*/
 
-void SurfDescriptorExtractor::read( const FileNode &fn )
+bool DescriptorExtractor::empty() const
 {
-    int nOctaves = fn["nOctaves"];
-    int nOctaveLayers = fn["nOctaveLayers"];
-    bool extended = (int)fn["extended"] != 0;
-
-    surf = SURF( 0.0, nOctaves, nOctaveLayers, extended );
+    return false;
 }
 
-void SurfDescriptorExtractor::write( FileStorage &fs ) const
+void DescriptorExtractor::removeBorderKeypoints( vector<KeyPoint>& keypoints,
+                                                 Size imageSize, int borderSize )
 {
-//    fs << "algorithm" << getAlgorithmName ();
-
-    fs << "nOctaves" << surf.nOctaves;
-    fs << "nOctaveLayers" << surf.nOctaveLayers;
-    fs << "extended" << surf.extended;
+    KeyPointsFilter::runByImageBorder( keypoints, imageSize, borderSize );
 }
 
-Ptr<DescriptorExtractor> createDescriptorExtractor( const string& descriptorExtractorType )
+Ptr<DescriptorExtractor> DescriptorExtractor::create(const string& descriptorExtractorType)
 {
-    DescriptorExtractor* de = 0;
-    if( !descriptorExtractorType.compare( "SIFT" ) )
+    if( descriptorExtractorType.find("Opponent") == 0 )
     {
-        de = new SiftDescriptorExtractor/*( double magnification=SIFT::DescriptorParams::GET_DEFAULT_MAGNIFICATION(),
-                             bool isNormalize=true, bool recalculateAngles=true,
-                             int nOctaves=SIFT::CommonParams::DEFAULT_NOCTAVES,
-                             int nOctaveLayers=SIFT::CommonParams::DEFAULT_NOCTAVE_LAYERS,
-                             int firstOctave=SIFT::CommonParams::DEFAULT_FIRST_OCTAVE,
-                             int angleMode=SIFT::CommonParams::FIRST_ANGLE )*/;
+        size_t pos = string("Opponent").size();
+        string type = descriptorExtractorType.substr(pos);
+        return new OpponentColorDescriptorExtractor(DescriptorExtractor::create(type));
     }
-    else if( !descriptorExtractorType.compare( "SURF" ) )
-    {
-        de = new SurfDescriptorExtractor/*( int nOctaves=4, int nOctaveLayers=2, bool extended=false )*/;
-    }
-    else
-    {
-        //CV_Error( CV_StsBadArg, "unsupported descriptor extractor type");
-    }
-    return de;
-}
 
-Ptr<DescriptorMatcher> createDescriptorMatcher( const string& descriptorMatcherType )
-{
-    DescriptorMatcher* dm = 0;
-    if( !descriptorMatcherType.compare( "BruteForce" ) )
-    {
-        dm = new BruteForceMatcher<L2<float> >();
-    }
-    else if ( !descriptorMatcherType.compare( "BruteForce-L1" ) )
-    {
-        dm = new BruteForceMatcher<L1<float> >();
-    }
-    else
-    {
-        //CV_Error( CV_StsBadArg, "unsupported descriptor matcher type");
-    }
-
-    return dm;
-}
-
-
-template<>
-void BruteForceMatcher<L2<float> >::matchImpl( const Mat& descriptors_1, const Mat& descriptors_2,
-                                             const Mat& mask, vector<int>& matches ) const
-{
-    matches.clear();
-    matches.reserve( descriptors_1.rows );
-//TODO: remove _DEBUG if bag 416 fixed
-#if (defined _DEBUG || !defined HAVE_EIGEN2)
-    Mat norms;
-    cv::reduce( descriptors_2.mul( descriptors_2 ), norms, 1, 0);
-    norms = norms.t();
-    Mat desc_2t = descriptors_2.t();
-    for( int i=0;i<descriptors_1.rows;i++ )
-    {
-        Mat distances = (-2)*descriptors_1.row(i)*desc_2t;
-        distances += norms;
-        Point minLoc;
-        minMaxLoc ( distances, 0, 0, &minLoc );
-        matches.push_back( minLoc.x );
-    }
-
-#else
-    Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> desc1t;
-    Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> desc2;
-    cv2eigen( descriptors_1.t(), desc1t);
-    cv2eigen( descriptors_2, desc2 );
-
-    //Eigen::Matrix<float, Eigen::Dynamic, 1> norms = desc2.rowwise().squaredNorm();
-    Eigen::Matrix<float, Eigen::Dynamic, 1> norms = desc2.rowwise().squaredNorm() / 2;
-    for( int i=0;i<descriptors_1.rows;i++ )
-    {
-        //Eigen::Matrix<float, Eigen::Dynamic, 1> distances = (-2) * (desc2*desc1t.col(i));
-        Eigen::Matrix<float, Eigen::Dynamic, 1> distances = desc2*desc1t.col(i);
-
-        //distances += norms;
-        distances -= norms;
-
-        int idx;
-
-        //distances.minCoeff(&idx);
-        distances.maxCoeff(&idx);
-        matches.push_back( idx );
-    }
-#endif
+    return Algorithm::create<DescriptorExtractor>("Feature2D." + descriptorExtractorType);
 }
 
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
 /****************************************************************************************\
-*                                GenericDescriptorMatch                                  *
+*                             OpponentColorDescriptorExtractor                           *
 \****************************************************************************************/
-/*
- * KeyPointCollection
- */
-void KeyPointCollection::add( const Mat& _image, const vector<KeyPoint>& _points )
+OpponentColorDescriptorExtractor::OpponentColorDescriptorExtractor( const Ptr<DescriptorExtractor>& _descriptorExtractor ) :
+        descriptorExtractor(_descriptorExtractor)
 {
-    // update m_start_indices
-    if( startIndices.empty() )
-        startIndices.push_back(0);
-    else
-        startIndices.push_back(*startIndices.rbegin() + points.rbegin()->size());
-
-    // add image and keypoints
-    images.push_back(_image);
-    points.push_back(_points);
+    CV_Assert( !descriptorExtractor.empty() );
 }
 
-KeyPoint KeyPointCollection::getKeyPoint( int index ) const
+static void convertBGRImageToOpponentColorSpace( const Mat& bgrImage, vector<Mat>& opponentChannels )
 {
-    size_t i = 0;
-    for(; i < startIndices.size() && startIndices[i] <= index; i++);
-    i--;
-    assert(i < startIndices.size() && (size_t)index - startIndices[i] < points[i].size());
+    if( bgrImage.type() != CV_8UC3 )
+        CV_Error( CV_StsBadArg, "input image must be an BGR image of type CV_8UC3" );
 
-    return points[i][index - startIndices[i]];
-}
+    // Split image into RGB to allow conversion to Opponent Color Space.
+    vector<Mat> bgrChannels(3);
+    split( bgrImage, bgrChannels );
 
-size_t KeyPointCollection::calcKeypointCount() const
-{
-    if( startIndices.empty() )
-        return 0;
-    return *startIndices.rbegin() + points.rbegin()->size();
-}
+    // Prepare opponent color space storage matrices.
+    opponentChannels.resize( 3 );
+    opponentChannels[0] = cv::Mat(bgrImage.size(), CV_8UC1); // R-G RED-GREEN
+    opponentChannels[1] = cv::Mat(bgrImage.size(), CV_8UC1); // R+G-2B YELLOW-BLUE
+    opponentChannels[2] = cv::Mat(bgrImage.size(), CV_8UC1); // R+G+B
 
-void KeyPointCollection::clear()
-{
-    images.clear();
-    points.clear();
-    startIndices.clear();
-}
-
-/*
- * GenericDescriptorMatch
- */
-
-void GenericDescriptorMatch::match( const Mat&, vector<KeyPoint>&, vector<DMatch>& )
-{
-}
-
-void GenericDescriptorMatch::match( const Mat&, vector<KeyPoint>&, vector<vector<DMatch> >&, float )
-{
-}
-
-void GenericDescriptorMatch::add( KeyPointCollection& collection )
-{
-    for( size_t i = 0; i < collection.images.size(); i++ )
-        add( collection.images[i], collection.points[i] );
-}
-
-void GenericDescriptorMatch::classify( const Mat& image, vector<cv::KeyPoint>& points )
-{
-    vector<int> keypointIndices;
-    match( image, points, keypointIndices );
-
-    // remap keypoint indices to descriptors
-    for( size_t i = 0; i < keypointIndices.size(); i++ )
-        points[i].class_id = collection.getKeyPoint(keypointIndices[i]).class_id;
-};
-
-void GenericDescriptorMatch::clear()
-{
-    collection.clear();
-}
-
-Ptr<GenericDescriptorMatch> createGenericDescriptorMatch( const string& genericDescritptorMatchType, const string &paramsFilename )
-{
-    GenericDescriptorMatch *descriptorMatch = 0;
-    if( ! genericDescritptorMatchType.compare ("ONEWAY") )
-    {
-        descriptorMatch = new OneWayDescriptorMatch ();
-    }
-    else if( ! genericDescritptorMatchType.compare ("FERN") )
-    {
-        FernDescriptorMatch::Params params;
-        params.signatureSize = numeric_limits<int>::max();
-        descriptorMatch = new FernDescriptorMatch (params);
-    }
-    else if( ! genericDescritptorMatchType.compare ("CALONDER") )
-    {
-        descriptorMatch = new CalonderDescriptorMatch ();
-    }
-
-    if( !paramsFilename.empty() && descriptorMatch != 0 )
+    // Calculate the channels of the opponent color space
     {
-        FileStorage fs = FileStorage( paramsFilename, FileStorage::READ );
-        if( fs.isOpened() )
+        // (R - G) / sqrt(2)
+        MatConstIterator_<signed char> rIt = bgrChannels[2].begin<signed char>();
+        MatConstIterator_<signed char> gIt = bgrChannels[1].begin<signed char>();
+        MatIterator_<unsigned char> dstIt = opponentChannels[0].begin<unsigned char>();
+        float factor = 1.f / sqrt(2.f);
+        for( ; dstIt != opponentChannels[0].end<unsigned char>(); ++rIt, ++gIt, ++dstIt )
         {
-            descriptorMatch->read( fs.root() );
-            fs.release();
+            int value = static_cast<int>( static_cast<float>(static_cast<int>(*gIt)-static_cast<int>(*rIt)) * factor );
+            if( value < 0 ) value = 0;
+            if( value > 255 ) value = 255;
+            (*dstIt) = static_cast<unsigned char>(value);
         }
     }
-
-    return descriptorMatch;
-}
-
-/****************************************************************************************\
-*                                OneWayDescriptorMatch                                  *
-\****************************************************************************************/
-OneWayDescriptorMatch::OneWayDescriptorMatch()
-{}
-
-OneWayDescriptorMatch::OneWayDescriptorMatch( const Params& _params)
-{
-    initialize(_params);
-}
-
-OneWayDescriptorMatch::~OneWayDescriptorMatch()
-{}
-
-void OneWayDescriptorMatch::initialize( const Params& _params, OneWayDescriptorBase *_base)
-{
-    base.release();
-    if (_base != 0)
-    {
-        base = _base;
-    }
-    params = _params;
-}
-
-void OneWayDescriptorMatch::add( const Mat& image, vector<KeyPoint>& keypoints )
-{
-    if( base.empty() )
-        base = new OneWayDescriptorObject( params.patchSize, params.poseCount, params.pcaFilename,
-                                           params.trainPath, params.trainImagesList, params.minScale, params.maxScale, params.stepScale);
-
-    size_t trainFeatureCount = keypoints.size();
-
-    base->Allocate( trainFeatureCount );
-
-    IplImage _image = image;
-    for( size_t i = 0; i < keypoints.size(); i++ )
-        base->InitializeDescriptor( i, &_image, keypoints[i], "" );
-
-    collection.add( Mat(), keypoints );
-
-#if defined(_KDTREE)
-    base->ConvertDescriptorsArrayToTree();
-#endif
-}
-
-void OneWayDescriptorMatch::add( KeyPointCollection& keypoints )
-{
-    if( base.empty() )
-        base = new OneWayDescriptorObject( params.patchSize, params.poseCount, params.pcaFilename,
-                                           params.trainPath, params.trainImagesList, params.minScale, params.maxScale, params.stepScale);
-
-    size_t trainFeatureCount = keypoints.calcKeypointCount();
-
-    base->Allocate( trainFeatureCount );
-
-    int count = 0;
-    for( size_t i = 0; i < keypoints.points.size(); i++ )
     {
-        for( size_t j = 0; j < keypoints.points[i].size(); j++ )
+        // (R + G - 2B)/sqrt(6)
+        MatConstIterator_<signed char> rIt = bgrChannels[2].begin<signed char>();
+        MatConstIterator_<signed char> gIt = bgrChannels[1].begin<signed char>();
+        MatConstIterator_<signed char> bIt = bgrChannels[0].begin<signed char>();
+        MatIterator_<unsigned char> dstIt = opponentChannels[1].begin<unsigned char>();
+        float factor = 1.f / sqrt(6.f);
+        for( ; dstIt != opponentChannels[1].end<unsigned char>(); ++rIt, ++gIt, ++bIt, ++dstIt )
         {
-            IplImage img = keypoints.images[i];
-            base->InitializeDescriptor( count++, &img, keypoints.points[i][j], "" );
+            int value = static_cast<int>( static_cast<float>(static_cast<int>(*rIt) + static_cast<int>(*gIt) - 2*static_cast<int>(*bIt)) *
+                                          factor );
+            if( value < 0 ) value = 0;
+            if( value > 255 ) value = 255;
+            (*dstIt) = static_cast<unsigned char>(value);
         }
-
-        collection.add( Mat(), keypoints.points[i] );
     }
-
-#if defined(_KDTREE)
-    base->ConvertDescriptorsArrayToTree();
-#endif
-}
-
-void OneWayDescriptorMatch::match( const Mat& image, vector<KeyPoint>& points, vector<int>& indices)
-{
-    vector<DMatch> matchings( points.size() );
-    indices.resize(points.size());
-
-    match( image, points, matchings );
-
-    for( size_t i = 0; i < points.size(); i++ )
-        indices[i] = matchings[i].indexTrain;
-}
-
-void OneWayDescriptorMatch::match( const Mat& image, vector<KeyPoint>& points, vector<DMatch>& matches )
-{
-    matches.resize( points.size() );
-    IplImage _image = image;
-    for( size_t i = 0; i < points.size(); i++ )
     {
-        int poseIdx = -1;
-
-        DMatch match;
-        match.indexQuery = i;
-        match.indexTrain = -1;
-        base->FindDescriptor( &_image, points[i].pt, match.indexTrain, poseIdx, match.distance );
-        matches[i] = match;
-    }
-}
-
-void OneWayDescriptorMatch::match( const Mat& image, vector<KeyPoint>& points, vector<vector<DMatch> >& matches, float threshold )
-{
-    matches.clear();
-    matches.resize( points.size() );
-    IplImage _image = image;
-
-
-    vector<DMatch> dmatches;
-    match( image, points, dmatches );
-    for( size_t i=0;i<matches.size();i++ )
-    {
-        matches[i].push_back( dmatches[i] );
-    }
-
-
-    /*
-    printf("Start matching %d points\n", points.size());
-    //std::cout << "Start matching " << points.size() << "points\n";
-    assert(collection.images.size() == 1);
-    int n = collection.points[0].size();
-
-    printf("n = %d\n", n);
-    for( size_t i = 0; i < points.size(); i++ )
-    {
-        //printf("Matching %d\n", i);
-        //int poseIdx = -1;
-
-        DMatch match;
-        match.indexQuery = i;
-        match.indexTrain = -1;
-
-
-        CvPoint pt = points[i].pt;
-        CvRect roi = cvRect(cvRound(pt.x - 24/4),
-                            cvRound(pt.y - 24/4),
-                            24/2, 24/2);
-        cvSetImageROI(&_image, roi);
-
-        std::vector<int> desc_idxs;
-        std::vector<int> pose_idxs;
-        std::vector<float> distances;
-        std::vector<float> _scales;
-
-
-        base->FindDescriptor(&_image, n, desc_idxs, pose_idxs, distances, _scales);
-        cvResetImageROI(&_image);
-
-        for( int j=0;j<n;j++ )
+        // (R + G + B)/sqrt(3)
+        MatConstIterator_<signed char> rIt = bgrChannels[2].begin<signed char>();
+        MatConstIterator_<signed char> gIt = bgrChannels[1].begin<signed char>();
+        MatConstIterator_<signed char> bIt = bgrChannels[0].begin<signed char>();
+        MatIterator_<unsigned char> dstIt = opponentChannels[2].begin<unsigned char>();
+        float factor = 1.f / sqrt(3.f);
+        for( ; dstIt != opponentChannels[2].end<unsigned char>(); ++rIt, ++gIt, ++bIt, ++dstIt )
         {
-            match.indexTrain = desc_idxs[j];
-            match.distance = distances[j];
-            matches[i].push_back( match );
+            int value = static_cast<int>( static_cast<float>(static_cast<int>(*rIt) + static_cast<int>(*gIt) + static_cast<int>(*bIt)) *
+                                          factor );
+            if( value < 0 ) value = 0;
+            if( value > 255 ) value = 255;
+            (*dstIt) = static_cast<unsigned char>(value);
         }
-
-        //sort( matches[i].begin(), matches[i].end(), compareIndexTrain );
-        //for( int j=0;j<n;j++ )
-        //{
-            //printf( "%d %f;  ",matches[i][j].indexTrain, matches[i][j].distance);
-        //}
-        //printf("\n\n\n");
-
-
-
-        //base->FindDescriptor( &_image, 100, points[i].pt, match.indexTrain, poseIdx, match.distance );
-        //matches[i].push_back( match );
     }
-    */
-}
-
-
-void OneWayDescriptorMatch::read( const FileNode &fn )
-{
-    base = new OneWayDescriptorObject( params.patchSize, params.poseCount, string (), string (), string (),
-                                       params.minScale, params.maxScale, params.stepScale );
-    base->Read (fn);
-}
-
-
-void OneWayDescriptorMatch::write( FileStorage& fs ) const
-{
-    base->Write (fs);
-}
-
-void OneWayDescriptorMatch::classify( const Mat& image, vector<KeyPoint>& points )
-{
-    IplImage _image = image;
-    for( size_t i = 0; i < points.size(); i++ )
-    {
-        int descIdx = -1;
-        int poseIdx = -1;
-        float distance;
-        base->FindDescriptor(&_image, points[i].pt, descIdx, poseIdx, distance);
-        points[i].class_id = collection.getKeyPoint(descIdx).class_id;
-    }
-}
-
-void OneWayDescriptorMatch::clear ()
-{
-    GenericDescriptorMatch::clear();
-    base->clear ();
-}
-
-/****************************************************************************************\
-*                                CalonderDescriptorMatch                                 *
-\****************************************************************************************/
-CalonderDescriptorMatch::Params::Params( const RNG& _rng, const PatchGenerator& _patchGen,
-                                         int _numTrees, int _depth, int _views,
-                                         size_t _reducedNumDim,
-                                         int _numQuantBits,
-                                         bool _printStatus,
-                                         int _patchSize ) :
-        rng(_rng), patchGen(_patchGen), numTrees(_numTrees), depth(_depth), views(_views),
-        patchSize(_patchSize), reducedNumDim(_reducedNumDim), numQuantBits(_numQuantBits), printStatus(_printStatus)
-{}
-
-CalonderDescriptorMatch::Params::Params( const string& _filename )
-{
-    filename = _filename;
 }
 
-CalonderDescriptorMatch::CalonderDescriptorMatch()
-{}
-
-CalonderDescriptorMatch::CalonderDescriptorMatch( const Params& _params )
+struct KP_LessThan
 {
-    initialize(_params);
-}
-
-CalonderDescriptorMatch::~CalonderDescriptorMatch()
-{}
-
-void CalonderDescriptorMatch::initialize( const Params& _params )
-{
-    classifier.release();
-    params = _params;
-    if( !params.filename.empty() )
+    KP_LessThan(const vector<KeyPoint>& _kp) : kp(&_kp) {}
+    bool operator()(int i, int j) const
     {
-        classifier = new RTreeClassifier;
-        classifier->read( params.filename.c_str() );
+        return (*kp)[i].class_id < (*kp)[j].class_id;
     }
-}
-
-void CalonderDescriptorMatch::add( const Mat& image, vector<KeyPoint>& keypoints )
-{
-    if( params.filename.empty() )
-        collection.add( image, keypoints );
-}
+    const vector<KeyPoint>* kp;
+};
 
-Mat CalonderDescriptorMatch::extractPatch( const Mat& image, const Point& pt, int patchSize ) const
+void OpponentColorDescriptorExtractor::computeImpl( const Mat& bgrImage, vector<KeyPoint>& keypoints, Mat& descriptors ) const
 {
-    const int offset = patchSize / 2;
-    return image( Rect(pt.x - offset, pt.y - offset, patchSize, patchSize) );
-}
+    vector<Mat> opponentChannels;
+    convertBGRImageToOpponentColorSpace( bgrImage, opponentChannels );
 
-void CalonderDescriptorMatch::calcBestProbAndMatchIdx( const Mat& image, const Point& pt,
-                                                       float& bestProb, int& bestMatchIdx, float* signature )
-{
-    IplImage roi = extractPatch( image, pt, params.patchSize );
-    classifier->getSignature( &roi, signature );
+    const int N = 3; // channels count
+    vector<KeyPoint> channelKeypoints[N];
+    Mat channelDescriptors[N];
+    vector<int> idxs[N];
 
-    bestProb = 0;
-    bestMatchIdx = -1;
-    for( size_t ci = 0; ci < (size_t)classifier->classes(); ci++ )
+    // Compute descriptors three times, once for each Opponent channel to concatenate into a single color descriptor
+    int maxKeypointsCount = 0;
+    for( int ci = 0; ci < N; ci++ )
     {
-        if( signature[ci] > bestProb )
-        {
-            bestProb = signature[ci];
-            bestMatchIdx = ci;
-        }
-    }
-}
+        channelKeypoints[ci].insert( channelKeypoints[ci].begin(), keypoints.begin(), keypoints.end() );
+        // Use class_id member to get indices into initial keypoints vector
+        for( size_t ki = 0; ki < channelKeypoints[ci].size(); ki++ )
+            channelKeypoints[ci][ki].class_id = (int)ki;
 
-void CalonderDescriptorMatch::trainRTreeClassifier()
-{
-    if( classifier.empty() )
-    {
-        assert( params.filename.empty() );
-        classifier = new RTreeClassifier;
-
-        vector<BaseKeypoint> baseKeyPoints;
-        vector<IplImage> iplImages( collection.images.size() );
-        for( size_t imageIdx = 0; imageIdx < collection.images.size(); imageIdx++ )
+        descriptorExtractor->compute( opponentChannels[ci], channelKeypoints[ci], channelDescriptors[ci] );
+        idxs[ci].resize( channelKeypoints[ci].size() );
+        for( size_t ki = 0; ki < channelKeypoints[ci].size(); ki++ )
         {
-            iplImages[imageIdx] = collection.images[imageIdx];
-            for( size_t pointIdx = 0; pointIdx < collection.points[imageIdx].size(); pointIdx++ )
-            {
-                BaseKeypoint bkp;
-                KeyPoint kp = collection.points[imageIdx][pointIdx];
-                bkp.x = cvRound(kp.pt.x);
-                bkp.y = cvRound(kp.pt.y);
-                bkp.image = &iplImages[imageIdx];
-                baseKeyPoints.push_back(bkp);
-            }
+            idxs[ci][ki] = (int)ki;
         }
-        classifier->train( baseKeyPoints, params.rng, params.patchGen, params.numTrees,
-                           params.depth, params.views, params.reducedNumDim, params.numQuantBits,
-                           params.printStatus );
-    }
-}
-
-void CalonderDescriptorMatch::match( const Mat& image, vector<KeyPoint>& keypoints, vector<int>& indices )
-{
-    trainRTreeClassifier();
-
-    float bestProb = 0;
-    AutoBuffer<float> signature( classifier->classes() );
-    indices.resize( keypoints.size() );
-
-    for( size_t pi = 0; pi < keypoints.size(); pi++ )
-        calcBestProbAndMatchIdx( image, keypoints[pi].pt, bestProb, indices[pi], signature );
-}
-
-void CalonderDescriptorMatch::classify( const Mat& image, vector<KeyPoint>& keypoints )
-{
-    trainRTreeClassifier();
-
-    AutoBuffer<float> signature( classifier->classes() );
-    for( size_t pi = 0; pi < keypoints.size(); pi++ )
-    {
-        float bestProb = 0;
-        int bestMatchIdx = -1;
-        calcBestProbAndMatchIdx( image, keypoints[pi].pt, bestProb, bestMatchIdx, signature );
-        keypoints[pi].class_id = collection.getKeyPoint(bestMatchIdx).class_id;
-    }
-}
-
-void CalonderDescriptorMatch::clear ()
-{
-    GenericDescriptorMatch::clear();
-    classifier.release();
-}
-
-void CalonderDescriptorMatch::read( const FileNode &fn )
-{
-    params.numTrees = fn["numTrees"];
-    params.depth = fn["depth"];
-    params.views = fn["views"];
-    params.patchSize = fn["patchSize"];
-    params.reducedNumDim = (int) fn["reducedNumDim"];
-    params.numQuantBits = fn["numQuantBits"];
-    params.printStatus = (int) fn["printStatus"];
-}
-
-void CalonderDescriptorMatch::write( FileStorage& fs ) const
-{
-    fs << "numTrees" << params.numTrees;
-    fs << "depth" << params.depth;
-    fs << "views" << params.views;
-    fs << "patchSize" << params.patchSize;
-    fs << "reducedNumDim" << (int) params.reducedNumDim;
-    fs << "numQuantBits" << params.numQuantBits;
-    fs << "printStatus" << params.printStatus;
-}
-
-/****************************************************************************************\
-*                                  FernDescriptorMatch                                   *
-\****************************************************************************************/
-FernDescriptorMatch::Params::Params( int _nclasses, int _patchSize, int _signatureSize,
-                                     int _nstructs, int _structSize, int _nviews, int _compressionMethod,
-                                     const PatchGenerator& _patchGenerator ) :
-    nclasses(_nclasses), patchSize(_patchSize), signatureSize(_signatureSize),
-    nstructs(_nstructs), structSize(_structSize), nviews(_nviews),
-    compressionMethod(_compressionMethod), patchGenerator(_patchGenerator)
-{}
-
-FernDescriptorMatch::Params::Params( const string& _filename )
-{
-    filename = _filename;
-}
-
-FernDescriptorMatch::FernDescriptorMatch()
-{}
-
-FernDescriptorMatch::FernDescriptorMatch( const Params& _params )
-{
-    params = _params;
-}
-
-FernDescriptorMatch::~FernDescriptorMatch()
-{}
-
-void FernDescriptorMatch::initialize( const Params& _params )
-{
-    classifier.release();
-    params = _params;
-    if( !params.filename.empty() )
-    {
-        classifier = new FernClassifier;
-        FileStorage fs(params.filename, FileStorage::READ);
-        if( fs.isOpened() )
-            classifier->read( fs.getFirstTopLevelNode() );
-    }
-}
-
-void FernDescriptorMatch::add( const Mat& image, vector<KeyPoint>& keypoints )
-{
-    if( params.filename.empty() )
-        collection.add( image, keypoints );
-}
-
-void FernDescriptorMatch::trainFernClassifier()
-{
-    if( classifier.empty() )
-    {
-        assert( params.filename.empty() );
-
-        vector<Point2f> points;
-        vector<Ptr<Mat> > refimgs;
-        vector<int> labels;
-        for( size_t imageIdx = 0; imageIdx < collection.images.size(); imageIdx++ )
+        std::sort( idxs[ci].begin(), idxs[ci].end(), KP_LessThan(channelKeypoints[ci]) );
+        maxKeypointsCount = std::max( maxKeypointsCount, (int)channelKeypoints[ci].size());
+    }
+
+    vector<KeyPoint> outKeypoints;
+    outKeypoints.reserve( keypoints.size() );
+
+    int dSize = descriptorExtractor->descriptorSize();
+    Mat mergedDescriptors( maxKeypointsCount, 3*dSize, descriptorExtractor->descriptorType() );
+    int mergedCount = 0;
+    // cp - current channel position
+    size_t cp[] = {0, 0, 0};
+    while( cp[0] < channelKeypoints[0].size() &&
+           cp[1] < channelKeypoints[1].size() &&
+           cp[2] < channelKeypoints[2].size() )
+    {
+        const int maxInitIdx = std::max( 0, std::max( channelKeypoints[0][idxs[0][cp[0]]].class_id,
+                                                      std::max( channelKeypoints[1][idxs[1][cp[1]]].class_id,
+                                                                channelKeypoints[2][idxs[2][cp[2]]].class_id ) ) );
+
+        while( channelKeypoints[0][idxs[0][cp[0]]].class_id < maxInitIdx && cp[0] < channelKeypoints[0].size() ) { cp[0]++; }
+        while( channelKeypoints[1][idxs[1][cp[1]]].class_id < maxInitIdx && cp[1] < channelKeypoints[1].size() ) { cp[1]++; }
+        while( channelKeypoints[2][idxs[2][cp[2]]].class_id < maxInitIdx && cp[2] < channelKeypoints[2].size() ) { cp[2]++; }
+        if( cp[0] >= channelKeypoints[0].size() || cp[1] >= channelKeypoints[1].size() || cp[2] >= channelKeypoints[2].size() )
+            break;
+
+        if( channelKeypoints[0][idxs[0][cp[0]]].class_id == maxInitIdx &&
+            channelKeypoints[1][idxs[1][cp[1]]].class_id == maxInitIdx &&
+            channelKeypoints[2][idxs[2][cp[2]]].class_id == maxInitIdx )
         {
-            for( size_t pointIdx = 0; pointIdx < collection.points[imageIdx].size(); pointIdx++ )
+            outKeypoints.push_back( keypoints[maxInitIdx] );
+            // merge descriptors
+            for( int ci = 0; ci < N; ci++ )
             {
-                refimgs.push_back(new Mat (collection.images[imageIdx]));
-                points.push_back(collection.points[imageIdx][pointIdx].pt);
-                labels.push_back(pointIdx);
+                Mat dst = mergedDescriptors(Range(mergedCount, mergedCount+1), Range(ci*dSize, (ci+1)*dSize));
+                channelDescriptors[ci].row( idxs[ci][cp[ci]] ).copyTo( dst );
+                cp[ci]++;
             }
+            mergedCount++;
         }
-
-        classifier = new FernClassifier( points, refimgs, labels, params.nclasses, params.patchSize,
-                                         params.signatureSize, params.nstructs, params.structSize, params.nviews,
-                                         params.compressionMethod, params.patchGenerator );
     }
+    mergedDescriptors.rowRange(0, mergedCount).copyTo( descriptors );
+    std::swap( outKeypoints, keypoints );
 }
 
-void FernDescriptorMatch::calcBestProbAndMatchIdx( const Mat& image, const Point2f& pt,
-                                                   float& bestProb, int& bestMatchIdx, vector<float>& signature )
+void OpponentColorDescriptorExtractor::read( const FileNode& fn )
 {
-    (*classifier)( image, pt, signature);
-
-    bestProb = -FLT_MAX;
-    bestMatchIdx = -1;
-    for( size_t ci = 0; ci < (size_t)classifier->getClassCount(); ci++ )
-    {
-        if( signature[ci] > bestProb )
-        {
-            bestProb = signature[ci];
-            bestMatchIdx = ci;
-        }
-    }
+    descriptorExtractor->read(fn);
 }
 
-void FernDescriptorMatch::match( const Mat& image, vector<KeyPoint>& keypoints, vector<int>& indices )
+void OpponentColorDescriptorExtractor::write( FileStorage& fs ) const
 {
-    trainFernClassifier();
-
-    indices.resize( keypoints.size() );
-    vector<float> signature( (size_t)classifier->getClassCount() );
-
-    for( size_t pi = 0; pi < keypoints.size(); pi++ )
-    {
-        //calcBestProbAndMatchIdx( image, keypoints[pi].pt, bestProb, indices[pi], signature );
-        //TODO: use octave and image pyramid
-        indices[pi] = (*classifier)(image, keypoints[pi].pt, signature);
-    }
-}
-
-void FernDescriptorMatch::match( const Mat& image, vector<KeyPoint>& keypoints, vector<DMatch>& matches )
-{
-    trainFernClassifier();
-
-    matches.resize( keypoints.size() );
-    vector<float> signature( (size_t)classifier->getClassCount() );
-
-    for( size_t pi = 0; pi < keypoints.size(); pi++ )
-    {
-        matches[pi].indexQuery = pi;
-        calcBestProbAndMatchIdx( image, keypoints[pi].pt, matches[pi].distance, matches[pi].indexTrain, signature );
-        //matching[pi].distance is log of probability so we need to transform it
-        matches[pi].distance = -matches[pi].distance;
-    }
-}
-
-void FernDescriptorMatch::match( const Mat& image, vector<KeyPoint>& keypoints, vector<vector<DMatch> >& matches, float threshold )
-{
-    trainFernClassifier();
-
-    matches.resize( keypoints.size() );
-    vector<float> signature( (size_t)classifier->getClassCount() );
-
-    for( size_t pi = 0; pi < keypoints.size(); pi++ )
-    {
-        (*classifier)( image, keypoints[pi].pt, signature);
-
-        DMatch match;
-        match.indexQuery = pi;
-
-        for( size_t ci = 0; ci < (size_t)classifier->getClassCount(); ci++ )
-        {
-            if( -signature[ci] < threshold )
-            {
-                match.distance = -signature[ci];
-                match.indexTrain = ci;
-                matches[pi].push_back( match );
-            }
-        }
-    }
-}
-
-void FernDescriptorMatch::classify( const Mat& image, vector<KeyPoint>& keypoints )
-{
-    trainFernClassifier();
-
-    vector<float> signature( (size_t)classifier->getClassCount() );
-    for( size_t pi = 0; pi < keypoints.size(); pi++ )
-    {
-        float bestProb = 0;
-        int bestMatchIdx = -1;
-        calcBestProbAndMatchIdx( image, keypoints[pi].pt, bestProb, bestMatchIdx, signature );
-        keypoints[pi].class_id = collection.getKeyPoint(bestMatchIdx).class_id;
-    }
+    descriptorExtractor->write(fs);
 }
 
-void FernDescriptorMatch::read( const FileNode &fn )
+int OpponentColorDescriptorExtractor::descriptorSize() const
 {
-    params.nclasses = fn["nclasses"];
-    params.patchSize = fn["patchSize"];
-    params.signatureSize = fn["signatureSize"];
-    params.nstructs = fn["nstructs"];
-    params.structSize = fn["structSize"];
-    params.nviews = fn["nviews"];
-    params.compressionMethod = fn["compressionMethod"];
-
-    //classifier->read(fn);
+    return 3*descriptorExtractor->descriptorSize();
 }
 
-void FernDescriptorMatch::write( FileStorage& fs ) const
+int OpponentColorDescriptorExtractor::descriptorType() const
 {
-    fs << "nclasses" << params.nclasses;
-    fs << "patchSize" << params.patchSize;
-    fs << "signatureSize" << params.signatureSize;
-    fs << "nstructs" << params.nstructs;
-    fs << "structSize" << params.structSize;
-    fs << "nviews" << params.nviews;
-    fs << "compressionMethod" << params.compressionMethod;
-
-//    classifier->write(fs);
+    return descriptorExtractor->descriptorType();
 }
 
-void FernDescriptorMatch::clear ()
+bool OpponentColorDescriptorExtractor::empty() const
 {
-    GenericDescriptorMatch::clear();
-    classifier.release();
+    return descriptorExtractor.empty() || (DescriptorExtractor*)(descriptorExtractor)->empty();
 }
 
 }