/*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // Intel License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000, Intel Corporation, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * The name of Intel Corporation may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and // any express or implied warranties, including, but not limited to, the implied // warranties of merchantability and fitness for a particular purpose are disclaimed. // In no event shall the Intel Corporation or contributors be liable for any direct, // indirect, incidental, special, exemplary, or consequential damages // (including, but not limited to, procurement of substitute goods or services; // loss of use, data, or profits; or business interruption) however caused // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. // //M*/ #include "precomp.hpp" using namespace std; namespace cv { /****************************************************************************************\ * DescriptorExtractor * \****************************************************************************************/ /* * DescriptorExtractor */ DescriptorExtractor::~DescriptorExtractor() {} void DescriptorExtractor::compute( const Mat& image, vector& keypoints, Mat& descriptors ) const { if( image.empty() || keypoints.empty() ) { descriptors.release(); return; } KeyPointsFilter::runByImageBorder( keypoints, image.size(), 0 ); KeyPointsFilter::runByKeypointSize( keypoints, std::numeric_limits::epsilon() ); computeImpl( image, keypoints, descriptors ); } void DescriptorExtractor::compute( const vector& imageCollection, vector >& pointCollection, vector& descCollection ) const { 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 DescriptorExtractor::read( const FileNode& ) {} void DescriptorExtractor::write( FileStorage& ) const {}*/ bool DescriptorExtractor::empty() const { return false; } void DescriptorExtractor::removeBorderKeypoints( vector& keypoints, Size imageSize, int borderSize ) { KeyPointsFilter::runByImageBorder( keypoints, imageSize, borderSize ); } Ptr DescriptorExtractor::create(const string& descriptorExtractorType) { if( descriptorExtractorType.find("Opponent") == 0 ) { size_t pos = string("Opponent").size(); string type = descriptorExtractorType.substr(pos); return new OpponentColorDescriptorExtractor(DescriptorExtractor::create(type)); } return Algorithm::create("Feature2D." + descriptorExtractorType); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// /****************************************************************************************\ * OpponentColorDescriptorExtractor * \****************************************************************************************/ OpponentColorDescriptorExtractor::OpponentColorDescriptorExtractor( const Ptr& _descriptorExtractor ) : descriptorExtractor(_descriptorExtractor) { CV_Assert( !descriptorExtractor.empty() ); } static 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" ); // 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 for(int y = 0; y < bgrImage.rows; ++y) for(int x = 0; x < bgrImage.cols; ++x) { Vec3b v = bgrImage.at(y, x); uchar& b = v[0]; uchar& g = v[1]; uchar& r = v[2]; opponentChannels[0].at(y, x) = saturate_cast(0.5f * (255 + g - r)); // (R - G)/sqrt(2), but converted to the destination data type opponentChannels[1].at(y, x) = saturate_cast(0.25f * (510 + r + g - 2*b)); // (R + G - 2B)/sqrt(6), but converted to the destination data type opponentChannels[2].at(y, x) = saturate_cast(1.f/3.f * (r + g + b)); // (R + G + B)/sqrt(3), but converted to the destination data type } } struct KP_LessThan { KP_LessThan(const vector& _kp) : kp(&_kp) {} bool operator()(int i, int j) const { return (*kp)[i].class_id < (*kp)[j].class_id; } const vector* kp; }; void OpponentColorDescriptorExtractor::computeImpl( const Mat& bgrImage, vector& keypoints, Mat& descriptors ) const { vector opponentChannels; convertBGRImageToOpponentColorSpace( bgrImage, opponentChannels ); const int N = 3; // channels count vector channelKeypoints[N]; Mat channelDescriptors[N]; vector idxs[N]; // 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++ ) { 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; descriptorExtractor->compute( opponentChannels[ci], channelKeypoints[ci], channelDescriptors[ci] ); idxs[ci].resize( channelKeypoints[ci].size() ); for( size_t ki = 0; ki < channelKeypoints[ci].size(); ki++ ) { idxs[ci][ki] = (int)ki; } std::sort( idxs[ci].begin(), idxs[ci].end(), KP_LessThan(channelKeypoints[ci]) ); maxKeypointsCount = std::max( maxKeypointsCount, (int)channelKeypoints[ci].size()); } vector 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 ) { outKeypoints.push_back( keypoints[maxInitIdx] ); // merge descriptors for( int ci = 0; ci < N; ci++ ) { 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++; } } mergedDescriptors.rowRange(0, mergedCount).copyTo( descriptors ); std::swap( outKeypoints, keypoints ); } void OpponentColorDescriptorExtractor::read( const FileNode& fn ) { descriptorExtractor->read(fn); } void OpponentColorDescriptorExtractor::write( FileStorage& fs ) const { descriptorExtractor->write(fs); } int OpponentColorDescriptorExtractor::descriptorSize() const { return 3*descriptorExtractor->descriptorSize(); } int OpponentColorDescriptorExtractor::descriptorType() const { return descriptorExtractor->descriptorType(); } bool OpponentColorDescriptorExtractor::empty() const { return descriptorExtractor.empty() || (DescriptorExtractor*)(descriptorExtractor)->empty(); } }