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 | 1411 | virtual cv::Size postWindowSize() const = 0; // window size after preprocessing |
| 1412 | 1412 | virtual int numFeatures() const = 0; |
| 1413 | 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 | 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 | 23 | { |
| 24 | 24 | if (!cv_node->left) { |
| 25 | 25 | node->value = cv_node->value; |
| 26 | - | |
| 27 | 26 | node->left = node->right = NULL; |
| 28 | 27 | } else { |
| 29 | 28 | if (maxCatCount > 0) |
| ... | ... | @@ -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 | 65 | static void writeRecursive(FileStorage &fs, const Node *node, int maxCatCount) |
| 44 | 66 | { |
| 45 | 67 | bool hasChildren = node->left ? true : false; |
| ... | ... | @@ -119,16 +141,17 @@ class BoostedForestClassifier : public Classifier |
| 119 | 141 | while (node->left) { |
| 120 | 142 | if (representation->maxCatCount() > 1) { |
| 121 | 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 | 145 | } else { |
| 124 | 146 | double val = representation->evaluate(image, node->featureIdx); |
| 125 | 147 | node = val <= node->threshold ? node->left : node->right; |
| 126 | 148 | } |
| 127 | 149 | } |
| 150 | + qDebug("value: %f", node->value); | |
| 128 | 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 | 157 | int numFeatures() const |
| ... | ... | @@ -146,6 +169,17 @@ class BoostedForestClassifier : public Classifier |
| 146 | 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 | 183 | void write(FileStorage &fs) const |
| 150 | 184 | { |
| 151 | 185 | fs << "stageThreshold" << threshold; | ... | ... |
openbr/plugins/classification/cascade.cpp
| ... | ... | @@ -154,10 +154,16 @@ class CascadeClassifier : public Classifier |
| 154 | 154 | |
| 155 | 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 | 169 | int numFeatures() const |
| ... | ... | @@ -175,6 +181,16 @@ class CascadeClassifier : public Classifier |
| 175 | 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 | 194 | void write(FileStorage &fs) const |
| 179 | 195 | { |
| 180 | 196 | fs << "stages" << "["; |
| ... | ... | @@ -196,7 +212,7 @@ private: |
| 196 | 212 | if (!imgHandler.getPos(pos)) |
| 197 | 213 | qFatal("Cannot get another positive sample!"); |
| 198 | 214 | |
| 199 | - if (classify(pos) == 1.0f) { | |
| 215 | + if (classify(pos) > 0.0f) { | |
| 200 | 216 | printf("POS current samples: %d\r", images.size()); |
| 201 | 217 | images.append(pos); |
| 202 | 218 | labels.append(1.0f); |
| ... | ... | @@ -212,7 +228,7 @@ private: |
| 212 | 228 | if (!imgHandler.getNeg(neg)) |
| 213 | 229 | qFatal("Cannot get another negative sample!"); |
| 214 | 230 | |
| 215 | - if (classify(neg) == 1.0f) { | |
| 231 | + if (classify(neg) > 0.0f) { | |
| 216 | 232 | printf("NEG current samples: %d\r", images.size() - posCount); |
| 217 | 233 | images.append(neg); |
| 218 | 234 | labels.append(0.0f); | ... | ... |
openbr/plugins/imgproc/slidingwindow.cpp
| ... | ... | @@ -19,8 +19,10 @@ |
| 19 | 19 | #include <openbr/plugins/openbr_internal.h> |
| 20 | 20 | #include <openbr/core/opencvutils.h> |
| 21 | 21 | #include <openbr/core/qtutils.h> |
| 22 | +#include <openbr/core/cascade.h> | |
| 22 | 23 | |
| 23 | 24 | #include <opencv2/highgui/highgui.hpp> |
| 25 | +#include <opencv2/imgproc/imgproc.hpp> | |
| 24 | 26 | |
| 25 | 27 | using namespace cv; |
| 26 | 28 | |
| ... | ... | @@ -38,8 +40,20 @@ class SlidingWindowTransform : public Transform |
| 38 | 40 | Q_OBJECT |
| 39 | 41 | |
| 40 | 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 | 49 | Q_PROPERTY(QString cascadeDir READ get_cascadeDir WRITE set_cascadeDir RESET reset_cascadeDir STORED false) |
| 42 | 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 | 57 | BR_PROPERTY(QString, cascadeDir, "") |
| 44 | 58 | |
| 45 | 59 | void train(const TemplateList &data) |
| ... | ... | @@ -49,12 +63,104 @@ class SlidingWindowTransform : public Transform |
| 49 | 63 | |
| 50 | 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 | 153 | void load(QDataStream &stream) |
| 56 | 154 | { |
| 57 | 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 | 164 | return; |
| 59 | 165 | } |
| 60 | 166 | |
| ... | ... | @@ -62,17 +168,17 @@ class SlidingWindowTransform : public Transform |
| 62 | 168 | { |
| 63 | 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 | 175 | FileStorage fs(filename.toStdString(), FileStorage::WRITE); |
| 70 | 176 | |
| 71 | 177 | if (!fs.isOpened()) { |
| 72 | - qWarning("Unable to open file: %s", qPrintable(filename)); | |
| 178 | + qWarning("Unable to open file: %s", qPrintable(filename)); | |
| 73 | 179 | return; |
| 74 | - } | |
| 75 | - | |
| 180 | + } | |
| 181 | + | |
| 76 | 182 | fs << FileStorage::getDefaultObjectName(filename.toStdString()) << "{"; |
| 77 | 183 | |
| 78 | 184 | classifier->write(fs); | ... | ... |
openbr/plugins/representation/mblbp.cpp
| ... | ... | @@ -58,7 +58,6 @@ class MBLBPRepresentation : public Representation |
| 58 | 58 | return result; |
| 59 | 59 | } |
| 60 | 60 | |
| 61 | - void write(FileStorage &fs, const Mat &featureMap); | |
| 62 | 61 | int numFeatures() const { return features.size(); } |
| 63 | 62 | Size preWindowSize() const { return Size(winWidth, winHeight); } |
| 64 | 63 | Size postWindowSize() const { return Size(winWidth + 1, winHeight + 1); } |
| ... | ... | @@ -69,7 +68,6 @@ class MBLBPRepresentation : public Representation |
| 69 | 68 | Feature() { rect = Rect(0, 0, 0, 0); } |
| 70 | 69 | Feature( int offset, int x, int y, int _block_w, int _block_h ); |
| 71 | 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 | 72 | Rect rect; |
| 75 | 73 | int p[16]; |
| ... | ... | @@ -79,20 +77,6 @@ class MBLBPRepresentation : public Representation |
| 79 | 77 | |
| 80 | 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 | 80 | MBLBPRepresentation::Feature::Feature( int offset, int x, int y, int _blockWidth, int _blockHeight ) |
| 97 | 81 | { |
| 98 | 82 | Rect tr = rect = cvRect(x, y, _blockWidth, _blockHeight); | ... | ... |