Commit e74ebed50a2a475dd84174c293189e35f82e8beb

Authored by Scott Klum
2 parents 8058f81d df3adb6f

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

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