diff --git a/openbr/core/boost.cpp b/openbr/core/boost.cpp index a8eb6d5..31ce0d7 100644 --- a/openbr/core/boost.cpp +++ b/openbr/core/boost.cpp @@ -155,8 +155,8 @@ void FeatureEvaluator::setImage(const Mat &img, uchar clsLabel, int idx) int dx, dy; Size windowSize = representation->windowSize(&dx, &dy); - Mat integralImg(Size(windowSize.width + dx, windowSize.height + dy), data.type(), data.ptr(idx)); - representation->preprocess(img, integralImg); + Mat pp(Size(windowSize.width + dx, windowSize.height + dy), data.type(), data.ptr(idx)); + representation->preprocess(img, pp); } //----------------------------- CascadeBoostParams ------------------------------------------------- @@ -778,7 +778,7 @@ void CascadeBoostTrainData::precalculate() parallel_for_( Range(0, minNum), FeatureValAndIdxPrecalc(featureEvaluator, buf, &valCache, sample_count, is_buf_16u!=0) ); parallel_for_( Range(minNum, numPrecalcVal), - FeatureValOnlyPrecalc(featureEvaluator, &valCache, sample_count) ); + FeatureValOnlyPrecalc(featureEvaluator, &valCache, sample_count) ); cout << "Precalculation time: " << (proctime + TIME( 0 )) << endl; } @@ -811,49 +811,6 @@ CvDTreeNode* CascadeBoostTree::predict( int sampleIdx ) const return node; } -/* -static void readRecursive(const FileNode &fn, CvDTreeNode *node, CvDTreeTrainData *data) -{ - bool hasChildren = (int)fn["hasChildren"]; - - if (!hasChildren) - node->value = (float)fn["value"]; - else { - int maxCatCount = ((CascadeBoostTrainData*)data)->featureEvaluator->getMaxCatCount(); - if (maxCatCount > 0) { - node->split = data->new_split_cat(0, 0); - FileNode subset_node = fn["subset"]; FileNodeIterator subset_it = subset_node.begin(); - for (int i = 0; i < (maxCatCount + 31) / 32; i++, ++subset_it) - node->split->subset[i] = (int)*subset_it; - } else { - float threshold = (float)fn["threshold"]; - node->split = data->new_split_ord(0, threshold, 0, 0, 0); - } - - node->split->var_idx = (int)fn["feature_idx"]; - - CvDTreeNode *leftChild = data->new_node(node, 0, 0, 0); - node->left = leftChild; - readRecursive(fn["left"], leftChild, data); - - CvDTreeNode *rightChild = data->new_node(node, 0, 0, 0); - node->right = rightChild; - readRecursive(fn["right"], rightChild, data); - } -} - -void CascadeBoostTree::read(const FileNode &fn, CvBoost* _ensemble, CvDTreeTrainData* _data) -{ - clear(); - data = _data; - ensemble = _ensemble; - pruned_tree_idx = 0; - - root = data->new_node(0, 0, 0, 0); - readRecursive(fn, root, data); -} -*/ - void CascadeBoostTree::split_node_data( CvDTreeNode* node ) { int n = node->sample_count, nl, nr, scount = data->sample_count; diff --git a/openbr/core/cascade.cpp b/openbr/core/cascade.cpp deleted file mode 100644 index d5b08eb..0000000 --- a/openbr/core/cascade.cpp +++ /dev/null @@ -1,275 +0,0 @@ -#include "cascade.h" - -using namespace br; - -void br::groupRectangles(vector& rectList, int groupThreshold, double eps, vector* weights, vector* levelWeights) -{ - if( groupThreshold <= 0 || rectList.empty() ) - { - if( weights ) - { - size_t i, sz = rectList.size(); - weights->resize(sz); - for( i = 0; i < sz; i++ ) - (*weights)[i] = 1; - } - return; - } - - vector labels; - int nclasses = partition(rectList, labels, SimilarRects(eps)); - - vector rrects(nclasses); - vector rweights(nclasses, 0); - vector rejectLevels(nclasses, 0); - vector rejectWeights(nclasses, DBL_MIN); - int i, j, nlabels = (int)labels.size(); - for( i = 0; i < nlabels; i++ ) - { - int cls = labels[i]; - rrects[cls].x += rectList[i].x; - rrects[cls].y += rectList[i].y; - rrects[cls].width += rectList[i].width; - rrects[cls].height += rectList[i].height; - rweights[cls]++; - } - if ( levelWeights && weights && !weights->empty() && !levelWeights->empty() ) - { - for( i = 0; i < nlabels; i++ ) - { - int cls = labels[i]; - if( (*weights)[i] > rejectLevels[cls] ) - { - rejectLevels[cls] = (*weights)[i]; - rejectWeights[cls] = (*levelWeights)[i]; - } - else if( ( (*weights)[i] == rejectLevels[cls] ) && ( (*levelWeights)[i] > rejectWeights[cls] ) ) - rejectWeights[cls] = (*levelWeights)[i]; - } - } - - for( i = 0; i < nclasses; i++ ) - { - Rect r = rrects[i]; - float s = 1.f/rweights[i]; - rrects[i] = Rect(saturate_cast(r.x*s), - saturate_cast(r.y*s), - saturate_cast(r.width*s), - saturate_cast(r.height*s)); - } - - rectList.clear(); - if( weights ) - weights->clear(); - if( levelWeights ) - levelWeights->clear(); - - for( i = 0; i < nclasses; i++ ) - { - Rect r1 = rrects[i]; - int n1 = levelWeights ? rejectLevels[i] : rweights[i]; - - double w1 = rejectWeights[i]; - if( n1 <= groupThreshold ) - continue; - // filter out small face rectangles inside large rectangles - for( j = 0; j < nclasses; j++ ) - { - int n2 = rweights[j]; - - if( j == i || n2 <= groupThreshold ) - continue; - Rect r2 = rrects[j]; - - int dx = saturate_cast( r2.width * eps ); - int dy = saturate_cast( r2.height * eps ); - - if( i != j && - r1.x >= r2.x - dx && - r1.y >= r2.y - dy && - r1.x + r1.width <= r2.x + r2.width + dx && - r1.y + r1.height <= r2.y + r2.height + dy && - (n2 > std::max(3, n1) || n1 < 3) ) - break; - } - - if( j == nclasses ) - { - rectList.push_back(r1); - if( weights ) - weights->push_back(n1); - if( levelWeights ) - levelWeights->push_back(w1); - } - } -} - -void br::groupRectangles(vector& rectList, int groupThreshold, double eps) -{ - groupRectangles(rectList, groupThreshold, eps, 0, 0); -} - -void br::groupRectangles(vector& rectList, vector& weights, int groupThreshold, double eps) -{ - groupRectangles(rectList, groupThreshold, eps, &weights, 0); -} - -void br::groupRectangles(vector& rectList, vector& rejectLevels, vector& levelWeights, int groupThreshold, double eps) -{ - groupRectangles(rectList, groupThreshold, eps, &rejectLevels, &levelWeights); -} - -// --------------------------------- Cascade Classifier ---------------------------------- - -static void loadRecursive(const FileNode &fn, _CascadeClassifier::Node *node, int maxCatCount) -{ - bool hasChildren = (int)fn["hasChildren"]; - if (!hasChildren) - node->value = (float)fn["value"]; - else { - if (maxCatCount > 0) { - 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["featureIdx"]; - - node->left = new _CascadeClassifier::Node; node->right = new _CascadeClassifier::Node; - loadRecursive(fn["left"], node->left, maxCatCount); - loadRecursive(fn["right"], node->right, maxCatCount); - } -} - -bool _CascadeClassifier::load(const string& filename) -{ - FileStorage fs(filename, FileStorage::READ); - if (!fs.isOpened()) - return false; - - FileNode root = fs.getFirstTopLevelNode(); - - const float THRESHOLD_EPS = 1e-5; - - int maxCatCount = representation->maxCatCount(); - - // load stages - FileNode stages_fn = root["stages"]; - if( stages_fn.empty() ) - return false; - - 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 (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 treeIdx = 0; treeIdx < stage.trees.size(); treeIdx++) { - Node *node = stage.trees[treeIdx]; - - 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); - node = val < node->threshold ? node->left : node->right; - } - } - sum += node->value; - } - - if (sum < stage.threshold) - return stageIdx; - } - - return stages.size(); -} - -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 (stages.empty()) - return; - - 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) { - int dx, dy; - Size originalWindowSize = representation->windowSize(&dx, &dy); - - Size windowSize(cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor) ); - Size scaledImageSize(cvRound(image.cols/factor ), cvRound(image.rows/factor)); - Size processingRectSize(scaledImageSize.width - originalWindowSize.width, scaledImageSize.height - originalWindowSize.height); - - if (processingRectSize.width <= 0 || processingRectSize.height <= 0) - break; - if (windowSize.width > maxSize.width || windowSize.height > maxSize.height) - break; - if (windowSize.width < minSize.width || windowSize.height < minSize.height) - continue; - - Mat scaledImage(scaledImageSize, CV_8U, imageBuffer.data); - resize(image, scaledImage, scaledImageSize, 0, 0, CV_INTER_LINEAR); - - Mat repImage; - representation->preprocess(scaledImage, repImage); - - int yStep = factor > 2. ? 1 : 2; - for (int y = 0; y < processingRectSize.height; y += yStep) { - for (int x = 0; x < processingRectSize.width; x += yStep) { - Mat window = repImage(Rect(Point(x, y), Size(originalWindowSize.width + dx, originalWindowSize.height + dy))).clone(); - - double gypWeight; - int result = predict(window, gypWeight); - - 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; - } - } - } - - groupRectangles(objects, rejectLevels, levelWeights, minNeighbors, GROUP_EPS); -} diff --git a/openbr/core/cascade.h b/openbr/core/cascade.h deleted file mode 100644 index 4f774aa..0000000 --- a/openbr/core/cascade.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef CASCADE_H -#define CASCADE_H - -#include -#include - -using namespace std; -using namespace cv; - -namespace br -{ - -// class for grouping object candidates, detected by Cascade Classifier, HOG etc. -// instance of the class is to be passed to cv::partition (see cxoperations.hpp) -class SimilarRects -{ -public: - SimilarRects(double _eps) : eps(_eps) {} - inline bool operator()(const Rect& r1, const Rect& r2) const - { - double delta = eps*(std::min(r1.width, r2.width) + std::min(r1.height, r2.height))*0.5; - return std::abs(r1.x - r2.x) <= delta && - std::abs(r1.y - r2.y) <= delta && - std::abs(r1.x + r1.width - r2.x - r2.width) <= delta && - std::abs(r1.y + r1.height - r2.y - r2.height) <= delta; - } - double eps; -}; - -void groupRectangles(vector& rectList, int groupThreshold, double eps=0.2); -void groupRectangles(vector& rectList, vector& weights, int groupThreshold, double eps=0.2); -void groupRectangles(vector& rectList, int groupThreshold, double eps, vector* weights, vector* levelWeights ); -void groupRectangles(vector& rectList, vector& rejectLevels, vector& levelWeights, int groupThreshold, double eps=0.2); - -class _CascadeClassifier -{ -public: - _CascadeClassifier() : representation(Representation::make("MBLBP(24,24)", NULL)) {} - _CascadeClassifier(const string& filename) : representation(Representation::make("MBLBP(24,24)", NULL)) { load(filename); } - ~_CascadeClassifier() {} - - bool load(const string& filename); - - void detectMultiScale(const Mat& image, - vector& objects, - vector& rejectLevels, - vector& levelWeights, - double scaleFactor=1.1, - int minNeighbors=3, - Size minSize=Size(), - Size maxSize=Size()) const; - - int predict(const Mat &image, double &weight) const; - - struct Node - { - 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; - }; - - struct Stage - { - QList trees; - float threshold; - }; - - QList stages; - Representation *representation; -}; - -} // namespace br - -#endif // CASCADE_H diff --git a/openbr/openbr_plugin.h b/openbr/openbr_plugin.h index 8e25c2d..22af3e1 100644 --- a/openbr/openbr_plugin.h +++ b/openbr/openbr_plugin.h @@ -1424,15 +1424,11 @@ public: virtual void train(const QList &images, const QList &labels) = 0; virtual float classify(const cv::Mat &image, bool process = true, float *confidence = NULL) const = 0; - // Slots for representation + // Slots for representations virtual cv::Mat preprocess(const cv::Mat &image) const = 0; virtual cv::Size windowSize(int *dx = NULL, int *dy = NULL) const = 0; - - // OpenCV compatibility virtual int numFeatures() const = 0; virtual int maxCatCount() const = 0; - virtual void write(cv::FileStorage &fs) const { (void)fs; } - virtual void read(const cv::FileNode &node) { (void)node; } }; /*! diff --git a/openbr/plugins/classification/boostedforest.cpp b/openbr/plugins/classification/boostedforest.cpp index 0ad21d6..cc90ad1 100644 --- a/openbr/plugins/classification/boostedforest.cpp +++ b/openbr/plugins/classification/boostedforest.cpp @@ -39,50 +39,47 @@ static void buildTreeRecursive(Node *node, const CvDTreeNode *cv_node, int maxCa } } -static void readRecursive(const FileNode &fn, Node *node, int maxCatCount) +static void loadRecursive(QDataStream &stream, Node *node, int maxCatCount) { - bool hasChildren = (int)fn["hasChildren"]; + bool hasChildren; stream >> hasChildren; + if (!hasChildren) { - node->value = (float)fn["value"]; + stream >> node->value; node->left = node->right = NULL; } else { - if (maxCatCount > 0) { - 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"]; - } + if (maxCatCount > 0) + for (int i = 0; i < (maxCatCount + 31)/32; i++) { + int s; stream >> s; node->subset.append(s); + } + else + stream >> node->threshold; - node->featureIdx = (int)fn["featureIdx"]; + stream >> node->featureIdx; node->left = new Node; node->right = new Node; - readRecursive(fn["left"], node->left, maxCatCount); - readRecursive(fn["right"], node->right, maxCatCount); + loadRecursive(stream, node->left, maxCatCount); + loadRecursive(stream, node->right, maxCatCount); } } -static void writeRecursive(FileStorage &fs, const Node *node, int maxCatCount) +static void storeRecursive(QDataStream &stream, const Node *node, int maxCatCount) { bool hasChildren = node->left ? true : false; - fs << "hasChildren" << hasChildren; + stream << hasChildren; if (!hasChildren) - fs << "value" << node->value; + stream << node->value; else { - if (maxCatCount > 0) { - fs << "subset" << "["; + if (maxCatCount > 0) for (int i = 0; i < (maxCatCount + 31)/32; i++) - fs << node->subset[i]; - fs << "]"; - } else { - fs << "threshold" << node->threshold; - } + stream << node->subset[i]; + else + stream << node->threshold; - fs << "featureIdx" << node->featureIdx; + stream << node->featureIdx; - fs << "left" << "{"; writeRecursive(fs, node->left, maxCatCount); fs << "}"; - fs << "right" << "{"; writeRecursive(fs, node->right, maxCatCount); fs << "}"; + storeRecursive(stream, node->left, maxCatCount); + storeRecursive(stream, node->right, maxCatCount); } } @@ -142,7 +139,7 @@ class BoostedForestClassifier : public Classifier Node *node = classifiers[i]; while (node->left) { - if (representation->maxCatCount() > 1) { + if (representation->maxCatCount() > 0) { int c = (int)representation->evaluate(m, node->featureIdx); node = (node->subset[c >> 5] & (1 << (c & 31))) ? node->left : node->right; } else { @@ -150,6 +147,7 @@ class BoostedForestClassifier : public Classifier node = val <= node->threshold ? node->left : node->right; } } + sum += node->value; } @@ -180,28 +178,23 @@ class BoostedForestClassifier : public Classifier return representation->windowSize(dx, dy); } - void read(const FileNode &node) + void load(QDataStream &stream) { - threshold = (float)node["stageThreshold"]; - FileNode weaks_fn = node["weakClassifiers"]; - for (FileNodeIterator weaks_it = weaks_fn.begin(); weaks_it != weaks_fn.end(); ++weaks_it) { - Node *root = new Node; - readRecursive(*weaks_it, root, representation->maxCatCount()); - classifiers.append(root); + stream >> threshold; + int numClassifiers; stream >> numClassifiers; + for (int i = 0; i < numClassifiers; i++) { + Node *classifier = new Node; + loadRecursive(stream, classifier, representation->maxCatCount()); + classifiers.append(classifier); } } - void write(FileStorage &fs) const + void store(QDataStream &stream) const { - fs << "stageThreshold" << threshold; - fs << "weakSize" << classifiers.size(); - fs << "weakClassifiers" << "["; - foreach (const Node *root, classifiers) { - fs << "{"; - writeRecursive(fs, root, representation->maxCatCount()); - fs << "}"; - } - fs << "]"; + stream << threshold; + stream << classifiers.size(); + foreach (const Node *classifier, classifiers) + storeRecursive(stream, classifier, representation->maxCatCount()); } }; diff --git a/openbr/plugins/classification/cascade.cpp b/openbr/plugins/classification/cascade.cpp index b87f568..d9cafc9 100644 --- a/openbr/plugins/classification/cascade.cpp +++ b/openbr/plugins/classification/cascade.cpp @@ -185,25 +185,21 @@ class CascadeClassifier : public Classifier return stages.first()->windowSize(dx, dy); } - void read(const FileNode &node) + void load(QDataStream &stream) { - FileNode stages_fn = node["stages"]; - for (FileNodeIterator stages_it = stages_fn.begin(); stages_it != stages_fn.end(); ++stages_it) { + int numStages; stream >> numStages; + for (int i = 0; i < numStages; i++) { Classifier *nextStage = Classifier::make(stageDescription, NULL); - nextStage->read(*stages_it); + nextStage->load(stream); stages.append(nextStage); } } - void write(FileStorage &fs) const + void store(QDataStream &stream) const { - fs << "stages" << "["; - foreach (const Classifier *stage, stages) { - fs << "{"; - stage->write(fs); - fs << "}"; - } - fs << "]"; + stream << stages.size(); + foreach (const Classifier *stage, stages) + stage->store(stream); } private: diff --git a/openbr/plugins/imgproc/slidingwindow.cpp b/openbr/plugins/imgproc/slidingwindow.cpp index 0411c68..de0c33c 100644 --- a/openbr/plugins/imgproc/slidingwindow.cpp +++ b/openbr/plugins/imgproc/slidingwindow.cpp @@ -151,36 +151,12 @@ class SlidingWindowTransform : public MetaTransform void load(QDataStream &stream) { - (void)stream; - - QString filename = model + "/cascade.xml"; - FileStorage fs(filename.toStdString(), FileStorage::READ); - if (!fs.isOpened()) - return; - - classifier->read(fs.getFirstTopLevelNode()); + classifier->load(stream); } void store(QDataStream &stream) const { - (void) stream; - - QString path = model; - QtUtils::touchDir(QDir(path)); - - QString filename = path + "/cascade.xml"; - FileStorage fs(filename.toStdString(), FileStorage::WRITE); - - if (!fs.isOpened()) { - qWarning("Unable to open file: %s", qPrintable(filename)); - return; - } - - fs << FileStorage::getDefaultObjectName(filename.toStdString()) << "{"; - - classifier->write(fs); - - fs << "}"; + classifier->store(stream); } };