Commit 6fcc63c6bddf62527d2ae28660e97b7208343047
1 parent
dfea239c
Integrating OpenCV frontend
Showing
5 changed files
with
172 additions
and
35 deletions
openbr/openbr_plugin.h
| @@ -1411,9 +1411,6 @@ public: | @@ -1411,9 +1411,6 @@ public: | ||
| 1411 | virtual cv::Size postWindowSize() const = 0; // window size after preprocessing | 1411 | virtual cv::Size postWindowSize() const = 0; // window size after preprocessing |
| 1412 | virtual int numFeatures() const = 0; | 1412 | virtual int numFeatures() const = 0; |
| 1413 | virtual int maxCatCount() const = 0; | 1413 | virtual int maxCatCount() const = 0; |
| 1414 | - | ||
| 1415 | - // Temporary for OpenCV compatibility | ||
| 1416 | - virtual void write( cv::FileStorage &fs, const cv::Mat &featureMap ) { (void)fs; (void)featureMap; } | ||
| 1417 | }; | 1414 | }; |
| 1418 | 1415 | ||
| 1419 | class BR_EXPORT Classifier : public Object | 1416 | class BR_EXPORT Classifier : public Object |
openbr/plugins/classification/boostedforest.cpp
| @@ -23,7 +23,6 @@ static void buildTreeRecursive(Node *node, const CvDTreeNode *cv_node, int maxCa | @@ -23,7 +23,6 @@ static void buildTreeRecursive(Node *node, const CvDTreeNode *cv_node, int maxCa | ||
| 23 | { | 23 | { |
| 24 | if (!cv_node->left) { | 24 | if (!cv_node->left) { |
| 25 | node->value = cv_node->value; | 25 | node->value = cv_node->value; |
| 26 | - | ||
| 27 | node->left = node->right = NULL; | 26 | node->left = node->right = NULL; |
| 28 | } else { | 27 | } else { |
| 29 | if (maxCatCount > 0) | 28 | if (maxCatCount > 0) |
| @@ -40,6 +39,29 @@ static void buildTreeRecursive(Node *node, const CvDTreeNode *cv_node, int maxCa | @@ -40,6 +39,29 @@ static void buildTreeRecursive(Node *node, const CvDTreeNode *cv_node, int maxCa | ||
| 40 | } | 39 | } |
| 41 | } | 40 | } |
| 42 | 41 | ||
| 42 | +static void readRecursive(const FileNode &fn, Node *node, int maxCatCount) | ||
| 43 | +{ | ||
| 44 | + bool hasChildren = (int)fn["hasChildren"]; | ||
| 45 | + if (!hasChildren) { | ||
| 46 | + node->value = (float)fn["value"]; | ||
| 47 | + node->left = node->right = NULL; | ||
| 48 | + } else { | ||
| 49 | + if (maxCatCount > 0) { | ||
| 50 | + FileNode subset_fn = fn["subset"]; | ||
| 51 | + for (FileNodeIterator subset_it = subset_fn.begin(); subset_it != subset_fn.end(); ++subset_it) | ||
| 52 | + node->subset.append((int)*subset_it); | ||
| 53 | + } else { | ||
| 54 | + node->threshold = (float)fn["threshold"]; | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + node->featureIdx = (int)fn["featureIdx"]; | ||
| 58 | + | ||
| 59 | + node->left = new Node; node->right = new Node; | ||
| 60 | + readRecursive(fn["left"], node->left, maxCatCount); | ||
| 61 | + readRecursive(fn["right"], node->right, maxCatCount); | ||
| 62 | + } | ||
| 63 | +} | ||
| 64 | + | ||
| 43 | static void writeRecursive(FileStorage &fs, const Node *node, int maxCatCount) | 65 | static void writeRecursive(FileStorage &fs, const Node *node, int maxCatCount) |
| 44 | { | 66 | { |
| 45 | bool hasChildren = node->left ? true : false; | 67 | bool hasChildren = node->left ? true : false; |
| @@ -119,16 +141,17 @@ class BoostedForestClassifier : public Classifier | @@ -119,16 +141,17 @@ class BoostedForestClassifier : public Classifier | ||
| 119 | while (node->left) { | 141 | while (node->left) { |
| 120 | if (representation->maxCatCount() > 1) { | 142 | if (representation->maxCatCount() > 1) { |
| 121 | int c = (int)representation->evaluate(image, node->featureIdx); | 143 | int c = (int)representation->evaluate(image, node->featureIdx); |
| 122 | - node = (2*((node->subset[c >> 5] & (1 << (c & 31))) == 0) - 1) < 0 ? node->left : node->right; | 144 | + node = (node->subset[c >> 5] & (1 << (c & 31))) ? node->left : node->right; |
| 123 | } else { | 145 | } else { |
| 124 | double val = representation->evaluate(image, node->featureIdx); | 146 | double val = representation->evaluate(image, node->featureIdx); |
| 125 | node = val <= node->threshold ? node->left : node->right; | 147 | node = val <= node->threshold ? node->left : node->right; |
| 126 | } | 148 | } |
| 127 | } | 149 | } |
| 150 | + qDebug("value: %f", node->value); | ||
| 128 | sum += node->value; | 151 | sum += node->value; |
| 129 | } | 152 | } |
| 130 | 153 | ||
| 131 | - return sum < threshold - THRESHOLD_EPS ? 0.0f : 1.0f; | 154 | + return sum < threshold - THRESHOLD_EPS ? -std::abs(sum) : std::abs(sum); |
| 132 | } | 155 | } |
| 133 | 156 | ||
| 134 | int numFeatures() const | 157 | int numFeatures() const |
| @@ -146,6 +169,17 @@ class BoostedForestClassifier : public Classifier | @@ -146,6 +169,17 @@ class BoostedForestClassifier : public Classifier | ||
| 146 | return representation->preWindowSize(); | 169 | return representation->preWindowSize(); |
| 147 | } | 170 | } |
| 148 | 171 | ||
| 172 | + void read(const FileNode &node) | ||
| 173 | + { | ||
| 174 | + threshold = (float)node["stageThreshold"]; | ||
| 175 | + FileNode weaks_fn = node["weakClassifiers"]; | ||
| 176 | + for (FileNodeIterator weaks_it = weaks_fn.begin(); weaks_it != weaks_fn.end(); ++weaks_it) { | ||
| 177 | + Node *root = new Node; | ||
| 178 | + readRecursive(*weaks_it, root, representation->maxCatCount()); | ||
| 179 | + classifiers.append(root); | ||
| 180 | + } | ||
| 181 | + } | ||
| 182 | + | ||
| 149 | void write(FileStorage &fs) const | 183 | void write(FileStorage &fs) const |
| 150 | { | 184 | { |
| 151 | fs << "stageThreshold" << threshold; | 185 | fs << "stageThreshold" << threshold; |
openbr/plugins/classification/cascade.cpp
| @@ -154,10 +154,16 @@ class CascadeClassifier : public Classifier | @@ -154,10 +154,16 @@ class CascadeClassifier : public Classifier | ||
| 154 | 154 | ||
| 155 | float classify(const Mat &image) const | 155 | float classify(const Mat &image) const |
| 156 | { | 156 | { |
| 157 | - foreach (const Classifier *stage, stages) | ||
| 158 | - if (stage->classify(image) == 0.0f) | ||
| 159 | - return 0.0f; | ||
| 160 | - return 1.0f; | 157 | + if (stages.empty()) |
| 158 | + return 1.0f; | ||
| 159 | + | ||
| 160 | + float val = 0.0f; | ||
| 161 | + for (int i = 0; i < stages.size(); i++) { | ||
| 162 | + val = stages[i]->classify(image); | ||
| 163 | + if (val < 0.0f) | ||
| 164 | + return stages.size() - i < 4 ? i * val : 0.0f; | ||
| 165 | + } | ||
| 166 | + return stages.size() * val; | ||
| 161 | } | 167 | } |
| 162 | 168 | ||
| 163 | int numFeatures() const | 169 | int numFeatures() const |
| @@ -175,6 +181,16 @@ class CascadeClassifier : public Classifier | @@ -175,6 +181,16 @@ class CascadeClassifier : public Classifier | ||
| 175 | return stages.first()->windowSize(); | 181 | return stages.first()->windowSize(); |
| 176 | } | 182 | } |
| 177 | 183 | ||
| 184 | + void read(const FileNode &node) | ||
| 185 | + { | ||
| 186 | + FileNode stages_fn = node["stages"]; | ||
| 187 | + for (FileNodeIterator stages_it = stages_fn.begin(); stages_it != stages_fn.end(); ++stages_it) { | ||
| 188 | + Classifier *nextStage = Classifier::make(stageDescription, NULL); | ||
| 189 | + nextStage->read(*stages_it); | ||
| 190 | + stages.append(nextStage); | ||
| 191 | + } | ||
| 192 | + } | ||
| 193 | + | ||
| 178 | void write(FileStorage &fs) const | 194 | void write(FileStorage &fs) const |
| 179 | { | 195 | { |
| 180 | fs << "stages" << "["; | 196 | fs << "stages" << "["; |
| @@ -196,7 +212,7 @@ private: | @@ -196,7 +212,7 @@ private: | ||
| 196 | if (!imgHandler.getPos(pos)) | 212 | if (!imgHandler.getPos(pos)) |
| 197 | qFatal("Cannot get another positive sample!"); | 213 | qFatal("Cannot get another positive sample!"); |
| 198 | 214 | ||
| 199 | - if (classify(pos) == 1.0f) { | 215 | + if (classify(pos) > 0.0f) { |
| 200 | printf("POS current samples: %d\r", images.size()); | 216 | printf("POS current samples: %d\r", images.size()); |
| 201 | images.append(pos); | 217 | images.append(pos); |
| 202 | labels.append(1.0f); | 218 | labels.append(1.0f); |
| @@ -212,7 +228,7 @@ private: | @@ -212,7 +228,7 @@ private: | ||
| 212 | if (!imgHandler.getNeg(neg)) | 228 | if (!imgHandler.getNeg(neg)) |
| 213 | qFatal("Cannot get another negative sample!"); | 229 | qFatal("Cannot get another negative sample!"); |
| 214 | 230 | ||
| 215 | - if (classify(neg) == 1.0f) { | 231 | + if (classify(neg) > 0.0f) { |
| 216 | printf("NEG current samples: %d\r", images.size() - posCount); | 232 | printf("NEG current samples: %d\r", images.size() - posCount); |
| 217 | images.append(neg); | 233 | images.append(neg); |
| 218 | labels.append(0.0f); | 234 | labels.append(0.0f); |
openbr/plugins/imgproc/slidingwindow.cpp
| @@ -19,8 +19,10 @@ | @@ -19,8 +19,10 @@ | ||
| 19 | #include <openbr/plugins/openbr_internal.h> | 19 | #include <openbr/plugins/openbr_internal.h> |
| 20 | #include <openbr/core/opencvutils.h> | 20 | #include <openbr/core/opencvutils.h> |
| 21 | #include <openbr/core/qtutils.h> | 21 | #include <openbr/core/qtutils.h> |
| 22 | +#include <openbr/core/cascade.h> | ||
| 22 | 23 | ||
| 23 | #include <opencv2/highgui/highgui.hpp> | 24 | #include <opencv2/highgui/highgui.hpp> |
| 25 | +#include <opencv2/imgproc/imgproc.hpp> | ||
| 24 | 26 | ||
| 25 | using namespace cv; | 27 | using namespace cv; |
| 26 | 28 | ||
| @@ -38,8 +40,20 @@ class SlidingWindowTransform : public Transform | @@ -38,8 +40,20 @@ class SlidingWindowTransform : public Transform | ||
| 38 | Q_OBJECT | 40 | Q_OBJECT |
| 39 | 41 | ||
| 40 | Q_PROPERTY(br::Classifier *classifier READ get_classifier WRITE set_classifier RESET reset_classifier STORED false) | 42 | Q_PROPERTY(br::Classifier *classifier READ get_classifier WRITE set_classifier RESET reset_classifier STORED false) |
| 43 | + Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) | ||
| 44 | + Q_PROPERTY(int maxSize READ get_maxSize WRITE set_maxSize RESET reset_maxSize STORED false) | ||
| 45 | + Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) | ||
| 46 | + Q_PROPERTY(int minNeighbors READ get_minNeighbors WRITE set_minNeighbors RESET reset_minNeighbors STORED false) | ||
| 47 | + Q_PROPERTY(float eps READ get_eps WRITE set_eps RESET reset_eps STORED false) | ||
| 48 | + | ||
| 41 | Q_PROPERTY(QString cascadeDir READ get_cascadeDir WRITE set_cascadeDir RESET reset_cascadeDir STORED false) | 49 | Q_PROPERTY(QString cascadeDir READ get_cascadeDir WRITE set_cascadeDir RESET reset_cascadeDir STORED false) |
| 42 | BR_PROPERTY(br::Classifier *, classifier, NULL) | 50 | BR_PROPERTY(br::Classifier *, classifier, NULL) |
| 51 | + BR_PROPERTY(int, minSize, 20) | ||
| 52 | + BR_PROPERTY(int, maxSize, -1) | ||
| 53 | + BR_PROPERTY(float, scaleFactor, 1.2) | ||
| 54 | + BR_PROPERTY(int, minNeighbors, 5) | ||
| 55 | + BR_PROPERTY(float, eps, 0.2) | ||
| 56 | + | ||
| 43 | BR_PROPERTY(QString, cascadeDir, "") | 57 | BR_PROPERTY(QString, cascadeDir, "") |
| 44 | 58 | ||
| 45 | void train(const TemplateList &data) | 59 | void train(const TemplateList &data) |
| @@ -49,12 +63,104 @@ class SlidingWindowTransform : public Transform | @@ -49,12 +63,104 @@ class SlidingWindowTransform : public Transform | ||
| 49 | 63 | ||
| 50 | void project(const Template &src, Template &dst) const | 64 | void project(const Template &src, Template &dst) const |
| 51 | { | 65 | { |
| 52 | - (void)src; (void)dst; | 66 | + TemplateList temp; |
| 67 | + project(TemplateList() << src, temp); | ||
| 68 | + if (!temp.isEmpty()) dst = temp.first(); | ||
| 53 | } | 69 | } |
| 54 | 70 | ||
| 71 | + void project(const TemplateList &src, TemplateList &dst) const | ||
| 72 | + { | ||
| 73 | + foreach (const Template &t, src) { | ||
| 74 | + const bool enrollAll = t.file.getBool("enrollAll"); | ||
| 75 | + | ||
| 76 | + // Mirror the behavior of ExpandTransform in the special case | ||
| 77 | + // of an empty template. | ||
| 78 | + if (t.empty() && !enrollAll) { | ||
| 79 | + dst.append(t); | ||
| 80 | + continue; | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + for (int i = 0; i < t.size(); i++) { | ||
| 84 | + Mat image; | ||
| 85 | + OpenCVUtils::cvtUChar(t[i], image); | ||
| 86 | + | ||
| 87 | + std::vector<Rect> rects; | ||
| 88 | + std::vector<int> rejectLevels; | ||
| 89 | + std::vector<double> levelWeights; | ||
| 90 | + | ||
| 91 | + Size minObjectSize(minSize, minSize); | ||
| 92 | + Size maxObjectSize(maxSize, maxSize); | ||
| 93 | + if (maxObjectSize.height <= 0 || maxObjectSize.width <= 0) | ||
| 94 | + maxObjectSize = image.size(); | ||
| 95 | + | ||
| 96 | + Mat imageBuffer(image.rows + 1, image.cols + 1, CV_8U); | ||
| 97 | + | ||
| 98 | + for (double factor = 1; ; factor *= scaleFactor) { | ||
| 99 | + Size originalWindowSize = classifier->windowSize(); | ||
| 100 | + | ||
| 101 | + Size windowSize(cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor) ); | ||
| 102 | + Size scaledImageSize(cvRound(image.cols/factor ), cvRound(image.rows/factor)); | ||
| 103 | + Size processingRectSize(scaledImageSize.width - originalWindowSize.width, scaledImageSize.height - originalWindowSize.height); | ||
| 104 | + | ||
| 105 | + if (processingRectSize.width <= 0 || processingRectSize.height <= 0) | ||
| 106 | + break; | ||
| 107 | + if (windowSize.width > maxObjectSize.width || windowSize.height > maxObjectSize.height) | ||
| 108 | + break; | ||
| 109 | + if (windowSize.width < minObjectSize.width || windowSize.height < minObjectSize.height) | ||
| 110 | + continue; | ||
| 111 | + | ||
| 112 | + Mat scaledImage(scaledImageSize, CV_8U, imageBuffer.data); | ||
| 113 | + resize(image, scaledImage, scaledImageSize, 0, 0, CV_INTER_LINEAR); | ||
| 114 | + | ||
| 115 | + int yStep = factor > 2. ? 1 : 2; | ||
| 116 | + for (int y = 0; y < processingRectSize.height; y += yStep) { | ||
| 117 | + for (int x = 0; x < processingRectSize.width; x += yStep) { | ||
| 118 | + Mat window = scaledImage(Rect(Point(x, y), classifier->windowSize())).clone(); | ||
| 119 | + | ||
| 120 | + float result = classifier->classify(window); | ||
| 121 | + qDebug("result: %f", result); | ||
| 122 | + if (result > 0) { | ||
| 123 | + rects.push_back(Rect(cvRound(x*factor), cvRound(y*factor), windowSize.width, windowSize.height)); | ||
| 124 | + rejectLevels.push_back(1); | ||
| 125 | + levelWeights.push_back(result); | ||
| 126 | + } | ||
| 127 | + if (result == 0) | ||
| 128 | + x = yStep; | ||
| 129 | + } | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + groupRectangles(rects, rejectLevels, levelWeights, minNeighbors, eps); | ||
| 134 | + | ||
| 135 | + if (!enrollAll && rects.empty()) | ||
| 136 | + rects.push_back(Rect(0, 0, image.cols, image.rows)); | ||
| 137 | + | ||
| 138 | + for (size_t j = 0; j < rects.size(); j++) { | ||
| 139 | + Template u(t.file, image); | ||
| 140 | + if (rejectLevels.size() > j) | ||
| 141 | + u.file.set("Confidence", rejectLevels[j]*levelWeights[j]); | ||
| 142 | + else | ||
| 143 | + u.file.set("Confidence", 1); | ||
| 144 | + const QRectF rect = OpenCVUtils::fromRect(rects[j]); | ||
| 145 | + u.file.appendRect(rect); | ||
| 146 | + u.file.set("Face", rect); | ||
| 147 | + dst.append(u); | ||
| 148 | + } | ||
| 149 | + } | ||
| 150 | + } | ||
| 151 | + } | ||
| 152 | + | ||
| 55 | void load(QDataStream &stream) | 153 | void load(QDataStream &stream) |
| 56 | { | 154 | { |
| 57 | (void) stream; | 155 | (void) stream; |
| 156 | + | ||
| 157 | + QString filename = Globals->sdkPath + "/share/openbr/models/openbrcascades/" + cascadeDir + "/cascade.xml"; | ||
| 158 | + FileStorage fs(filename.toStdString(), FileStorage::READ); | ||
| 159 | + if (!fs.isOpened()) | ||
| 160 | + return; | ||
| 161 | + | ||
| 162 | + classifier->read(fs.getFirstTopLevelNode()); | ||
| 163 | + | ||
| 58 | return; | 164 | return; |
| 59 | } | 165 | } |
| 60 | 166 | ||
| @@ -62,17 +168,17 @@ class SlidingWindowTransform : public Transform | @@ -62,17 +168,17 @@ class SlidingWindowTransform : public Transform | ||
| 62 | { | 168 | { |
| 63 | (void) stream; | 169 | (void) stream; |
| 64 | 170 | ||
| 65 | - QString path = Globals->sdkPath + "/share/openbr/models/openbrcascades/" + cascadeDir; | ||
| 66 | - QtUtils::touchDir(QDir(path)); | 171 | + QString path = Globals->sdkPath + "/share/openbr/models/openbrcascades/" + cascadeDir; |
| 172 | + QtUtils::touchDir(QDir(path)); | ||
| 67 | 173 | ||
| 68 | - QString filename = path + "/cascade.xml"; | 174 | + QString filename = path + "/cascade.xml"; |
| 69 | FileStorage fs(filename.toStdString(), FileStorage::WRITE); | 175 | FileStorage fs(filename.toStdString(), FileStorage::WRITE); |
| 70 | 176 | ||
| 71 | if (!fs.isOpened()) { | 177 | if (!fs.isOpened()) { |
| 72 | - qWarning("Unable to open file: %s", qPrintable(filename)); | 178 | + qWarning("Unable to open file: %s", qPrintable(filename)); |
| 73 | return; | 179 | return; |
| 74 | - } | ||
| 75 | - | 180 | + } |
| 181 | + | ||
| 76 | fs << FileStorage::getDefaultObjectName(filename.toStdString()) << "{"; | 182 | fs << FileStorage::getDefaultObjectName(filename.toStdString()) << "{"; |
| 77 | 183 | ||
| 78 | classifier->write(fs); | 184 | classifier->write(fs); |
openbr/plugins/representation/mblbp.cpp
| @@ -58,7 +58,6 @@ class MBLBPRepresentation : public Representation | @@ -58,7 +58,6 @@ class MBLBPRepresentation : public Representation | ||
| 58 | return result; | 58 | return result; |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | - void write(FileStorage &fs, const Mat &featureMap); | ||
| 62 | int numFeatures() const { return features.size(); } | 61 | int numFeatures() const { return features.size(); } |
| 63 | Size preWindowSize() const { return Size(winWidth, winHeight); } | 62 | Size preWindowSize() const { return Size(winWidth, winHeight); } |
| 64 | Size postWindowSize() const { return Size(winWidth + 1, winHeight + 1); } | 63 | Size postWindowSize() const { return Size(winWidth + 1, winHeight + 1); } |
| @@ -69,7 +68,6 @@ class MBLBPRepresentation : public Representation | @@ -69,7 +68,6 @@ class MBLBPRepresentation : public Representation | ||
| 69 | Feature() { rect = Rect(0, 0, 0, 0); } | 68 | Feature() { rect = Rect(0, 0, 0, 0); } |
| 70 | Feature( int offset, int x, int y, int _block_w, int _block_h ); | 69 | Feature( int offset, int x, int y, int _block_w, int _block_h ); |
| 71 | uchar calc(const Mat &img) const; | 70 | uchar calc(const Mat &img) const; |
| 72 | - void write( FileStorage &fs ) const { fs << "rect" << "[:" << rect.x << rect.y << rect.width << rect.height << "]"; } | ||
| 73 | 71 | ||
| 74 | Rect rect; | 72 | Rect rect; |
| 75 | int p[16]; | 73 | int p[16]; |
| @@ -79,20 +77,6 @@ class MBLBPRepresentation : public Representation | @@ -79,20 +77,6 @@ class MBLBPRepresentation : public Representation | ||
| 79 | 77 | ||
| 80 | BR_REGISTER(Representation, MBLBPRepresentation) | 78 | BR_REGISTER(Representation, MBLBPRepresentation) |
| 81 | 79 | ||
| 82 | -void MBLBPRepresentation::write(FileStorage &fs, const Mat &featureMap) | ||
| 83 | -{ | ||
| 84 | - fs << "features" << "["; | ||
| 85 | - const Mat_<int>& featureMap_ = (const Mat_<int>&)featureMap; | ||
| 86 | - for ( int fi = 0; fi < featureMap.cols; fi++ ) | ||
| 87 | - if ( featureMap_(0, fi) >= 0 ) | ||
| 88 | - { | ||
| 89 | - fs << "{"; | ||
| 90 | - features[fi].write( fs ); | ||
| 91 | - fs << "}"; | ||
| 92 | - } | ||
| 93 | - fs << "]"; | ||
| 94 | -} | ||
| 95 | - | ||
| 96 | MBLBPRepresentation::Feature::Feature( int offset, int x, int y, int _blockWidth, int _blockHeight ) | 80 | MBLBPRepresentation::Feature::Feature( int offset, int x, int y, int _blockWidth, int _blockHeight ) |
| 97 | { | 81 | { |
| 98 | Rect tr = rect = cvRect(x, y, _blockWidth, _blockHeight); | 82 | Rect tr = rect = cvRect(x, y, _blockWidth, _blockHeight); |