allow minObjectSize==maxObjectSize for single scale detection capability
authorStevenPuttemans <steven.puttemans@kuleuven.be>
Wed, 12 Oct 2016 11:59:45 +0000 (13:59 +0200)
committerStevenPuttemans <steven.puttemans@kuleuven.be>
Thu, 13 Oct 2016 14:05:23 +0000 (16:05 +0200)
modules/objdetect/include/opencv2/objdetect.hpp
modules/objdetect/src/cascadedetect.cpp

index c57bcde..cd444d2 100644 (file)
@@ -255,7 +255,7 @@ public:
     @param flags Parameter with the same meaning for an old cascade as in the function
     cvHaarDetectObjects. It is not used for a new cascade.
     @param minSize Minimum possible object size. Objects smaller than that are ignored.
-    @param maxSize Maximum possible object size. Objects larger than that are ignored.
+    @param maxSize Maximum possible object size. Objects larger than that are ignored. If `maxSize == minSize` model is evaluated on single scale.
 
     The function is parallelized with the TBB library.
 
@@ -283,7 +283,7 @@ public:
     @param flags Parameter with the same meaning for an old cascade as in the function
     cvHaarDetectObjects. It is not used for a new cascade.
     @param minSize Minimum possible object size. Objects smaller than that are ignored.
-    @param maxSize Maximum possible object size. Objects larger than that are ignored.
+    @param maxSize Maximum possible object size. Objects larger than that are ignored. If `maxSize == minSize` model is evaluated on single scale.
     */
     CV_WRAP_AS(detectMultiScale2) void detectMultiScale( InputArray image,
                           CV_OUT std::vector<Rect>& objects,
index 9eef693..1b99a7c 100644 (file)
@@ -41,6 +41,7 @@
 
 #include "precomp.hpp"
 #include <cstdio>
+#include <iostream>
 
 #include "cascadedetect.hpp"
 #include "opencv2/objdetect/objdetect_c.h"
@@ -1221,7 +1222,6 @@ static void detectMultiScaleOldFormat( const Mat& image, Ptr<CvHaarClassifierCas
     std::transform(vecAvgComp.begin(), vecAvgComp.end(), objects.begin(), getRect());
 }
 
-
 void CascadeClassifierImpl::detectMultiScaleNoGrouping( InputArray _image, std::vector<Rect>& candidates,
                                                     std::vector<int>& rejectLevels, std::vector<double>& levelWeights,
                                                     double scaleFactor, Size minObjectSize, Size maxObjectSize,
@@ -1230,17 +1230,64 @@ void CascadeClassifierImpl::detectMultiScaleNoGrouping( InputArray _image, std::
     CV_INSTRUMENT_REGION()
 
     Size imgsz = _image.size();
+    Size originalWindowSize = getOriginalWindowSize();
 
-    Mat grayImage;
-    _InputArray gray;
+    if( maxObjectSize.height == 0 || maxObjectSize.width == 0 )
+        maxObjectSize = imgsz;
+
+    // If a too small image patch is entering the function, break early before any processing
+    if( (imgsz.height < originalWindowSize.height) || (imgsz.width < originalWindowSize.width) )
+        return;
+
+    std::vector<float> all_scales, scales;
+    all_scales.reserve(1024);
+    scales.reserve(1024);
+
+    // First calculate all possible scales for the given image and model, then remove undesired scales
+    // This allows us to cope with single scale detections (minSize == maxSize) that do not fall on precalculated scale
+    for( double factor = 1; ; factor *= scaleFactor )
+    {
+        Size windowSize( cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor) );
+        if( windowSize.width > imgsz.width || windowSize.height > imgsz.height )
+            break;
+        all_scales.push_back((float)factor);
+    }
+
+    // This will capture allowed scales and a minSize==maxSize scale, if it is in the precalculated scales
+    for( size_t index = 0; index < all_scales.size(); index++){
+        Size windowSize( cvRound(originalWindowSize.width*all_scales[index]), cvRound(originalWindowSize.height*all_scales[index]) );
+        if( windowSize.width > maxObjectSize.width || windowSize.height > maxObjectSize.height)
+            break;
+        if( windowSize.width < minObjectSize.width || windowSize.height < minObjectSize.height )
+            continue;
+        scales.push_back(all_scales[index]);
+    }
+
+    // If minSize and maxSize parameter are equal and scales is not filled yet, then the scale was not available in the precalculated scales
+    // In that case we want to return the most fitting scale (closest corresponding scale using L2 distance)
+    if( scales.empty() && !all_scales.empty() ){
+        std::vector<double> distances;
+        // Calculate distances
+        for(size_t v = 0; v < all_scales.size(); v++){
+            Size windowSize( cvRound(originalWindowSize.width*all_scales[v]), cvRound(originalWindowSize.height*all_scales[v]) );
+            double d = (minObjectSize.width - windowSize.width) * (minObjectSize.width - windowSize.width)
+                       + (minObjectSize.height - windowSize.height) * (minObjectSize.height - windowSize.height);
+            distances.push_back(d);
+        }
+        // Take the index of lowest value
+        // Use that index to push the correct scale parameter
+        size_t iMin=0;
+        for(size_t i = 0; i < distances.size(); ++i){
+            if(distances[iMin] > distances[i])
+                    iMin=i;
+        }
+        scales.push_back(all_scales[iMin]);
+    }
 
     candidates.clear();
     rejectLevels.clear();
     levelWeights.clear();
 
-    if( maxObjectSize.height == 0 || maxObjectSize.width == 0 )
-        maxObjectSize = imgsz;
-
 #ifdef HAVE_OPENCL
     bool use_ocl = tryOpenCL && ocl::useOpenCL() &&
          featureEvaluator->getLocalSize().area() > 0 &&
@@ -1251,44 +1298,18 @@ void CascadeClassifierImpl::detectMultiScaleNoGrouping( InputArray _image, std::
          !outputRejectLevels;
 #endif
 
-    /*if( use_ocl )
-    {
-        if (_image.channels() > 1)
-            cvtColor(_image, ugrayImage, COLOR_BGR2GRAY);
-        else if (_image.isUMat())
-            ugrayImage = _image.getUMat();
-        else
-            _image.copyTo(ugrayImage);
-        gray = ugrayImage;
-    }
-    else*/
-    {
-        if (_image.channels() > 1)
-            cvtColor(_image, grayImage, COLOR_BGR2GRAY);
-        else if (_image.isMat())
-            grayImage = _image.getMat();
-        else
-            _image.copyTo(grayImage);
-        gray = grayImage;
-    }
-
-    std::vector<float> scales;
-    scales.reserve(1024);
-
-    for( double factor = 1; ; factor *= scaleFactor )
-    {
-        Size originalWindowSize = getOriginalWindowSize();
+    Mat grayImage;
+    _InputArray gray;
 
-        Size windowSize( cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor) );
-        if( windowSize.width > maxObjectSize.width || windowSize.height > maxObjectSize.height ||
-            windowSize.width > imgsz.width || windowSize.height > imgsz.height )
-            break;
-        if( windowSize.width < minObjectSize.width || windowSize.height < minObjectSize.height )
-            continue;
-        scales.push_back((float)factor);
-    }
+    if (_image.channels() > 1)
+        cvtColor(_image, grayImage, COLOR_BGR2GRAY);
+    else if (_image.isMat())
+        grayImage = _image.getMat();
+    else
+        _image.copyTo(grayImage);
+    gray = grayImage;
 
-    if( scales.size() == 0 || !featureEvaluator->setImage(gray, scales) )
+    if( !featureEvaluator->setImage(gray, scales) )
         return;
 
 #ifdef HAVE_OPENCL