Commit 98249fb4280f22d67a0f589996e30ff572bf3ad8
Merge branch 'cascade' of https://github.com/biometrics/openbr into HEAD
Showing
6 changed files
with
255 additions
and
5 deletions
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 &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<Rect>& rectList, vector<int>& 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& 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& 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 &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 &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 | ... | ... |