diff --git a/openbr/core/eval.cpp b/openbr/core/eval.cpp index c7dcf66..cfe7666 100755 --- a/openbr/core/eval.cpp +++ b/openbr/core/eval.cpp @@ -842,7 +842,10 @@ static QStringList computeDetectionResults(const QList &detec } } - if (discrete) qDebug("Total TP vs. FP: %f to %f", TP, FP); + if (discrete) { + qDebug("Total TP vs. FP: %f to %f", TP, FP); + qDebug("Overall Recall (TP vs. possible TP): %f (%f vs. %d)", TP / totalTrueDetections, TP, totalTrueDetections); + } const int keep = qMin(points.size(), Max_Points); if (keep < 1) qFatal("Insufficient points."); diff --git a/openbr/plugins/classification/cascade.cpp b/openbr/plugins/classification/cascade.cpp index 20787b3..55aa32b 100644 --- a/openbr/plugins/classification/cascade.cpp +++ b/openbr/plugins/classification/cascade.cpp @@ -92,6 +92,8 @@ struct Miner * \br_property int numPos The number of positives to feed each stage during training * \br_property int numNegs The number of negatives to feed each stage during training. A negative sample must have been classified by the previous stages in the cascade as positive to be fed to the next stage during training. * \br_property float maxFAR A termination parameter. Calculated as (number of passed negatives) / (total number of checked negatives) for a given stage during training. If that number is below the given maxFAR cascade training is terminated early. This can help prevent overfitting. + * \br_property bool requireAllStages If true, the cascade will train until it has the number of stages specified by numStages even if the FAR at a given is lower than maxFAR. + * \br_property int maxStage Parameter to limit the stages used at test time. If -1 (default), all numStages stages will be used. * \br_paper Paul Viola, Michael Jones * Rapid Object Detection using a Boosted Cascade of Simple Features * CVPR, 2001 @@ -107,6 +109,7 @@ class CascadeClassifier : public Classifier Q_PROPERTY(int numNegs READ get_numNegs WRITE set_numNegs RESET reset_numNegs STORED false) Q_PROPERTY(float maxFAR READ get_maxFAR WRITE set_maxFAR RESET reset_maxFAR STORED false) Q_PROPERTY(bool requireAllStages READ get_requireAllStages WRITE set_requireAllStages RESET reset_requireAllStages STORED false) + Q_PROPERTY(int maxStage READ get_maxStage WRITE set_maxStage RESET reset_maxStage STORED false) BR_PROPERTY(QString, stageDescription, "") BR_PROPERTY(int, numStages, 20) @@ -114,6 +117,7 @@ class CascadeClassifier : public Classifier BR_PROPERTY(int, numNegs, 1000) BR_PROPERTY(float, maxFAR, pow(0.5, numStages)) BR_PROPERTY(bool, requireAllStages, false) + BR_PROPERTY(int, maxStage, -1) QList stages; TemplateList posImages, negImages; @@ -231,7 +235,11 @@ class CascadeClassifier : public Classifier float classify(const Template &src, bool process, float *confidence) const { float stageConf = 0.0f; - foreach (const Classifier *stage, stages) { + const int stopStage = maxStage == -1 ? numStages : maxStage; + int stageIndex = 0; + foreach (const Classifier *stage, stages) { + if (stageIndex == stopStage) + break; float result = stage->classify(src, process, &stageConf); if (confidence) *confidence += stageConf; diff --git a/openbr/plugins/imgproc/slidingwindow.cpp b/openbr/plugins/imgproc/slidingwindow.cpp index b0b7dd2..1e6c3f9 100644 --- a/openbr/plugins/imgproc/slidingwindow.cpp +++ b/openbr/plugins/imgproc/slidingwindow.cpp @@ -19,6 +19,7 @@ #include #include +#include using namespace cv; @@ -50,6 +51,7 @@ class SlidingWindowTransform : public MetaTransform Q_PROPERTY(float eps READ get_eps WRITE set_eps RESET reset_eps STORED false) Q_PROPERTY(float minNeighbors READ get_minNeighbors WRITE set_minNeighbors RESET reset_minNeighbors STORED false) Q_PROPERTY(bool group READ get_group WRITE set_group RESET reset_group STORED false) + Q_PROPERTY(int shrinkingFactor READ get_shrinkingFactor WRITE set_shrinkingFactor RESET reset_shrinkingFactor STORED false) BR_PROPERTY(br::Classifier*, classifier, NULL) BR_PROPERTY(int, minSize, 20) BR_PROPERTY(int, maxSize, -1) @@ -58,6 +60,7 @@ class SlidingWindowTransform : public MetaTransform BR_PROPERTY(float, eps, 0.2) BR_PROPERTY(int, minNeighbors, 3) BR_PROPERTY(bool, group, true) + BR_PROPERTY(int, shrinkingFactor, 1) void train(const TemplateList &data) { @@ -100,42 +103,47 @@ class SlidingWindowTransform : public MetaTransform QList confidences; int dx, dy; - const Size originalWindowSize = classifier->windowSize(&dx, &dy); + const Size classifierSize = classifier->windowSize(&dx, &dy); for (double factor = 1; ; factor *= scaleFactor) { - const Size windowSize(cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor)); - const Size scaledImageSize(cvRound(imageSize.width/factor), cvRound(imageSize.height/factor)); - const Size processingRectSize(scaledImageSize.width - originalWindowSize.width, scaledImageSize.height - originalWindowSize.height); + // TODO: This should support non-square sizes + // Compute the size of the window in which we will detect faces + const Size detectionSize(cvRound(minSize*factor),cvRound(minSize*factor)); - if (processingRectSize.width <= 0 || processingRectSize.height <= 0) - break; - if (windowSize.width < minSize || windowSize.height < minSize) - continue; + // Stop if detection size is bigger than the image itself + if (detectionSize.width > imageSize.width || detectionSize.height > imageSize.height) + break; + + const float widthScale = (float)classifierSize.width/detectionSize.width; + const float heightScale = (float)classifierSize.height/detectionSize.height; + + // Scale the image such that the detection size within the image corresponds to the respresentation size + const Size scaledImageSize(cvRound(imageSize.width*widthScale), cvRound(imageSize.height*heightScale)); Template rep(t.file); foreach (const Mat &m, t) { Mat scaledImage; - resize(m, scaledImage, scaledImageSize, 0, 0, CV_INTER_LINEAR); + resize(m, scaledImage, scaledImageSize, 0, 0, CV_INTER_AREA); rep.append(scaledImage); } rep = classifier->preprocess(rep); - + // Pre-allocate the window to avoid constructing this every iteration Template window(t.file); for (int i=0; i 2.0 ? 1 : 2; - for (int y = 0; y < processingRectSize.height; y += step) { - for (int x = 0; x < processingRectSize.width; x += step) { + const int step = factor > 2.0 ? shrinkingFactor : shrinkingFactor*2; + for (int y = 0; y < scaledImageSize.height-classifierSize.height; y += step) { + for (int x = 0; x < scaledImageSize.width-classifierSize.width; x += step) { for (int i=0; iclassify(window, false, &confidence); if (result == 1) { - rects.append(Rect(cvRound(x*factor), cvRound(y*factor), windowSize.width, windowSize.height)); + rects.append(Rect(cvRound(x/widthScale), cvRound(y/heightScale), detectionSize.width, detectionSize.height)); confidences.append(confidence); }