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,6 +806,7 @@ CvDTreeNode* CascadeBoostTree::predict( int sampleIdx ) const | ||
| 806 | return node; | 806 | return node; |
| 807 | } | 807 | } |
| 808 | 808 | ||
| 809 | +<<<<<<< HEAD | ||
| 809 | 810 | ||
| 810 | static void writeRecursive(FileStorage &fs, CvDTreeNode *node, int maxCatCount) | 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,6 +881,8 @@ void CascadeBoostTree::read(const FileNode &fn, CvBoost* _ensemble, CvDTreeTrain | ||
| 880 | } | 881 | } |
| 881 | */ | 882 | */ |
| 882 | 883 | ||
| 884 | +======= | ||
| 885 | +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e | ||
| 883 | void CascadeBoostTree::split_node_data( CvDTreeNode* node ) | 886 | void CascadeBoostTree::split_node_data( CvDTreeNode* node ) |
| 884 | { | 887 | { |
| 885 | int n = node->sample_count, nl, nr, scount = data->sample_count; | 888 | int n = node->sample_count, nl, nr, scount = data->sample_count; |
| @@ -1135,7 +1138,11 @@ bool CascadeBoost::train( const FeatureEvaluator* _featureEvaluator, | @@ -1135,7 +1138,11 @@ bool CascadeBoost::train( const FeatureEvaluator* _featureEvaluator, | ||
| 1135 | break; | 1138 | break; |
| 1136 | } | 1139 | } |
| 1137 | 1140 | ||
| 1141 | +<<<<<<< HEAD | ||
| 1138 | classifiers.append(tree); | 1142 | classifiers.append(tree); |
| 1143 | +======= | ||
| 1144 | + trees.append(tree); | ||
| 1145 | +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e | ||
| 1139 | cvSeqPush( weak, &tree ); | 1146 | cvSeqPush( weak, &tree ); |
| 1140 | update_weights( tree ); | 1147 | update_weights( tree ); |
| 1141 | trim_weights(); | 1148 | trim_weights(); |
| @@ -1463,4 +1470,7 @@ bool CascadeBoost::isErrDesired() | @@ -1463,4 +1470,7 @@ bool CascadeBoost::isErrDesired() | ||
| 1463 | 1470 | ||
| 1464 | return falseAlarm <= maxFalseAlarm; | 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,7 +111,6 @@ class CascadeBoostTree : public CvBoostTree | ||
| 111 | { | 111 | { |
| 112 | public: | 112 | public: |
| 113 | virtual CvDTreeNode* predict(int sampleIdx) const; | 113 | virtual CvDTreeNode* predict(int sampleIdx) const; |
| 114 | - void write(cv::FileStorage &fs); | ||
| 115 | 114 | ||
| 116 | protected: | 115 | protected: |
| 117 | virtual void split_node_data(CvDTreeNode* n); | 116 | virtual void split_node_data(CvDTreeNode* n); |
| @@ -126,14 +125,22 @@ public: | @@ -126,14 +125,22 @@ public: | ||
| 126 | virtual float predict( int sampleIdx, bool returnSum = false ) const; | 125 | virtual float predict( int sampleIdx, bool returnSum = false ) const; |
| 127 | 126 | ||
| 128 | float getThreshold() const { return threshold; } | 127 | float getThreshold() const { return threshold; } |
| 128 | +<<<<<<< HEAD | ||
| 129 | QList<CvBoostTree*> getClassifiers() const { return classifiers; } | 129 | QList<CvBoostTree*> getClassifiers() const { return classifiers; } |
| 130 | +======= | ||
| 131 | + const QList<CvBoostTree*> getTrees() const { return trees; } | ||
| 132 | +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e | ||
| 130 | 133 | ||
| 131 | protected: | 134 | protected: |
| 132 | virtual bool set_params(const CvBoostParams& _params); | 135 | virtual bool set_params(const CvBoostParams& _params); |
| 133 | virtual void update_weights(CvBoostTree* tree); | 136 | virtual void update_weights(CvBoostTree* tree); |
| 134 | virtual bool isErrDesired(); | 137 | virtual bool isErrDesired(); |
| 135 | 138 | ||
| 139 | +<<<<<<< HEAD | ||
| 136 | QList<CvBoostTree*> classifiers; | 140 | QList<CvBoostTree*> classifiers; |
| 141 | +======= | ||
| 142 | + QList<CvBoostTree*> trees; | ||
| 143 | +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e | ||
| 137 | 144 | ||
| 138 | float threshold; | 145 | float threshold; |
| 139 | float minHitRate, maxFalseAlarm; | 146 | float minHitRate, maxFalseAlarm; |
openbr/core/cascade.cpp
| @@ -124,6 +124,7 @@ void br::groupRectangles(vector<Rect>& rectList, vector<int>& rejectLevels, vect | @@ -124,6 +124,7 @@ void br::groupRectangles(vector<Rect>& rectList, vector<int>& rejectLevels, vect | ||
| 124 | static void loadRecursive(const FileNode &fn, _CascadeClassifier::Node *node, int maxCatCount) | 124 | static void loadRecursive(const FileNode &fn, _CascadeClassifier::Node *node, int maxCatCount) |
| 125 | { | 125 | { |
| 126 | bool hasChildren = (int)fn["hasChildren"]; | 126 | bool hasChildren = (int)fn["hasChildren"]; |
| 127 | + | ||
| 127 | if (hasChildren) { | 128 | if (hasChildren) { |
| 128 | if (maxCatCount > 1) { | 129 | if (maxCatCount > 1) { |
| 129 | FileNode subset_fn = fn["subset"]; | 130 | FileNode subset_fn = fn["subset"]; |
| @@ -158,6 +159,7 @@ bool _CascadeClassifier::load(const string& filename) | @@ -158,6 +159,7 @@ bool _CascadeClassifier::load(const string& filename) | ||
| 158 | 159 | ||
| 159 | // load stages | 160 | // load stages |
| 160 | FileNode stages_fn = root["stages"]; | 161 | FileNode stages_fn = root["stages"]; |
| 162 | + | ||
| 161 | if( stages_fn.empty() ) | 163 | if( stages_fn.empty() ) |
| 162 | return false; | 164 | return false; |
| 163 | 165 | ||
| @@ -168,6 +170,7 @@ bool _CascadeClassifier::load(const string& filename) | @@ -168,6 +170,7 @@ bool _CascadeClassifier::load(const string& filename) | ||
| 168 | stage.threshold = (float)stage_fn["stageThreshold"] - THRESHOLD_EPS; | 170 | stage.threshold = (float)stage_fn["stageThreshold"] - THRESHOLD_EPS; |
| 169 | 171 | ||
| 170 | FileNode nodes_fn = stage_fn["weakClassifiers"]; | 172 | FileNode nodes_fn = stage_fn["weakClassifiers"]; |
| 173 | + | ||
| 171 | if(nodes_fn.empty()) | 174 | if(nodes_fn.empty()) |
| 172 | return false; | 175 | return false; |
| 173 | 176 |
openbr/plugins/classification/boostedforest.cpp
| @@ -10,16 +10,24 @@ struct Node | @@ -10,16 +10,24 @@ struct Node | ||
| 10 | { | 10 | { |
| 11 | Node() : left(NULL), right(NULL) {} | 11 | Node() : left(NULL), right(NULL) {} |
| 12 | 12 | ||
| 13 | +<<<<<<< HEAD | ||
| 13 | float value; | 14 | float value; |
| 14 | 15 | ||
| 15 | float threshold; // For ordered features | 16 | float threshold; // For ordered features |
| 16 | QList<int> subset; // For categorical features | 17 | QList<int> subset; // For categorical features |
| 17 | int featureIdx; | 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 | Node *left; | 26 | Node *left; |
| 20 | Node *right; | 27 | Node *right; |
| 21 | }; | 28 | }; |
| 22 | 29 | ||
| 30 | +<<<<<<< HEAD | ||
| 23 | static void buildTreeRecursive(Node *node, const CvDTreeNode *cv_node, int maxCatCount) | 31 | static void buildTreeRecursive(Node *node, const CvDTreeNode *cv_node, int maxCatCount) |
| 24 | { | 32 | { |
| 25 | if (!cv_node->left) // Write the leaf value | 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,6 +45,26 @@ static void buildTreeRecursive(Node *node, const CvDTreeNode *cv_node, int maxCa | ||
| 37 | buildTreeRecursive(node->left, cv_node->left, maxCatCount); | 45 | buildTreeRecursive(node->left, cv_node->left, maxCatCount); |
| 38 | node->right = new Node; | 46 | node->right = new Node; |
| 39 | buildTreeRecursive(node->right, cv_node->right, maxCatCount); | 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,10 +74,17 @@ static void writeRecursive(FileStorage &fs, const Node *node, int maxCatCount) | ||
| 46 | fs << "hasChildren" << hasChildren; | 74 | fs << "hasChildren" << hasChildren; |
| 47 | 75 | ||
| 48 | if (!hasChildren) // Write the leaf value | 76 | if (!hasChildren) // Write the leaf value |
| 77 | +<<<<<<< HEAD | ||
| 49 | fs << "value" << node->value; // value of the node. Only relevant for leaf nodes | 78 | fs << "value" << node->value; // value of the node. Only relevant for leaf nodes |
| 50 | else { // Write the splitting information and then the children | 79 | else { // Write the splitting information and then the children |
| 51 | if (maxCatCount > 1) { | 80 | if (maxCatCount > 1) { |
| 52 | fs << "subset" << "[:"; | 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 | for (int i = 0; i < ((maxCatCount + 31) / 32); i++) | 88 | for (int i = 0; i < ((maxCatCount + 31) / 32); i++) |
| 54 | fs << node->subset[i]; // subset to split on (categorical features) | 89 | fs << node->subset[i]; // subset to split on (categorical features) |
| 55 | fs << "]"; | 90 | fs << "]"; |
| @@ -64,6 +99,32 @@ static void writeRecursive(FileStorage &fs, const Node *node, int maxCatCount) | @@ -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 | class BoostedForestClassifier : public Classifier | 128 | class BoostedForestClassifier : public Classifier |
| 68 | { | 129 | { |
| 69 | Q_OBJECT | 130 | Q_OBJECT |
| @@ -97,10 +158,18 @@ class BoostedForestClassifier : public Classifier | @@ -97,10 +158,18 @@ class BoostedForestClassifier : public Classifier | ||
| 97 | 158 | ||
| 98 | CascadeBoost boost; | 159 | CascadeBoost boost; |
| 99 | boost.train(&featureEvaluator, images.size(), 2048, 2048, params); | 160 | boost.train(&featureEvaluator, images.size(), 2048, 2048, params); |
| 161 | +<<<<<<< HEAD | ||
| 100 | 162 | ||
| 101 | threshold = boost.getThreshold(); | 163 | threshold = boost.getThreshold(); |
| 102 | 164 | ||
| 103 | foreach (const CvBoostTree *tree, boost.getClassifiers()) { | 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 | Node *root = new Node; | 173 | Node *root = new Node; |
| 105 | buildTreeRecursive(root, tree->get_root(), representation->maxCatCount()); | 174 | buildTreeRecursive(root, tree->get_root(), representation->maxCatCount()); |
| 106 | weakClassifiers.append(root); | 175 | weakClassifiers.append(root); |
| @@ -109,6 +178,7 @@ class BoostedForestClassifier : public Classifier | @@ -109,6 +178,7 @@ class BoostedForestClassifier : public Classifier | ||
| 109 | 178 | ||
| 110 | float classify(const Mat &image) const | 179 | float classify(const Mat &image) const |
| 111 | { | 180 | { |
| 181 | +<<<<<<< HEAD | ||
| 112 | float sum = 0; | 182 | float sum = 0; |
| 113 | foreach (const Node *root, weakClassifiers) { | 183 | foreach (const Node *root, weakClassifiers) { |
| 114 | const Node *node = root; | 184 | const Node *node = root; |
| @@ -119,12 +189,33 @@ class BoostedForestClassifier : public Classifier | @@ -119,12 +189,33 @@ class BoostedForestClassifier : public Classifier | ||
| 119 | node = (node->subset[c >> 5] & (1 << (c & 31))) ? node->left : node->right; | 189 | node = (node->subset[c >> 5] & (1 << (c & 31))) ? node->left : node->right; |
| 120 | } else { | 190 | } else { |
| 121 | float val = representation->evaluate(image, node->featureIdx); | 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 | node = val < node->threshold ? node->left : node->right; | 206 | node = val < node->threshold ? node->left : node->right; |
| 123 | } | 207 | } |
| 124 | } | 208 | } |
| 125 | sum += node->value; | 209 | sum += node->value; |
| 126 | } | 210 | } |
| 211 | +<<<<<<< HEAD | ||
| 127 | return sum < threshold - FLT_EPSILON ? 0.0 : 1.0; | 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 | int numFeatures() const | 221 | int numFeatures() const |
| @@ -144,7 +235,11 @@ class BoostedForestClassifier : public Classifier | @@ -144,7 +235,11 @@ class BoostedForestClassifier : public Classifier | ||
| 144 | 235 | ||
| 145 | void write(FileStorage &fs) const | 236 | void write(FileStorage &fs) const |
| 146 | { | 237 | { |
| 238 | +<<<<<<< HEAD | ||
| 147 | fs << "weakCount" << weakClassifiers.size(); | 239 | fs << "weakCount" << weakClassifiers.size(); |
| 240 | +======= | ||
| 241 | + fs << "numWeak" << weakClassifiers.size(); | ||
| 242 | +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e | ||
| 148 | fs << "stageThreshold" << threshold; | 243 | fs << "stageThreshold" << threshold; |
| 149 | fs << "weakClassifiers" << "["; | 244 | fs << "weakClassifiers" << "["; |
| 150 | foreach (const Node *root, weakClassifiers) { | 245 | foreach (const Node *root, weakClassifiers) { |
| @@ -153,6 +248,25 @@ class BoostedForestClassifier : public Classifier | @@ -153,6 +248,25 @@ class BoostedForestClassifier : public Classifier | ||
| 153 | fs << "}"; | 248 | fs << "}"; |
| 154 | } | 249 | } |
| 155 | fs << "]"; | 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,12 +112,14 @@ class CascadeClassifier : public Classifier | ||
| 112 | Q_PROPERTY(int numPos READ get_numPos WRITE set_numPos RESET reset_numPos STORED false) | 112 | Q_PROPERTY(int numPos READ get_numPos WRITE set_numPos RESET reset_numPos STORED false) |
| 113 | Q_PROPERTY(int numNegs READ get_numNegs WRITE set_numNegs RESET reset_numNegs STORED false) | 113 | Q_PROPERTY(int numNegs READ get_numNegs WRITE set_numNegs RESET reset_numNegs STORED false) |
| 114 | Q_PROPERTY(float maxFAR READ get_maxFAR WRITE set_maxFAR RESET reset_maxFAR STORED false) | 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 | BR_PROPERTY(QString, stageDescription, "") | 117 | BR_PROPERTY(QString, stageDescription, "") |
| 117 | BR_PROPERTY(int, numStages, 20) | 118 | BR_PROPERTY(int, numStages, 20) |
| 118 | BR_PROPERTY(int, numPos, 1000) | 119 | BR_PROPERTY(int, numPos, 1000) |
| 119 | BR_PROPERTY(int, numNegs, 1000) | 120 | BR_PROPERTY(int, numNegs, 1000) |
| 120 | BR_PROPERTY(float, maxFAR, pow(0.5, numStages)) | 121 | BR_PROPERTY(float, maxFAR, pow(0.5, numStages)) |
| 122 | + BR_PROPERTY(bool, ROCMode, false) | ||
| 121 | 123 | ||
| 122 | QList<Classifier *> stages; | 124 | QList<Classifier *> stages; |
| 123 | 125 | ||
| @@ -156,9 +158,21 @@ class CascadeClassifier : public Classifier | @@ -156,9 +158,21 @@ class CascadeClassifier : public Classifier | ||
| 156 | float classify(const Mat &image) const | 158 | float classify(const Mat &image) const |
| 157 | { | 159 | { |
| 158 | foreach (const Classifier *stage, stages) | 160 | foreach (const Classifier *stage, stages) |
| 159 | - if (stage->classify(image) == 0.0f) | 161 | + if (stage->classify(image) == 0) |
| 160 | return 0.0f; | 162 | return 0.0f; |
| 161 | return 1.0f; | 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 | int numFeatures() const | 178 | int numFeatures() const |
| @@ -178,9 +192,13 @@ class CascadeClassifier : public Classifier | @@ -178,9 +192,13 @@ class CascadeClassifier : public Classifier | ||
| 178 | 192 | ||
| 179 | void write(FileStorage &fs) const | 193 | void write(FileStorage &fs) const |
| 180 | { | 194 | { |
| 195 | +<<<<<<< HEAD | ||
| 181 | fs << "stageCount" << stages.size(); | 196 | fs << "stageCount" << stages.size(); |
| 182 | 197 | ||
| 183 | fs << "stages" << "["; | 198 | fs << "stages" << "["; |
| 199 | +======= | ||
| 200 | + fs << CC_STAGES << "["; | ||
| 201 | +>>>>>>> 4fab7f69ddc82d6ba40a73fc6233e3cc9871473e | ||
| 184 | foreach (const Classifier *stage, stages) { | 202 | foreach (const Classifier *stage, stages) { |
| 185 | fs << "{"; | 203 | fs << "{"; |
| 186 | stage->write(fs); | 204 | stage->write(fs); |
| @@ -199,7 +217,7 @@ private: | @@ -199,7 +217,7 @@ private: | ||
| 199 | if (!imgHandler.getPos(pos)) | 217 | if (!imgHandler.getPos(pos)) |
| 200 | qFatal("Cannot get another positive sample!"); | 218 | qFatal("Cannot get another positive sample!"); |
| 201 | 219 | ||
| 202 | - if (classify(pos) == 1.0f) { | 220 | + if (classify(pos) > 0.0f) { |
| 203 | printf("POS current samples: %d\r", images.size()); | 221 | printf("POS current samples: %d\r", images.size()); |
| 204 | images.append(pos); | 222 | images.append(pos); |
| 205 | labels.append(1.0f); | 223 | labels.append(1.0f); |
| @@ -215,7 +233,7 @@ private: | @@ -215,7 +233,7 @@ private: | ||
| 215 | if (!imgHandler.getNeg(neg)) | 233 | if (!imgHandler.getNeg(neg)) |
| 216 | qFatal("Cannot get another negative sample!"); | 234 | qFatal("Cannot get another negative sample!"); |
| 217 | 235 | ||
| 218 | - if (classify(neg) == 1.0f) { | 236 | + if (classify(neg) > 0.0f) { |
| 219 | printf("NEG current samples: %d\r", images.size() - posCount); | 237 | printf("NEG current samples: %d\r", images.size() - posCount); |
| 220 | images.append(neg); | 238 | images.append(neg); |
| 221 | labels.append(0.0f); | 239 | labels.append(0.0f); |
openbr/plugins/imgproc/slidingwindow.cpp
| @@ -21,6 +21,8 @@ | @@ -21,6 +21,8 @@ | ||
| 21 | #include <openbr/core/qtutils.h> | 21 | #include <openbr/core/qtutils.h> |
| 22 | 22 | ||
| 23 | #include <opencv2/highgui/highgui.hpp> | 23 | #include <opencv2/highgui/highgui.hpp> |
| 24 | +#include <opencv2/imgproc/imgproc.hpp> | ||
| 25 | +#include <opencv2/objdetect/objdetect.hpp> | ||
| 24 | 26 | ||
| 25 | using namespace cv; | 27 | using namespace cv; |
| 26 | 28 | ||
| @@ -39,11 +41,23 @@ class SlidingWindowTransform : public Transform | @@ -39,11 +41,23 @@ class SlidingWindowTransform : public Transform | ||
| 39 | Q_OBJECT | 41 | Q_OBJECT |
| 40 | 42 | ||
| 41 | Q_PROPERTY(br::Classifier *classifier READ get_classifier WRITE set_classifier RESET reset_classifier STORED false) | 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 | Q_PROPERTY(QString cascadeDir READ get_cascadeDir WRITE set_cascadeDir RESET reset_cascadeDir STORED false) | 50 | Q_PROPERTY(QString cascadeDir READ get_cascadeDir WRITE set_cascadeDir RESET reset_cascadeDir STORED false) |
| 43 | Q_PROPERTY(QString vecFile READ get_vecFile WRITE set_vecFile RESET reset_vecFile STORED false) | 51 | Q_PROPERTY(QString vecFile READ get_vecFile WRITE set_vecFile RESET reset_vecFile STORED false) |
| 44 | Q_PROPERTY(QString negFile READ get_negFile WRITE set_negFile RESET reset_negFile STORED false) | 52 | Q_PROPERTY(QString negFile READ get_negFile WRITE set_negFile RESET reset_negFile STORED false) |
| 45 | 53 | ||
| 46 | BR_PROPERTY(br::Classifier *, classifier, NULL) | 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 | BR_PROPERTY(QString, cascadeDir, "") | 61 | BR_PROPERTY(QString, cascadeDir, "") |
| 48 | BR_PROPERTY(QString, vecFile, "vec.vec") | 62 | BR_PROPERTY(QString, vecFile, "vec.vec") |
| 49 | BR_PROPERTY(QString, negFile, "neg.txt") | 63 | BR_PROPERTY(QString, negFile, "neg.txt") |
| @@ -162,7 +176,91 @@ class SlidingWindowTransform : public Transform | @@ -162,7 +176,91 @@ class SlidingWindowTransform : public Transform | ||
| 162 | 176 | ||
| 163 | void project(const Template &src, Template &dst) const | 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 |