Commit e74ebed50a2a475dd84174c293189e35f82e8beb

Authored by Scott Klum
2 parents 8058f81d df3adb6f

Merge branch 'master' of https://github.com/biometrics/openbr

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 = &quot;.*&quot;, 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
... ... @@ -52,11 +52,6 @@ private:
52 52 {
53 53 (void) stream;
54 54 }
55   -
56   - void init()
57   - {
58   - TimeVaryingTransform::init();
59   - }
60 55 };
61 56  
62 57 BR_REGISTER(Transform, AggregateFrames)
... ...
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());
... ...