diff --git a/modules/objdetect/doc/cascade_classification.rst b/modules/objdetect/doc/cascade_classification.rst index 961cf0aa5..46272d2f0 100644 --- a/modules/objdetect/doc/cascade_classification.rst +++ b/modules/objdetect/doc/cascade_classification.rst @@ -189,6 +189,7 @@ CascadeClassifier::detectMultiScale Detects objects of different sizes in the input image. The detected objects are returned as a list of rectangles. .. ocv:function:: void CascadeClassifier::detectMultiScale( const Mat& image, vector& objects, double scaleFactor=1.1, int minNeighbors=3, int flags=0, Size minSize=Size(), Size maxSize=Size()) +.. ocv:function:: void CascadeClassifier::detectMultiScale( const Mat& image, vector& objects, vector& numDetections, double scaleFactor=1.1, int minNeighbors=3, int flags=0, Size minSize=Size(), Size maxSize=Size()) .. ocv:pyfunction:: cv2.CascadeClassifier.detectMultiScale(image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize]]]]]) -> objects .. ocv:pyfunction:: cv2.CascadeClassifier.detectMultiScale(image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize[, outputRejectLevels]]]]]]) -> objects, rejectLevels, levelWeights @@ -201,6 +202,8 @@ Detects objects of different sizes in the input image. The detected objects are :param objects: Vector of rectangles where each rectangle contains the detected object, the rectangles may be partially outside the original image. + :param numDetections: Vector of detection numbers for the corresponding objects. An object's number of detections is the number of neighboring positively classified rectangles that were joined together to form the object. + :param scaleFactor: Parameter specifying how much the image size is reduced at each image scale. :param minNeighbors: Parameter specifying how many neighbors each candidate rectangle should have to retain it. diff --git a/modules/objdetect/include/opencv2/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect.hpp index 3ccb057e3..d263b2eb7 100644 --- a/modules/objdetect/include/opencv2/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect.hpp @@ -149,6 +149,14 @@ public: Size minSize = Size(), Size maxSize = Size() ); + CV_WRAP virtual void detectMultiScale( const Mat& image, + CV_OUT std::vector& objects, + CV_OUT std::vector& numDetections, + double scaleFactor=1.1, + int minNeighbors=3, int flags=0, + Size minSize=Size(), + Size maxSize=Size() ); + CV_WRAP virtual void detectMultiScale( const Mat& image, CV_OUT std::vector& objects, CV_OUT std::vector& rejectLevels, @@ -168,7 +176,12 @@ public: protected: virtual bool detectSingleScale( const Mat& image, int stripCount, Size processingRectSize, int stripSize, int yStep, double factor, std::vector& candidates, - std::vector& rejectLevels, std::vector& levelWeights, bool outputRejectLevels = false); + std::vector& rejectLevels, std::vector& levelWeights, bool outputRejectLevels = false ); + + virtual void detectMultiScaleNoGrouping( const Mat& image, std::vector& candidates, + std::vector& rejectLevels, std::vector& levelWeights, + double scaleFactor, Size minObjectSize, Size maxObjectSize, + bool outputRejectLevels = false ); protected: enum { BOOST = 0 diff --git a/modules/objdetect/src/cascadedetect.cpp b/modules/objdetect/src/cascadedetect.cpp index 6d1b287d8..13422b97e 100644 --- a/modules/objdetect/src/cascadedetect.cpp +++ b/modules/objdetect/src/cascadedetect.cpp @@ -1022,6 +1022,7 @@ public: }; struct getRect { Rect operator ()(const CvAvgComp& e) const { return e.rect; } }; +struct getNeighbors { int operator ()(const CvAvgComp& e) const { return e.neighbors; } }; bool CascadeClassifier::detectSingleScale( const Mat& image, int stripCount, Size processingRectSize, @@ -1086,39 +1087,33 @@ bool CascadeClassifier::setImage(const Mat& image) return featureEvaluator->setImage(image, data.origWinSize); } -void CascadeClassifier::detectMultiScale( const Mat& image, std::vector& objects, - std::vector& rejectLevels, - std::vector& levelWeights, - double scaleFactor, int minNeighbors, - int flags, Size minObjectSize, Size maxObjectSize, - bool outputRejectLevels ) +static void detectMultiScaleOldFormat( const Mat& image, Ptr oldCascade, + std::vector& objects, + std::vector& rejectLevels, + std::vector& levelWeights, + std::vector& vecAvgComp, + double scaleFactor, int minNeighbors, + int flags, Size minObjectSize, Size maxObjectSize, + bool outputRejectLevels = false ) { - const double GROUP_EPS = 0.2; + MemStorage storage(cvCreateMemStorage(0)); + CvMat _image = image; + CvSeq* _objects = cvHaarDetectObjectsForROC( &_image, oldCascade, storage, rejectLevels, levelWeights, scaleFactor, + minNeighbors, flags, minObjectSize, maxObjectSize, outputRejectLevels ); + Seq(_objects).copyTo(vecAvgComp); + objects.resize(vecAvgComp.size()); + std::transform(vecAvgComp.begin(), vecAvgComp.end(), objects.begin(), getRect()); +} - CV_Assert( scaleFactor > 1 && image.depth() == CV_8U ); +void CascadeClassifier::detectMultiScaleNoGrouping( const Mat& image, std::vector& candidates, + std::vector& rejectLevels, std::vector& levelWeights, + double scaleFactor, Size minObjectSize, Size maxObjectSize, + bool outputRejectLevels ) +{ + candidates.clear(); - if( empty() ) - return; - - if( isOldFormatCascade() ) - { - MemStorage storage(cvCreateMemStorage(0)); - CvMat _image = image; - CvSeq* _objects = cvHaarDetectObjectsForROC( &_image, oldCascade, storage, rejectLevels, levelWeights, scaleFactor, - minNeighbors, flags, minObjectSize, maxObjectSize, outputRejectLevels ); - std::vector vecAvgComp; - Seq(_objects).copyTo(vecAvgComp); - objects.resize(vecAvgComp.size()); - std::transform(vecAvgComp.begin(), vecAvgComp.end(), objects.begin(), getRect()); - return; - } - - objects.clear(); - - if (!maskGenerator.empty()) { + if (!maskGenerator.empty()) maskGenerator->initializeMask(image); - } - if( maxObjectSize.height == 0 || maxObjectSize.width == 0 ) maxObjectSize = image.size(); @@ -1132,7 +1127,6 @@ void CascadeClassifier::detectMultiScale( const Mat& image, std::vector& o } Mat imageBuffer(image.rows + 1, image.cols + 1, CV_8U); - std::vector candidates; for( double factor = 1; ; factor *= scaleFactor ) { @@ -1173,18 +1167,39 @@ void CascadeClassifier::detectMultiScale( const Mat& image, std::vector& o rejectLevels, levelWeights, outputRejectLevels ) ) break; } +} +void CascadeClassifier::detectMultiScale( const Mat& image, std::vector& objects, + std::vector& rejectLevels, + std::vector& levelWeights, + double scaleFactor, int minNeighbors, + int flags, Size minObjectSize, Size maxObjectSize, + bool outputRejectLevels ) +{ + CV_Assert( scaleFactor > 1 && image.depth() == CV_8U ); - objects.resize(candidates.size()); - std::copy(candidates.begin(), candidates.end(), objects.begin()); + if( empty() ) + return; - if( outputRejectLevels ) + if( isOldFormatCascade() ) { - groupRectangles( objects, rejectLevels, levelWeights, minNeighbors, GROUP_EPS ); + std::vector fakeVecAvgComp; + detectMultiScaleOldFormat( image, oldCascade, objects, rejectLevels, levelWeights, fakeVecAvgComp, scaleFactor, + minNeighbors, flags, minObjectSize, maxObjectSize, outputRejectLevels ); } else { - groupRectangles( objects, minNeighbors, GROUP_EPS ); + detectMultiScaleNoGrouping( image, objects, rejectLevels, levelWeights, scaleFactor, minObjectSize, maxObjectSize, + outputRejectLevels ); + const double GROUP_EPS = 0.2; + if( outputRejectLevels ) + { + groupRectangles( objects, rejectLevels, levelWeights, minNeighbors, GROUP_EPS ); + } + else + { + groupRectangles( objects, minNeighbors, GROUP_EPS ); + } } } @@ -1195,7 +1210,35 @@ void CascadeClassifier::detectMultiScale( const Mat& image, std::vector& o std::vector fakeLevels; std::vector fakeWeights; detectMultiScale( image, objects, fakeLevels, fakeWeights, scaleFactor, - minNeighbors, flags, minObjectSize, maxObjectSize, false ); + minNeighbors, flags, minObjectSize, maxObjectSize ); +} + +void CascadeClassifier::detectMultiScale( const Mat& image, std::vector& objects, + std::vector& numDetections, double scaleFactor, + int minNeighbors, int flags, Size minObjectSize, + Size maxObjectSize ) +{ + CV_Assert( scaleFactor > 1 && image.depth() == CV_8U ); + + if( empty() ) + return; + + std::vector fakeLevels; + std::vector fakeWeights; + if( isOldFormatCascade() ) + { + std::vector vecAvgComp; + detectMultiScaleOldFormat( image, oldCascade, objects, fakeLevels, fakeWeights, vecAvgComp, scaleFactor, + minNeighbors, flags, minObjectSize, maxObjectSize ); + numDetections.resize(vecAvgComp.size()); + std::transform(vecAvgComp.begin(), vecAvgComp.end(), numDetections.begin(), getNeighbors()); + } + else + { + detectMultiScaleNoGrouping( image, objects, fakeLevels, fakeWeights, scaleFactor, minObjectSize, maxObjectSize ); + const double GROUP_EPS = 0.2; + groupRectangles( objects, numDetections, minNeighbors, GROUP_EPS ); + } } bool CascadeClassifier::Data::read(const FileNode &root)