From 26dbbcc070339356fb6f42f05f661e4466e42044 Mon Sep 17 00:00:00 2001 From: Maria Dimashova Date: Thu, 23 Sep 2010 16:17:48 +0000 Subject: [PATCH] added bag of words; did some renaming --- .../include/opencv2/features2d/features2d.hpp | 69 +++++++++++- modules/features2d/src/bagofwords.cpp | 104 ++++++++++++++++++ modules/features2d/src/evaluation.cpp | 10 +- samples/cpp/descriptor_extractor_matcher.cpp | 2 +- .../cv/src/adetectordescriptor_evaluation.cpp | 6 +- 5 files changed, 176 insertions(+), 15 deletions(-) create mode 100755 modules/features2d/src/bagofwords.cpp diff --git a/modules/features2d/include/opencv2/features2d/features2d.hpp b/modules/features2d/include/opencv2/features2d/features2d.hpp index 14f10c7b59..ab209932f3 100644 --- a/modules/features2d/include/opencv2/features2d/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d/features2d.hpp @@ -2188,7 +2188,7 @@ CV_EXPORTS void drawMatches( const Mat& img1, const vector& keypoints1 const vector >& matchesMask=vector >(), int flags=DrawMatchesFlags::DEFAULT ); /****************************************************************************************\ -* Evaluation functions * +* Functions to evaluate the feature detectors and [generic] descriptor extractors * \****************************************************************************************/ CV_EXPORTS void evaluateFeatureDetector( const Mat& img1, const Mat& img2, const Mat& H1to2, @@ -2201,13 +2201,70 @@ CV_EXPORTS void computeRecallPrecisionCurve( const vector >& matc vector& recallPrecisionCurve ); CV_EXPORTS float getRecall( const vector& recallPrecisionCurve, float l_precision ); -CV_EXPORTS void evaluateDescriptorMatch( const Mat& img1, const Mat& img2, const Mat& H1to2, - vector& keypoints1, vector& keypoints2, - vector >* matches1to2, vector >* correctMatches1to2Mask, - vector& recallPrecisionCurve, - const Ptr& dmatch=Ptr() ); +CV_EXPORTS void evaluateGenericDescriptorMatcher( const Mat& img1, const Mat& img2, const Mat& H1to2, + vector& keypoints1, vector& keypoints2, + vector >* matches1to2, vector >* correctMatches1to2Mask, + vector& recallPrecisionCurve, + const Ptr& dmatch=Ptr() ); +/****************************************************************************************\ +* Bag of visual words * +\****************************************************************************************/ +/* + * Abstract base class for training of a 'bag of visual words' vocabulary from a set of descriptors + */ +class BOWTrainer +{ +public: + /* + * Train visual words vocabulary, that is cluster training descriptors and + * compute cluster centers. + * + * descriptors Training descriptors computed on images keypoints. + * vocabulary Vocabulary is cluster centers. + */ + virtual void cluster( const Mat& descriptors, Mat& vocabulary ) = 0; +}; + +/* + * This is BOWTrainer using cv::kmeans to get vocabulary. + */ +class BOWKMeansTrainer : public BOWTrainer +{ +public: + BOWKMeansTrainer( int clusterCount, const TermCriteria& termcrit=TermCriteria(), + int attempts=3, int flags=KMEANS_PP_CENTERS ); + + virtual void cluster( const Mat& descriptors, Mat& vocabulary ); + +protected: + int clusterCount; + TermCriteria termcrit; + int attempts; + int flags; +}; + +/* + * Class to compute image descriptor using bad of visual words. + */ +class BOWImgDescriptorExtractor +{ +public: + BOWImgDescriptorExtractor( const Ptr& dextractor, + const Ptr& dmatcher ); + void set( const Mat& vocabulary ); + void compute( const Mat& image, vector& keypoints, Mat& imgDescriptor, + vector >& pointIdxsInClusters ); + int descriptorSize() const { return vocabulary.empty() ? 0 : vocabulary.rows; } + int descriptorType() const { return CV_32FC1; } + +protected: + Mat vocabulary; + Ptr dextractor; + Ptr dmatcher; +}; + } /* namespace cv */ #endif /* __cplusplus */ diff --git a/modules/features2d/src/bagofwords.cpp b/modules/features2d/src/bagofwords.cpp new file mode 100755 index 0000000000..5b158389ac --- /dev/null +++ b/modules/features2d/src/bagofwords.cpp @@ -0,0 +1,104 @@ +/*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 +{ + +BOWKMeansTrainer::BOWKMeansTrainer( int _clusterCount, const TermCriteria& _termcrit, + int _attempts, int _flags ) : + clusterCount(_clusterCount), termcrit(_termcrit), attempts(_attempts), flags(_flags) +{} + +void BOWKMeansTrainer::cluster( const Mat& descriptors, Mat& vocabulary ) +{ + Mat labels; + kmeans( descriptors, clusterCount, labels, termcrit, attempts, flags, &vocabulary ); +} + + +BOWImgDescriptorExtractor::BOWImgDescriptorExtractor( const Ptr& _dextractor, + const Ptr& _dmatcher ) : + dextractor(_dextractor), dmatcher(_dmatcher) +{} + +void BOWImgDescriptorExtractor::set( const Mat& _vocabulary ) +{ + dmatcher->clear(); + vocabulary = _vocabulary; + dmatcher->add( vocabulary ); +} + +void BOWImgDescriptorExtractor::compute( const Mat& image, vector& keypoints, Mat& imgDescriptor, + vector >& pointIdxsInClusters ) +{ + int clusterCount = descriptorSize(); // = vocabulary.rows + + // Compute descriptors for the image. + Mat descriptors; + dextractor->compute( image, keypoints, descriptors ); + + // Match keypoint descriptors to cluster center (to vocabulary) + vector matches; + dmatcher->match( descriptors, matches ); + + // Compute image descriptor + pointIdxsInClusters = vector >(clusterCount); + imgDescriptor = Mat( 1, clusterCount, descriptorType(), Scalar::all(0.0) ); + float *dptr = (float*)imgDescriptor.data; + for( size_t i = 0; i < matches.size(); i++ ) + { + int queryIdx = matches[i].indexQuery; + int trainIdx = matches[i].indexTrain; // cluster index + CV_Assert( queryIdx == (int)i ); + + dptr[trainIdx] = dptr[trainIdx] + 1.f; + pointIdxsInClusters[trainIdx].push_back( queryIdx ); + } + + // Normalize image descriptor. + imgDescriptor /= descriptors.rows; +} + +} diff --git a/modules/features2d/src/evaluation.cpp b/modules/features2d/src/evaluation.cpp index df885a579a..efe98b32fd 100644 --- a/modules/features2d/src/evaluation.cpp +++ b/modules/features2d/src/evaluation.cpp @@ -452,11 +452,11 @@ float cv::getRecall( const vector& recallPrecisionCurve, float l_precis return recall; } -void cv::evaluateDescriptorMatch( const Mat& img1, const Mat& img2, const Mat& H1to2, - vector& keypoints1, vector& keypoints2, - vector >* _matches1to2, vector >* _correctMatches1to2Mask, - vector& recallPrecisionCurve, - const Ptr& _dmatch ) +void cv::evaluateGenericDescriptorMatcher( const Mat& img1, const Mat& img2, const Mat& H1to2, + vector& keypoints1, vector& keypoints2, + vector >* _matches1to2, vector >* _correctMatches1to2Mask, + vector& recallPrecisionCurve, + const Ptr& _dmatch ) { Ptr dmatch = _dmatch; dmatch->clear(); diff --git a/samples/cpp/descriptor_extractor_matcher.cpp b/samples/cpp/descriptor_extractor_matcher.cpp index 782ee70f0f..f26da36ced 100644 --- a/samples/cpp/descriptor_extractor_matcher.cpp +++ b/samples/cpp/descriptor_extractor_matcher.cpp @@ -73,7 +73,7 @@ void doIteration( const Mat& img1, Mat& img2, bool isWarpPerspective, cout << "< Evaluate descriptor match..." << endl; vector curve; Ptr gdm = new VectorDescriptorMatch( descriptorExtractor, descriptorMatcher ); - evaluateDescriptorMatch( img1, img2, H12, keypoints1, keypoints2, 0, 0, curve, gdm ); + evaluateGenericDescriptorMatcher( img1, img2, H12, keypoints1, keypoints2, 0, 0, curve, gdm ); for( float l_p = 0; l_p < 1 - FLT_EPSILON; l_p+=0.1 ) cout << "1-precision = " << l_p << "; recall = " << getRecall( curve, l_p ) << endl; cout << ">" << endl; diff --git a/tests/cv/src/adetectordescriptor_evaluation.cpp b/tests/cv/src/adetectordescriptor_evaluation.cpp index 89aaa3e8c9..9e6f01e016 100644 --- a/tests/cv/src/adetectordescriptor_evaluation.cpp +++ b/tests/cv/src/adetectordescriptor_evaluation.cpp @@ -1077,9 +1077,9 @@ void DescriptorQualityTest::runDatasetTest (const vector &imgs, const vecto vector > correctMatchesMask; vector recallPrecisionCurve; // not used because we need recallPrecisionCurve for // all images in dataset - evaluateDescriptorMatch( imgs[0], imgs[ci+1], Hs[ci], keypoints1, keypoints2, - &matches1to2, &correctMatchesMask, recallPrecisionCurve, - descMatch ); + evaluateGenericDescriptorMatcher( imgs[0], imgs[ci+1], Hs[ci], keypoints1, keypoints2, + &matches1to2, &correctMatchesMask, recallPrecisionCurve, + descMatch ); allMatches1to2.insert( allMatches1to2.end(), matches1to2.begin(), matches1to2.end() ); allCorrectMatchesMask.insert( allCorrectMatchesMask.end(), correctMatchesMask.begin(), correctMatchesMask.end() ); }