diff --git a/modules/features2d/include/opencv2/features2d/features2d.hpp b/modules/features2d/include/opencv2/features2d/features2d.hpp index 70463772a..14f10c7b5 100644 --- a/modules/features2d/include/opencv2/features2d/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d/features2d.hpp @@ -1394,7 +1394,6 @@ protected: virtual void detectImpl( const Mat& image, const Mat& mask, vector& keypoints ) const; }; - CV_EXPORTS Mat windowedMatchingMask( const vector& keypoints1, const vector& keypoints2, float maxDeltaX, float maxDeltaY ); @@ -1429,6 +1428,9 @@ public: virtual void read( const FileNode& ) {}; virtual void write( FileStorage& ) const {}; + virtual int descriptorSize() const = 0; + virtual int descriptorType() const = 0; + protected: /* * Remove keypoints within border_pixels of an image edge. @@ -1451,6 +1453,9 @@ public: virtual void read( const FileNode &fn ); virtual void write( FileStorage &fs ) const; + virtual int descriptorSize() const { return sift.descriptorSize(); } + virtual int descriptorType() const { return CV_32FC1; } + protected: SIFT sift; }; @@ -1465,6 +1470,9 @@ public: virtual void read( const FileNode &fn ); virtual void write( FileStorage &fs ) const; + virtual int descriptorSize() const { return surf.descriptorSize(); } + virtual int descriptorType() const { return CV_32FC1; } + protected: SURF surf; }; @@ -1479,6 +1487,9 @@ public: virtual void read( const FileNode &fn ); virtual void write( FileStorage &fs ) const; + virtual int descriptorSize() const { return classifier_.classes(); } + virtual int descriptorType() const { return DataType::type; } + protected: RTreeClassifier classifier_; static const int BORDER_SIZE = 16; @@ -1518,6 +1529,30 @@ template void CalonderDescriptorExtractor::write( FileStorage& ) const {} +/* + * Adapts a descriptor extractor to compute descripors in Opponent Color Space + * (refer to van de Sande et al., CGIV 2008 "Color Descriptors for Object Category Recognition"). + * Input RGB image is transformed in Opponent Color Space. Then unadapted descriptor extractor + * (set in constructor) computes descriptors on each of the three channel and concatenate + * them into a single color descriptor. + */ +class OpponentColorDescriptorExtractor : public DescriptorExtractor +{ +public: + OpponentColorDescriptorExtractor( const Ptr& dextractor ); + + virtual void compute( const Mat& image, vector& keypoints, Mat& descriptors ) const; + + virtual void read( const FileNode& ); + virtual void write( FileStorage& ) const; + + virtual int descriptorSize() const { return 3*dextractor->descriptorSize(); } + virtual int descriptorType() const { return dextractor->descriptorType(); } + +protected: + Ptr dextractor; +}; + CV_EXPORTS Ptr createDescriptorExtractor( const string& descriptorExtractorType ); /****************************************************************************************\ diff --git a/modules/features2d/src/descriptors.cpp b/modules/features2d/src/descriptors.cpp index f9f4eb4a2..a4b7fbaa7 100644 --- a/modules/features2d/src/descriptors.cpp +++ b/modules/features2d/src/descriptors.cpp @@ -147,8 +147,16 @@ static void _prepareImgAndDrawKeypoints( const Mat& img1, const vector outImg.create( size, CV_MAKETYPE(img1.depth(), 3) ); outImg1 = outImg( Rect(0, 0, img1.cols, img1.rows) ); outImg2 = outImg( Rect(img1.cols, 0, img2.cols, img2.rows) ); - cvtColor( img1, outImg1, CV_GRAY2RGB ); - cvtColor( img2, outImg2, CV_GRAY2RGB ); + + if( img1.type() == CV_8U ) + cvtColor( img1, outImg1, CV_GRAY2BGR ); + else + img1.copyTo( outImg1 ); + + if( img2.type() == CV_8U ) + cvtColor( img2, outImg2, CV_GRAY2BGR ); + else + img2.copyTo( outImg2 ); } // draw keypoints @@ -308,7 +316,10 @@ void SiftDescriptorExtractor::compute( const Mat& image, Mat& descriptors) const { bool useProvidedKeypoints = true; - sift(image, Mat(), keypoints, descriptors, useProvidedKeypoints); + Mat grayImage = image; + if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); + + sift(grayImage, Mat(), keypoints, descriptors, useProvidedKeypoints); } void SiftDescriptorExtractor::read (const FileNode &fn) @@ -355,7 +366,10 @@ void SurfDescriptorExtractor::compute( const Mat& image, vector _descriptors; Mat mask; bool useProvidedKeypoints = true; - surf(image, mask, keypoints, _descriptors, useProvidedKeypoints); + Mat grayImage = image; + if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); + + surf(grayImage, mask, keypoints, _descriptors, useProvidedKeypoints); descriptors.create((int)keypoints.size(), (int)surf.descriptorSize(), CV_32FC1); assert( (int)_descriptors.size() == descriptors.rows * descriptors.cols ); @@ -380,6 +394,104 @@ void SurfDescriptorExtractor::write( FileStorage &fs ) const fs << "extended" << surf.extended; } +/****************************************************************************************\ +* OpponentColorDescriptorExtractor * +\****************************************************************************************/ +OpponentColorDescriptorExtractor::OpponentColorDescriptorExtractor( const Ptr& _dextractor ) : + dextractor(_dextractor) +{} + +void convertBGRImageToOpponentColorSpace( const Mat& bgrImage, vector& opponentChannels ) +{ + if( bgrImage.type() != CV_8UC3 ) + CV_Error( CV_StsBadArg, "input image must be an BGR image of type CV_8UC3" ); + + // Split image into RGB to allow conversion to Opponent Color Space. + vector bgrChannels(3); + split( bgrImage, bgrChannels ); + + // 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 + + // Calculate the channels of the opponent color space + { + // (R - G) / sqrt(2) + MatConstIterator_ rIt = bgrChannels[2].begin(); + MatConstIterator_ gIt = bgrChannels[1].begin(); + MatIterator_ dstIt = opponentChannels[0].begin(); + float factor = 1.f / sqrt(2.0); + for( ; dstIt != opponentChannels[0].end(); ++rIt, ++gIt, ++dstIt ) + { + int value = static_cast( static_cast(static_cast(*gIt)-static_cast(*rIt)) * factor ); + if( value < 0 ) value = 0; + if( value > 255 ) value = 255; + (*dstIt) = static_cast(value); + } + } + { + // (R + G - 2B)/sqrt(6) + MatConstIterator_ rIt = bgrChannels[2].begin(); + MatConstIterator_ gIt = bgrChannels[1].begin(); + MatConstIterator_ bIt = bgrChannels[0].begin(); + MatIterator_ dstIt = opponentChannels[1].begin(); + float factor = 1.f / sqrt(6.0); + for( ; dstIt != opponentChannels[1].end(); ++rIt, ++gIt, ++bIt, ++dstIt ) + { + int value = static_cast( static_cast(static_cast(*rIt) + static_cast(*gIt) - 2*static_cast(*bIt)) * + factor ); + if( value < 0 ) value = 0; + if( value > 255 ) value = 255; + (*dstIt) = static_cast(value); + } + } + { + // (R + G + B)/sqrt(3) + MatConstIterator_ rIt = bgrChannels[2].begin(); + MatConstIterator_ gIt = bgrChannels[1].begin(); + MatConstIterator_ bIt = bgrChannels[0].begin(); + MatIterator_ dstIt = opponentChannels[2].begin(); + float factor = 1.f / sqrt(3.0); + for( ; dstIt != opponentChannels[2].end(); ++rIt, ++gIt, ++bIt, ++dstIt ) + { + int value = static_cast( static_cast(static_cast(*rIt) + static_cast(*gIt) + static_cast(*bIt)) * + factor ); + if( value < 0 ) value = 0; + if( value > 255 ) value = 255; + (*dstIt) = static_cast(value); + } + } +} + +void OpponentColorDescriptorExtractor::compute( const Mat& bgrImage, vector& keypoints, Mat& descriptors ) const +{ + vector opponentChannels; + convertBGRImageToOpponentColorSpace( bgrImage, opponentChannels ); + + // Compute descriptors three times, once for each Opponent channel + // and concatenate into a single color surf descriptor + int descriptorSize = dextractor->descriptorSize(); + descriptors.create( static_cast(keypoints.size()), 3*descriptorSize, CV_32FC1 ); + for( int i = 0; i < 3/*channel count*/; i++ ) + { + CV_Assert( opponentChannels[i].type() == CV_8UC1 ); + Mat opponentDescriptors = descriptors.colRange( i*descriptorSize, (i+1)*descriptorSize ); + dextractor->compute( opponentChannels[i], keypoints, opponentDescriptors ); + } +} + +void OpponentColorDescriptorExtractor::read( const FileNode& fn ) +{ + dextractor->read( fn ); +} + +void OpponentColorDescriptorExtractor::write( FileStorage& fs ) const +{ + dextractor->write( fs ); +} + /****************************************************************************************\ * Factory functions for descriptor extractor and matcher creating * \****************************************************************************************/ @@ -389,20 +501,19 @@ Ptr createDescriptorExtractor( const string& descriptorExtr DescriptorExtractor* de = 0; if( !descriptorExtractorType.compare( "SIFT" ) ) { - 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 )*/; + de = new SiftDescriptorExtractor(); } else if( !descriptorExtractorType.compare( "SURF" ) ) { - de = new SurfDescriptorExtractor/*( int nOctaves=4, int nOctaveLayers=2, bool extended=false )*/; + de = new SurfDescriptorExtractor(); } - else + else if( !descriptorExtractorType.compare( "OpponentSIFT" ) ) { - //CV_Error( CV_StsBadArg, "unsupported descriptor extractor type"); + de = new OpponentColorDescriptorExtractor( new SiftDescriptorExtractor ); + } + else if( !descriptorExtractorType.compare( "OpponentSURF" ) ) + { + de = new OpponentColorDescriptorExtractor( new SurfDescriptorExtractor ); } return de; } @@ -414,14 +525,10 @@ Ptr createDescriptorMatcher( const string& descriptorMatcherT { dm = new BruteForceMatcher >(); } - else if ( !descriptorMatcherType.compare( "BruteForce-L1" ) ) + else if( !descriptorMatcherType.compare( "BruteForce-L1" ) ) { dm = new BruteForceMatcher >(); } - else - { - //CV_Error( CV_StsBadArg, "unsupported descriptor matcher type"); - } return dm; } diff --git a/modules/features2d/src/detectors.cpp b/modules/features2d/src/detectors.cpp index a750ffadc..726e12fcf 100644 --- a/modules/features2d/src/detectors.cpp +++ b/modules/features2d/src/detectors.cpp @@ -90,7 +90,9 @@ void FastFeatureDetector::write (FileStorage& fs) const void FastFeatureDetector::detectImpl( const Mat& image, const Mat& mask, vector& keypoints) const { - FAST( image, keypoints, threshold, nonmaxSuppression ); + Mat grayImage = image; + if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); + FAST( grayImage, keypoints, threshold, nonmaxSuppression ); removeInvalidPoints( mask, keypoints ); } @@ -127,8 +129,11 @@ void GoodFeaturesToTrackDetector::write (FileStorage& fs) const void GoodFeaturesToTrackDetector::detectImpl( const Mat& image, const Mat& mask, vector& keypoints ) const { + Mat grayImage = image; + if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); + vector corners; - goodFeaturesToTrack( image, corners, maxCorners, qualityLevel, minDistance, mask, + goodFeaturesToTrack( grayImage, corners, maxCorners, qualityLevel, minDistance, mask, blockSize, useHarrisDetector, k ); keypoints.resize(corners.size()); vector::const_iterator corner_it = corners.begin(); @@ -190,7 +195,11 @@ void MserFeatureDetector::write (FileStorage& fs) const void MserFeatureDetector::detectImpl( const Mat& image, const Mat& mask, vector& keypoints ) const { vector > msers; - mser(image, msers, mask); + + Mat grayImage = image; + if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); + + mser(grayImage, msers, mask); keypoints.resize( msers.size() ); vector >::const_iterator contour_it = msers.begin(); @@ -239,7 +248,10 @@ void StarFeatureDetector::write (FileStorage& fs) const void StarFeatureDetector::detectImpl( const Mat& image, const Mat& mask, vector& keypoints) const { - star(image, keypoints); + Mat grayImage = image; + if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); + + star(grayImage, keypoints); removeInvalidPoints(mask, keypoints); } @@ -282,7 +294,10 @@ void SiftFeatureDetector::write (FileStorage& fs) const void SiftFeatureDetector::detectImpl( const Mat& image, const Mat& mask, vector& keypoints) const { - sift(image, mask, keypoints); + Mat grayImage = image; + if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); + + sift(grayImage, mask, keypoints); } /* @@ -313,7 +328,10 @@ void SurfFeatureDetector::write (FileStorage& fs) const void SurfFeatureDetector::detectImpl( const Mat& image, const Mat& mask, vector& keypoints) const { - surf(image, mask, keypoints); + Mat grayImage = image; + if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); + + surf(grayImage, mask, keypoints); } Ptr createFeatureDetector( const string& detectorType ) @@ -353,10 +371,6 @@ Ptr createFeatureDetector( const string& detectorType ) fd = new GoodFeaturesToTrackDetector( 1000/*maxCorners*/, 0.01/*qualityLevel*/, 1./*minDistance*/, 3/*int _blockSize*/, true/*useHarrisDetector*/, 0.04/*k*/ ); } - else - { - //CV_Error( CV_StsBadArg, "unsupported feature detector type"); - } return fd; } diff --git a/samples/cpp/descriptor_extractor_matcher.cpp b/samples/cpp/descriptor_extractor_matcher.cpp index ac7b2c28a..782ee70f0 100644 --- a/samples/cpp/descriptor_extractor_matcher.cpp +++ b/samples/cpp/descriptor_extractor_matcher.cpp @@ -154,9 +154,9 @@ int main(int argc, char** argv) } cout << "< Reading the images..." << endl; - Mat img1 = imread( argv[3], CV_LOAD_IMAGE_GRAYSCALE), img2; + Mat img1 = imread( argv[3] ), img2; if( !isWarpPerspective ) - img2 = imread( argv[4], CV_LOAD_IMAGE_GRAYSCALE); + img2 = imread( argv[4] ); cout << ">" << endl; if( img1.empty() || (!isWarpPerspective && img2.empty()) ) { diff --git a/tests/cv/src/afeatures2d.cpp b/tests/cv/src/afeatures2d.cpp index 5e6118352..2688ba5f9 100644 --- a/tests/cv/src/afeatures2d.cpp +++ b/tests/cv/src/afeatures2d.cpp @@ -321,9 +321,14 @@ public: }; //CV_DescriptorExtractorTest siftDescriptorTest( "descriptor_sift", 0.001f, - // createDescriptorExtractor("SIFT"), 8.06652f ); +// createDescriptorExtractor("SIFT"), 8.06652f ); //CV_DescriptorExtractorTest surfDescriptorTest( "descriptor_surf", 0.004f, - // createDescriptorExtractor("SURF"), 0.147372f ); +// createDescriptorExtractor("SURF"), 0.147372f ); +//CV_DescriptorExtractorTest siftDescriptorTest( "descriptor_opponent_sift", 0.001f, +// createDescriptorExtractor("OpponentSIFT"), 8.06652f ); +//CV_DescriptorExtractorTest surfDescriptorTest( "descriptor_opponent_surf", 0.004f, +// createDescriptorExtractor("OpponentSURF"), 0.147372f ); + #if CV_SSE2 CV_CalonderDescriptorExtractorTest ucharCalonderTest( "descriptor_calonder_uchar", std::numeric_limits::epsilon() + 1,