Commit c1a1921a62621a0358b41c0c5950061e04bcec09
Merge branch 'master' of https://github.com/biometrics/openbr
Showing
5 changed files
with
153 additions
and
52 deletions
openbr/openbr_plugin.cpp
| ... | ... | @@ -234,6 +234,11 @@ void File::appendRect(const QRectF &rect) |
| 234 | 234 | m_metadata["Rects"] = newRects; |
| 235 | 235 | } |
| 236 | 236 | |
| 237 | +void File::appendRect(const Rect &rect) | |
| 238 | +{ | |
| 239 | + appendRect(OpenCVUtils::fromRect(rect)); | |
| 240 | +} | |
| 241 | + | |
| 237 | 242 | void File::appendRects(const QList<QRectF> &rects) |
| 238 | 243 | { |
| 239 | 244 | QList<QVariant> newRects = m_metadata["Rects"].toList(); |
| ... | ... | @@ -242,6 +247,11 @@ void File::appendRects(const QList<QRectF> &rects) |
| 242 | 247 | m_metadata["Rects"] = newRects; |
| 243 | 248 | } |
| 244 | 249 | |
| 250 | +void File::appendRects(const QList<Rect> &rects) | |
| 251 | +{ | |
| 252 | + appendRects(OpenCVUtils::fromRects(rects)); | |
| 253 | +} | |
| 254 | + | |
| 245 | 255 | /* File - private methods */ |
| 246 | 256 | void File::init(const QString &file) |
| 247 | 257 | { | ... | ... |
openbr/openbr_plugin.h
| ... | ... | @@ -41,8 +41,6 @@ |
| 41 | 41 | #include <QVector> |
| 42 | 42 | #include <opencv2/core/core.hpp> |
| 43 | 43 | #include <openbr/openbr.h> |
| 44 | -#include <openbr/core/qtutils.h> | |
| 45 | -#include <openbr/core/opencvutils.h> | |
| 46 | 44 | |
| 47 | 45 | /*! |
| 48 | 46 | * \defgroup cpp_plugin_sdk C++ Plugin SDK |
| ... | ... | @@ -222,7 +220,11 @@ struct BR_EXPORT File |
| 222 | 220 | template <typename T> |
| 223 | 221 | void setList(const QString &key, const QList<T> &value) |
| 224 | 222 | { |
| 225 | - set(key, QtUtils::toVariantList(value)); | |
| 223 | + QVariantList variantList; | |
| 224 | + variantList.reserve(value.size()); | |
| 225 | + foreach (const T &item, value) | |
| 226 | + variantList << item; | |
| 227 | + set(key, variantList); | |
| 226 | 228 | } |
| 227 | 229 | |
| 228 | 230 | inline void remove(const QString &key) { m_metadata.remove(key); } /*!< \brief Remove the metadata key. */ |
| ... | ... | @@ -315,9 +317,9 @@ struct BR_EXPORT File |
| 315 | 317 | QList<QRectF> namedRects() const; /*!< \brief Returns rects convertible from metadata values. */ |
| 316 | 318 | QList<QRectF> rects() const; /*!< \brief Returns the file's rects list. */ |
| 317 | 319 | void appendRect(const QRectF &rect); /*!< \brief Adds a rect to the file's rect list. */ |
| 318 | - void appendRect(const cv::Rect &rect) { appendRect(OpenCVUtils::fromRect(rect)); } /*!< \brief Adds a rect to the file's rect list. */ | |
| 320 | + void appendRect(const cv::Rect &rect); /*!< \brief Adds a rect to the file's rect list. */ | |
| 319 | 321 | void appendRects(const QList<QRectF> &rects); /*!< \brief Adds rects to the file's rect list. */ |
| 320 | - void appendRects(const QList<cv::Rect> &rects) { appendRects(OpenCVUtils::fromRects(rects)); } /*!< \brief Adds rects to the file's rect list. */ | |
| 322 | + void appendRects(const QList<cv::Rect> &rects); /*!< \brief Adds rects to the file's rect list. */ | |
| 321 | 323 | inline void clearRects() { m_metadata["Rects"] = QList<QVariant>(); } /*!< \brief Clears the file's rect list. */ |
| 322 | 324 | inline void setRects(const QList<QRectF> &rects) { clearRects(); appendRects(rects); } /*!< \brief Overwrites the file's rect list. */ |
| 323 | 325 | inline void setRects(const QList<cv::Rect> &rects) { clearRects(); appendRects(rects); } /*!< \brief Overwrites the file's rect list. */ | ... | ... |
openbr/plugins/eigen3.cpp
| ... | ... | @@ -302,11 +302,13 @@ class LDATransform : public Transform |
| 302 | 302 | Q_PROPERTY(int directLDA READ get_directLDA WRITE set_directLDA RESET reset_directLDA STORED false) |
| 303 | 303 | Q_PROPERTY(float directDrop READ get_directDrop WRITE set_directDrop RESET reset_directDrop STORED false) |
| 304 | 304 | Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) |
| 305 | + Q_PROPERTY(bool isBinary READ get_isBinary WRITE set_isBinary RESET reset_isBinary STORED false) | |
| 305 | 306 | BR_PROPERTY(float, pcaKeep, 0.98) |
| 306 | 307 | BR_PROPERTY(bool, pcaWhiten, false) |
| 307 | 308 | BR_PROPERTY(int, directLDA, 0) |
| 308 | 309 | BR_PROPERTY(float, directDrop, 0.1) |
| 309 | 310 | BR_PROPERTY(QString, inputVariable, "Label") |
| 311 | + BR_PROPERTY(bool, isBinary, false) | |
| 310 | 312 | |
| 311 | 313 | int dimsOut; |
| 312 | 314 | Eigen::VectorXf mean; |
| ... | ... | @@ -316,7 +318,6 @@ class LDATransform : public Transform |
| 316 | 318 | { |
| 317 | 319 | // creates "Label" |
| 318 | 320 | TemplateList trainingSet = TemplateList::relabel(_trainingSet, inputVariable); |
| 319 | - | |
| 320 | 321 | int instances = trainingSet.size(); |
| 321 | 322 | |
| 322 | 323 | // Perform PCA dimensionality reduction |
| ... | ... | @@ -450,6 +451,34 @@ class LDATransform : public Transform |
| 450 | 451 | // Compute final projection matrix |
| 451 | 452 | projection = ((space2.eVecs.transpose() * space1.eVecs.transpose()) * pca.eVecs.transpose()).transpose(); |
| 452 | 453 | dimsOut = dim2; |
| 454 | + | |
| 455 | + if (isBinary) { | |
| 456 | + assert(dimsOut == 1); | |
| 457 | + TemplateList projected; | |
| 458 | + float posVal = 0; | |
| 459 | + float negVal = 0; | |
| 460 | + for (int i = 0; i < trainingSet.size(); i++) { | |
| 461 | + Template t; | |
| 462 | + project(trainingSet[i],t); | |
| 463 | + //Note: the positive class is assumed to be 0 b/c it will | |
| 464 | + // typically be the first gallery template in the TemplateList structure | |
| 465 | + if (classes[i] == 0) | |
| 466 | + posVal += t.m().at<float>(0,0); | |
| 467 | + else if (classes[i] == 1) | |
| 468 | + negVal += t.m().at<float>(0,0); | |
| 469 | + else | |
| 470 | + qFatal("Binary mode only supports two class problems."); | |
| 471 | + } | |
| 472 | + posVal /= classCounts[0]; | |
| 473 | + negVal /= classCounts[1]; | |
| 474 | + | |
| 475 | + if (posVal < negVal) { | |
| 476 | + //Ensure positive value is supposed to be > 0 after projection | |
| 477 | + Eigen::MatrixXf invert = Eigen::MatrixXf::Ones(dimsIn,1); | |
| 478 | + invert *= -1; | |
| 479 | + projection = invert.transpose() * projection; | |
| 480 | + } | |
| 481 | + } | |
| 453 | 482 | } |
| 454 | 483 | |
| 455 | 484 | void project(const Template &src, Template &dst) const |
| ... | ... | @@ -462,6 +491,10 @@ class LDATransform : public Transform |
| 462 | 491 | |
| 463 | 492 | // Do projection |
| 464 | 493 | outMap = projection.transpose() * (inMap - mean); |
| 494 | + | |
| 495 | + if (isBinary) { | |
| 496 | + dst.file.set("conf",dst.m().at<float>(0,0)); | |
| 497 | + } | |
| 465 | 498 | } |
| 466 | 499 | |
| 467 | 500 | void store(QDataStream &stream) const | ... | ... |
openbr/plugins/slidingwindow.cpp
| ... | ... | @@ -8,11 +8,29 @@ |
| 8 | 8 | |
| 9 | 9 | using namespace cv; |
| 10 | 10 | |
| 11 | +namespace br | |
| 12 | +{ | |
| 13 | + | |
| 11 | 14 | // Because MSVC doesn't provide a round() function in math.h |
| 12 | 15 | static int round(float x) { return (floor(x + 0.5)); } |
| 13 | - | |
| 14 | -namespace br | |
| 16 | +// Find avg aspect ratio | |
| 17 | +static float getAspectRatio(const TemplateList &data) | |
| 15 | 18 | { |
| 19 | + double tempRatio = 0; | |
| 20 | + int ratioCnt = 0; | |
| 21 | + | |
| 22 | + foreach (const Template &tmpl, data) { | |
| 23 | + QList<Rect> posRects = OpenCVUtils::toRects(tmpl.file.rects()); | |
| 24 | + foreach (const Rect &posRect, posRects) { | |
| 25 | + if (posRect.x + posRect.width >= tmpl.m().cols || posRect.y + posRect.height >= tmpl.m().rows || posRect.x < 0 || posRect.y < 0) { | |
| 26 | + continue; | |
| 27 | + } | |
| 28 | + tempRatio += (float)posRect.width / (float)posRect.height; | |
| 29 | + ratioCnt += 1; | |
| 30 | + } | |
| 31 | + } | |
| 32 | + return tempRatio / (double)ratioCnt; | |
| 33 | +} | |
| 16 | 34 | |
| 17 | 35 | /*! |
| 18 | 36 | * \ingroup transforms |
| ... | ... | @@ -25,23 +43,19 @@ class SlidingWindowTransform : public Transform |
| 25 | 43 | Q_OBJECT |
| 26 | 44 | Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false) |
| 27 | 45 | Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) |
| 28 | - Q_PROPERTY(double scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) | |
| 29 | 46 | Q_PROPERTY(int stepSize READ get_stepSize WRITE set_stepSize RESET reset_stepSize STORED false) |
| 30 | - Q_PROPERTY(bool takeLargestScale READ get_takeLargestScale WRITE set_takeLargestScale RESET reset_takeLargestScale STORED false) | |
| 47 | + Q_PROPERTY(bool takeFirst READ get_takeFirst WRITE set_takeFirst RESET reset_takeFirst STORED false) | |
| 31 | 48 | Q_PROPERTY(bool negSamples READ get_negSamples WRITE set_negSamples RESET reset_negSamples STORED false) |
| 32 | 49 | Q_PROPERTY(int negToPosRatio READ get_negToPosRatio WRITE set_negToPosRatio RESET reset_negToPosRatio STORED false) |
| 33 | 50 | Q_PROPERTY(double maxOverlap READ get_maxOverlap WRITE set_maxOverlap RESET reset_maxOverlap STORED false) |
| 34 | - Q_PROPERTY(float aspectRatio READ get_aspectRatio WRITE set_aspectRatio RESET reset_aspectRatio STORED true) | |
| 35 | 51 | Q_PROPERTY(int windowWidth READ get_windowWidth WRITE set_windowWidth RESET reset_windowWidth STORED false) |
| 36 | 52 | BR_PROPERTY(br::Transform *, transform, NULL) |
| 37 | 53 | BR_PROPERTY(int, minSize, 8) |
| 38 | - BR_PROPERTY(double, scaleFactor, 0.75) | |
| 39 | 54 | BR_PROPERTY(int, stepSize, 1) |
| 40 | - BR_PROPERTY(bool, takeLargestScale, true) | |
| 55 | + BR_PROPERTY(bool, takeFirst, true) | |
| 41 | 56 | BR_PROPERTY(bool, negSamples, true) |
| 42 | 57 | BR_PROPERTY(int, negToPosRatio, 1) |
| 43 | 58 | BR_PROPERTY(double, maxOverlap, 0) |
| 44 | - BR_PROPERTY(float, aspectRatio, 1) | |
| 45 | 59 | BR_PROPERTY(int, windowWidth, 24) |
| 46 | 60 | |
| 47 | 61 | public: |
| ... | ... | @@ -50,24 +64,14 @@ private: |
| 50 | 64 | |
| 51 | 65 | void train(const TemplateList &data) |
| 52 | 66 | { |
| 67 | + // only calculate if the work hasn't been done | |
| 68 | + aspectRatio = data.first().file.get<float>("aspectRatio", -1); | |
| 69 | + if (aspectRatio == -1) | |
| 70 | + aspectRatio = getAspectRatio(data); | |
| 71 | + | |
| 53 | 72 | if (transform->trainable) { |
| 54 | - double tempRatio = 0; | |
| 55 | - int ratioCnt = 0; | |
| 56 | 73 | TemplateList full; |
| 57 | 74 | |
| 58 | - //First find avg aspect ratio | |
| 59 | - foreach (const Template &tmpl, data) { | |
| 60 | - QList<Rect> posRects = OpenCVUtils::toRects(tmpl.file.rects()); | |
| 61 | - foreach (const Rect &posRect, posRects) { | |
| 62 | - if (posRect.x + posRect.width >= tmpl.m().cols || posRect.y + posRect.height >= tmpl.m().rows || posRect.x < 0 || posRect.y < 0) { | |
| 63 | - continue; | |
| 64 | - } | |
| 65 | - tempRatio += (float)posRect.width / (float)posRect.height; | |
| 66 | - ratioCnt += 1; | |
| 67 | - } | |
| 68 | - } | |
| 69 | - aspectRatio = tempRatio / (double)ratioCnt; | |
| 70 | - | |
| 71 | 75 | foreach (const Template &tmpl, data) { |
| 72 | 76 | QList<Rect> posRects = OpenCVUtils::toRects(tmpl.file.rects()); |
| 73 | 77 | QList<Rect> negRects; |
| ... | ... | @@ -132,6 +136,72 @@ private: |
| 132 | 136 | if (src.file.getBool("Train", false)) return; |
| 133 | 137 | |
| 134 | 138 | dst.file.clearRects(); |
| 139 | + int windowHeight = (int) round((float) windowWidth / aspectRatio); | |
| 140 | + int scale = src.file.get<float>("scale", 1); | |
| 141 | + | |
| 142 | + for (double y = 0; y + windowHeight < src.m().rows; y += stepSize) { | |
| 143 | + for (double x = 0; x + windowWidth < src.m().cols; x += stepSize) { | |
| 144 | + Rect window(x, y, windowWidth, windowHeight); | |
| 145 | + Template windowMat(src.file, Mat(src, window)); | |
| 146 | + Template detect; | |
| 147 | + transform->project(windowMat, detect); | |
| 148 | + float conf = detect.file.get<float>("conf"); | |
| 149 | + | |
| 150 | + // the result will be in the Label | |
| 151 | + if (conf > 0) { | |
| 152 | + dst.file.appendRect(QRectF((float) x * scale, (float) y * scale, (float) windowWidth * scale, (float) windowHeight * scale)); | |
| 153 | + QList<float> confidences = dst.file.getList<float>("Confidences", QList<float>()); | |
| 154 | + confidences.append(conf); | |
| 155 | + dst.file.setList<float>("Confidences", confidences); | |
| 156 | + if (takeFirst) | |
| 157 | + return; | |
| 158 | + } | |
| 159 | + } | |
| 160 | + } | |
| 161 | + } | |
| 162 | + | |
| 163 | + float aspectRatio; | |
| 164 | +}; | |
| 165 | + | |
| 166 | +BR_REGISTER(Transform, SlidingWindowTransform) | |
| 167 | + | |
| 168 | +/*! | |
| 169 | + * \ingroup transforms | |
| 170 | + * \brief . | |
| 171 | + * \author Austin Blanton \cite imaus10 | |
| 172 | + */ | |
| 173 | +class BuildScalesTransform : public Transform | |
| 174 | +{ | |
| 175 | + Q_OBJECT | |
| 176 | + Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false) | |
| 177 | + Q_PROPERTY(double scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) | |
| 178 | + Q_PROPERTY(bool takeLargestScale READ get_takeLargestScale WRITE set_takeLargestScale RESET reset_takeLargestScale STORED false) | |
| 179 | + Q_PROPERTY(int windowWidth READ get_windowWidth WRITE set_windowWidth RESET reset_windowWidth STORED false) | |
| 180 | + BR_PROPERTY(br::Transform *, transform, NULL) | |
| 181 | + BR_PROPERTY(double, scaleFactor, 0.75) | |
| 182 | + BR_PROPERTY(bool, takeLargestScale, true) | |
| 183 | + BR_PROPERTY(int, windowWidth, 24) | |
| 184 | + | |
| 185 | +public: | |
| 186 | + BuildScalesTransform() : Transform(false, true) {} | |
| 187 | +private: | |
| 188 | + | |
| 189 | + void train(const TemplateList &data) | |
| 190 | + { | |
| 191 | + aspectRatio = getAspectRatio(data); | |
| 192 | + // have to make a copy b/c data is const | |
| 193 | + TemplateList cp = data; | |
| 194 | + cp.first().file.set("aspectRatio", aspectRatio); | |
| 195 | + if (transform->trainable) | |
| 196 | + transform->train(cp); | |
| 197 | + } | |
| 198 | + | |
| 199 | + void project(const Template &src, Template &dst) const | |
| 200 | + { | |
| 201 | + dst = src; | |
| 202 | + // do not scale images during training | |
| 203 | + if (src.file.getBool("Train", false)) return; | |
| 204 | + | |
| 135 | 205 | int rows = src.m().rows; |
| 136 | 206 | int cols = src.m().cols; |
| 137 | 207 | int windowHeight = (int) round((float) windowWidth / aspectRatio); |
| ... | ... | @@ -140,34 +210,20 @@ private: |
| 140 | 210 | startScale = round((float) rows / (float) windowHeight); |
| 141 | 211 | else |
| 142 | 212 | startScale = round((float) cols / (float) windowWidth); |
| 143 | - | |
| 144 | 213 | for (float scale = startScale; scale >= 1.0; scale -= (1.0 - scaleFactor)) { |
| 145 | - Mat scaleImg; | |
| 214 | + Template scaleImg(src.file, Mat()); | |
| 215 | + scaleImg.file.set("scale", scale); | |
| 146 | 216 | resize(src, scaleImg, Size(round(cols / scale), round(rows / scale))); |
| 147 | - | |
| 148 | - for (double y = 0; y + windowHeight < scaleImg.rows; y += stepSize) { | |
| 149 | - for (double x = 0; x + windowWidth < scaleImg.cols; x += stepSize) { | |
| 150 | -qDebug() << "x=" << x << "\ty=" << y; | |
| 151 | - Rect window(x, y, windowWidth, windowHeight); | |
| 152 | - Template windowMat(src.file, Mat(scaleImg, window)); | |
| 153 | - Template detect; | |
| 154 | - transform->project(windowMat, detect); | |
| 155 | - // the result will be in the Label | |
| 156 | - if (detect.file.get<QString>("Label") == "pos") { | |
| 157 | - dst.file.appendRect(QRectF((float) x * scale, (float) y * scale, (float) windowWidth * scale, (float) windowHeight * scale)); | |
| 158 | - float confidence = detect.file.get<float>("Dist"); | |
| 159 | - QList<float> confidences = dst.file.getList<float>("Confidences", QList<float>()); | |
| 160 | - confidences.append(confidence); | |
| 161 | - dst.file.setList<float>("Confidences", confidences); | |
| 162 | - if (takeLargestScale) return; | |
| 163 | - } | |
| 164 | - } | |
| 165 | - } | |
| 217 | + transform->project(scaleImg, dst); | |
| 218 | + if (takeLargestScale && !dst.file.rects().empty()) | |
| 219 | + return; | |
| 166 | 220 | } |
| 167 | 221 | } |
| 222 | + | |
| 223 | + float aspectRatio; | |
| 168 | 224 | }; |
| 169 | 225 | |
| 170 | -BR_REGISTER(Transform, SlidingWindowTransform) | |
| 226 | +BR_REGISTER(Transform, BuildScalesTransform) | |
| 171 | 227 | |
| 172 | 228 | /*! |
| 173 | 229 | * \ingroup transforms | ... | ... |
openbr/plugins/svm.cpp
| ... | ... | @@ -157,7 +157,7 @@ private: |
| 157 | 157 | dst = src; |
| 158 | 158 | float prediction = svm.predict(src.m().reshape(1, 1), returnDFVal); |
| 159 | 159 | if (returnDFVal) { |
| 160 | - dst.file.set("Dist", prediction); | |
| 160 | + dst.file.set("conf", prediction); | |
| 161 | 161 | // positive values ==> first class |
| 162 | 162 | // negative values ==> second class |
| 163 | 163 | prediction = prediction > 0 ? 0 : 1; | ... | ... |