Path: blob/master/modules/features2d/src/evaluation.cpp
16337 views
//*M///////////////////////////////////////////////////////////////////////////////////////1//2// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.3//4// By downloading, copying, installing or using the software you agree to this license.5// If you do not agree to this license, do not download, install,6// copy or use the software.7//8//9// License Agreement10// For Open Source Computer Vision Library11//12// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.13// Copyright (C) 2009, Willow Garage Inc., all rights reserved.14// Third party copyrights are property of their respective owners.15//16// Redistribution and use in source and binary forms, with or without modification,17// are permitted provided that the following conditions are met:18//19// * Redistribution's of source code must retain the above copyright notice,20// this list of conditions and the following disclaimer.21//22// * Redistribution's in binary form must reproduce the above copyright notice,23// this list of conditions and the following disclaimer in the documentation24// and/or other materials provided with the distribution.25//26// * The name of the copyright holders may not be used to endorse or promote products27// derived from this software without specific prior written permission.28//29// This software is provided by the copyright holders and contributors "as is" and30// any express or implied warranties, including, but not limited to, the implied31// warranties of merchantability and fitness for a particular purpose are disclaimed.32// In no event shall the Intel Corporation or contributors be liable for any direct,33// indirect, incidental, special, exemplary, or consequential damages34// (including, but not limited to, procurement of substitute goods or services;35// loss of use, data, or profits; or business interruption) however caused36// and on any theory of liability, whether in contract, strict liability,37// or tort (including negligence or otherwise) arising in any way out of38// the use of this software, even if advised of the possibility of such damage.39//40//M*/4142#include "precomp.hpp"43#include <limits>4445using namespace cv;4647template<typename _Tp> static int solveQuadratic(_Tp a, _Tp b, _Tp c, _Tp& x1, _Tp& x2)48{49if( a == 0 )50{51if( b == 0 )52{53x1 = x2 = 0;54return c == 0;55}56x1 = x2 = -c/b;57return 1;58}5960_Tp d = b*b - 4*a*c;61if( d < 0 )62{63x1 = x2 = 0;64return 0;65}66if( d > 0 )67{68d = std::sqrt(d);69double s = 1/(2*a);70x1 = (-b - d)*s;71x2 = (-b + d)*s;72if( x1 > x2 )73std::swap(x1, x2);74return 2;75}76x1 = x2 = -b/(2*a);77return 1;78}7980//for android ndk81#undef _S82static inline Point2f applyHomography( const Mat_<double>& H, const Point2f& pt )83{84double z = H(2,0)*pt.x + H(2,1)*pt.y + H(2,2);85if( z )86{87double w = 1./z;88return Point2f( (float)((H(0,0)*pt.x + H(0,1)*pt.y + H(0,2))*w), (float)((H(1,0)*pt.x + H(1,1)*pt.y + H(1,2))*w) );89}90return Point2f( std::numeric_limits<float>::max(), std::numeric_limits<float>::max() );91}9293static inline void linearizeHomographyAt( const Mat_<double>& H, const Point2f& pt, Mat_<double>& A )94{95A.create(2,2);96double p1 = H(0,0)*pt.x + H(0,1)*pt.y + H(0,2),97p2 = H(1,0)*pt.x + H(1,1)*pt.y + H(1,2),98p3 = H(2,0)*pt.x + H(2,1)*pt.y + H(2,2),99p3_2 = p3*p3;100if( p3 )101{102A(0,0) = H(0,0)/p3 - p1*H(2,0)/p3_2; // fxdx103A(0,1) = H(0,1)/p3 - p1*H(2,1)/p3_2; // fxdy104105A(1,0) = H(1,0)/p3 - p2*H(2,0)/p3_2; // fydx106A(1,1) = H(1,1)/p3 - p2*H(2,1)/p3_2; // fydx107}108else109A.setTo(Scalar::all(std::numeric_limits<double>::max()));110}111112class EllipticKeyPoint113{114public:115EllipticKeyPoint();116EllipticKeyPoint( const Point2f& _center, const Scalar& _ellipse );117118static void convert( const std::vector<KeyPoint>& src, std::vector<EllipticKeyPoint>& dst );119static void convert( const std::vector<EllipticKeyPoint>& src, std::vector<KeyPoint>& dst );120121static Mat_<double> getSecondMomentsMatrix( const Scalar& _ellipse );122Mat_<double> getSecondMomentsMatrix() const;123124void calcProjection( const Mat_<double>& H, EllipticKeyPoint& projection ) const;125static void calcProjection( const std::vector<EllipticKeyPoint>& src, const Mat_<double>& H, std::vector<EllipticKeyPoint>& dst );126127Point2f center;128Scalar ellipse; // 3 elements a, b, c: ax^2+2bxy+cy^2=1129Size_<float> axes; // half length of ellipse axes130Size_<float> boundingBox; // half sizes of bounding box which sides are parallel to the coordinate axes131};132133EllipticKeyPoint::EllipticKeyPoint()134{135*this = EllipticKeyPoint(Point2f(0,0), Scalar(1, 0, 1) );136}137138EllipticKeyPoint::EllipticKeyPoint( const Point2f& _center, const Scalar& _ellipse )139{140center = _center;141ellipse = _ellipse;142143double a = ellipse[0], b = ellipse[1], c = ellipse[2];144double ac_b2 = a*c - b*b;145double x1, x2;146solveQuadratic(1., -(a+c), ac_b2, x1, x2);147axes.width = (float)(1/sqrt(x1));148axes.height = (float)(1/sqrt(x2));149150boundingBox.width = (float)sqrt(ellipse[2]/ac_b2);151boundingBox.height = (float)sqrt(ellipse[0]/ac_b2);152}153154Mat_<double> EllipticKeyPoint::getSecondMomentsMatrix( const Scalar& _ellipse )155{156Mat_<double> M(2, 2);157M(0,0) = _ellipse[0];158M(1,0) = M(0,1) = _ellipse[1];159M(1,1) = _ellipse[2];160return M;161}162163Mat_<double> EllipticKeyPoint::getSecondMomentsMatrix() const164{165return getSecondMomentsMatrix(ellipse);166}167168void EllipticKeyPoint::calcProjection( const Mat_<double>& H, EllipticKeyPoint& projection ) const169{170Point2f dstCenter = applyHomography(H, center);171172Mat_<double> invM; invert(getSecondMomentsMatrix(), invM);173Mat_<double> Aff; linearizeHomographyAt(H, center, Aff);174Mat_<double> dstM; invert(Aff*invM*Aff.t(), dstM);175176projection = EllipticKeyPoint( dstCenter, Scalar(dstM(0,0), dstM(0,1), dstM(1,1)) );177}178179void EllipticKeyPoint::convert( const std::vector<KeyPoint>& src, std::vector<EllipticKeyPoint>& dst )180{181CV_INSTRUMENT_REGION();182183if( !src.empty() )184{185dst.resize(src.size());186for( size_t i = 0; i < src.size(); i++ )187{188float rad = src[i].size/2;189CV_Assert( rad );190float fac = 1.f/(rad*rad);191dst[i] = EllipticKeyPoint( src[i].pt, Scalar(fac, 0, fac) );192}193}194}195196void EllipticKeyPoint::convert( const std::vector<EllipticKeyPoint>& src, std::vector<KeyPoint>& dst )197{198CV_INSTRUMENT_REGION();199200if( !src.empty() )201{202dst.resize(src.size());203for( size_t i = 0; i < src.size(); i++ )204{205Size_<float> axes = src[i].axes;206float rad = sqrt(axes.height*axes.width);207dst[i] = KeyPoint(src[i].center, 2*rad );208}209}210}211212void EllipticKeyPoint::calcProjection( const std::vector<EllipticKeyPoint>& src, const Mat_<double>& H, std::vector<EllipticKeyPoint>& dst )213{214if( !src.empty() )215{216CV_Assert( !H.empty() && H.cols == 3 && H.rows == 3);217dst.resize(src.size());218std::vector<EllipticKeyPoint>::const_iterator srcIt = src.begin();219std::vector<EllipticKeyPoint>::iterator dstIt = dst.begin();220for( ; srcIt != src.end() && dstIt != dst.end(); ++srcIt, ++dstIt )221srcIt->calcProjection(H, *dstIt);222}223}224225static void filterEllipticKeyPointsByImageSize( std::vector<EllipticKeyPoint>& keypoints, const Size& imgSize )226{227if( !keypoints.empty() )228{229std::vector<EllipticKeyPoint> filtered;230filtered.reserve(keypoints.size());231std::vector<EllipticKeyPoint>::const_iterator it = keypoints.begin();232for( int i = 0; it != keypoints.end(); ++it, i++ )233{234if( it->center.x + it->boundingBox.width < imgSize.width &&235it->center.x - it->boundingBox.width > 0 &&236it->center.y + it->boundingBox.height < imgSize.height &&237it->center.y - it->boundingBox.height > 0 )238filtered.push_back(*it);239}240keypoints.assign(filtered.begin(), filtered.end());241}242}243244struct IntersectAreaCounter245{246IntersectAreaCounter( float _dr, int _minx,247int _miny, int _maxy,248const Point2f& _diff,249const Scalar& _ellipse1, const Scalar& _ellipse2 ) :250dr(_dr), bua(0), bna(0), minx(_minx), miny(_miny), maxy(_maxy),251diff(_diff), ellipse1(_ellipse1), ellipse2(_ellipse2) {}252IntersectAreaCounter( const IntersectAreaCounter& counter, Split )253{254*this = counter;255bua = 0;256bna = 0;257}258259void operator()( const BlockedRange& range )260{261CV_Assert( miny < maxy );262CV_Assert( dr > FLT_EPSILON );263264int temp_bua = bua, temp_bna = bna;265for( int i = range.begin(); i != range.end(); i++ )266{267float rx1 = minx + i*dr;268float rx2 = rx1 - diff.x;269for( float ry1 = (float)miny; ry1 <= (float)maxy; ry1 += dr )270{271float ry2 = ry1 - diff.y;272//compute the distance from the ellipse center273float e1 = (float)(ellipse1[0]*rx1*rx1 + 2*ellipse1[1]*rx1*ry1 + ellipse1[2]*ry1*ry1);274float e2 = (float)(ellipse2[0]*rx2*rx2 + 2*ellipse2[1]*rx2*ry2 + ellipse2[2]*ry2*ry2);275//compute the area276if( e1<1 && e2<1 ) temp_bna++;277if( e1<1 || e2<1 ) temp_bua++;278}279}280bua = temp_bua;281bna = temp_bna;282}283284void join( IntersectAreaCounter& ac )285{286bua += ac.bua;287bna += ac.bna;288}289290float dr;291int bua, bna;292293int minx;294int miny, maxy;295296Point2f diff;297Scalar ellipse1, ellipse2;298299};300301struct SIdx302{303SIdx() : S(-1), i1(-1), i2(-1) {}304SIdx(float _S, int _i1, int _i2) : S(_S), i1(_i1), i2(_i2) {}305float S;306int i1;307int i2;308309bool operator<(const SIdx& v) const { return S > v.S; }310311struct UsedFinder312{313UsedFinder(const SIdx& _used) : used(_used) {}314const SIdx& used;315bool operator()(const SIdx& v) const { return (v.i1 == used.i1 || v.i2 == used.i2); }316UsedFinder& operator=(const UsedFinder&);317};318};319320static void computeOneToOneMatchedOverlaps( const std::vector<EllipticKeyPoint>& keypoints1, const std::vector<EllipticKeyPoint>& keypoints2t,321bool commonPart, std::vector<SIdx>& overlaps, float minOverlap )322{323CV_Assert( minOverlap >= 0.f );324overlaps.clear();325if( keypoints1.empty() || keypoints2t.empty() )326return;327328overlaps.clear();329overlaps.reserve(cvRound(keypoints1.size() * keypoints2t.size() * 0.01));330331for( size_t i1 = 0; i1 < keypoints1.size(); i1++ )332{333EllipticKeyPoint kp1 = keypoints1[i1];334float maxDist = sqrt(kp1.axes.width*kp1.axes.height),335fac = 30.f/maxDist;336if( !commonPart )337fac=3;338339maxDist = maxDist*4;340fac = 1.f/(fac*fac);341342EllipticKeyPoint keypoint1a = EllipticKeyPoint( kp1.center, Scalar(fac*kp1.ellipse[0], fac*kp1.ellipse[1], fac*kp1.ellipse[2]) );343344for( size_t i2 = 0; i2 < keypoints2t.size(); i2++ )345{346EllipticKeyPoint kp2 = keypoints2t[i2];347Point2f diff = kp2.center - kp1.center;348349if( norm(diff) < maxDist )350{351EllipticKeyPoint keypoint2a = EllipticKeyPoint( kp2.center, Scalar(fac*kp2.ellipse[0], fac*kp2.ellipse[1], fac*kp2.ellipse[2]) );352//find the largest eigenvalue353int maxx = (int)ceil(( keypoint1a.boundingBox.width > (diff.x+keypoint2a.boundingBox.width)) ?354keypoint1a.boundingBox.width : (diff.x+keypoint2a.boundingBox.width));355int minx = (int)floor((-keypoint1a.boundingBox.width < (diff.x-keypoint2a.boundingBox.width)) ?356-keypoint1a.boundingBox.width : (diff.x-keypoint2a.boundingBox.width));357358int maxy = (int)ceil(( keypoint1a.boundingBox.height > (diff.y+keypoint2a.boundingBox.height)) ?359keypoint1a.boundingBox.height : (diff.y+keypoint2a.boundingBox.height));360int miny = (int)floor((-keypoint1a.boundingBox.height < (diff.y-keypoint2a.boundingBox.height)) ?361-keypoint1a.boundingBox.height : (diff.y-keypoint2a.boundingBox.height));362int mina = (maxx-minx) < (maxy-miny) ? (maxx-minx) : (maxy-miny) ;363364//compute the area365float dr = (float)mina/50.f;366int N = (int)floor((float)(maxx - minx) / dr);367IntersectAreaCounter ac( dr, minx, miny, maxy, diff, keypoint1a.ellipse, keypoint2a.ellipse );368parallel_reduce( BlockedRange(0, N+1), ac );369if( ac.bna > 0 )370{371float ov = (float)ac.bna / (float)ac.bua;372if( ov >= minOverlap )373overlaps.push_back(SIdx(ov, (int)i1, (int)i2));374}375}376}377}378379std::sort( overlaps.begin(), overlaps.end() );380381typedef std::vector<SIdx>::iterator It;382383It pos = overlaps.begin();384It end = overlaps.end();385386while(pos != end)387{388It prev = pos++;389end = std::remove_if(pos, end, SIdx::UsedFinder(*prev));390}391overlaps.erase(pos, overlaps.end());392}393394static void calculateRepeatability( const Mat& img1, const Mat& img2, const Mat& H1to2,395const std::vector<KeyPoint>& _keypoints1, const std::vector<KeyPoint>& _keypoints2,396float& repeatability, int& correspondencesCount,397Mat* thresholdedOverlapMask=0 )398{399std::vector<EllipticKeyPoint> keypoints1, keypoints2, keypoints1t, keypoints2t;400EllipticKeyPoint::convert( _keypoints1, keypoints1 );401EllipticKeyPoint::convert( _keypoints2, keypoints2 );402403// calculate projections of key points404EllipticKeyPoint::calcProjection( keypoints1, H1to2, keypoints1t );405Mat H2to1; invert(H1to2, H2to1);406EllipticKeyPoint::calcProjection( keypoints2, H2to1, keypoints2t );407408float overlapThreshold;409bool ifEvaluateDetectors = thresholdedOverlapMask == 0;410if( ifEvaluateDetectors )411{412overlapThreshold = 1.f - 0.4f;413414// remove key points from outside of the common image part415Size sz1 = img1.size(), sz2 = img2.size();416filterEllipticKeyPointsByImageSize( keypoints1, sz1 );417filterEllipticKeyPointsByImageSize( keypoints1t, sz2 );418filterEllipticKeyPointsByImageSize( keypoints2, sz2 );419filterEllipticKeyPointsByImageSize( keypoints2t, sz1 );420}421else422{423overlapThreshold = 1.f - 0.5f;424425thresholdedOverlapMask->create( (int)keypoints1.size(), (int)keypoints2t.size(), CV_8UC1 );426thresholdedOverlapMask->setTo( Scalar::all(0) );427}428size_t size1 = keypoints1.size(), size2 = keypoints2t.size();429size_t minCount = MIN( size1, size2 );430431// calculate overlap errors432std::vector<SIdx> overlaps;433computeOneToOneMatchedOverlaps( keypoints1, keypoints2t, ifEvaluateDetectors, overlaps, overlapThreshold/*min overlap*/ );434435correspondencesCount = -1;436repeatability = -1.f;437if( overlaps.empty() )438return;439440if( ifEvaluateDetectors )441{442// regions one-to-one matching443correspondencesCount = (int)overlaps.size();444repeatability = minCount ? (float)correspondencesCount / minCount : -1;445}446else447{448for( size_t i = 0; i < overlaps.size(); i++ )449{450int y = overlaps[i].i1;451int x = overlaps[i].i2;452thresholdedOverlapMask->at<uchar>(y,x) = 1;453}454}455}456457void cv::evaluateFeatureDetector( const Mat& img1, const Mat& img2, const Mat& H1to2,458std::vector<KeyPoint>* _keypoints1, std::vector<KeyPoint>* _keypoints2,459float& repeatability, int& correspCount,460const Ptr<FeatureDetector>& _fdetector )461{462CV_INSTRUMENT_REGION();463464Ptr<FeatureDetector> fdetector(_fdetector);465std::vector<KeyPoint> *keypoints1, *keypoints2, buf1, buf2;466keypoints1 = _keypoints1 != 0 ? _keypoints1 : &buf1;467keypoints2 = _keypoints2 != 0 ? _keypoints2 : &buf2;468469if( (keypoints1->empty() || keypoints2->empty()) && !fdetector )470CV_Error( Error::StsBadArg, "fdetector must not be empty when keypoints1 or keypoints2 is empty" );471472if( keypoints1->empty() )473fdetector->detect( img1, *keypoints1 );474if( keypoints2->empty() )475fdetector->detect( img2, *keypoints2 );476477calculateRepeatability( img1, img2, H1to2, *keypoints1, *keypoints2, repeatability, correspCount );478}479480struct DMatchForEvaluation : public DMatch481{482uchar isCorrect;483DMatchForEvaluation( const DMatch &dm ) : DMatch( dm ), isCorrect(0) {}484};485486static inline float recall( int correctMatchCount, int correspondenceCount )487{488return correspondenceCount ? (float)correctMatchCount / (float)correspondenceCount : -1;489}490491static inline float precision( int correctMatchCount, int falseMatchCount )492{493return correctMatchCount + falseMatchCount ? (float)correctMatchCount / (float)(correctMatchCount + falseMatchCount) : -1;494}495496void cv::computeRecallPrecisionCurve( const std::vector<std::vector<DMatch> >& matches1to2,497const std::vector<std::vector<uchar> >& correctMatches1to2Mask,498std::vector<Point2f>& recallPrecisionCurve )499{500CV_INSTRUMENT_REGION();501502CV_Assert( matches1to2.size() == correctMatches1to2Mask.size() );503504std::vector<DMatchForEvaluation> allMatches;505int correspondenceCount = 0;506for( size_t i = 0; i < matches1to2.size(); i++ )507{508for( size_t j = 0; j < matches1to2[i].size(); j++ )509{510DMatchForEvaluation match = matches1to2[i][j];511match.isCorrect = correctMatches1to2Mask[i][j] ;512allMatches.push_back( match );513correspondenceCount += match.isCorrect != 0 ? 1 : 0;514}515}516517std::sort( allMatches.begin(), allMatches.end() );518519int correctMatchCount = 0, falseMatchCount = 0;520recallPrecisionCurve.resize( allMatches.size() );521for( size_t i = 0; i < allMatches.size(); i++ )522{523if( allMatches[i].isCorrect )524correctMatchCount++;525else526falseMatchCount++;527528float r = recall( correctMatchCount, correspondenceCount );529float p = precision( correctMatchCount, falseMatchCount );530recallPrecisionCurve[i] = Point2f(1-p, r);531}532}533534float cv::getRecall( const std::vector<Point2f>& recallPrecisionCurve, float l_precision )535{536CV_INSTRUMENT_REGION();537538int nearestPointIndex = getNearestPoint( recallPrecisionCurve, l_precision );539540float recall = -1.f;541542if( nearestPointIndex >= 0 )543recall = recallPrecisionCurve[nearestPointIndex].y;544545return recall;546}547548int cv::getNearestPoint( const std::vector<Point2f>& recallPrecisionCurve, float l_precision )549{550CV_INSTRUMENT_REGION();551552int nearestPointIndex = -1;553554if( l_precision >= 0 && l_precision <= 1 )555{556float minDiff = FLT_MAX;557for( size_t i = 0; i < recallPrecisionCurve.size(); i++ )558{559float curDiff = std::fabs(l_precision - recallPrecisionCurve[i].x);560if( curDiff <= minDiff )561{562nearestPointIndex = (int)i;563minDiff = curDiff;564}565}566}567568return nearestPointIndex;569}570571572