diff --git a/openbr/core/opencvutils.cpp b/openbr/core/opencvutils.cpp index ccee66a..3928aeb 100644 --- a/openbr/core/opencvutils.cpp +++ b/openbr/core/opencvutils.cpp @@ -382,6 +382,131 @@ bool OpenCVUtils::overlaps(const QList &posRects, const Rect &negRect, dou return false; } +// 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 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; +}; + +// TODO: Make sure case where no confidences are inputted works. +void OpenCVUtils::group(vector &rects, vector &confidences, float epsilon) +{ + if (rects.empty()) + return; + + const bool useConfidences = !confidences.empty(); + + vector labels; + int nClasses = cv::partition(rects, labels, SimilarRects(epsilon)); + + // Rect for each class (class meaning identity assigned by partition) + vector rrects(nClasses); + + // Total number of rects in each class + vector rweights(nClasses, 0); + vector rejectWeights(nClasses, -std::numeric_limits::max()); + + for (int i = 0; i < labels.size(); i++) + { + int cls = labels[i]; + rrects[cls].x += rects[i].x; + rrects[cls].y += rects[i].y; + rrects[cls].width += rects[i].width; + rrects[cls].height += rects[i].height; + rweights[cls]++; + } + + if (useConfidences) + { + // For each class, find maximum confidence + for (int i = 0; i < labels.size(); i++) + { + int cls = labels[i]; + if (confidences[i] > rejectWeights[cls]) + rejectWeights[cls] = confidences[i]; + } + } + + // Find average rectangle for all classes + for (int i = 0; i < nClasses; i++) + { + Rect r = rrects[i]; + float s = 1.f/rweights[i]; + rrects[i] = Rect(saturate_cast(r.x*s), + saturate_cast(r.y*s), + saturate_cast(r.width*s), + saturate_cast(r.height*s)); + } + + rects.clear(); + confidences.clear(); + + const double threshold = 2; + + // Aggregate by comparing average rectangles against other average rectangels + for (int i = 0; i < nClasses; i++) + { + // Average rectangle + Rect r1 = rrects[i]; + + // Used to eliminate rectangles with few neighbors in the case of no weights + // int n1 = levelWeights ? rejectLevels[i] : rweights[i]; + double w1 = rejectWeights[i]; + + // Eliminate rectangle if it doesn't meet confidence criteria + if (w1 <= threshold) + continue; + + // filter out small face rectangles inside large rectangles + int j; + for (j = 0; j < nClasses; j++) + { + double w2 = rejectWeights[j]; + + if (j == i) //|| n2 <= groupThreshold ) + continue; + + Rect r2 = rrects[j]; + + int dx = saturate_cast(r2.width * epsilon); + int dy = saturate_cast(r2.height * epsilon); + + // If, r1 is within the r2 AND + // the second rectangle reaches a later stage than the first + // where both the first and the second must have a stage greater than three OR + // the first doens't reach the third stage. + // Changeto: second rectangle has a higher confidence than the first OR + // the first has a low confidence. + // Then, eliminate the first rectangle. + 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 && + (w2 > std::max(threshold, w1))) + break; + } + + // Need to return rects and confidences + if( j == nClasses ) + { + rects.push_back(r1); + if (useConfidences) + confidences.push_back(w1); + } + } +} + QDataStream &operator<<(QDataStream &stream, const Mat &m) { // Write header diff --git a/openbr/core/opencvutils.h b/openbr/core/opencvutils.h index 516fb1b..6d8d762 100644 --- a/openbr/core/opencvutils.h +++ b/openbr/core/opencvutils.h @@ -98,6 +98,9 @@ namespace OpenCVUtils float overlap(const cv::Rect &rect1, const cv::Rect &rect2); float overlap(const QRectF &rect1, const QRectF &rect2); + // Misc + void group(std::vector &rects, std::vector &confidences, float epsilon); + int getFourcc(); } diff --git a/openbr/plugins/classification/cascade.cpp b/openbr/plugins/classification/cascade.cpp index 48ab310..b87f568 100644 --- a/openbr/plugins/classification/cascade.cpp +++ b/openbr/plugins/classification/cascade.cpp @@ -157,11 +157,10 @@ class CascadeClassifier : public Classifier float stageConf = 0.0f; foreach (const Classifier *stage, stages) { float result = stage->classify(image, process, &stageConf); - if (result == 0.0f) { - if (confidence) - *confidence += stageConf; + if (confidence) + *confidence += stageConf; + if (result == 0.0f) return 0.0f; - } } return 1.0f; } diff --git a/openbr/plugins/imgproc/slidingwindow.cpp b/openbr/plugins/imgproc/slidingwindow.cpp index 1009a51..78599f3 100644 --- a/openbr/plugins/imgproc/slidingwindow.cpp +++ b/openbr/plugins/imgproc/slidingwindow.cpp @@ -116,7 +116,7 @@ class SlidingWindowTransform : public MetaTransform for (int x = 0; x < processingRectSize.width; x += step) { Mat window = repImage(Rect(Point(x, y), Size(originalWindowSize.width + dx, originalWindowSize.height + dy))).clone(); - float confidence; + float confidence = 0; int result = classifier->classify(window, false, &confidence); if (result == 1) { @@ -124,7 +124,7 @@ class SlidingWindowTransform : public MetaTransform confidences.push_back(confidence); } - // Add ROC mode + // TODO: Add ROC mode if (result == 0) x += step; @@ -132,18 +132,14 @@ class SlidingWindowTransform : public MetaTransform } } - // groupRectangles(rects, confidences, eps); - groupRectangles(rects, rejectLevels, levelWeights, minNeighbors, eps); + OpenCVUtils::group(rects, confidences, eps); if (!enrollAll && rects.empty()) rects.push_back(Rect(0, 0, m.cols, m.rows)); for (size_t j=0; j j) - u.file.set("Confidence", rejectLevels[j]*levelWeights[j]); - else - u.file.set("Confidence", 1); + u.file.set("Confidence", confidences[j]); const QRectF rect = OpenCVUtils::fromRect(rects[j]); u.file.appendRect(rect); u.file.set(model, rect); @@ -157,7 +153,7 @@ class SlidingWindowTransform : public MetaTransform { (void)stream; - QString filename = Globals->sdkPath + "/share/openbr/models/openbrcascades/" + model + "/cascade.xml"; + QString filename = model + "/cascade.xml"; FileStorage fs(filename.toStdString(), FileStorage::READ); if (!fs.isOpened()) return; @@ -169,7 +165,7 @@ class SlidingWindowTransform : public MetaTransform { (void) stream; - QString path = Globals->sdkPath + "/share/openbr/models/openbrcascades/" + model; + QString path = model; QtUtils::touchDir(QDir(path)); QString filename = path + "/cascade.xml";