diff --git a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp index 8d7efb0ba..d5d6f0b24 100644 --- a/modules/objdetect/include/opencv2/objdetect/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect/objdetect.hpp @@ -192,12 +192,12 @@ typedef struct CvLSVMFilterObject{ // data type: STRUCT CvLatentSvmDetector // structure contains internal representation of trained Latent SVM detector -// num_filters - total number of filters (root plus part) in model -// num_components - number of components in model -// num_part_filters - array containing number of part filters for each component -// filters - root and part filters for all model components -// b - biases for all model components -// score_threshold - confidence level threshold +// num_filters - total number of filters (root plus part) in model +// num_components - number of components in model +// num_part_filters - array containing number of part filters for each component +// filters - root and part filters for all model components +// b - biases for all model components +// score_threshold - confidence level threshold typedef struct CvLatentSvmDetector { int num_filters; @@ -211,8 +211,8 @@ CvLatentSvmDetector; // data type: STRUCT CvObjectDetection // structure contains the bounding box and confidence level for detected object -// rect - bounding box for a detected object -// score - confidence level +// rect - bounding box for a detected object +// score - confidence level typedef struct CvObjectDetection { CvRect rect; @@ -228,7 +228,7 @@ typedef struct CvObjectDetection // API // CvLatentSvmDetector* cvLoadLatentSvmDetector(const char* filename); // INPUT -// filename - path to the file containing the parameters of +// filename - path to the file containing the parameters of - trained Latent SVM detector // OUTPUT // trained Latent SVM detector in internal representation @@ -241,7 +241,7 @@ CVAPI(CvLatentSvmDetector*) cvLoadLatentSvmDetector(const char* filename); // API // void cvReleaseLatentSvmDetector(CvLatentSvmDetector** detector); // INPUT -// detector - CvLatentSvmDetector structure to be released +// detector - CvLatentSvmDetector structure to be released // OUTPUT */ CVAPI(void) cvReleaseLatentSvmDetector(CvLatentSvmDetector** detector); @@ -252,16 +252,16 @@ CVAPI(void) cvReleaseLatentSvmDetector(CvLatentSvmDetector** detector); // // API // CvSeq* cvLatentSvmDetectObjects(const IplImage* image, -// CvLatentSvmDetector* detector, -// CvMemStorage* storage, -// float overlap_threshold = 0.5f, +// CvLatentSvmDetector* detector, +// CvMemStorage* storage, +// float overlap_threshold = 0.5f, // int numThreads = -1); // INPUT -// image - image to detect objects in -// detector - Latent SVM detector in internal representation -// storage - memory storage to store the resultant sequence -// of the object candidate rectangles -// overlap_threshold - threshold for the non-maximum suppression algorithm +// image - image to detect objects in +// detector - Latent SVM detector in internal representation +// storage - memory storage to store the resultant sequence +// of the object candidate rectangles +// overlap_threshold - threshold for the non-maximum suppression algorithm = 0.5f [here will be the reference to original paper] // OUTPUT // sequence of detected objects (bounding boxes and confidence levels stored in CvObjectDetection structures) @@ -327,6 +327,23 @@ private: vector classNames; }; +// class for grouping object candidates, detected by Cascade Classifier, HOG etc. +// instance of the class is to be passed to cv::partition (see cxoperations.hpp) +class CV_EXPORTS SimilarRects +{ +public: + SimilarRects(double _eps) : eps(_eps) {} + inline bool operator()(const Rect& r1, const Rect& r2) const + { + double delta = eps*(std::min(r1.width, r2.width) + std::min(r1.height, r2.height))*0.5; + return std::abs(r1.x - r2.x) <= delta && + std::abs(r1.y - r2.y) <= delta && + std::abs(r1.x + r1.width - r2.x - r2.width) <= delta && + std::abs(r1.y + r1.height - r2.y - r2.height) <= delta; + } + double eps; +}; + CV_EXPORTS void groupRectangles(CV_OUT CV_IN_OUT vector& rectList, int groupThreshold, double eps=0.2); CV_EXPORTS_W void groupRectangles(CV_OUT CV_IN_OUT vector& rectList, CV_OUT vector& weights, int groupThreshold, double eps=0.2); CV_EXPORTS void groupRectangles( vector& rectList, int groupThreshold, double eps, vector* weights, vector* levelWeights ); @@ -611,6 +628,7 @@ public: // read/parse Dalal's alt model file void readALTModel(std::string modelfile); + void groupRectangles(vector& rectList, vector& weights, int groupThreshold, double eps) const; }; diff --git a/modules/objdetect/src/cascadedetect.cpp b/modules/objdetect/src/cascadedetect.cpp index 46a232ed6..de46a6899 100644 --- a/modules/objdetect/src/cascadedetect.cpp +++ b/modules/objdetect/src/cascadedetect.cpp @@ -114,24 +114,6 @@ struct Logger namespace cv { -// class for grouping object candidates, detected by Cascade Classifier, HOG etc. -// instance of the class is to be passed to cv::partition (see cxoperations.hpp) -class CV_EXPORTS SimilarRects -{ -public: - SimilarRects(double _eps) : eps(_eps) {} - inline bool operator()(const Rect& r1, const Rect& r2) const - { - double delta = eps*(std::min(r1.width, r2.width) + std::min(r1.height, r2.height))*0.5; - return std::abs(r1.x - r2.x) <= delta && - std::abs(r1.y - r2.y) <= delta && - std::abs(r1.x + r1.width - r2.x - r2.width) <= delta && - std::abs(r1.y + r1.height - r2.y - r2.height) <= delta; - } - double eps; -}; - - void groupRectangles(vector& rectList, int groupThreshold, double eps, vector* weights, vector* levelWeights) { if( groupThreshold <= 0 || rectList.empty() ) diff --git a/modules/objdetect/src/hog.cpp b/modules/objdetect/src/hog.cpp index 0ed9bbca4..9e5fd1ba5 100644 --- a/modules/objdetect/src/hog.cpp +++ b/modules/objdetect/src/hog.cpp @@ -1060,9 +1060,7 @@ void HOGDescriptor::detectMultiScale( } else { - vector dummy; - dummy.resize(foundLocations.size(), INT_MAX); - groupRectangles(foundLocations, (int)finalThreshold, 0.2, &dummy, &foundWeights); + groupRectangles(foundLocations, foundWeights, (int)finalThreshold, 0.2); } } @@ -2636,4 +2634,82 @@ void HOGDescriptor::readALTModel(std::string modelfile) fclose(modelfl); } +void HOGDescriptor::groupRectangles(vector& rectList, vector& weights, int groupThreshold, double eps) const +{ + if( groupThreshold <= 0 || rectList.empty() ) + { + return; + } + + CV_Assert(rectList.size() == weights.size()); + + vector labels; + int nclasses = partition(rectList, labels, SimilarRects(eps)); + + vector> rrects(nclasses); + vector numInClass(nclasses, 0); + vector foundWeights(nclasses, DBL_MIN); + vector totalFactorsPerClass(nclasses, 1); + int i, j, nlabels = (int)labels.size(); + + for( i = 0; i < nlabels; i++ ) + { + int cls = labels[i]; + rrects[cls].x += rectList[i].x; + rrects[cls].y += rectList[i].y; + rrects[cls].width += rectList[i].width; + rrects[cls].height += rectList[i].height; + foundWeights[cls] = max(foundWeights[cls], weights[i]); + numInClass[cls]++; + } + + for( i = 0; i < nclasses; i++ ) + { + // find the average of all ROI in the cluster + cv::Rect_ r = rrects[i]; + float s = 1.f/numInClass[i]; + rrects[i] = cv::Rect_(cv::saturate_cast(r.x*s), + cv::saturate_cast(r.y*s), + cv::saturate_cast(r.width*s), + cv::saturate_cast(r.height*s)); + } + + rectList.clear(); + weights.clear(); + + for( i = 0; i < nclasses; i++ ) + { + cv::Rect r1 = rrects[i]; + int n1 = numInClass[i]; + double w1 = foundWeights[i]; + if( n1 <= groupThreshold ) + continue; + // filter out small rectangles inside large rectangles + for( j = 0; j < nclasses; j++ ) + { + int n2 = numInClass[j]; + + if( j == i || n2 <= groupThreshold ) + continue; + + cv::Rect r2 = rrects[j]; + + int dx = cv::saturate_cast( r2.width * eps ); + int dy = cv::saturate_cast( r2.height * eps ); + + if( r1.x >= r2.x - dx && + r1.y >= r2.y - dy && + r1.x + r1.width <= r2.x + r2.width + dx && + r1.y + r1.height <= r2.y + r2.height + dy && + (n2 > std::max(3, n1) || n1 < 3) ) + break; + } + + if( j == nclasses ) + { + rectList.push_back(r1); + weights.push_back(w1); + } + } +} }