Commit 98249fb4280f22d67a0f589996e30ff572bf3ad8

Authored by Jordan Cheney
2 parents d14551dd 4fab7f69

Merge branch 'cascade' of https://github.com/biometrics/openbr into HEAD

openbr/core/boost.cpp
... ... @@ -806,6 +806,7 @@ CvDTreeNode* CascadeBoostTree::predict( int sampleIdx ) const
806 806 return node;
807 807 }
808 808  
  809 +<<<<<<< HEAD
809 810  
810 811 static void writeRecursive(FileStorage &fs, CvDTreeNode *node, int maxCatCount)
811 812 {
... ... @@ -880,6 +881,8 @@ void CascadeBoostTree::read(const FileNode &amp;fn, CvBoost* _ensemble, CvDTreeTrain
880 881 }
881 882 */
882 883  
  884 +=======
  885 +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e
883 886 void CascadeBoostTree::split_node_data( CvDTreeNode* node )
884 887 {
885 888 int n = node->sample_count, nl, nr, scount = data->sample_count;
... ... @@ -1135,7 +1138,11 @@ bool CascadeBoost::train( const FeatureEvaluator* _featureEvaluator,
1135 1138 break;
1136 1139 }
1137 1140  
  1141 +<<<<<<< HEAD
1138 1142 classifiers.append(tree);
  1143 +=======
  1144 + trees.append(tree);
  1145 +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e
1139 1146 cvSeqPush( weak, &tree );
1140 1147 update_weights( tree );
1141 1148 trim_weights();
... ... @@ -1463,4 +1470,7 @@ bool CascadeBoost::isErrDesired()
1463 1470  
1464 1471 return falseAlarm <= maxFalseAlarm;
1465 1472 }
  1473 +<<<<<<< HEAD
1466 1474  
  1475 +=======
  1476 +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e
... ...
openbr/core/boost.h
... ... @@ -111,7 +111,6 @@ class CascadeBoostTree : public CvBoostTree
111 111 {
112 112 public:
113 113 virtual CvDTreeNode* predict(int sampleIdx) const;
114   - void write(cv::FileStorage &fs);
115 114  
116 115 protected:
117 116 virtual void split_node_data(CvDTreeNode* n);
... ... @@ -126,14 +125,22 @@ public:
126 125 virtual float predict( int sampleIdx, bool returnSum = false ) const;
127 126  
128 127 float getThreshold() const { return threshold; }
  128 +<<<<<<< HEAD
129 129 QList<CvBoostTree*> getClassifiers() const { return classifiers; }
  130 +=======
  131 + const QList<CvBoostTree*> getTrees() const { return trees; }
  132 +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e
130 133  
131 134 protected:
132 135 virtual bool set_params(const CvBoostParams& _params);
133 136 virtual void update_weights(CvBoostTree* tree);
134 137 virtual bool isErrDesired();
135 138  
  139 +<<<<<<< HEAD
136 140 QList<CvBoostTree*> classifiers;
  141 +=======
  142 + QList<CvBoostTree*> trees;
  143 +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e
137 144  
138 145 float threshold;
139 146 float minHitRate, maxFalseAlarm;
... ...
openbr/core/cascade.cpp
... ... @@ -124,6 +124,7 @@ void br::groupRectangles(vector&lt;Rect&gt;&amp; rectList, vector&lt;int&gt;&amp; rejectLevels, vect
124 124 static void loadRecursive(const FileNode &fn, _CascadeClassifier::Node *node, int maxCatCount)
125 125 {
126 126 bool hasChildren = (int)fn["hasChildren"];
  127 +
127 128 if (hasChildren) {
128 129 if (maxCatCount > 1) {
129 130 FileNode subset_fn = fn["subset"];
... ... @@ -158,6 +159,7 @@ bool _CascadeClassifier::load(const string&amp; filename)
158 159  
159 160 // load stages
160 161 FileNode stages_fn = root["stages"];
  162 +
161 163 if( stages_fn.empty() )
162 164 return false;
163 165  
... ... @@ -168,6 +170,7 @@ bool _CascadeClassifier::load(const string&amp; filename)
168 170 stage.threshold = (float)stage_fn["stageThreshold"] - THRESHOLD_EPS;
169 171  
170 172 FileNode nodes_fn = stage_fn["weakClassifiers"];
  173 +
171 174 if(nodes_fn.empty())
172 175 return false;
173 176  
... ...
openbr/plugins/classification/boostedforest.cpp
... ... @@ -10,16 +10,24 @@ struct Node
10 10 {
11 11 Node() : left(NULL), right(NULL) {}
12 12  
  13 +<<<<<<< HEAD
13 14 float value;
14 15  
15 16 float threshold; // For ordered features
16 17 QList<int> subset; // For categorical features
17 18 int featureIdx;
18 19  
  20 +=======
  21 + int featureIdx;
  22 + float threshold; // for ordered features only
  23 + QList<int> subset; // for categorical features only
  24 + float value; // for leaf nodes only
  25 +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e
19 26 Node *left;
20 27 Node *right;
21 28 };
22 29  
  30 +<<<<<<< HEAD
23 31 static void buildTreeRecursive(Node *node, const CvDTreeNode *cv_node, int maxCatCount)
24 32 {
25 33 if (!cv_node->left) // Write the leaf value
... ... @@ -37,6 +45,26 @@ static void buildTreeRecursive(Node *node, const CvDTreeNode *cv_node, int maxCa
37 45 buildTreeRecursive(node->left, cv_node->left, maxCatCount);
38 46 node->right = new Node;
39 47 buildTreeRecursive(node->right, cv_node->right, maxCatCount);
  48 +=======
  49 +static void buildTreeRecursive(Node *node, const CvDTreeNode *tree_node, int maxCatCount)
  50 +{
  51 + if (tree_node->left) {
  52 + if (maxCatCount > 1) {
  53 + for (int i = 0; i < (maxCatCount + 31)/32; i++)
  54 + node->subset.append(tree_node->split->subset[i]);
  55 + } else {
  56 + node->threshold = tree_node->split->ord.c;
  57 + }
  58 +
  59 + node->featureIdx = tree_node->split->var_idx;
  60 +
  61 + node->left = new Node;
  62 + buildTreeRecursive(node->left, tree_node->left, maxCatCount);
  63 + node->right = new Node;
  64 + buildTreeRecursive(node->right, tree_node->right, maxCatCount);
  65 + } else {
  66 + node->value = tree_node->value;
  67 +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e
40 68 }
41 69 }
42 70  
... ... @@ -46,10 +74,17 @@ static void writeRecursive(FileStorage &amp;fs, const Node *node, int maxCatCount)
46 74 fs << "hasChildren" << hasChildren;
47 75  
48 76 if (!hasChildren) // Write the leaf value
  77 +<<<<<<< HEAD
49 78 fs << "value" << node->value; // value of the node. Only relevant for leaf nodes
50 79 else { // Write the splitting information and then the children
51 80 if (maxCatCount > 1) {
52 81 fs << "subset" << "[:";
  82 +=======
  83 + fs << "value" << node->value; // value of the node.
  84 + else { // Write the splitting information and then the children
  85 + if (maxCatCount > 1) {
  86 + fs << "subset" << "[";
  87 +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e
53 88 for (int i = 0; i < ((maxCatCount + 31) / 32); i++)
54 89 fs << node->subset[i]; // subset to split on (categorical features)
55 90 fs << "]";
... ... @@ -64,6 +99,32 @@ static void writeRecursive(FileStorage &amp;fs, const Node *node, int maxCatCount)
64 99 }
65 100 }
66 101  
  102 +<<<<<<< HEAD
  103 +=======
  104 +static void readRecursive(const FileNode &fn, Node *node, int maxCatCount)
  105 +{
  106 + bool hasChildren = (int)fn["hasChildren"];
  107 + if (!hasChildren) {
  108 + node->value = (float)fn["value"];
  109 + } else {
  110 + if (maxCatCount > 1) {
  111 + FileNode subset_fn = fn["subset"];
  112 + for (FileNodeIterator subset_it = subset_fn.begin(); subset_it != subset_fn.end(); ++subset_it)
  113 + node->subset.append((int)*subset_it);
  114 + } else {
  115 + node->threshold = (float)fn["threshold"];
  116 + }
  117 +
  118 + node->featureIdx = (int)fn["feature_idx"];
  119 +
  120 + node->left = new Node;
  121 + readRecursive(fn["left"], node->left, maxCatCount);
  122 + node->right = new Node;
  123 + readRecursive(fn["right"], node->right, maxCatCount);
  124 + }
  125 +}
  126 +
  127 +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e
67 128 class BoostedForestClassifier : public Classifier
68 129 {
69 130 Q_OBJECT
... ... @@ -97,10 +158,18 @@ class BoostedForestClassifier : public Classifier
97 158  
98 159 CascadeBoost boost;
99 160 boost.train(&featureEvaluator, images.size(), 2048, 2048, params);
  161 +<<<<<<< HEAD
100 162  
101 163 threshold = boost.getThreshold();
102 164  
103 165 foreach (const CvBoostTree *tree, boost.getClassifiers()) {
  166 +=======
  167 +
  168 + // Convert into simpler, cleaner cascade after training
  169 + threshold = boost.getThreshold();
  170 +
  171 + foreach (const CvBoostTree *tree, boost.getTrees()) {
  172 +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e
104 173 Node *root = new Node;
105 174 buildTreeRecursive(root, tree->get_root(), representation->maxCatCount());
106 175 weakClassifiers.append(root);
... ... @@ -109,6 +178,7 @@ class BoostedForestClassifier : public Classifier
109 178  
110 179 float classify(const Mat &image) const
111 180 {
  181 +<<<<<<< HEAD
112 182 float sum = 0;
113 183 foreach (const Node *root, weakClassifiers) {
114 184 const Node *node = root;
... ... @@ -119,12 +189,33 @@ class BoostedForestClassifier : public Classifier
119 189 node = (node->subset[c >> 5] & (1 << (c & 31))) ? node->left : node->right;
120 190 } else {
121 191 float val = representation->evaluate(image, node->featureIdx);
  192 +=======
  193 + Mat pp;
  194 + representation->preprocess(image, pp);
  195 +
  196 + float sum = 0;
  197 +
  198 + foreach (const Node *node, weakClassifiers) {
  199 + while (node->left) {
  200 + if (representation->maxCatCount() > 1) {
  201 + int c = (int)representation->evaluate(pp, node->featureIdx);
  202 + node = (node->subset[c >> 5] & (1 << (c & 31))) ? node->left : node->right;
  203 + } else {
  204 + double val = representation->evaluate(pp, node->featureIdx);
  205 +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e
122 206 node = val < node->threshold ? node->left : node->right;
123 207 }
124 208 }
125 209 sum += node->value;
126 210 }
  211 +<<<<<<< HEAD
127 212 return sum < threshold - FLT_EPSILON ? 0.0 : 1.0;
  213 +=======
  214 +
  215 + if (sum < threshold)
  216 + return 0.0f; //-std::abs(sum);
  217 + return 1.0f; //std::abs(sum);
  218 +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e
128 219 }
129 220  
130 221 int numFeatures() const
... ... @@ -144,7 +235,11 @@ class BoostedForestClassifier : public Classifier
144 235  
145 236 void write(FileStorage &fs) const
146 237 {
  238 +<<<<<<< HEAD
147 239 fs << "weakCount" << weakClassifiers.size();
  240 +=======
  241 + fs << "numWeak" << weakClassifiers.size();
  242 +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e
148 243 fs << "stageThreshold" << threshold;
149 244 fs << "weakClassifiers" << "[";
150 245 foreach (const Node *root, weakClassifiers) {
... ... @@ -153,6 +248,25 @@ class BoostedForestClassifier : public Classifier
153 248 fs << "}";
154 249 }
155 250 fs << "]";
  251 +<<<<<<< HEAD
  252 +=======
  253 + }
  254 +
  255 + void read(const FileNode &node)
  256 + {
  257 + weakClassifiers.reserve((int)node["numWeak"]);
  258 + threshold = (float)node["stageThreshold"];
  259 +
  260 + FileNode weaks_fn = node["weakClassifiers"];
  261 + for (FileNodeIterator weaks_it = weaks_fn.begin(); weaks_it != weaks_fn.end(); ++weaks_it) {
  262 + FileNode weak_fn = *weaks_it;
  263 +
  264 + Node *root = new Node;
  265 + readRecursive(weak_fn, root, representation->maxCatCount());
  266 +
  267 + weakClassifiers.append(root);
  268 + }
  269 +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e
156 270 }
157 271 };
158 272  
... ...
openbr/plugins/classification/cascade.cpp
... ... @@ -112,12 +112,14 @@ class CascadeClassifier : public Classifier
112 112 Q_PROPERTY(int numPos READ get_numPos WRITE set_numPos RESET reset_numPos STORED false)
113 113 Q_PROPERTY(int numNegs READ get_numNegs WRITE set_numNegs RESET reset_numNegs STORED false)
114 114 Q_PROPERTY(float maxFAR READ get_maxFAR WRITE set_maxFAR RESET reset_maxFAR STORED false)
  115 + Q_PROPERTY(bool ROCMode READ get_ROCMode WRITE set_ROCMode RESET reset_ROCMode STORED false)
115 116  
116 117 BR_PROPERTY(QString, stageDescription, "")
117 118 BR_PROPERTY(int, numStages, 20)
118 119 BR_PROPERTY(int, numPos, 1000)
119 120 BR_PROPERTY(int, numNegs, 1000)
120 121 BR_PROPERTY(float, maxFAR, pow(0.5, numStages))
  122 + BR_PROPERTY(bool, ROCMode, false)
121 123  
122 124 QList<Classifier *> stages;
123 125  
... ... @@ -156,9 +158,21 @@ class CascadeClassifier : public Classifier
156 158 float classify(const Mat &image) const
157 159 {
158 160 foreach (const Classifier *stage, stages)
159   - if (stage->classify(image) == 0.0f)
  161 + if (stage->classify(image) == 0)
160 162 return 0.0f;
161 163 return 1.0f;
  164 +
  165 + /*if (stages.size() == 0) // special case for empty cascade
  166 + return 1.0f;
  167 +
  168 + float result = 0.0f;
  169 + for (int stageIdx = 0; stageIdx < stages.size(); stageIdx++) {
  170 + result = stages[stageIdx]->classify(image);
  171 +
  172 + if (result < 0)
  173 + return stageIdx > (stages.size() - 4) ? stageIdx * result : 0.0f;
  174 + }
  175 + return std::abs(stages.size() * result);*/
162 176 }
163 177  
164 178 int numFeatures() const
... ... @@ -178,9 +192,13 @@ class CascadeClassifier : public Classifier
178 192  
179 193 void write(FileStorage &fs) const
180 194 {
  195 +<<<<<<< HEAD
181 196 fs << "stageCount" << stages.size();
182 197  
183 198 fs << "stages" << "[";
  199 +=======
  200 + fs << CC_STAGES << "[";
  201 +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e
184 202 foreach (const Classifier *stage, stages) {
185 203 fs << "{";
186 204 stage->write(fs);
... ... @@ -199,7 +217,7 @@ private:
199 217 if (!imgHandler.getPos(pos))
200 218 qFatal("Cannot get another positive sample!");
201 219  
202   - if (classify(pos) == 1.0f) {
  220 + if (classify(pos) > 0.0f) {
203 221 printf("POS current samples: %d\r", images.size());
204 222 images.append(pos);
205 223 labels.append(1.0f);
... ... @@ -215,7 +233,7 @@ private:
215 233 if (!imgHandler.getNeg(neg))
216 234 qFatal("Cannot get another negative sample!");
217 235  
218   - if (classify(neg) == 1.0f) {
  236 + if (classify(neg) > 0.0f) {
219 237 printf("NEG current samples: %d\r", images.size() - posCount);
220 238 images.append(neg);
221 239 labels.append(0.0f);
... ...
openbr/plugins/imgproc/slidingwindow.cpp
... ... @@ -21,6 +21,8 @@
21 21 #include <openbr/core/qtutils.h>
22 22  
23 23 #include <opencv2/highgui/highgui.hpp>
  24 +#include <opencv2/imgproc/imgproc.hpp>
  25 +#include <opencv2/objdetect/objdetect.hpp>
24 26  
25 27 using namespace cv;
26 28  
... ... @@ -39,11 +41,23 @@ class SlidingWindowTransform : public Transform
39 41 Q_OBJECT
40 42  
41 43 Q_PROPERTY(br::Classifier *classifier READ get_classifier WRITE set_classifier RESET reset_classifier STORED false)
  44 + Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false)
  45 + Q_PROPERTY(int maxSize READ get_maxSize WRITE set_maxSize RESET reset_maxSize STORED false)
  46 + Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false)
  47 + Q_PROPERTY(int minNeighbors READ get_minNeighbors WRITE set_minNeighbors RESET reset_minNeighbors STORED false)
  48 + Q_PROPERTY(float eps READ get_eps WRITE set_eps RESET reset_eps STORED false)
  49 +
42 50 Q_PROPERTY(QString cascadeDir READ get_cascadeDir WRITE set_cascadeDir RESET reset_cascadeDir STORED false)
43 51 Q_PROPERTY(QString vecFile READ get_vecFile WRITE set_vecFile RESET reset_vecFile STORED false)
44 52 Q_PROPERTY(QString negFile READ get_negFile WRITE set_negFile RESET reset_negFile STORED false)
45 53  
46 54 BR_PROPERTY(br::Classifier *, classifier, NULL)
  55 + BR_PROPERTY(int, minSize, 24)
  56 + BR_PROPERTY(int, maxSize, -1)
  57 + BR_PROPERTY(float, scaleFactor, 1.2)
  58 + BR_PROPERTY(int, minNeighbors, 5)
  59 + BR_PROPERTY(float, eps, 0.2)
  60 +
47 61 BR_PROPERTY(QString, cascadeDir, "")
48 62 BR_PROPERTY(QString, vecFile, "vec.vec")
49 63 BR_PROPERTY(QString, negFile, "neg.txt")
... ... @@ -162,7 +176,91 @@ class SlidingWindowTransform : public Transform
162 176  
163 177 void project(const Template &src, Template &dst) const
164 178 {
165   - (void)src; (void)dst;
  179 + TemplateList temp;
  180 + project(TemplateList() << src, temp);
  181 + if (!temp.isEmpty()) dst = temp.first();
  182 + }
  183 +
  184 + void project(const TemplateList &src, TemplateList &dst) const
  185 + {
  186 + foreach (const Template &t, src) {
  187 + const bool enrollAll = t.file.getBool("enrollAll");
  188 +
  189 + // Mirror the behavior of ExpandTransform in the special case
  190 + // of an empty template.
  191 + if (t.empty() && !enrollAll) {
  192 + dst.append(t);
  193 + continue;
  194 + }
  195 +
  196 + for (int i = 0; i < t.size(); i++) {
  197 + Mat image;
  198 + OpenCVUtils::cvtUChar(t[i], image);
  199 +
  200 + std::vector<Rect> rects;
  201 + std::vector<int> rejectLevels;
  202 + std::vector<double> levelWeights;
  203 +
  204 + Size minObjectSize(minSize, minSize);
  205 + Size maxObjectSize(maxSize, maxSize);
  206 + if (maxObjectSize.height == 0 || maxObjectSize.width == 0)
  207 + maxObjectSize = image.size();
  208 +
  209 + Mat imageBuffer(image.rows + 1, image.cols + 1, CV_8U);
  210 +
  211 + for (double factor = 1; ; factor *= scaleFactor) {
  212 + Size originalWindowSize = classifier->windowSize();
  213 +
  214 + Size windowSize(cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor) );
  215 + Size scaledImageSize(cvRound(image.cols/factor ), cvRound(image.rows/factor));
  216 + Size processingRectSize(scaledImageSize.width - originalWindowSize.width, scaledImageSize.height - originalWindowSize.height);
  217 +
  218 + if (processingRectSize.width <= 0 || processingRectSize.height <= 0)
  219 + break;
  220 + if (windowSize.width > maxObjectSize.width || windowSize.height > maxObjectSize.height)
  221 + break;
  222 + if (windowSize.width < minObjectSize.width || windowSize.height < minObjectSize.height)
  223 + continue;
  224 +
  225 + Mat scaledImage(scaledImageSize, CV_8U, imageBuffer.data);
  226 + resize(image, scaledImage, scaledImageSize, 0, 0, CV_INTER_LINEAR);
  227 +
  228 + int yStep = factor > 2. ? 1 : 2;
  229 + for (int y = 0; y < processingRectSize.height; y += yStep) {
  230 + for (int x = 0; x < processingRectSize.width; x += yStep) {
  231 + Mat window = scaledImage(Rect(Point(x, y), classifier->windowSize())).clone();
  232 +
  233 + float result = classifier->classify(window);
  234 +
  235 + if (result > 0) {
  236 + rects.push_back(Rect(cvRound(x*factor), cvRound(y*factor), windowSize.width, windowSize.height));
  237 + rejectLevels.push_back(1);
  238 + levelWeights.push_back(result);
  239 + }
  240 + if (result == 0)
  241 + x += yStep;
  242 + }
  243 + }
  244 + }
  245 +
  246 + groupRectangles(rects, rejectLevels, levelWeights, minNeighbors, eps);
  247 +
  248 + if (!enrollAll && rects.empty())
  249 + rects.push_back(Rect(0, 0, image.cols, image.rows));
  250 +
  251 + for (size_t j = 0; j < rects.size(); j++) {
  252 + Template u(t.file, image);
  253 + if (rejectLevels.size() > j)
  254 + u.file.set("Confidence", rejectLevels[j]*levelWeights[j]);
  255 + else
  256 + u.file.set("Confidence", 1);
  257 + const QRectF rect = OpenCVUtils::fromRect(rects[j]);
  258 + u.file.appendRect(rect);
  259 + u.file.set("Face", rect);
  260 + dst.append(u);
  261 + }
  262 + }
  263 + }
166 264 }
167 265 };
168 266  
... ...