Commit e74ebed50a2a475dd84174c293189e35f82e8beb
Merge branch 'master' of https://github.com/biometrics/openbr
Showing
9 changed files
with
40 additions
and
40 deletions
CHANGELOG.md
| 1 | 0.4.0 - ??/??/?? | 1 | 0.4.0 - ??/??/?? |
| 2 | ================ | 2 | ================ |
| 3 | +* Added -evalLandmarking and -plotLandmarking for evaluating and plotting landmarking accuracy (#9) | ||
| 3 | * Added -evalDetection and -plotDetection for evaluating and plotting object detection accuracy (#9) | 4 | * Added -evalDetection and -plotDetection for evaluating and plotting object detection accuracy (#9) |
| 4 | * Deprecated Transform::backProject | 5 | * Deprecated Transform::backProject |
| 5 | 6 |
openbr/core/plot.cpp
| @@ -228,7 +228,7 @@ bool Plot(const QStringList &files, const File &destination, bool show) | @@ -228,7 +228,7 @@ bool Plot(const QStringList &files, const File &destination, bool show) | ||
| 228 | QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") + | 228 | QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") + |
| 229 | (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + | 229 | (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + |
| 230 | (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + | 230 | (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + |
| 231 | - QString(" + scale_x_log10(labels=percent) + scale_y_continuous(labels=percent) + annotation_logticks(sides=\"b\")\n\n"))); | 231 | + QString(" + scale_x_log10(labels=percent, limits=c(min(DET$X),1)) + scale_y_continuous(labels=percent) + annotation_logticks(sides=\"b\")\n\n"))); |
| 232 | 232 | ||
| 233 | p.file.write(qPrintable(QString("qplot(X, Y, data=DET%1").arg((p.major.smooth || p.minor.smooth) ? ", geom=\"smooth\", method=loess, level=0.99" : ", geom=\"line\"") + | 233 | p.file.write(qPrintable(QString("qplot(X, Y, data=DET%1").arg((p.major.smooth || p.minor.smooth) ? ", geom=\"smooth\", method=loess, level=0.99" : ", geom=\"line\"") + |
| 234 | (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) + | 234 | (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) + |
| @@ -236,7 +236,7 @@ bool Plot(const QStringList &files, const File &destination, bool show) | @@ -236,7 +236,7 @@ bool Plot(const QStringList &files, const File &destination, bool show) | ||
| 236 | QString(", xlab=\"False Accept Rate\", ylab=\"False Reject Rate\") + geom_abline(alpha=0.5, colour=\"grey\", linetype=\"dashed\") + theme_minimal()") + | 236 | QString(", xlab=\"False Accept Rate\", ylab=\"False Reject Rate\") + geom_abline(alpha=0.5, colour=\"grey\", linetype=\"dashed\") + theme_minimal()") + |
| 237 | (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + | 237 | (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + |
| 238 | (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + | 238 | (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + |
| 239 | - QString(" + scale_x_log10(labels=percent) + scale_y_log10(labels=percent) + annotation_logticks()\n\n"))); | 239 | + QString(" + scale_x_log10(labels=percent, limits=c(min(DET$X),1)) + scale_y_log10(labels=percent) + annotation_logticks()\n\n"))); |
| 240 | 240 | ||
| 241 | p.file.write(qPrintable(QString("qplot(X, data=SD, geom=\"histogram\", fill=Y, position=\"identity\", alpha=I(1/2)") + | 241 | p.file.write(qPrintable(QString("qplot(X, data=SD, geom=\"histogram\", fill=Y, position=\"identity\", alpha=I(1/2)") + |
| 242 | QString(", xlab=\"Score%1\"").arg((p.flip ? p.major.size : p.minor.size) > 1 ? " / " + (p.flip ? p.major.header : p.minor.header) : QString()) + | 242 | QString(", xlab=\"Score%1\"").arg((p.flip ? p.major.size : p.minor.size) > 1 ? " / " + (p.flip ? p.major.header : p.minor.header) : QString()) + |
| @@ -355,7 +355,12 @@ bool PlotLandmarking(const QStringList &files, const File &destination, bool sho | @@ -355,7 +355,12 @@ bool PlotLandmarking(const QStringList &files, const File &destination, bool sho | ||
| 355 | "rm(data)\n" | 355 | "rm(data)\n" |
| 356 | "\n"); | 356 | "\n"); |
| 357 | 357 | ||
| 358 | - p.file.write("ggplot(Box, aes(factor(X),Y)) + geom_boxplot() + geom_jitter(size=1.33,alpha=0.66) + scale_x_discrete(\"Landmark\") + scale_y_log10(\"Normalized Error\", breaks=c(0.01,0.1,1,10)) + annotation_logticks(sides=\"l\")\n\n"); | 358 | + p.file.write(qPrintable(QString("ggplot(Box, aes(Y,%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) + |
| 359 | + QString(" + annotation_logticks(sides=\"b\") + stat_ecdf() + scale_x_log10(\"Normalized Error\", breaks=c(0.001,0.01,0.1,1,10)) + scale_y_continuous(\"Cumulative Density\", label=percent) + theme_minimal()\n\n"))); | ||
| 360 | + p.file.write(qPrintable(QString("ggplot(Box, aes(factor(X), Y%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) + | ||
| 361 | + QString("+ annotation_logticks(sides=\"l\") + geom_boxplot(alpha=0.5) + geom_jitter(size=1, alpha=0.5) + scale_x_discrete(\"Landmark\") + scale_y_log10(\"Normalized Error\", breaks=c(0.01,0.1,1,10)) + theme_minimal()\n\n"))); | ||
| 362 | + p.file.write(qPrintable(QString("ggplot(Box, aes(factor(X), Y%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) + | ||
| 363 | + QString("+ annotation_logticks(sides=\"l\") + geom_violin(alpha=0.5) + scale_x_discrete(\"Landmark\") + scale_y_log10(\"Normalized Error\", breaks=c(0.001,0.01,0.1,1,10))\n\n"))); | ||
| 359 | 364 | ||
| 360 | return p.finalize(show); | 365 | return p.finalize(show); |
| 361 | } | 366 | } |
openbr/core/resource.h
| @@ -40,6 +40,9 @@ class DefaultResourceMaker : public ResourceMaker<T> | @@ -40,6 +40,9 @@ class DefaultResourceMaker : public ResourceMaker<T> | ||
| 40 | T *make() const { return new T(); } | 40 | T *make() const { return new T(); } |
| 41 | }; | 41 | }; |
| 42 | 42 | ||
| 43 | +// Manage multiple copies of a limited resource in a thread-safe manner. | ||
| 44 | +// TimeVaryingTransform makes a strong assumption that ResourceMaker::Make | ||
| 45 | +// is only called in acquire, not in the constructor. | ||
| 43 | template <typename T> | 46 | template <typename T> |
| 44 | class Resource | 47 | class Resource |
| 45 | { | 48 | { |
openbr/openbr.h
| @@ -265,6 +265,12 @@ BR_EXPORT const char *br_objects(const char *abstractions = ".*", const char *im | @@ -265,6 +265,12 @@ BR_EXPORT const char *br_objects(const char *abstractions = ".*", const char *im | ||
| 265 | * - <i>destination</i><tt>.R</tt> which is the auto-generated R script used to render the figures. | 265 | * - <i>destination</i><tt>.R</tt> which is the auto-generated R script used to render the figures. |
| 266 | * - <i>destination</i><tt>.pdf</tt> which has all of the figures in one file multi-page file. | 266 | * - <i>destination</i><tt>.pdf</tt> which has all of the figures in one file multi-page file. |
| 267 | * | 267 | * |
| 268 | + * OpenBR uses file and folder names to automatically determine the plot legend. | ||
| 269 | + * For example, let's consider the case where three algorithms (<tt>A</tt>, <tt>B</tt>, & <tt>C</tt>) were each evaluated on two datasets (<tt>Y</tt> & <tt>Z</tt>). | ||
| 270 | + * The suggested way to plot these experiments on the same graph is to create a folder named <tt>Algorithm_Dataset</tt> that contains the six <tt>.csv</tt> files produced by \ref br_eval: <tt>A_Y.csv</tt>, <tt>A_Z.csv</tt>, <tt>B_Y.csv</tt>, <tt>B_Z.csv</tt>, <tt>C_Y.csv</tt>, & <tt>C_Z.csv</tt>. | ||
| 271 | + * The '<tt>_</tt>' character plays a special role in determining the legend title(s) and value(s). | ||
| 272 | + * In this case, <tt>A</tt>, <tt>B</tt>, & <tt>C</tt> will be identified as different values of type <tt>Algorithm</tt>, and each will be assigned its own color; <tt>Y</tt> & <tt>Z</tt> will be identified as different values of type Dataset, and each will be assigned its own line style. | ||
| 273 | + * | ||
| 268 | * \param num_files Number of <tt>.csv</tt> files. | 274 | * \param num_files Number of <tt>.csv</tt> files. |
| 269 | * \param files <tt>.csv</tt> files created using \ref br_eval. | 275 | * \param files <tt>.csv</tt> files created using \ref br_eval. |
| 270 | * \param destination Basename for the resulting figures. | 276 | * \param destination Basename for the resulting figures. |
| @@ -298,7 +304,9 @@ BR_EXPORT bool br_plot_detection(int num_files, const char *files[], const char | @@ -298,7 +304,9 @@ BR_EXPORT bool br_plot_detection(int num_files, const char *files[], const char | ||
| 298 | * \brief Renders landmarking performance figures for a set of <tt>.csv</tt> files created by \ref br_eval_landmarking. | 304 | * \brief Renders landmarking performance figures for a set of <tt>.csv</tt> files created by \ref br_eval_landmarking. |
| 299 | * | 305 | * |
| 300 | * In order of their output, the figures are: | 306 | * In order of their output, the figures are: |
| 301 | - * -# Normalized error box plots (Box) | 307 | + * -# Cumulative landmarks less than normalized error (CD) |
| 308 | + * -# Normalized error box and whisker plots (Box) | ||
| 309 | + * -# Normalized error violin plots (Violin) | ||
| 302 | * | 310 | * |
| 303 | * Landmarking error is normalized against the distance between two predifined points, usually inter-ocular distance (IOD). | 311 | * Landmarking error is normalized against the distance between two predifined points, usually inter-ocular distance (IOD). |
| 304 | * | 312 | * |
openbr/plugins/frames.cpp
openbr/plugins/gui.cpp
| @@ -434,8 +434,6 @@ public: | @@ -434,8 +434,6 @@ public: | ||
| 434 | if (!Globals->useGui) | 434 | if (!Globals->useGui) |
| 435 | return; | 435 | return; |
| 436 | 436 | ||
| 437 | - TimeVaryingTransform::init(); | ||
| 438 | - | ||
| 439 | if (displayBuffer) | 437 | if (displayBuffer) |
| 440 | delete displayBuffer; | 438 | delete displayBuffer; |
| 441 | displayBuffer = new QPixmap(); | 439 | displayBuffer = new QPixmap(); |
| @@ -714,7 +712,6 @@ public: | @@ -714,7 +712,6 @@ public: | ||
| 714 | target_wait = 1000.0 / targetFPS; | 712 | target_wait = 1000.0 / targetFPS; |
| 715 | timer.start(); | 713 | timer.start(); |
| 716 | last_time = timer.elapsed(); | 714 | last_time = timer.elapsed(); |
| 717 | - TimeVaryingTransform::init(); | ||
| 718 | } | 715 | } |
| 719 | 716 | ||
| 720 | protected: | 717 | protected: |
| @@ -768,7 +765,6 @@ public: | @@ -768,7 +765,6 @@ public: | ||
| 768 | { | 765 | { |
| 769 | initialized = false; | 766 | initialized = false; |
| 770 | framesSeen = 0; | 767 | framesSeen = 0; |
| 771 | - TimeVaryingTransform::init(); | ||
| 772 | } | 768 | } |
| 773 | 769 | ||
| 774 | protected: | 770 | protected: |
openbr/plugins/openbr_internal.h
| @@ -110,12 +110,12 @@ public: | @@ -110,12 +110,12 @@ public: | ||
| 110 | 110 | ||
| 111 | virtual void project(const Template &src, Template &dst) const | 111 | virtual void project(const Template &src, Template &dst) const |
| 112 | { | 112 | { |
| 113 | - timeInvariantAlias->project(src,dst); | 113 | + timeInvariantAlias.project(src,dst); |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | virtual void project(const TemplateList &src, TemplateList &dst) const | 116 | virtual void project(const TemplateList &src, TemplateList &dst) const |
| 117 | { | 117 | { |
| 118 | - timeInvariantAlias->project(src,dst); | 118 | + timeInvariantAlias.project(src,dst); |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | // Get a compile failure if this isn't here to go along with the other | 121 | // Get a compile failure if this isn't here to go along with the other |
| @@ -144,21 +144,13 @@ public: | @@ -144,21 +144,13 @@ public: | ||
| 144 | return this->clone(); | 144 | return this->clone(); |
| 145 | } | 145 | } |
| 146 | 146 | ||
| 147 | - void init() | ||
| 148 | - { | ||
| 149 | - delete timeInvariantAlias; | ||
| 150 | - timeInvariantAlias = new TimeInvariantWrapperTransform(this); | ||
| 151 | - } | ||
| 152 | - | ||
| 153 | protected: | 147 | protected: |
| 154 | - Transform * timeInvariantAlias; | ||
| 155 | - TimeVaryingTransform(bool independent = true, bool trainable = true) : Transform(independent, trainable) | 148 | + // Since copies aren't actually made until project is called, we can set up |
| 149 | + // timeInvariantAlias in the constructor. | ||
| 150 | + TimeInvariantWrapperTransform timeInvariantAlias; | ||
| 151 | + TimeVaryingTransform(bool independent = true, bool trainable = true) : Transform(independent, trainable), timeInvariantAlias(this) | ||
| 156 | { | 152 | { |
| 157 | - timeInvariantAlias = NULL; | ||
| 158 | - } | ||
| 159 | - ~TimeVaryingTransform() | ||
| 160 | - { | ||
| 161 | - delete timeInvariantAlias; | 153 | + // |
| 162 | } | 154 | } |
| 163 | }; | 155 | }; |
| 164 | 156 | ||
| @@ -177,7 +169,7 @@ public: | @@ -177,7 +169,7 @@ public: | ||
| 177 | virtual void project(const Template &src, Template &dst) const | 169 | virtual void project(const Template &src, Template &dst) const |
| 178 | { | 170 | { |
| 179 | if (timeVarying()) { | 171 | if (timeVarying()) { |
| 180 | - timeInvariantAlias->project(src,dst); | 172 | + timeInvariantAlias.project(src,dst); |
| 181 | return; | 173 | return; |
| 182 | } | 174 | } |
| 183 | _project(src, dst); | 175 | _project(src, dst); |
| @@ -186,7 +178,7 @@ public: | @@ -186,7 +178,7 @@ public: | ||
| 186 | virtual void project(const TemplateList &src, TemplateList &dst) const | 178 | virtual void project(const TemplateList &src, TemplateList &dst) const |
| 187 | { | 179 | { |
| 188 | if (timeVarying()) { | 180 | if (timeVarying()) { |
| 189 | - timeInvariantAlias->project(src,dst); | 181 | + timeInvariantAlias.project(src,dst); |
| 190 | return; | 182 | return; |
| 191 | } | 183 | } |
| 192 | _project(src, dst); | 184 | _project(src, dst); |
| @@ -203,10 +195,6 @@ public: | @@ -203,10 +195,6 @@ public: | ||
| 203 | isTimeVarying = isTimeVarying || transform->timeVarying(); | 195 | isTimeVarying = isTimeVarying || transform->timeVarying(); |
| 204 | trainable = trainable || transform->trainable; | 196 | trainable = trainable || transform->trainable; |
| 205 | } | 197 | } |
| 206 | - | ||
| 207 | - // If we are time varying, set up timeInvariantAlias | ||
| 208 | - if (this->timeVarying()) | ||
| 209 | - TimeVaryingTransform::init(); | ||
| 210 | } | 198 | } |
| 211 | 199 | ||
| 212 | /*! | 200 | /*! |
openbr/plugins/stasm4.cpp
| @@ -54,6 +54,9 @@ class StasmTransform : public UntrainableTransform | @@ -54,6 +54,9 @@ class StasmTransform : public UntrainableTransform | ||
| 54 | { | 54 | { |
| 55 | Q_OBJECT | 55 | Q_OBJECT |
| 56 | 56 | ||
| 57 | + Q_PROPERTY(bool stasm3Format READ get_stasm3Format WRITE set_stasm3Format RESET reset_stasm3Format STORED false) | ||
| 58 | + BR_PROPERTY(bool, stasm3Format, false) | ||
| 59 | + | ||
| 57 | Resource<StasmCascadeClassifier> stasmCascadeResource; | 60 | Resource<StasmCascadeClassifier> stasmCascadeResource; |
| 58 | 61 | ||
| 59 | void init() | 62 | void init() |
| @@ -69,14 +72,20 @@ class StasmTransform : public UntrainableTransform | @@ -69,14 +72,20 @@ class StasmTransform : public UntrainableTransform | ||
| 69 | StasmCascadeClassifier *stasmCascade = stasmCascadeResource.acquire(); | 72 | StasmCascadeClassifier *stasmCascade = stasmCascadeResource.acquire(); |
| 70 | 73 | ||
| 71 | int foundface; | 74 | int foundface; |
| 75 | + int nLandmarks = stasm_NLANDMARKS; | ||
| 72 | float landmarks[2 * stasm_NLANDMARKS]; | 76 | float landmarks[2 * stasm_NLANDMARKS]; |
| 73 | stasm_search_single(&foundface, landmarks, reinterpret_cast<const char*>(src.m().data), src.m().cols, src.m().rows, *stasmCascade, NULL, NULL); | 77 | stasm_search_single(&foundface, landmarks, reinterpret_cast<const char*>(src.m().data), src.m().cols, src.m().rows, *stasmCascade, NULL, NULL); |
| 74 | 78 | ||
| 79 | + if (stasm3Format) { | ||
| 80 | + nLandmarks = 76; | ||
| 81 | + stasm_convert_shape(landmarks, nLandmarks); | ||
| 82 | + } | ||
| 83 | + | ||
| 75 | stasmCascadeResource.release(stasmCascade); | 84 | stasmCascadeResource.release(stasmCascade); |
| 76 | 85 | ||
| 77 | if (!foundface) qWarning("No face found in %s", qPrintable(src.file.fileName())); | 86 | if (!foundface) qWarning("No face found in %s", qPrintable(src.file.fileName())); |
| 78 | else { | 87 | else { |
| 79 | - for (int i = 0; i < stasm_NLANDMARKS; i++) | 88 | + for (int i = 0; i < nLandmarks; i++) |
| 80 | dst.file.appendPoint(QPointF(landmarks[2 * i], landmarks[2 * i + 1])); | 89 | dst.file.appendPoint(QPointF(landmarks[2 * i], landmarks[2 * i + 1])); |
| 81 | } | 90 | } |
| 82 | 91 |
openbr/plugins/stream.cpp
| @@ -1300,11 +1300,6 @@ public: | @@ -1300,11 +1300,6 @@ public: | ||
| 1300 | if (!transform) | 1300 | if (!transform) |
| 1301 | return; | 1301 | return; |
| 1302 | 1302 | ||
| 1303 | - // Set up timeInvariantAlias | ||
| 1304 | - // this is only safe because copies are actually made in project | ||
| 1305 | - // calls, not during init. | ||
| 1306 | - TimeVaryingTransform::init(); | ||
| 1307 | - | ||
| 1308 | trainable = transform->trainable; | 1303 | trainable = transform->trainable; |
| 1309 | 1304 | ||
| 1310 | basis.setParent(this->parent()); | 1305 | basis.setParent(this->parent()); |