1 //*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 // By downloading, copying, installing or using the software you agree to this license.
6 // If you do not agree to this license, do not download, install,
7 // copy or use the software.
8 //
9 //
10 // License Agreement
11 // For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
14 // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
15 // Third party copyrights are property of their respective owners.
16 //
17 // Redistribution and use in source and binary forms, with or without modification,
18 // are permitted provided that the following conditions are met:
19 //
20 // * Redistribution's of source code must retain the above copyright notice,
21 // this list of conditions and the following disclaimer.
22 //
23 // * Redistribution's in binary form must reproduce the above copyright notice,
24 // this list of conditions and the following disclaimer in the documentation
25 // and/or other materials provided with the distribution.
26 //
27 // * The name of the copyright holders may not be used to endorse or promote products
28 // derived from this software without specific prior written permission.
29 //
30 // This software is provided by the copyright holders and contributors "as is" and
31 // any express or implied warranties, including, but not limited to, the implied
32 // warranties of merchantability and fitness for a particular purpose are disclaimed.
33 // In no event shall the Intel Corporation or contributors be liable for any direct,
34 // indirect, incidental, special, exemplary, or consequential damages
35 // (including, but not limited to, procurement of substitute goods or services;
36 // loss of use, data, or profits; or business interruption) however caused
37 // and on any theory of liability, whether in contract, strict liability,
38 // or tort (including negligence or otherwise) arising in any way out of
39 // the use of this software, even if advised of the possibility of such damage.
40 //
41 //M*/
42
43 #include "precomp.hpp"
44 #include <limits>
45
46 using namespace cv;
47
solveQuadratic(_Tp a,_Tp b,_Tp c,_Tp & x1,_Tp & x2)48 template<typename _Tp> static int solveQuadratic(_Tp a, _Tp b, _Tp c, _Tp& x1, _Tp& x2)
49 {
50 if( a == 0 )
51 {
52 if( b == 0 )
53 {
54 x1 = x2 = 0;
55 return c == 0;
56 }
57 x1 = x2 = -c/b;
58 return 1;
59 }
60
61 _Tp d = b*b - 4*a*c;
62 if( d < 0 )
63 {
64 x1 = x2 = 0;
65 return 0;
66 }
67 if( d > 0 )
68 {
69 d = std::sqrt(d);
70 double s = 1/(2*a);
71 x1 = (-b - d)*s;
72 x2 = (-b + d)*s;
73 if( x1 > x2 )
74 std::swap(x1, x2);
75 return 2;
76 }
77 x1 = x2 = -b/(2*a);
78 return 1;
79 }
80
81 //for android ndk
82 #undef _S
applyHomography(const Mat_<double> & H,const Point2f & pt)83 static inline Point2f applyHomography( const Mat_<double>& H, const Point2f& pt )
84 {
85 double z = H(2,0)*pt.x + H(2,1)*pt.y + H(2,2);
86 if( z )
87 {
88 double w = 1./z;
89 return 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) );
90 }
91 return Point2f( std::numeric_limits<float>::max(), std::numeric_limits<float>::max() );
92 }
93
linearizeHomographyAt(const Mat_<double> & H,const Point2f & pt,Mat_<double> & A)94 static inline void linearizeHomographyAt( const Mat_<double>& H, const Point2f& pt, Mat_<double>& A )
95 {
96 A.create(2,2);
97 double p1 = H(0,0)*pt.x + H(0,1)*pt.y + H(0,2),
98 p2 = H(1,0)*pt.x + H(1,1)*pt.y + H(1,2),
99 p3 = H(2,0)*pt.x + H(2,1)*pt.y + H(2,2),
100 p3_2 = p3*p3;
101 if( p3 )
102 {
103 A(0,0) = H(0,0)/p3 - p1*H(2,0)/p3_2; // fxdx
104 A(0,1) = H(0,1)/p3 - p1*H(2,1)/p3_2; // fxdy
105
106 A(1,0) = H(1,0)/p3 - p2*H(2,0)/p3_2; // fydx
107 A(1,1) = H(1,1)/p3 - p2*H(2,1)/p3_2; // fydx
108 }
109 else
110 A.setTo(Scalar::all(std::numeric_limits<double>::max()));
111 }
112
113 class EllipticKeyPoint
114 {
115 public:
116 EllipticKeyPoint();
117 EllipticKeyPoint( const Point2f& _center, const Scalar& _ellipse );
118
119 static void convert( const std::vector<KeyPoint>& src, std::vector<EllipticKeyPoint>& dst );
120 static void convert( const std::vector<EllipticKeyPoint>& src, std::vector<KeyPoint>& dst );
121
122 static Mat_<double> getSecondMomentsMatrix( const Scalar& _ellipse );
123 Mat_<double> getSecondMomentsMatrix() const;
124
125 void calcProjection( const Mat_<double>& H, EllipticKeyPoint& projection ) const;
126 static void calcProjection( const std::vector<EllipticKeyPoint>& src, const Mat_<double>& H, std::vector<EllipticKeyPoint>& dst );
127
128 Point2f center;
129 Scalar ellipse; // 3 elements a, b, c: ax^2+2bxy+cy^2=1
130 Size_<float> axes; // half length of ellipse axes
131 Size_<float> boundingBox; // half sizes of bounding box which sides are parallel to the coordinate axes
132 };
133
EllipticKeyPoint()134 EllipticKeyPoint::EllipticKeyPoint()
135 {
136 *this = EllipticKeyPoint(Point2f(0,0), Scalar(1, 0, 1) );
137 }
138
EllipticKeyPoint(const Point2f & _center,const Scalar & _ellipse)139 EllipticKeyPoint::EllipticKeyPoint( const Point2f& _center, const Scalar& _ellipse )
140 {
141 center = _center;
142 ellipse = _ellipse;
143
144 double a = ellipse[0], b = ellipse[1], c = ellipse[2];
145 double ac_b2 = a*c - b*b;
146 double x1, x2;
147 solveQuadratic(1., -(a+c), ac_b2, x1, x2);
148 axes.width = (float)(1/sqrt(x1));
149 axes.height = (float)(1/sqrt(x2));
150
151 boundingBox.width = (float)sqrt(ellipse[2]/ac_b2);
152 boundingBox.height = (float)sqrt(ellipse[0]/ac_b2);
153 }
154
getSecondMomentsMatrix(const Scalar & _ellipse)155 Mat_<double> EllipticKeyPoint::getSecondMomentsMatrix( const Scalar& _ellipse )
156 {
157 Mat_<double> M(2, 2);
158 M(0,0) = _ellipse[0];
159 M(1,0) = M(0,1) = _ellipse[1];
160 M(1,1) = _ellipse[2];
161 return M;
162 }
163
getSecondMomentsMatrix() const164 Mat_<double> EllipticKeyPoint::getSecondMomentsMatrix() const
165 {
166 return getSecondMomentsMatrix(ellipse);
167 }
168
calcProjection(const Mat_<double> & H,EllipticKeyPoint & projection) const169 void EllipticKeyPoint::calcProjection( const Mat_<double>& H, EllipticKeyPoint& projection ) const
170 {
171 Point2f dstCenter = applyHomography(H, center);
172
173 Mat_<double> invM; invert(getSecondMomentsMatrix(), invM);
174 Mat_<double> Aff; linearizeHomographyAt(H, center, Aff);
175 Mat_<double> dstM; invert(Aff*invM*Aff.t(), dstM);
176
177 projection = EllipticKeyPoint( dstCenter, Scalar(dstM(0,0), dstM(0,1), dstM(1,1)) );
178 }
179
convert(const std::vector<KeyPoint> & src,std::vector<EllipticKeyPoint> & dst)180 void EllipticKeyPoint::convert( const std::vector<KeyPoint>& src, std::vector<EllipticKeyPoint>& dst )
181 {
182 if( !src.empty() )
183 {
184 dst.resize(src.size());
185 for( size_t i = 0; i < src.size(); i++ )
186 {
187 float rad = src[i].size/2;
188 CV_Assert( rad );
189 float fac = 1.f/(rad*rad);
190 dst[i] = EllipticKeyPoint( src[i].pt, Scalar(fac, 0, fac) );
191 }
192 }
193 }
194
convert(const std::vector<EllipticKeyPoint> & src,std::vector<KeyPoint> & dst)195 void EllipticKeyPoint::convert( const std::vector<EllipticKeyPoint>& src, std::vector<KeyPoint>& dst )
196 {
197 if( !src.empty() )
198 {
199 dst.resize(src.size());
200 for( size_t i = 0; i < src.size(); i++ )
201 {
202 Size_<float> axes = src[i].axes;
203 float rad = sqrt(axes.height*axes.width);
204 dst[i] = KeyPoint(src[i].center, 2*rad );
205 }
206 }
207 }
208
calcProjection(const std::vector<EllipticKeyPoint> & src,const Mat_<double> & H,std::vector<EllipticKeyPoint> & dst)209 void EllipticKeyPoint::calcProjection( const std::vector<EllipticKeyPoint>& src, const Mat_<double>& H, std::vector<EllipticKeyPoint>& dst )
210 {
211 if( !src.empty() )
212 {
213 CV_Assert( !H.empty() && H.cols == 3 && H.rows == 3);
214 dst.resize(src.size());
215 std::vector<EllipticKeyPoint>::const_iterator srcIt = src.begin();
216 std::vector<EllipticKeyPoint>::iterator dstIt = dst.begin();
217 for( ; srcIt != src.end(); ++srcIt, ++dstIt )
218 srcIt->calcProjection(H, *dstIt);
219 }
220 }
221
filterEllipticKeyPointsByImageSize(std::vector<EllipticKeyPoint> & keypoints,const Size & imgSize)222 static void filterEllipticKeyPointsByImageSize( std::vector<EllipticKeyPoint>& keypoints, const Size& imgSize )
223 {
224 if( !keypoints.empty() )
225 {
226 std::vector<EllipticKeyPoint> filtered;
227 filtered.reserve(keypoints.size());
228 std::vector<EllipticKeyPoint>::const_iterator it = keypoints.begin();
229 for( int i = 0; it != keypoints.end(); ++it, i++ )
230 {
231 if( it->center.x + it->boundingBox.width < imgSize.width &&
232 it->center.x - it->boundingBox.width > 0 &&
233 it->center.y + it->boundingBox.height < imgSize.height &&
234 it->center.y - it->boundingBox.height > 0 )
235 filtered.push_back(*it);
236 }
237 keypoints.assign(filtered.begin(), filtered.end());
238 }
239 }
240
241 struct IntersectAreaCounter
242 {
IntersectAreaCounterIntersectAreaCounter243 IntersectAreaCounter( float _dr, int _minx,
244 int _miny, int _maxy,
245 const Point2f& _diff,
246 const Scalar& _ellipse1, const Scalar& _ellipse2 ) :
247 dr(_dr), bua(0), bna(0), minx(_minx), miny(_miny), maxy(_maxy),
248 diff(_diff), ellipse1(_ellipse1), ellipse2(_ellipse2) {}
IntersectAreaCounterIntersectAreaCounter249 IntersectAreaCounter( const IntersectAreaCounter& counter, Split )
250 {
251 *this = counter;
252 bua = 0;
253 bna = 0;
254 }
255
operator ()IntersectAreaCounter256 void operator()( const BlockedRange& range )
257 {
258 CV_Assert( miny < maxy );
259 CV_Assert( dr > FLT_EPSILON );
260
261 int temp_bua = bua, temp_bna = bna;
262 for( int i = range.begin(); i != range.end(); i++ )
263 {
264 float rx1 = minx + i*dr;
265 float rx2 = rx1 - diff.x;
266 for( float ry1 = (float)miny; ry1 <= (float)maxy; ry1 += dr )
267 {
268 float ry2 = ry1 - diff.y;
269 //compute the distance from the ellipse center
270 float e1 = (float)(ellipse1[0]*rx1*rx1 + 2*ellipse1[1]*rx1*ry1 + ellipse1[2]*ry1*ry1);
271 float e2 = (float)(ellipse2[0]*rx2*rx2 + 2*ellipse2[1]*rx2*ry2 + ellipse2[2]*ry2*ry2);
272 //compute the area
273 if( e1<1 && e2<1 ) temp_bna++;
274 if( e1<1 || e2<1 ) temp_bua++;
275 }
276 }
277 bua = temp_bua;
278 bna = temp_bna;
279 }
280
joinIntersectAreaCounter281 void join( IntersectAreaCounter& ac )
282 {
283 bua += ac.bua;
284 bna += ac.bna;
285 }
286
287 float dr;
288 int bua, bna;
289
290 int minx;
291 int miny, maxy;
292
293 Point2f diff;
294 Scalar ellipse1, ellipse2;
295
296 };
297
298 struct SIdx
299 {
SIdxSIdx300 SIdx() : S(-1), i1(-1), i2(-1) {}
SIdxSIdx301 SIdx(float _S, int _i1, int _i2) : S(_S), i1(_i1), i2(_i2) {}
302 float S;
303 int i1;
304 int i2;
305
operator <SIdx306 bool operator<(const SIdx& v) const { return S > v.S; }
307
308 struct UsedFinder
309 {
UsedFinderSIdx::UsedFinder310 UsedFinder(const SIdx& _used) : used(_used) {}
311 const SIdx& used;
operator ()SIdx::UsedFinder312 bool operator()(const SIdx& v) const { return (v.i1 == used.i1 || v.i2 == used.i2); }
313 UsedFinder& operator=(const UsedFinder&);
314 };
315 };
316
computeOneToOneMatchedOverlaps(const std::vector<EllipticKeyPoint> & keypoints1,const std::vector<EllipticKeyPoint> & keypoints2t,bool commonPart,std::vector<SIdx> & overlaps,float minOverlap)317 static void computeOneToOneMatchedOverlaps( const std::vector<EllipticKeyPoint>& keypoints1, const std::vector<EllipticKeyPoint>& keypoints2t,
318 bool commonPart, std::vector<SIdx>& overlaps, float minOverlap )
319 {
320 CV_Assert( minOverlap >= 0.f );
321 overlaps.clear();
322 if( keypoints1.empty() || keypoints2t.empty() )
323 return;
324
325 overlaps.clear();
326 overlaps.reserve(cvRound(keypoints1.size() * keypoints2t.size() * 0.01));
327
328 for( size_t i1 = 0; i1 < keypoints1.size(); i1++ )
329 {
330 EllipticKeyPoint kp1 = keypoints1[i1];
331 float maxDist = sqrt(kp1.axes.width*kp1.axes.height),
332 fac = 30.f/maxDist;
333 if( !commonPart )
334 fac=3;
335
336 maxDist = maxDist*4;
337 fac = 1.f/(fac*fac);
338
339 EllipticKeyPoint keypoint1a = EllipticKeyPoint( kp1.center, Scalar(fac*kp1.ellipse[0], fac*kp1.ellipse[1], fac*kp1.ellipse[2]) );
340
341 for( size_t i2 = 0; i2 < keypoints2t.size(); i2++ )
342 {
343 EllipticKeyPoint kp2 = keypoints2t[i2];
344 Point2f diff = kp2.center - kp1.center;
345
346 if( norm(diff) < maxDist )
347 {
348 EllipticKeyPoint keypoint2a = EllipticKeyPoint( kp2.center, Scalar(fac*kp2.ellipse[0], fac*kp2.ellipse[1], fac*kp2.ellipse[2]) );
349 //find the largest eigenvalue
350 int maxx = (int)ceil(( keypoint1a.boundingBox.width > (diff.x+keypoint2a.boundingBox.width)) ?
351 keypoint1a.boundingBox.width : (diff.x+keypoint2a.boundingBox.width));
352 int minx = (int)floor((-keypoint1a.boundingBox.width < (diff.x-keypoint2a.boundingBox.width)) ?
353 -keypoint1a.boundingBox.width : (diff.x-keypoint2a.boundingBox.width));
354
355 int maxy = (int)ceil(( keypoint1a.boundingBox.height > (diff.y+keypoint2a.boundingBox.height)) ?
356 keypoint1a.boundingBox.height : (diff.y+keypoint2a.boundingBox.height));
357 int miny = (int)floor((-keypoint1a.boundingBox.height < (diff.y-keypoint2a.boundingBox.height)) ?
358 -keypoint1a.boundingBox.height : (diff.y-keypoint2a.boundingBox.height));
359 int mina = (maxx-minx) < (maxy-miny) ? (maxx-minx) : (maxy-miny) ;
360
361 //compute the area
362 float dr = (float)mina/50.f;
363 int N = (int)floor((float)(maxx - minx) / dr);
364 IntersectAreaCounter ac( dr, minx, miny, maxy, diff, keypoint1a.ellipse, keypoint2a.ellipse );
365 parallel_reduce( BlockedRange(0, N+1), ac );
366 if( ac.bna > 0 )
367 {
368 float ov = (float)ac.bna / (float)ac.bua;
369 if( ov >= minOverlap )
370 overlaps.push_back(SIdx(ov, (int)i1, (int)i2));
371 }
372 }
373 }
374 }
375
376 std::sort( overlaps.begin(), overlaps.end() );
377
378 typedef std::vector<SIdx>::iterator It;
379
380 It pos = overlaps.begin();
381 It end = overlaps.end();
382
383 while(pos != end)
384 {
385 It prev = pos++;
386 end = std::remove_if(pos, end, SIdx::UsedFinder(*prev));
387 }
388 overlaps.erase(pos, overlaps.end());
389 }
390
calculateRepeatability(const Mat & img1,const Mat & img2,const Mat & H1to2,const std::vector<KeyPoint> & _keypoints1,const std::vector<KeyPoint> & _keypoints2,float & repeatability,int & correspondencesCount,Mat * thresholdedOverlapMask=0)391 static void calculateRepeatability( const Mat& img1, const Mat& img2, const Mat& H1to2,
392 const std::vector<KeyPoint>& _keypoints1, const std::vector<KeyPoint>& _keypoints2,
393 float& repeatability, int& correspondencesCount,
394 Mat* thresholdedOverlapMask=0 )
395 {
396 std::vector<EllipticKeyPoint> keypoints1, keypoints2, keypoints1t, keypoints2t;
397 EllipticKeyPoint::convert( _keypoints1, keypoints1 );
398 EllipticKeyPoint::convert( _keypoints2, keypoints2 );
399
400 // calculate projections of key points
401 EllipticKeyPoint::calcProjection( keypoints1, H1to2, keypoints1t );
402 Mat H2to1; invert(H1to2, H2to1);
403 EllipticKeyPoint::calcProjection( keypoints2, H2to1, keypoints2t );
404
405 float overlapThreshold;
406 bool ifEvaluateDetectors = thresholdedOverlapMask == 0;
407 if( ifEvaluateDetectors )
408 {
409 overlapThreshold = 1.f - 0.4f;
410
411 // remove key points from outside of the common image part
412 Size sz1 = img1.size(), sz2 = img2.size();
413 filterEllipticKeyPointsByImageSize( keypoints1, sz1 );
414 filterEllipticKeyPointsByImageSize( keypoints1t, sz2 );
415 filterEllipticKeyPointsByImageSize( keypoints2, sz2 );
416 filterEllipticKeyPointsByImageSize( keypoints2t, sz1 );
417 }
418 else
419 {
420 overlapThreshold = 1.f - 0.5f;
421
422 thresholdedOverlapMask->create( (int)keypoints1.size(), (int)keypoints2t.size(), CV_8UC1 );
423 thresholdedOverlapMask->setTo( Scalar::all(0) );
424 }
425 size_t size1 = keypoints1.size(), size2 = keypoints2t.size();
426 size_t minCount = MIN( size1, size2 );
427
428 // calculate overlap errors
429 std::vector<SIdx> overlaps;
430 computeOneToOneMatchedOverlaps( keypoints1, keypoints2t, ifEvaluateDetectors, overlaps, overlapThreshold/*min overlap*/ );
431
432 correspondencesCount = -1;
433 repeatability = -1.f;
434 if( overlaps.empty() )
435 return;
436
437 if( ifEvaluateDetectors )
438 {
439 // regions one-to-one matching
440 correspondencesCount = (int)overlaps.size();
441 repeatability = minCount ? (float)correspondencesCount / minCount : -1;
442 }
443 else
444 {
445 for( size_t i = 0; i < overlaps.size(); i++ )
446 {
447 int y = overlaps[i].i1;
448 int x = overlaps[i].i2;
449 thresholdedOverlapMask->at<uchar>(y,x) = 1;
450 }
451 }
452 }
453
evaluateFeatureDetector(const Mat & img1,const Mat & img2,const Mat & H1to2,std::vector<KeyPoint> * _keypoints1,std::vector<KeyPoint> * _keypoints2,float & repeatability,int & correspCount,const Ptr<FeatureDetector> & _fdetector)454 void cv::evaluateFeatureDetector( const Mat& img1, const Mat& img2, const Mat& H1to2,
455 std::vector<KeyPoint>* _keypoints1, std::vector<KeyPoint>* _keypoints2,
456 float& repeatability, int& correspCount,
457 const Ptr<FeatureDetector>& _fdetector )
458 {
459 Ptr<FeatureDetector> fdetector(_fdetector);
460 std::vector<KeyPoint> *keypoints1, *keypoints2, buf1, buf2;
461 keypoints1 = _keypoints1 != 0 ? _keypoints1 : &buf1;
462 keypoints2 = _keypoints2 != 0 ? _keypoints2 : &buf2;
463
464 if( (keypoints1->empty() || keypoints2->empty()) && !fdetector )
465 CV_Error( Error::StsBadArg, "fdetector must not be empty when keypoints1 or keypoints2 is empty" );
466
467 if( keypoints1->empty() )
468 fdetector->detect( img1, *keypoints1 );
469 if( keypoints2->empty() )
470 fdetector->detect( img2, *keypoints2 );
471
472 calculateRepeatability( img1, img2, H1to2, *keypoints1, *keypoints2, repeatability, correspCount );
473 }
474
475 struct DMatchForEvaluation : public DMatch
476 {
477 uchar isCorrect;
DMatchForEvaluationDMatchForEvaluation478 DMatchForEvaluation( const DMatch &dm ) : DMatch( dm ) {}
479 };
480
recall(int correctMatchCount,int correspondenceCount)481 static inline float recall( int correctMatchCount, int correspondenceCount )
482 {
483 return correspondenceCount ? (float)correctMatchCount / (float)correspondenceCount : -1;
484 }
485
precision(int correctMatchCount,int falseMatchCount)486 static inline float precision( int correctMatchCount, int falseMatchCount )
487 {
488 return correctMatchCount + falseMatchCount ? (float)correctMatchCount / (float)(correctMatchCount + falseMatchCount) : -1;
489 }
490
computeRecallPrecisionCurve(const std::vector<std::vector<DMatch>> & matches1to2,const std::vector<std::vector<uchar>> & correctMatches1to2Mask,std::vector<Point2f> & recallPrecisionCurve)491 void cv::computeRecallPrecisionCurve( const std::vector<std::vector<DMatch> >& matches1to2,
492 const std::vector<std::vector<uchar> >& correctMatches1to2Mask,
493 std::vector<Point2f>& recallPrecisionCurve )
494 {
495 CV_Assert( matches1to2.size() == correctMatches1to2Mask.size() );
496
497 std::vector<DMatchForEvaluation> allMatches;
498 int correspondenceCount = 0;
499 for( size_t i = 0; i < matches1to2.size(); i++ )
500 {
501 for( size_t j = 0; j < matches1to2[i].size(); j++ )
502 {
503 DMatchForEvaluation match = matches1to2[i][j];
504 match.isCorrect = correctMatches1to2Mask[i][j] ;
505 allMatches.push_back( match );
506 correspondenceCount += match.isCorrect != 0 ? 1 : 0;
507 }
508 }
509
510 std::sort( allMatches.begin(), allMatches.end() );
511
512 int correctMatchCount = 0, falseMatchCount = 0;
513 recallPrecisionCurve.resize( allMatches.size() );
514 for( size_t i = 0; i < allMatches.size(); i++ )
515 {
516 if( allMatches[i].isCorrect )
517 correctMatchCount++;
518 else
519 falseMatchCount++;
520
521 float r = recall( correctMatchCount, correspondenceCount );
522 float p = precision( correctMatchCount, falseMatchCount );
523 recallPrecisionCurve[i] = Point2f(1-p, r);
524 }
525 }
526
getRecall(const std::vector<Point2f> & recallPrecisionCurve,float l_precision)527 float cv::getRecall( const std::vector<Point2f>& recallPrecisionCurve, float l_precision )
528 {
529 int nearestPointIndex = getNearestPoint( recallPrecisionCurve, l_precision );
530
531 float recall = -1.f;
532
533 if( nearestPointIndex >= 0 )
534 recall = recallPrecisionCurve[nearestPointIndex].y;
535
536 return recall;
537 }
538
getNearestPoint(const std::vector<Point2f> & recallPrecisionCurve,float l_precision)539 int cv::getNearestPoint( const std::vector<Point2f>& recallPrecisionCurve, float l_precision )
540 {
541 int nearestPointIndex = -1;
542
543 if( l_precision >= 0 && l_precision <= 1 )
544 {
545 float minDiff = FLT_MAX;
546 for( size_t i = 0; i < recallPrecisionCurve.size(); i++ )
547 {
548 float curDiff = std::fabs(l_precision - recallPrecisionCurve[i].x);
549 if( curDiff <= minDiff )
550 {
551 nearestPointIndex = (int)i;
552 minDiff = curDiff;
553 }
554 }
555 }
556
557 return nearestPointIndex;
558 }
559