Commit fdda9f5c7bcbc72568f0438b913a2c5c011f2724
Merge branch 'temp'
Showing
3 changed files
with
36 additions
and
17 deletions
openbr/core/eval.cpp
| @@ -842,7 +842,10 @@ static QStringList computeDetectionResults(const QList<ResolvedDetection> &detec | @@ -842,7 +842,10 @@ static QStringList computeDetectionResults(const QList<ResolvedDetection> &detec | ||
| 842 | } | 842 | } |
| 843 | } | 843 | } |
| 844 | 844 | ||
| 845 | - if (discrete) qDebug("Total TP vs. FP: %f to %f", TP, FP); | 845 | + if (discrete) { |
| 846 | + qDebug("Total TP vs. FP: %f to %f", TP, FP); | ||
| 847 | + qDebug("Overall Recall (TP vs. possible TP): %f (%f vs. %d)", TP / totalTrueDetections, TP, totalTrueDetections); | ||
| 848 | + } | ||
| 846 | 849 | ||
| 847 | const int keep = qMin(points.size(), Max_Points); | 850 | const int keep = qMin(points.size(), Max_Points); |
| 848 | if (keep < 1) qFatal("Insufficient points."); | 851 | if (keep < 1) qFatal("Insufficient points."); |
openbr/plugins/classification/cascade.cpp
| @@ -92,6 +92,8 @@ struct Miner | @@ -92,6 +92,8 @@ struct Miner | ||
| 92 | * \br_property int numPos The number of positives to feed each stage during training | 92 | * \br_property int numPos The number of positives to feed each stage during training |
| 93 | * \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. | 93 | * \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. |
| 94 | * \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. | 94 | * \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. |
| 95 | + * \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. | ||
| 96 | + * \br_property int maxStage Parameter to limit the stages used at test time. If -1 (default), all numStages stages will be used. | ||
| 95 | * \br_paper Paul Viola, Michael Jones | 97 | * \br_paper Paul Viola, Michael Jones |
| 96 | * Rapid Object Detection using a Boosted Cascade of Simple Features | 98 | * Rapid Object Detection using a Boosted Cascade of Simple Features |
| 97 | * CVPR, 2001 | 99 | * CVPR, 2001 |
| @@ -107,6 +109,7 @@ class CascadeClassifier : public Classifier | @@ -107,6 +109,7 @@ class CascadeClassifier : public Classifier | ||
| 107 | Q_PROPERTY(int numNegs READ get_numNegs WRITE set_numNegs RESET reset_numNegs STORED false) | 109 | Q_PROPERTY(int numNegs READ get_numNegs WRITE set_numNegs RESET reset_numNegs STORED false) |
| 108 | Q_PROPERTY(float maxFAR READ get_maxFAR WRITE set_maxFAR RESET reset_maxFAR STORED false) | 110 | Q_PROPERTY(float maxFAR READ get_maxFAR WRITE set_maxFAR RESET reset_maxFAR STORED false) |
| 109 | Q_PROPERTY(bool requireAllStages READ get_requireAllStages WRITE set_requireAllStages RESET reset_requireAllStages STORED false) | 111 | Q_PROPERTY(bool requireAllStages READ get_requireAllStages WRITE set_requireAllStages RESET reset_requireAllStages STORED false) |
| 112 | + Q_PROPERTY(int maxStage READ get_maxStage WRITE set_maxStage RESET reset_maxStage STORED false) | ||
| 110 | 113 | ||
| 111 | BR_PROPERTY(QString, stageDescription, "") | 114 | BR_PROPERTY(QString, stageDescription, "") |
| 112 | BR_PROPERTY(int, numStages, 20) | 115 | BR_PROPERTY(int, numStages, 20) |
| @@ -114,6 +117,7 @@ class CascadeClassifier : public Classifier | @@ -114,6 +117,7 @@ class CascadeClassifier : public Classifier | ||
| 114 | BR_PROPERTY(int, numNegs, 1000) | 117 | BR_PROPERTY(int, numNegs, 1000) |
| 115 | BR_PROPERTY(float, maxFAR, pow(0.5, numStages)) | 118 | BR_PROPERTY(float, maxFAR, pow(0.5, numStages)) |
| 116 | BR_PROPERTY(bool, requireAllStages, false) | 119 | BR_PROPERTY(bool, requireAllStages, false) |
| 120 | + BR_PROPERTY(int, maxStage, -1) | ||
| 117 | 121 | ||
| 118 | QList<Classifier *> stages; | 122 | QList<Classifier *> stages; |
| 119 | TemplateList posImages, negImages; | 123 | TemplateList posImages, negImages; |
| @@ -231,7 +235,11 @@ class CascadeClassifier : public Classifier | @@ -231,7 +235,11 @@ class CascadeClassifier : public Classifier | ||
| 231 | float classify(const Template &src, bool process, float *confidence) const | 235 | float classify(const Template &src, bool process, float *confidence) const |
| 232 | { | 236 | { |
| 233 | float stageConf = 0.0f; | 237 | float stageConf = 0.0f; |
| 234 | - foreach (const Classifier *stage, stages) { | 238 | + const int stopStage = maxStage == -1 ? numStages : maxStage; |
| 239 | + int stageIndex = 0; | ||
| 240 | + foreach (const Classifier *stage, stages) { | ||
| 241 | + if (stageIndex == stopStage) | ||
| 242 | + break; | ||
| 235 | float result = stage->classify(src, process, &stageConf); | 243 | float result = stage->classify(src, process, &stageConf); |
| 236 | if (confidence) | 244 | if (confidence) |
| 237 | *confidence += stageConf; | 245 | *confidence += stageConf; |
openbr/plugins/imgproc/slidingwindow.cpp
| @@ -19,6 +19,7 @@ | @@ -19,6 +19,7 @@ | ||
| 19 | #include <openbr/core/qtutils.h> | 19 | #include <openbr/core/qtutils.h> |
| 20 | 20 | ||
| 21 | #include <opencv2/imgproc/imgproc.hpp> | 21 | #include <opencv2/imgproc/imgproc.hpp> |
| 22 | +#include <opencv2/highgui/highgui.hpp> | ||
| 22 | 23 | ||
| 23 | using namespace cv; | 24 | using namespace cv; |
| 24 | 25 | ||
| @@ -50,6 +51,7 @@ class SlidingWindowTransform : public MetaTransform | @@ -50,6 +51,7 @@ class SlidingWindowTransform : public MetaTransform | ||
| 50 | Q_PROPERTY(float eps READ get_eps WRITE set_eps RESET reset_eps STORED false) | 51 | Q_PROPERTY(float eps READ get_eps WRITE set_eps RESET reset_eps STORED false) |
| 51 | Q_PROPERTY(float minNeighbors READ get_minNeighbors WRITE set_minNeighbors RESET reset_minNeighbors STORED false) | 52 | Q_PROPERTY(float minNeighbors READ get_minNeighbors WRITE set_minNeighbors RESET reset_minNeighbors STORED false) |
| 52 | Q_PROPERTY(bool group READ get_group WRITE set_group RESET reset_group STORED false) | 53 | Q_PROPERTY(bool group READ get_group WRITE set_group RESET reset_group STORED false) |
| 54 | + Q_PROPERTY(int shrinkingFactor READ get_shrinkingFactor WRITE set_shrinkingFactor RESET reset_shrinkingFactor STORED false) | ||
| 53 | BR_PROPERTY(br::Classifier*, classifier, NULL) | 55 | BR_PROPERTY(br::Classifier*, classifier, NULL) |
| 54 | BR_PROPERTY(int, minSize, 20) | 56 | BR_PROPERTY(int, minSize, 20) |
| 55 | BR_PROPERTY(int, maxSize, -1) | 57 | BR_PROPERTY(int, maxSize, -1) |
| @@ -58,6 +60,7 @@ class SlidingWindowTransform : public MetaTransform | @@ -58,6 +60,7 @@ class SlidingWindowTransform : public MetaTransform | ||
| 58 | BR_PROPERTY(float, eps, 0.2) | 60 | BR_PROPERTY(float, eps, 0.2) |
| 59 | BR_PROPERTY(int, minNeighbors, 3) | 61 | BR_PROPERTY(int, minNeighbors, 3) |
| 60 | BR_PROPERTY(bool, group, true) | 62 | BR_PROPERTY(bool, group, true) |
| 63 | + BR_PROPERTY(int, shrinkingFactor, 1) | ||
| 61 | 64 | ||
| 62 | void train(const TemplateList &data) | 65 | void train(const TemplateList &data) |
| 63 | { | 66 | { |
| @@ -100,42 +103,47 @@ class SlidingWindowTransform : public MetaTransform | @@ -100,42 +103,47 @@ class SlidingWindowTransform : public MetaTransform | ||
| 100 | QList<float> confidences; | 103 | QList<float> confidences; |
| 101 | 104 | ||
| 102 | int dx, dy; | 105 | int dx, dy; |
| 103 | - const Size originalWindowSize = classifier->windowSize(&dx, &dy); | 106 | + const Size classifierSize = classifier->windowSize(&dx, &dy); |
| 104 | 107 | ||
| 105 | for (double factor = 1; ; factor *= scaleFactor) { | 108 | for (double factor = 1; ; factor *= scaleFactor) { |
| 106 | - const Size windowSize(cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor)); | ||
| 107 | - const Size scaledImageSize(cvRound(imageSize.width/factor), cvRound(imageSize.height/factor)); | ||
| 108 | - const Size processingRectSize(scaledImageSize.width - originalWindowSize.width, scaledImageSize.height - originalWindowSize.height); | 109 | + // TODO: This should support non-square sizes |
| 110 | + // Compute the size of the window in which we will detect faces | ||
| 111 | + const Size detectionSize(cvRound(minSize*factor),cvRound(minSize*factor)); | ||
| 109 | 112 | ||
| 110 | - if (processingRectSize.width <= 0 || processingRectSize.height <= 0) | ||
| 111 | - break; | ||
| 112 | - if (windowSize.width < minSize || windowSize.height < minSize) | ||
| 113 | - continue; | 113 | + // Stop if detection size is bigger than the image itself |
| 114 | + if (detectionSize.width > imageSize.width || detectionSize.height > imageSize.height) | ||
| 115 | + break; | ||
| 116 | + | ||
| 117 | + const float widthScale = (float)classifierSize.width/detectionSize.width; | ||
| 118 | + const float heightScale = (float)classifierSize.height/detectionSize.height; | ||
| 119 | + | ||
| 120 | + // Scale the image such that the detection size within the image corresponds to the respresentation size | ||
| 121 | + const Size scaledImageSize(cvRound(imageSize.width*widthScale), cvRound(imageSize.height*heightScale)); | ||
| 114 | 122 | ||
| 115 | Template rep(t.file); | 123 | Template rep(t.file); |
| 116 | foreach (const Mat &m, t) { | 124 | foreach (const Mat &m, t) { |
| 117 | Mat scaledImage; | 125 | Mat scaledImage; |
| 118 | - resize(m, scaledImage, scaledImageSize, 0, 0, CV_INTER_LINEAR); | 126 | + resize(m, scaledImage, scaledImageSize, 0, 0, CV_INTER_AREA); |
| 119 | rep.append(scaledImage); | 127 | rep.append(scaledImage); |
| 120 | } | 128 | } |
| 121 | rep = classifier->preprocess(rep); | 129 | rep = classifier->preprocess(rep); |
| 122 | - | 130 | + |
| 123 | // Pre-allocate the window to avoid constructing this every iteration | 131 | // Pre-allocate the window to avoid constructing this every iteration |
| 124 | Template window(t.file); | 132 | Template window(t.file); |
| 125 | for (int i=0; i<rep.size(); i++) | 133 | for (int i=0; i<rep.size(); i++) |
| 126 | window.append(Mat()); | 134 | window.append(Mat()); |
| 127 | 135 | ||
| 128 | - const int step = factor > 2.0 ? 1 : 2; | ||
| 129 | - for (int y = 0; y < processingRectSize.height; y += step) { | ||
| 130 | - for (int x = 0; x < processingRectSize.width; x += step) { | 136 | + const int step = factor > 2.0 ? shrinkingFactor : shrinkingFactor*2; |
| 137 | + for (int y = 0; y < scaledImageSize.height-classifierSize.height; y += step) { | ||
| 138 | + for (int x = 0; x < scaledImageSize.width-classifierSize.width; x += step) { | ||
| 131 | for (int i=0; i<rep.size(); i++) | 139 | for (int i=0; i<rep.size(); i++) |
| 132 | - window[i] = rep[i](Rect(Point(x, y), Size(originalWindowSize.width + dx, originalWindowSize.height + dy))).clone(); | 140 | + window[i] = rep[i](Rect(Point(x, y), Size(classifierSize.width+dx, classifierSize.height+dy))).clone(); |
| 133 | 141 | ||
| 134 | float confidence = 0; | 142 | float confidence = 0; |
| 135 | int result = classifier->classify(window, false, &confidence); | 143 | int result = classifier->classify(window, false, &confidence); |
| 136 | 144 | ||
| 137 | if (result == 1) { | 145 | if (result == 1) { |
| 138 | - rects.append(Rect(cvRound(x*factor), cvRound(y*factor), windowSize.width, windowSize.height)); | 146 | + rects.append(Rect(cvRound(x/widthScale), cvRound(y/heightScale), detectionSize.width, detectionSize.height)); |
| 139 | confidences.append(confidence); | 147 | confidences.append(confidence); |
| 140 | } | 148 | } |
| 141 | 149 |