diff --git a/openbr/core/cascade.cpp b/openbr/core/cascade.cpp index 22ecf8c..84be00d 100644 --- a/openbr/core/cascade.cpp +++ b/openbr/core/cascade.cpp @@ -121,81 +121,118 @@ void br::groupRectangles(vector& rectList, vector& rejectLevels, vect // --------------------------------- Cascade Classifier ---------------------------------- -bool _CascadeClassifier::load(const string& filename) +static void loadRecursive(const FileNode &fn, _CascadeClassifier::Node *node, int maxCatCount) { - data = Data(); + bool hasChildren = (int)fn["hasChildren"]; + if (hasChildren) { + if (maxCatCount > 1) { + FileNode subset_fn = fn["subset"]; + for (FileNodeIterator subset_it = subset_fn.begin(); subset_it != subset_fn.end(); ++subset_it) + node->subset.append((int)*subset_it); + } else { + node->threshold = (float)fn["threshold"]; + } + + node->featureIdx = (int)fn["feature_idx"]; + node->left = new _CascadeClassifier::Node; + loadRecursive(fn["left"], node->left, maxCatCount); + node->right = new _CascadeClassifier::Node; + loadRecursive(fn["right"], node->right, maxCatCount); + } else { + node->value = (float)fn["value"]; + } +} + +bool _CascadeClassifier::load(const string& filename) +{ FileStorage fs(filename, FileStorage::READ); if (!fs.isOpened()) return false; - return data.read(fs.getFirstTopLevelNode()); -} + FileNode root = fs.getFirstTopLevelNode(); -int _CascadeClassifier::predict(const Mat &image, double &sum) const -{ - int nstages = (int)data.stages.size(); - int nodeOfs = 0, leafOfs = 0; + const float THRESHOLD_EPS = 1e-5; + + int maxCatCount = representation->maxCatCount(); - size_t subsetSize = (data.ncategories + 31)/32; - const int *cascadeSubsets = &data.subsets[0]; + // load stages + FileNode stages_fn = root["stages"]; + if( stages_fn.empty() ) + return false; - const float *cascadeLeaves = &data.leaves[0]; - const Data::DTreeNode *cascadeNodes = &data.nodes[0]; - const Data::DTree *cascadeWeaks = &data.classifiers[0]; - const Data::Stage *cascadeStages = &data.stages[0]; + for (FileNodeIterator stage_it = stages_fn.begin(); stage_it != stages_fn.end(); ++stage_it) { + FileNode stage_fn = *stage_it; + + Stage stage; + stage.threshold = (float)stage_fn["stageThreshold"] - THRESHOLD_EPS; + + FileNode nodes_fn = stage_fn["weakClassifiers"]; + if(nodes_fn.empty()) + return false; - for (int stageIdx = 0; stageIdx < nstages; stageIdx++) { - const Data::Stage &stage = cascadeStages[stageIdx]; + for (FileNodeIterator node_it = nodes_fn.begin(); node_it != nodes_fn.end(); ++node_it) { + FileNode node_fn = *node_it; + + Node *root = new Node; + loadRecursive(node_fn, root, maxCatCount); + + stage.trees.append(root); + } + + stages.append(stage); + } + + return true; +} + +int _CascadeClassifier::predict(const Mat &image, double &sum) const +{ + for (int stageIdx = 0; stageIdx < stages.size(); stageIdx++) { + Stage stage = stages[stageIdx]; sum = 0; - for (int wi = 0; wi < stage.ntrees; wi++) { - const Data::DTree &weak = cascadeWeaks[stage.first + wi]; - int idx = 0, root = nodeOfs; + for (int treeIdx = 0; treeIdx < stage.trees.size(); treeIdx++) { + Node *node = stage.trees[treeIdx]; - do { - const Data::DTreeNode &node = cascadeNodes[root + idx]; - if (data.ncategories > 0) { - int c = (int)representation->evaluate(image, node.featureIdx); - const int* subset = &cascadeSubsets[(root + idx)*subsetSize]; - idx = (subset[c>>5] & (1 << (c & 31))) ? node.left : node.right; + while (node->left) { + if (representation->maxCatCount() > 1) { + int c = (int)representation->evaluate(image, node->featureIdx); + node = (node->subset[c >> 5] & (1 << (c & 31))) ? node->left : node->right; } else { - double val = representation->evaluate(image, node.featureIdx); - idx = val < node.threshold ? node.left : node.right; + double val = representation->evaluate(image, node->featureIdx); + node = val < node->threshold ? node->left : node->right; } - } while( idx > 0 ); - - sum += cascadeLeaves[leafOfs - idx]; - nodeOfs += weak.nodeCount; - leafOfs += weak.nodeCount + 1; + } + sum += node->value; } - if( sum < stage.threshold ) - return -stageIdx; + + if (sum < stage.threshold) + return stageIdx; } - return 1; + + return stages.size(); } -void _CascadeClassifier::detectMultiScale( const Mat& image, vector& objects, - vector& rejectLevels, - vector& levelWeights, - double scaleFactor, int minNeighbors, - Size minObjectSize, Size maxObjectSize, - bool outputRejectLevels ) +void _CascadeClassifier::detectMultiScale(const Mat& image, vector& objects, vector& rejectLevels, + vector& levelWeights, + double scaleFactor, int minNeighbors, + Size minSize, Size maxSize) const { const double GROUP_EPS = 0.2; CV_Assert( scaleFactor > 1 && image.depth() == CV_8U ); - if (data.stages.empty()) + if (stages.empty()) return; - if( maxObjectSize.height == 0 || maxObjectSize.width == 0 ) - maxObjectSize = image.size(); + if( maxSize.height == 0 || maxSize.width == 0 ) + maxSize = image.size(); Mat imageBuffer(image.rows + 1, image.cols + 1, CV_8U); for (double factor = 1; ; factor *= scaleFactor) { - Size originalWindowSize = data.origWinSize; + Size originalWindowSize = representation->preWindowSize(); Size windowSize(cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor) ); Size scaledImageSize(cvRound(image.cols/factor ), cvRound(image.rows/factor)); @@ -203,9 +240,9 @@ void _CascadeClassifier::detectMultiScale( const Mat& image, vector& objec if (processingRectSize.width <= 0 || processingRectSize.height <= 0) break; - if (windowSize.width > maxObjectSize.width || windowSize.height > maxObjectSize.height) + if (windowSize.width > maxSize.width || windowSize.height > maxSize.height) break; - if (windowSize.width < minObjectSize.width || windowSize.height < minObjectSize.height) + if (windowSize.width < minSize.width || windowSize.height < minSize.height) continue; Mat scaledImage(scaledImageSize, CV_8U, imageBuffer.data); @@ -222,126 +259,17 @@ void _CascadeClassifier::detectMultiScale( const Mat& image, vector& objec double gypWeight; int result = predict(window, gypWeight); - if (outputRejectLevels) { - if (result == 1) - result = -(int)data.stages.size(); - if (data.stages.size() + result < 4) { - objects.push_back(Rect(cvRound(x*factor), cvRound(y*factor), windowSize.width, windowSize.height)); - rejectLevels.push_back(-result); - levelWeights.push_back(gypWeight); - } - } - else if (result > 0) { + if (stages.size() - result < 4) { objects.push_back(Rect(cvRound(x*factor), cvRound(y*factor), windowSize.width, windowSize.height)); + rejectLevels.push_back(result); + levelWeights.push_back(gypWeight); } + if (result == 0) x += yStep; } } } - if (outputRejectLevels) - groupRectangles(objects, rejectLevels, levelWeights, minNeighbors, GROUP_EPS); - else - groupRectangles(objects, minNeighbors, GROUP_EPS); -} - -void _CascadeClassifier::detectMultiScale(const Mat& image, vector& objects, - double scaleFactor, int minNeighbors, Size minObjectSize, Size maxObjectSize) -{ - vector fakeLevels; - vector fakeWeights; - detectMultiScale( image, objects, fakeLevels, fakeWeights, scaleFactor, - minNeighbors, minObjectSize, maxObjectSize, false ); -} - -bool _CascadeClassifier::Data::read(const FileNode &root) -{ - static const float THRESHOLD_EPS = 1e-5f; - - origWinSize.width = (int)root[CC_WIDTH]; - origWinSize.height = (int)root[CC_HEIGHT]; - CV_Assert( origWinSize.height > 0 && origWinSize.width > 0 ); - - isStumpBased = (int)(root[CC_STAGE_PARAMS][CC_MAX_DEPTH]) == 1 ? true : false; - - // load feature params - FileNode fn = root[CC_FEATURE_PARAMS]; - if( fn.empty() ) - return false; - - ncategories = fn[CC_MAX_CAT_COUNT]; - int subsetSize = (ncategories + 31)/32, - nodeStep = 3 + ( ncategories>0 ? subsetSize : 1 ); - - // load stages - fn = root[CC_STAGES]; - if( fn.empty() ) - return false; - - stages.reserve(fn.size()); - classifiers.clear(); - nodes.clear(); - - FileNodeIterator it = fn.begin(), it_end = fn.end(); - - for( int si = 0; it != it_end; si++, ++it ) - { - FileNode fns = *it; - Stage stage; - stage.threshold = (float)fns[CC_STAGE_THRESHOLD] - THRESHOLD_EPS; - fns = fns[CC_WEAK_CLASSIFIERS]; - if(fns.empty()) - return false; - stage.ntrees = (int)fns.size(); - stage.first = (int)classifiers.size(); - stages.push_back(stage); - classifiers.reserve(stages[si].first + stages[si].ntrees); - - FileNodeIterator it1 = fns.begin(), it1_end = fns.end(); - for( ; it1 != it1_end; ++it1 ) // weak trees - { - FileNode fnw = *it1; - FileNode internalNodes = fnw[CC_INTERNAL_NODES]; - FileNode leafValues = fnw[CC_LEAF_VALUES]; - if( internalNodes.empty() || leafValues.empty() ) - return false; - - DTree tree; - tree.nodeCount = (int)internalNodes.size()/nodeStep; - classifiers.push_back(tree); - - nodes.reserve(nodes.size() + tree.nodeCount); - leaves.reserve(leaves.size() + leafValues.size()); - if( subsetSize > 0 ) - subsets.reserve(subsets.size() + tree.nodeCount*subsetSize); - - FileNodeIterator internalNodesIter = internalNodes.begin(), internalNodesEnd = internalNodes.end(); - - for( ; internalNodesIter != internalNodesEnd; ) // nodes - { - DTreeNode node; - node.left = (int)*internalNodesIter; ++internalNodesIter; - node.right = (int)*internalNodesIter; ++internalNodesIter; - node.featureIdx = (int)*internalNodesIter; ++internalNodesIter; - if( subsetSize > 0 ) - { - for( int j = 0; j < subsetSize; j++, ++internalNodesIter ) - subsets.push_back((int)*internalNodesIter); - node.threshold = 0.f; - } - else - { - node.threshold = (float)*internalNodesIter; ++internalNodesIter; - } - nodes.push_back(node); - } - - internalNodesIter = leafValues.begin(), internalNodesEnd = leafValues.end(); - - for( ; internalNodesIter != internalNodesEnd; ++internalNodesIter ) // leaves - leaves.push_back((float)*internalNodesIter); - } - } - return true; + groupRectangles(objects, rejectLevels, levelWeights, minNeighbors, GROUP_EPS); } diff --git a/openbr/core/cascade.h b/openbr/core/cascade.h index 7a2eb7e..4f774aa 100644 --- a/openbr/core/cascade.h +++ b/openbr/core/cascade.h @@ -4,31 +4,6 @@ #include #include -#define CC_CASCADE_PARAMS "cascadeParams" -#define CC_STAGE_TYPE "stageType" -#define CC_FEATURE_TYPE "featureType" -#define CC_HEIGHT "height" -#define CC_WIDTH "width" - -#define CC_STAGE_NUM "stageNum" -#define CC_STAGES "stages" -#define CC_STAGE_PARAMS "stageParams" - -#define CC_BOOST "BOOST" -#define CC_MAX_DEPTH "maxDepth" -#define CC_WEAK_COUNT "maxWeakCount" -#define CC_STAGE_THRESHOLD "stageThreshold" -#define CC_WEAK_CLASSIFIERS "weakClassifiers" -#define CC_INTERNAL_NODES "internalNodes" -#define CC_LEAF_VALUES "leafValues" - -#define CC_FEATURES "features" -#define CC_FEATURE_PARAMS "featureParams" -#define CC_MAX_CAT_COUNT "maxCatCount" - -#define CC_LBP "LBP" - - using namespace std; using namespace cv; @@ -65,65 +40,37 @@ public: ~_CascadeClassifier() {} bool load(const string& filename); - void detectMultiScale(const Mat& image, - vector& objects, - double scaleFactor=1.1, - int minNeighbors=3, - Size minSize=Size(), - Size maxSize=Size()); - void detectMultiScale( const Mat& image, + void detectMultiScale(const Mat& image, vector& objects, vector& rejectLevels, vector& levelWeights, double scaleFactor=1.1, int minNeighbors=3, Size minSize=Size(), - Size maxSize=Size(), - bool outputRejectLevels=false ); + Size maxSize=Size()) const; int predict(const Mat &image, double &weight) const; - class Data + struct Node { - public: - struct DTreeNode - { - int featureIdx; - float threshold; // for ordered features only - int left; - int right; - }; - - struct DTree - { - int nodeCount; - }; - - struct Stage - { - int first; - int ntrees; - float threshold; - }; - - bool read(const FileNode &node); - - bool isStumpBased; - - int stageType; - int featureType; - int ncategories; - Size origWinSize; + Node() : left(NULL), right(NULL) {} + + int featureIdx; + float threshold; // for ordered features only + QList subset; // for categorical features only + float value; // for leaf nodes only + Node *left; + Node *right; + }; - vector stages; - vector classifiers; - vector nodes; - vector leaves; - vector subsets; + struct Stage + { + QList trees; + float threshold; }; - Data data; + QList stages; Representation *representation; }; diff --git a/openbr/plugins/metadata/cascade.cpp b/openbr/plugins/metadata/cascade.cpp index 6ab8c8b..6bc19c6 100644 --- a/openbr/plugins/metadata/cascade.cpp +++ b/openbr/plugins/metadata/cascade.cpp @@ -75,7 +75,7 @@ class CascadeTransform : public UntrainableMetaTransform BR_PROPERTY(QString, model, "FrontalFace") BR_PROPERTY(int, minSize, 64) BR_PROPERTY(int, minNeighbors, 5) - BR_PROPERTY(bool, ROCMode, false) + BR_PROPERTY(bool, ROCMode, false) Resource<_CascadeClassifier> cascadeResource; @@ -112,8 +112,7 @@ class CascadeTransform : public UntrainableMetaTransform std::vector rects; std::vector rejectLevels; std::vector levelWeights; - if (ROCMode) cascade->detectMultiScale(m, rects, rejectLevels, levelWeights, 1.2, minNeighbors, Size(minSize, minSize), Size(), true); - else cascade->detectMultiScale(m, rects, 1.2, minNeighbors, Size(minSize, minSize)); + cascade->detectMultiScale(m, rects, rejectLevels, levelWeights, 1.2, minNeighbors, Size(minSize, minSize), Size()); if (!enrollAll && rects.empty()) rects.push_back(Rect(0, 0, m.cols, m.rows));