Commit e74ebed50a2a475dd84174c293189e35f82e8beb
Merge branch 'master' of https://github.com/biometrics/openbr
Showing
9 changed files
with
40 additions
and
40 deletions
CHANGELOG.md
| 1 | 1 | 0.4.0 - ??/??/?? |
| 2 | 2 | ================ |
| 3 | +* Added -evalLandmarking and -plotLandmarking for evaluating and plotting landmarking accuracy (#9) | |
| 3 | 4 | * Added -evalDetection and -plotDetection for evaluating and plotting object detection accuracy (#9) |
| 4 | 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 | 228 | QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") + |
| 229 | 229 | (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + |
| 230 | 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 | 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 | 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 | 236 | QString(", xlab=\"False Accept Rate\", ylab=\"False Reject Rate\") + geom_abline(alpha=0.5, colour=\"grey\", linetype=\"dashed\") + theme_minimal()") + |
| 237 | 237 | (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + |
| 238 | 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 | 241 | p.file.write(qPrintable(QString("qplot(X, data=SD, geom=\"histogram\", fill=Y, position=\"identity\", alpha=I(1/2)") + |
| 242 | 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 | 355 | "rm(data)\n" |
| 356 | 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 | 365 | return p.finalize(show); |
| 361 | 366 | } | ... | ... |
openbr/core/resource.h
| ... | ... | @@ -40,6 +40,9 @@ class DefaultResourceMaker : public ResourceMaker<T> |
| 40 | 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 | 46 | template <typename T> |
| 44 | 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 | 265 | * - <i>destination</i><tt>.R</tt> which is the auto-generated R script used to render the figures. |
| 266 | 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 | 274 | * \param num_files Number of <tt>.csv</tt> files. |
| 269 | 275 | * \param files <tt>.csv</tt> files created using \ref br_eval. |
| 270 | 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 | 304 | * \brief Renders landmarking performance figures for a set of <tt>.csv</tt> files created by \ref br_eval_landmarking. |
| 299 | 305 | * |
| 300 | 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 | 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 | 434 | if (!Globals->useGui) |
| 435 | 435 | return; |
| 436 | 436 | |
| 437 | - TimeVaryingTransform::init(); | |
| 438 | - | |
| 439 | 437 | if (displayBuffer) |
| 440 | 438 | delete displayBuffer; |
| 441 | 439 | displayBuffer = new QPixmap(); |
| ... | ... | @@ -714,7 +712,6 @@ public: |
| 714 | 712 | target_wait = 1000.0 / targetFPS; |
| 715 | 713 | timer.start(); |
| 716 | 714 | last_time = timer.elapsed(); |
| 717 | - TimeVaryingTransform::init(); | |
| 718 | 715 | } |
| 719 | 716 | |
| 720 | 717 | protected: |
| ... | ... | @@ -768,7 +765,6 @@ public: |
| 768 | 765 | { |
| 769 | 766 | initialized = false; |
| 770 | 767 | framesSeen = 0; |
| 771 | - TimeVaryingTransform::init(); | |
| 772 | 768 | } |
| 773 | 769 | |
| 774 | 770 | protected: | ... | ... |
openbr/plugins/openbr_internal.h
| ... | ... | @@ -110,12 +110,12 @@ public: |
| 110 | 110 | |
| 111 | 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 | 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 | 121 | // Get a compile failure if this isn't here to go along with the other |
| ... | ... | @@ -144,21 +144,13 @@ public: |
| 144 | 144 | return this->clone(); |
| 145 | 145 | } |
| 146 | 146 | |
| 147 | - void init() | |
| 148 | - { | |
| 149 | - delete timeInvariantAlias; | |
| 150 | - timeInvariantAlias = new TimeInvariantWrapperTransform(this); | |
| 151 | - } | |
| 152 | - | |
| 153 | 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 | 169 | virtual void project(const Template &src, Template &dst) const |
| 178 | 170 | { |
| 179 | 171 | if (timeVarying()) { |
| 180 | - timeInvariantAlias->project(src,dst); | |
| 172 | + timeInvariantAlias.project(src,dst); | |
| 181 | 173 | return; |
| 182 | 174 | } |
| 183 | 175 | _project(src, dst); |
| ... | ... | @@ -186,7 +178,7 @@ public: |
| 186 | 178 | virtual void project(const TemplateList &src, TemplateList &dst) const |
| 187 | 179 | { |
| 188 | 180 | if (timeVarying()) { |
| 189 | - timeInvariantAlias->project(src,dst); | |
| 181 | + timeInvariantAlias.project(src,dst); | |
| 190 | 182 | return; |
| 191 | 183 | } |
| 192 | 184 | _project(src, dst); |
| ... | ... | @@ -203,10 +195,6 @@ public: |
| 203 | 195 | isTimeVarying = isTimeVarying || transform->timeVarying(); |
| 204 | 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 | 54 | { |
| 55 | 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 | 60 | Resource<StasmCascadeClassifier> stasmCascadeResource; |
| 58 | 61 | |
| 59 | 62 | void init() |
| ... | ... | @@ -69,14 +72,20 @@ class StasmTransform : public UntrainableTransform |
| 69 | 72 | StasmCascadeClassifier *stasmCascade = stasmCascadeResource.acquire(); |
| 70 | 73 | |
| 71 | 74 | int foundface; |
| 75 | + int nLandmarks = stasm_NLANDMARKS; | |
| 72 | 76 | float landmarks[2 * stasm_NLANDMARKS]; |
| 73 | 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 | 84 | stasmCascadeResource.release(stasmCascade); |
| 76 | 85 | |
| 77 | 86 | if (!foundface) qWarning("No face found in %s", qPrintable(src.file.fileName())); |
| 78 | 87 | else { |
| 79 | - for (int i = 0; i < stasm_NLANDMARKS; i++) | |
| 88 | + for (int i = 0; i < nLandmarks; i++) | |
| 80 | 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 | 1300 | if (!transform) |
| 1301 | 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 | 1303 | trainable = transform->trainable; |
| 1309 | 1304 | |
| 1310 | 1305 | basis.setParent(this->parent()); | ... | ... |