Commit 1ade6b11faeae9496b9e00bf96655adb07760739

Authored by bklare
2 parents 3b2c84da 5d6385a5

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

CMakeLists.txt
... ... @@ -142,6 +142,7 @@ endif()
142 142 # Look for extensions to OpenBR
143 143 set(BR_THIRDPARTY_PLUGINS_DIR CACHE PATH "")
144 144 set(BR_THIRDPARTY_APPS_DIR CACHE PATH "")
  145 +set(BR_THIRDPARTY_DIR CACHE PATH "")
145 146 mark_as_advanced(BR_THIRDPARTY_PLUGINS_DIR)
146 147 mark_as_advanced(BR_THIRDPARTY_APPS_DIR)
147 148  
... ...
README.md
... ... @@ -8,4 +8,4 @@ Identify the latest [tagged release](https://github.com/biometrics/openbr/releas
8 8 $ git submodule init
9 9 $ git submodule update
10 10  
11   -**[Build Instructions](http://openbiometrics.org/doxygen/latest/installation.html)**
  11 +**[Build Instructions](http://openbiometrics.org/docs/install/)**
... ...
docs/docs/api_docs/c_api/functions.md
... ... @@ -635,8 +635,8 @@ textSize | float | Size of text for title, legend and axes
635 635 xTitle/yTitle | [QString] | Title for x/y axis
636 636 xLog/yLog | bool | Plot log scale for x/y axis
637 637 xLimits/yLimits | [QPointF] | Set x/y axis limits, ex. xLimits=(lower,upper)
638   -xLabels/yLabels | [QString] | Labels for ticks on x/y axis, ex. xLabeles=percent or xLabels=c(1,5,10)
639   -xBreaks/yBreaks | [QString] | Specify breaks/ticks on x/y axis, ex. xBreaks=pretty_breaks(n=10) or xBreaks=c(1,5,10)
  638 +xLabels/yLabels | [QString] | Labels for ticks on x/y axis, ex. xLabeles=percent or xLabels=(1,5,10)
  639 +xBreaks/yBreaks | [QString] | Specify breaks/ticks on x/y axis, ex. xBreaks=pretty_breaks(n=10) or xBreaks=(1,5,10)
640 640  
641 641 If specifying plot options it is a good idea to wrap the destination file in single quotes to avoid parsing errors.
642 642 The example below plots plots the six br_eval results in the Algorithm_Dataset folder described above, sets the number of legend columns and specifies some options for the CMC plot.
... ...
openbr/core/plot.cpp
... ... @@ -23,6 +23,25 @@ using namespace cv;
23 23 namespace br
24 24 {
25 25  
  26 +// Flattens file metadata to an R list()
  27 +static QString toRList(const File &opts)
  28 +{
  29 + QStringList retValues;
  30 + QString format = "%1=%2";
  31 + foreach (const QString &key, opts.localKeys()) {
  32 + const QString value = QtUtils::toString(opts.value(key));
  33 + if (value.startsWith("("))
  34 + retValues.append(format.arg(key, "\"c" + value + "\""));
  35 + else if (value == "true")
  36 + retValues.append(format.arg(key, "TRUE"));
  37 + else if (value == "false")
  38 + retValues.append(format.arg(key, "FALSE"));
  39 + else
  40 + retValues.append(format.arg(key, "\"" + value + "\""));
  41 + }
  42 + return retValues.join(",");
  43 +}
  44 +
26 45 static QStringList getPivots(const QString &file, bool headers)
27 46 {
28 47 QString str;
... ... @@ -31,14 +50,6 @@ static QStringList getPivots(const QString &file, bool headers)
31 50 return str.split("_");
32 51 }
33 52  
34   -static QString getScale(const QString &mode, const QString &title, int vals)
35   -{
36   - if (vals > 12) return " + scale_"+mode+"_discrete(\""+title+"\")";
37   - else if (vals > 11) return " + scale_"+mode+"_brewer(\""+title+"\", palette=\"Set3\")";
38   - else if (vals > 9) return " + scale_"+mode+"_brewer(\""+title+"\", palette=\"Paired\")";
39   - else return " + scale_"+mode+"_brewer(\""+title+"\", palette=\"Set1\")";
40   -}
41   -
42 53 // Custom sorting method to ensure datasets are ordered nicely
43 54 static bool sortFiles(const QString &fileA, const QString &fileB)
44 55 {
... ... @@ -51,10 +62,6 @@ struct RPlot
51 62 QFile file;
52 63 QStringList pivotHeaders;
53 64 QVector< QSet<QString> > pivotItems;
54   - float confidence; // confidence interval for plotting across splits
55   - int ncol; // Number of columns for plot legends
56   -
57   - bool flip;
58 65  
59 66 struct Pivot
60 67 {
... ... @@ -68,7 +75,7 @@ struct RPlot
68 75  
69 76 Pivot major, minor;
70 77  
71   - RPlot(QStringList files, const File &destination, bool isEvalFormat = true)
  78 + RPlot(QStringList files, const File &destination)
72 79 {
73 80 if (files.isEmpty()) qFatal("Empty file list.");
74 81 qSort(files.begin(), files.end(), sortFiles);
... ... @@ -83,13 +90,9 @@ struct RPlot
83 90 bool success = file.open(QFile::WriteOnly);
84 91 if (!success) qFatal("Failed to open %s for writing.", qPrintable(file.fileName()));
85 92  
86   - file.write("# Load libraries\n"
87   - "library(ggplot2)\n"
88   - "library(gplots)\n"
89   - "library(reshape)\n"
90   - "library(scales)\n"
91   - "\n"
92   - "# Read CSVs\n"
  93 + // Copy plot_utils.R into output script with source()
  94 + file.write(qPrintable(QString("source(\"%1\")\n\n").arg(Globals->sdkPath + "/share/openbr/plotting/plot_utils.R")));
  95 + file.write("# Read CSVs\n"
93 96 "data <- NULL\n");
94 97  
95 98 // Read files and retrieve pivots
... ... @@ -123,8 +126,6 @@ struct RPlot
123 126 }
124 127  
125 128 const QString &smooth = destination.get<QString>("smooth", "");
126   - confidence = destination.get<float>("confidence", 95) / 100.0;
127   -
128 129 major.smooth = !smooth.isEmpty() && (major.header == smooth) && (major.size > 1);
129 130 minor.smooth = !smooth.isEmpty() && (minor.header == smooth) && (minor.size > 1);
130 131 if (major.smooth) major.size = 1;
... ... @@ -132,68 +133,19 @@ struct RPlot
132 133 if (major.size < minor.size)
133 134 std::swap(major, minor);
134 135  
135   - ncol = destination.get<int>("ncol", major.size > 1 ? major.size : (minor.header.isEmpty() ? major.size : minor.size));
136   - flip = minor.header == "Algorithm";
137   - // Format data
138   - if (isEvalFormat)
139   - file.write(qPrintable(QString("\n"
140   - "# Split data into individual plots\n"
141   - "plot_index = which(names(data)==\"Plot\")\n"
142   - "Metadata <- data[grep(\"Metadata\",data$Plot),-c(1)]\n"
143   - "IM <- data[grep(\"IM\",data$Plot),-c(1)]\n"
144   - "GM <- data[grep(\"GM\",data$Plot),-c(1)]\n"
145   - "DET <- data[grep(\"DET\",data$Plot),-c(1)]\n"
146   - "IET <- data[grep(\"IET\",data$Plot),-c(1)]\n"
147   - "FAR <- data[grep(\"FAR\",data$Plot),-c(1)]\n"
148   - "FRR <- data[grep(\"FRR\",data$Plot),-c(1)]\n"
149   - "SD <- data[grep(\"SD\",data$Plot),-c(1)]\n"
150   - "TF <- data[grep(\"TF\",data$Plot),-c(1)]\n"
151   - "FT <- data[grep(\"FT\",data$Plot),-c(1)]\n"
152   - "CT <- data[grep(\"CT\",data$Plot),-c(1)]\n"
153   - "BC <- data[grep(\"BC\",data$Plot),-c(1)]\n"
154   - "TS <- data[grep(\"TS\",data$Plot),-c(1)]\n"
155   - "CMC <- data[grep(\"CMC\",data$Plot),-c(1)]\n"
156   - "FAR$Error <- \"FAR\"\n"
157   - "FRR$Error <- \"FRR\"\n"
158   - "ERR <- rbind(FAR, FRR)\n"
159   - "rm(data, FAR, FRR)\n"
160   - "\n"
161   - "# Format data\n"
162   - "Metadata$Y<-factor(Metadata$Y, levels=c(\"Genuine\",\"Impostor\",\"Ignored\",\"Gallery\",\"Probe\"))\n"
163   - "IM$Y <- as.character(IM$Y)\n"
164   - "GM$Y <- as.character(GM$Y)\n"
165   - "DET$Y <- as.numeric(as.character(DET$Y))\n"
166   - "IET$Y <- as.numeric(as.character(IET$Y))\n"
167   - "ERR$Y <- as.numeric(as.character(ERR$Y))\n"
168   - "SD$Y <- as.factor(unique(as.character(SD$Y)))\n"
169   - "TF$Y <- as.numeric(as.character(TF$Y))\n"
170   - "FT$Y <- as.numeric(as.character(FT$Y))\n"
171   - "CT$Y <- as.numeric(as.character(CT$Y))\n"
172   - "BC$Y <- as.numeric(as.character(BC$Y))\n"
173   - "TS$Y <- as.character(TS$Y)\n"
174   - "CMC$Y <- as.numeric(as.character(CMC$Y))\n"
175   - "\n"
176   - "if (%1) {\n\tsummarySE <- function(data=NULL, measurevar, groupvars=NULL, na.rm=FALSE, conf.interval=%3, .drop=TRUE) {\n\t\t"
177   - "require(plyr)\n\n\t\tlength2 <- function (x, na.rm=FALSE) {\n\t\t\tif (na.rm) sum(!is.na(x))\n\t\t\telse length(x)"
178   - "\n\t\t}\n\n\t\tdatac <- ddply(data, groupvars, .drop=.drop, .fun = function(xx, col) {\n\t\t\t"
179   - "c(N=length2(xx[[col]], na.rm=na.rm), mean=mean(xx[[col]], na.rm=na.rm), sd=sd(xx[[col]], na.rm=na.rm))\n\t\t\t},"
180   - "\n\t\t\tmeasurevar\n\t\t)\n\n\t\tdatac <- rename(datac, c(\"mean\" = measurevar))\n\t\tdatac$se <- datac$sd / sqrt(datac$N)"
181   - "\n\t\tciMult <- qt(conf.interval/2 + .5, datac$N-1)\n\t\tdatac$ci <- datac$se * ciMult\n\n\t\t"
182   - "datac$upper <- if(datac[, measurevar] + datac$ci < 1) (datac[, measurevar] + datac$ci) else 1\n\t\t"
183   - "datac$lower <- if(datac[, measurevar] - datac$ci > 0) (datac[, measurevar] - datac$ci) else 0\n\n\t\treturn(datac)\n\t}\n\t"
184   - "DET <- summarySE(DET, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"))\n\t"
185   - "IET <- summarySE(IET, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"))\n\t"
186   - "CMC <- summarySE(CMC, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"))\n\t"
187   - "ERR <- summarySE(ERR, measurevar=\"X\", groupvars=c(\"Error\", \"%2\", \"Y\"))\n\t"
188   - "TF <- summarySE(TF, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"))\n\t"
189   - "FT <- summarySE(FT, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"))\n\t"
190   - "CT <- summarySE(CT, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"))\n}\n\n"
191   - "# Code to format FAR values\n"
192   - "far_names <- list('0.001'=\"FAR = 0.1%\", '0.01'=\"FAR = 1%\")\n"
193   - "far_labeller <- function(variable,value) { return(far_names[as.character(value)]) }\n"
194   - "\n").arg((major.smooth || minor.smooth) ? "TRUE" : "FALSE",
195   - major.size > 1 ? major.header : (minor.header.isEmpty() ? major.header : minor.header),
196   - QString::number(confidence))));
  136 + // Set variables in R
  137 + file.write(qPrintable(QString("\nconfidence <- %1\n").arg(destination.get<float>("confidence", 95) / 100.0)));
  138 + file.write(qPrintable(QString("ncol <- %1\n").arg(destination.get<int>("ncol", major.size > 1 ? major.size : (minor.header.isEmpty() ? major.size : minor.size)))));
  139 + file.write(qPrintable(QString("basename <- \"%1\"\n").arg(basename)));
  140 + file.write(qPrintable(QString("smooth <- %1\n").arg((major.smooth || minor.smooth) && (destination.get<float>("confidence", 95) / 100.0) != 0 ? "TRUE" : "FALSE")));
  141 + file.write(qPrintable(QString("csv <- %1\n").arg(destination.getBool("csv") ? "TRUE" : "FALSE")));
  142 + file.write(qPrintable(QString("majorHeader <- \"%1\"\n").arg(major.header)));
  143 + file.write(qPrintable(QString("majorSize <- %1\n").arg(major.size)));
  144 + file.write(qPrintable(QString("majorSmooth <- %1\n").arg(major.smooth ? "TRUE" : "FALSE")));
  145 + file.write(qPrintable(QString("minorHeader <- \"%1\"\n").arg(minor.header)));
  146 + file.write(qPrintable(QString("minorSize <- %1\n").arg(minor.size)));
  147 + file.write(qPrintable(QString("minorSmooth <- %1\n").arg(minor.smooth ? "TRUE" : "FALSE")));
  148 + file.write(qPrintable(QString("flip <- %1\n").arg(minor.header == "Algorithm" ? "TRUE" : "FALSE")));
197 149  
198 150 // Open output device
199 151 file.write(qPrintable(QString("\n"
... ... @@ -205,98 +157,6 @@ struct RPlot
205 157 "# Write figures\n");
206 158 }
207 159  
208   - void plotMetadata(bool csv)
209   - {
210   - file.write(qPrintable(QString("# Code to format TAR@FAR table\n"
211   - "algs <- unique(%4)\n"
212   - "algs <- algs[!duplicated(algs)]\n"
213   - "mat <- matrix(%1,nrow=6,ncol=length(algs),byrow=FALSE)\n"
214   - "colnames(mat) <- algs \n"
215   - "rownames(mat) <- c(\"FAR = 1e-06\", \"FAR = 1e-05\", \"FAR = 1e-04\", \"FAR = 1e-03\", \"FAR = 1e-02\", \"FAR = 1e-01\")\n"
216   - "TFtable <- as.table(mat)\n"
217   - "\n"
218   - "# Code to format FAR@TAR table\n"
219   - "mat <- matrix(%2,nrow=6,ncol=length(algs),byrow=FALSE)\n"
220   - "colnames(mat) <- algs \n"
221   - "rownames(mat) <- c(\"TAR = 0.40\", \"TAR = 0.50\", \"TAR = 0.65\", \"TAR = 0.75\", \"TAR = 0.85\", \"TAR = 0.95\")\n"
222   - "FTtable <- as.table(mat)\n"
223   - "\n"
224   - "# Code to format CMC Table\n"
225   - "mat <- matrix(%3,nrow=6,ncol=length(algs),byrow=FALSE)\n"
226   - "colnames(mat) <- algs \n"
227   - "rownames(mat) <- c(\"Rank 1\", \"Rank 5\", \"Rank 10\", \"Rank 20\", \"Rank 50\", \"Rank 100\")\n"
228   - "CMCtable <- as.table(mat)\n"
229   - "\n"
230   - "# Code to format Template Size Table\n"
231   - "if (nrow(TS) != 0) {\n\t"
232   - "mat <- matrix(TS$Y,nrow=1,ncol=length(algs),byrow=FALSE)\n\t"
233   - "colnames(mat) <- algs\n\t"
234   - "rownames(mat) <- c(\"Template Size (bytes):\")\n\t"
235   - "TStable <- as.table(mat)\n}"
236   - "\n").arg((major.smooth || minor.smooth) && confidence != 0 ? "paste(as.character(round(TF$Y, 3)), round(TF$ci, 3), sep=\"\\u00b1\")" : "TF$Y",
237   - (major.smooth || minor.smooth) && confidence != 0 ? "paste(as.character(round(FT$Y, 3)), round(FT$ci, 3), sep=\"\\u00b1\")" : "FT$Y",
238   - (major.smooth || minor.smooth) && confidence != 0 ? "paste(as.character(round(CT$Y, 3)), round(CT$ci, 3), sep=\"\\u00b1\")" : "CT$Y",
239   - (major.size > 1 && minor.size > 1) && !(major.smooth || minor.smooth) ? QString("paste(TF$%1, TF$%2, sep=\"_\")").arg(major.header, minor.header)
240   - : QString("TF$%1").arg(major.size > 1 ? major.header : (minor.header.isEmpty() ? major.header : minor.header)))));
241   -
242   - file.write("\n# Write metadata table\n");
243   - QString textplot = "MT <- as.data.frame(Metadata[c(1,2,3,4,5),])\n"
244   - "par(mfrow=c(4,1))\n"
245   - "plot.new()\n"
246   - "print(title(paste(\"%1 - %2\",date(),sep=\"\\n\")))\n"
247   - "mat <- matrix(MT$X[c(1,2)],ncol=2)\n"
248   - "colnames(mat) <- c(\"Gallery\", \"Probe\")\n"
249   - "imageTable <- as.table(mat)\n"
250   - "print(textplot(imageTable,show.rownames=FALSE))\n"
251   - "print(title(\"Images\"))\n"
252   - "mat <- matrix(MT$X[c(3,4,5)],ncol=3)\n"
253   - "colnames(mat) <- c(\"Genuine\", \"Impostor\", \"Ignored\")\n"
254   - "matchTable <- as.table(mat)\n"
255   - "print(textplot(matchTable,show.rownames=FALSE))\n"
256   - "print(title(\"Matches\"))\n"
257   - "plot.new()\n"
258   - "print(title(\"Gallery * Probe = Genuine + Impostor + Ignored\"))\n";
259   - file.write(qPrintable(textplot.arg(PRODUCT_NAME, PRODUCT_VERSION)));
260   -
261   - if (csv)
262   - textplot = QString("write.csv(TFtable,file=\"%1_TF.csv\")\n"
263   - "write.csv(FTtable,file=\"%1_FT.csv\")\n"
264   - "write.csv(CMCtable,file=\"%1_CMC.csv\")\n\n").arg(basename);
265   - else
266   - textplot = "plot.new()\n"
267   - "print(textplot(TFtable))\n"
268   - "print(title(\"Table of True Accept Rates at various False Accept Rates\"))\n"
269   - "print(textplot(FTtable))\n"
270   - "print(title(\"Table of False Accept Rates at various True Accept Rates\"))\n"
271   - "print(textplot(CMCtable))\n"
272   - "print(title(\"Table of retrieval rate at various ranks\"))\n"
273   - "if (nrow(TS) != 0) {\n\t"
274   - "print(textplot(TStable, cex=1.15))\n\t"
275   - "print(title(\"Template Size by Algorithm\"))\n}\n\n";
276   - file.write(qPrintable(textplot));
277   - }
278   -
279   - void qplot(QString geom, QString data, bool flipY, File opts)
280   - {
281   - file.write(qPrintable(QString("qplot(X, %1, data=%2, geom=\"%3\", main=\"%4\"").arg(flipY ? "1-Y" : "Y", data, geom, opts.get<QString>("title", "")) +
282   - (opts.contains("size") ? QString(", size=I(%1)").arg(opts.get<QString>("size")) : QString()) +
283   - (major.size > 1 ? QString(", colour=factor(%1)").arg(major.header) : QString()) +
284   - (minor.size > 1 ? QString(", linetype=factor(%1)").arg(minor.header) : QString()) +
285   - (QString(", xlab=\"%1\", ylab=\"%2\") + theme_minimal()").arg(opts.get<QString>("xTitle"), opts.get<QString>("yTitle"))) +
286   - ((major.smooth || minor.smooth) && confidence != 0 && data != "CMC" ? QString(" + geom_errorbar(data=%1[seq(1, NROW(%1), by = 29),], aes(x=X, ymin=%2), width=0.1, alpha=I(1/2))").arg(data, flipY ? "(1-lower), ymax=(1-upper)" : "lower, ymax=upper") : QString()) +
287   - (major.size > 1 ? getScale("colour", major.header, major.size) : QString()) +
288   - (minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(minor.header) : QString()) +
289   - (opts.getBool("xLog") ? QString(" + scale_x_log10(labels=%1, breaks=%2) + annotation_logticks(sides=\"b\")").arg(opts.get<QString>("xLabels", "trans_format(\"log10\", math_format())"), opts.get<QString>("xBreaks", "waiver()"))
290   - : QString(" + scale_x_continuous(labels=%1, breaks=%2)").arg(opts.get<QString>("xLabels", "percent"), opts.get<QString>("xBreaks", "pretty_breaks(n=10)"))) +
291   - (opts.getBool("yLog") ? QString(" + scale_y_log10(labels=%1, breaks=%2) + annotation_logticks(sides=\"l\")").arg(opts.get<QString>("yLabels", "trans_format(\"log10\", math_format())"), opts.get<QString>("yBreaks", "waiver()"))
292   - : QString(" + scale_y_continuous(labels=%1, breaks=%2)").arg(opts.get<QString>("yLabels", "percent"), opts.get<QString>("yBreaks", "pretty_breaks(n=10)"))) +
293   - (opts.contains("xLimits") ? QString(" + xlim%1").arg(QtUtils::toString(opts.get<QPointF>("xLimits", QPointF()))) : QString()) +
294   - (opts.contains("yLimits") ? QString(" + ylim%1").arg(QtUtils::toString(opts.get<QPointF>("yLimits", QPointF()))) : QString()) +
295   - QString(" + theme(legend.title = element_text(size = %1), legend.text = element_text(size = %1), plot.title = element_text(size = %1), axis.text = element_text(size = %1), axis.title.x = element_text(size = %1), axis.title.y = element_text(size = %1),").arg(QString::number(opts.get<float>("textSize",12))) +
296   - QString(" legend.position=%1, legend.background = element_rect(fill = 'white'), panel.grid.major = element_line(colour = \"gray\"), panel.grid.minor = element_line(colour = \"gray\", linetype = \"dashed\"))").arg(opts.contains("legendPosition") ? "c"+QtUtils::toString(opts.get<QPointF>("legendPosition")) : "'bottom'") +
297   - QString(" + guides(col=guide_legend(ncol=%1))\n\n").arg(ncol)));
298   - }
299   -
300 160 bool finalize(bool show = false)
301 161 {
302 162 file.write("dev.off()\n");
... ... @@ -315,13 +175,27 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
315 175 qDebug("Plotting %d file(s) to %s", files.size(), qPrintable(destination));
316 176  
317 177 RPlot p(files, destination);
  178 + p.file.write("\nformatData()\n\n");
  179 + p.file.write(qPrintable(QString("algs <- %1\n").arg((p.major.size > 1 && p.minor.size > 1) && !(p.major.smooth || p.minor.smooth) ? QString("paste(TF$%1, TF$%2, sep=\"_\")").arg(p.major.header, p.minor.header)
  180 + : QString("TF$%1").arg(p.major.size > 1 ? p.major.header : (p.minor.header.isEmpty() ? p.major.header : p.minor.header)))));
  181 + p.file.write("algs <- algs[!duplicated(algs)]\n");
  182 +
  183 + if (p.major.smooth || p.minor.smooth) {
  184 + QString groupvar = p.major.size > 1 ? p.major.header : (p.minor.header.isEmpty() ? p.major.header : p.minor.header);
  185 + foreach(const QString &data, QStringList() << "DET" << "IET" << "CMC" << "TF" << "FT" << "CT") {
  186 + p.file.write(qPrintable(QString("%1 <- summarySE(%1, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"), conf.interval=confidence)"
  187 + "\n").arg(data, groupvar)));
  188 + }
  189 + p.file.write(qPrintable(QString("%1 <- summarySE(%1, measurevar=\"X\", groupvars=c(\"Error\", \"%2\", \"Y\"), conf.interval=confidence)"
  190 + "\n\n").arg("ERR", groupvar)));
  191 + }
318 192  
319 193 // Use a br::file for simple storage of plot options
320 194 QMap<QString,File> optMap;
321 195 optMap.insert("rocOptions", File(QString("[xTitle=False Accept Rate,yTitle=True Accept Rate,xLog=true,yLog=false]")));
322 196 optMap.insert("detOptions", File(QString("[xTitle=False Accept Rate,yTitle=False Reject Rate,xLog=true,yLog=true]")));
323 197 optMap.insert("ietOptions", File(QString("[xTitle=False Positive Identification Rate (FPIR),yTitle=False Negative Identification Rate (FNIR),xLog=true,yLog=true]")));
324   - optMap.insert("cmcOptions", File(QString("[xTitle=Rank,yTitle=Retrieval Rate,xLog=true,yLog=false,size=1,xLabels=c(1,5,10,50,100),xBreaks=c(1,5,10,50,100)]")));
  198 + optMap.insert("cmcOptions", File(QString("[xTitle=Rank,yTitle=Retrieval Rate,xLog=true,yLog=false,size=1,xLabels=(1,5,10,50,100),xBreaks=(1,5,10,50,100)]")));
325 199  
326 200 foreach (const QString &key, optMap.keys()) {
327 201 const QStringList options = destination.get<QStringList>(key, QStringList());
... ... @@ -333,56 +207,33 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
333 207 }
334 208  
335 209 // optional plot metadata and accuracy tables
336   - if (destination.getBool("metadata", true))
337   - p.plotMetadata(destination.getBool("csv", false));
338   - p.qplot("line", "DET", true, optMap["rocOptions"]);
339   - p.qplot("line", "DET", false, optMap["detOptions"]);
340   - p.qplot("line", "IET", false, optMap["ietOptions"]);
341   - p.qplot("line", "CMC", false, optMap["cmcOptions"]);
342   -
343   - p.file.write(qPrintable(QString("qplot(X, data=SD, geom=\"histogram\", fill=Y, position=\"identity\", alpha=I(1/2)") +
344   - QString(", xlab=\"Score\", ylab=\"Frequency\"") +
345   - QString(") + scale_fill_manual(\"Ground Truth\", values=c(\"blue\", \"red\")) + theme_minimal() + scale_x_continuous(minor_breaks=NULL) + scale_y_continuous(minor_breaks=NULL) + theme(axis.text.y=element_blank(), axis.ticks=element_blank(), axis.text.x=element_text(angle=-90, hjust=0))") +
346   - (p.major.size > 1 ? (p.minor.size > 1 ? QString(" + facet_grid(%2 ~ %1, scales=\"free\")").arg((p.flip ? p.major.header : p.minor.header), (p.flip ? p.minor.header : p.major.header)) : QString(" + facet_wrap(~ %1, scales = \"free\")").arg(p.major.header)) : QString()) +
347   - QString(" + theme(aspect.ratio=1)\n\n")));
348   -
349   - p.file.write(qPrintable(QString("qplot(factor(%1)%2, data=BC, %3").arg(p.major.smooth ? (p.minor.header.isEmpty() ? "Algorithm" : p.minor.header) : p.major.header, (p.major.smooth || p.minor.smooth) ? ", Y" : "", (p.major.smooth || p.minor.smooth) ? "geom=\"boxplot\"" : "geom=\"bar\", position=\"dodge\", weight=Y") +
350   - (p.major.size > 1 ? QString(", fill=factor(%1)").arg(p.major.header) : QString()) +
351   - QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") +
352   - (p.major.size > 1 ? getScale("fill", p.major.header, p.major.size) : QString()) +
353   - (p.minor.size > 1 ? QString(" + facet_grid(%2 ~ X)").arg(p.minor.header) : QString(" + facet_grid(. ~ X, labeller=far_labeller)")) +
354   - QString(" + scale_y_continuous(labels=percent) + theme(legend.position=\"none\", axis.text.x=element_text(angle=-90, hjust=0))%1").arg((p.major.smooth || p.minor.smooth) ? "" : " + geom_text(data=BC, aes(label=Y, y=0.05))") + "\n\n"));
355   -
356   - p.file.write(qPrintable(QString("qplot(X, Y, data=ERR, geom=\"line\", linetype=Error") +
357   - ((p.flip ? p.major.size : p.minor.size) > 1 ? QString(", colour=factor(%1)").arg(p.flip ? p.major.header : p.minor.header) : QString()) +
358   - QString(", xlab=\"Score\", ylab=\"Error Rate\") + theme_minimal()") +
359   - ((p.flip ? p.major.size : p.minor.size) > 1 ? getScale("colour", p.flip ? p.major.header : p.minor.header, p.flip ? p.major.size : p.minor.size) : QString()) +
360   - QString(" + scale_y_log10(labels=percent) + annotation_logticks(sides=\"l\")") +
361   - ((p.flip ? p.minor.size : p.major.size) > 1 ? QString(" + facet_wrap(~ %1, scales=\"free_x\")").arg(p.flip ? p.minor.header : p.major.header) : QString()) +
362   - QString(" + theme(aspect.ratio=1)\n\n")));
363   -
364   - p.file.write(qPrintable(QString("if (nrow(IM) != 0) {\n\tlibrary(jpeg)\n\tlibrary(png)\n\tlibrary(grid)\n\t") +
365   - QString("multiplot <- function(..., plotlist=NULL, cols) {\n\t") +
366   - QString("\trequire(grid)\n\n\t\t# Make a list from the ... arguments and plotlist\n\t\tplots <- c(list(...), plotlist)\n") +
367   - QString("\t\tnumPlots = length(plots)\n\n\t\t# Make the panel\n\t\tplotCols = cols\n\t\tplotRows = ceiling(numPlots/plotCols)\n\n") +
368   - QString("\t\t# Set up the page\n\t\tgrid.newpage()\n\t\tpushViewport(viewport(layout = grid.layout(plotRows, plotCols)))\n\t\tvplayout <- function(x, y)\n\t\t\tviewport(layout.pos.row = x, layout.pos.col = y)\n\n") +
369   - QString("\t\t# Make each plot, in the correct location\n\t\tfor (i in 1:numPlots) {\n\t\t\tcurRow = ceiling(i/plotCols)\n\t\t\tcurCol = (i-1) %% plotCols + 1\n\t\t\tprint(plots[[i]], vp = vplayout(curRow, curCol))\n\t\t}\n\t}\n\n")));
370   -
371   - p.file.write(qPrintable(QString("\t# Print impostor matches above the EER\n\tfor (i in 1:nrow(IM)) {\n\t\tscore <- IM[i,1]\n\t\tfiles <- IM[i,2]\n\t\talg <- IM[i,3]\n\t\tfiles <- unlist(strsplit(files, \"[:]\"))\n\n\t\text1 <- unlist(strsplit(files[2], \"[.]\"))[2]\n\t\text2 <- unlist(strsplit(files[4], \"[.]\"))[2]\n\t\t") +
372   - QString("if (ext1 == \"jpg\" || ext1 == \"JPEG\" || ext1 == \"jpeg\" || ext1 == \"JPG\") {\n\t\t\timg1 <- readJPEG(files[2])\n\t\t} else if (ext1 == \"PNG\" || ext1 == \"png\") {\n\t\t\timg1 <- readPNG(files[2])\n\t\t} else if (ext1 == \"TIFF\" || ext1 == \"tiff\" || ext1 == \"TIF\" || ext1 == \"tif\") {\n\t\t\timg1 <- readTIFF(files[2])\n\t\t} else {\n\t\t\tnext\n\t\t}\n\t\tif (ext2 == \"jpg\" || ext2 == \"JPEG\" || ext2 == \"jpeg\" || ext2 == \"JPG\") {\n\t\t\timg2 <- readJPEG(files[4])\n\t\t} ") +
373   - QString("else if (ext2 == \"PNG\" || ext2 == \"png\") {\n\t\t\timg2 <- readPNG(files[4])\n\t\t} else if (ext2 == \"TIFF\" || ext2 == \"tiff\" || ext2 == \"TIF\" || ext2 == \"tif\") {\n\t\t\timg2 <- readTIFF(files[4])\n\t\t} else {\n\t\t\tnext\n\t\t}") +
374   - QString("\n\t\tname1 <- files[1]\n\t\tname2 <- files[3]\n\n\t\tg1 <- rasterGrob(img1, interpolate=TRUE)\n\t\tg2 <- rasterGrob(img2, interpolate=TRUE)\n\n\t\t") +
375   - QString("plot1 <- qplot(1:10, 1:10, geom=\"blank\") + annotation_custom(g1, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) + theme(axis.line=element_blank(), axis.text.x=element_blank(), axis.text.y=element_blank(), axis.ticks=element_blank(), panel.background=element_blank()) + labs(title=alg) + ylab(unlist(strsplit(files[2], \"[/]\"))[length(unlist(strsplit(files[2], \"[/]\")))]) + xlab(name1)\n\t\t") +
376   - QString("plot2 <- qplot(1:10, 1:10, geom=\"blank\") + annotation_custom(g2, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) + theme(axis.line=element_blank(), axis.text.x=element_blank(), axis.text.y=element_blank(), axis.ticks=element_blank(), panel.background=element_blank()) + labs(title=paste(\"Impostor score =\", score)) + ylab(unlist(strsplit(files[4], \"[/]\"))[length(unlist(strsplit(files[4], \"[/]\")))]) + xlab(name2)\n\n\t\t") +
377   - QString("multiplot(plot1, plot2, cols=2)\n\t}")));
378   -
379   - p.file.write(qPrintable(QString("\n\n\t# Print genuine matches below the EER\n\tfor (i in 1:nrow(GM)) {\n\t\tscore <- GM[i,1]\n\t\tfiles <- GM[i,2]\n\t\talg <- GM[i,3]\n\t\tfiles <- unlist(strsplit(files, \"[:]\"))\n\n\t\text1 <- unlist(strsplit(files[2], \"[.]\"))[2]\n\t\text2 <- unlist(strsplit(files[4], \"[.]\"))[2]\n\t\t") +
380   - QString("if (ext1 == \"jpg\" || ext1 == \"JPEG\" || ext1 == \"jpeg\" || ext1 == \"JPG\") {\n\t\t\timg1 <- readJPEG(files[2])\n\t\t} else if (ext1 == \"PNG\" || ext1 == \"png\") {\n\t\t\timg1 <- readPNG(files[2])\n\t\t} else if (ext1 == \"TIFF\" || ext1 == \"tiff\" || ext1 == \"TIF\" || ext1 == \"tif\") {\n\t\t\timg1 <- readTIFF(files[2])\n\t\t} else {\n\t\t\tnext\n\t\t}\n\t\tif (ext2 == \"jpg\" || ext2 == \"JPEG\" || ext2 == \"jpeg\" || ext2 == \"JPG\") {\n\t\t\timg2 <- readJPEG(files[4])\n\t\t} ") +
381   - QString("else if (ext2 == \"PNG\" || ext2 == \"png\") {\n\t\t\timg2 <- readPNG(files[4])\n\t\t} else if (ext2 == \"TIFF\" || ext2 == \"tiff\" || ext2 == \"TIF\" || ext2 == \"tif\") {\n\t\t\timg2 <- readTIFF(files[4])\n\t\t} else {\n\t\t\tnext\n\t\t}") +
382   - QString("\n\t\tname1 <- files[1]\n\t\tname2 <- files[3]\n\n\t\tg1 <- rasterGrob(img1, interpolate=TRUE)\n\t\tg2 <- rasterGrob(img2, interpolate=TRUE)\n\n\t\t") +
383   - QString("plot1 <- qplot(1:10, 1:10, geom=\"blank\") + annotation_custom(g1, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) + theme(axis.line=element_blank(), axis.text.x=element_blank(), axis.text.y=element_blank(), axis.ticks=element_blank(), panel.background=element_blank()) + labs(title=alg) + ylab(unlist(strsplit(files[2], \"[/]\"))[length(unlist(strsplit(files[2], \"[/]\")))]) + xlab(name1)\n\t\t") +
384   - QString("plot2 <- qplot(1:10, 1:10, geom=\"blank\") + annotation_custom(g2, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) + theme(axis.line=element_blank(), axis.text.x=element_blank(), axis.text.y=element_blank(), axis.ticks=element_blank(), panel.background=element_blank()) + labs(title=paste(\"Genuine score =\", score)) + ylab(unlist(strsplit(files[4], \"[/]\"))[length(unlist(strsplit(files[4], \"[/]\")))]) + xlab(name2)\n\n\t\t") +
385   - QString("multiplot(plot1, plot2, cols=2)\n\t}\n}\n\n")));
  210 + if (destination.getBool("metadata", true)) {
  211 + p.file.write("\n# Write metadata table\n");
  212 + p.file.write(qPrintable(QString("plotMetadata(metadata=Metadata, title=\"%1 - %2\")\n").arg(PRODUCT_NAME, PRODUCT_VERSION)));
  213 +
  214 + if (!destination.getBool("csv")) p.file.write("plot.new()\n");
  215 + QString table = "plotTable(tableData=%1, name=%2, labels=%3)\n";
  216 + p.file.write(qPrintable(table.arg("TF", "\"Table of True Accept Rates at various False Accept Rates\"",
  217 + "c(\"FAR = 1e-06\", \"FAR = 1e-05\", \"FAR = 1e-04\", \"FAR = 1e-03\", \"FAR = 1e-02\", \"FAR = 1e-01\")")));
  218 + p.file.write(qPrintable(table.arg("FT", "\"Table of False Accept Rates at various True Accept Rates\"",
  219 + "c(\"TAR = 0.40\", \"TAR = 0.50\", \"TAR = 0.65\", \"TAR = 0.75\", \"TAR = 0.85\", \"TAR = 0.95\")")));
  220 + p.file.write(qPrintable(table.arg("CT", "\"Table of retrieval rate at various ranks\"",
  221 + "c(\"Rank 1\", \"Rank 5\", \"Rank 10\", \"Rank 20\", \"Rank 50\", \"Rank 100\")")));
  222 + p.file.write(qPrintable(table.arg("TS", "\"Template Size by Algorithm\"",
  223 + "c(\"Template Size (bytes):\")")));
  224 + p.file.write("\n");
  225 + }
  226 +
  227 + // Write plots
  228 + QString plot = "plot <- plotLine(lineData=%1, options=list(%2), flipY=%3)\nplot\n";
  229 + p.file.write(qPrintable(QString(plot).arg("DET", toRList(optMap["rocOptions"]), "TRUE")));
  230 + p.file.write(qPrintable(QString(plot).arg("DET", toRList(optMap["detOptions"]), "FALSE")));
  231 + p.file.write(qPrintable(QString(plot).arg("IET", toRList(optMap["ietOptions"]), "FALSE")));
  232 + p.file.write(qPrintable(QString(plot).arg("CMC", toRList(optMap["cmcOptions"]), "FALSE")));
  233 + p.file.write("plot <- plotSD(sdData=SD)\nplot\n");
  234 + p.file.write("plot <- plotBC(bcData=BC)\nplot\n");
  235 + p.file.write("plot <- plotERR(errData=ERR)\nplot\n");
  236 + p.file.write("plotEERSamples(imData=IM, gmData=GM)\n\n");
386 237  
387 238 return p.finalize(show);
388 239 }
... ... @@ -420,7 +271,8 @@ bool filesHaveSinglePoint(const QStringList &amp;files) {
420 271 bool PlotDetection(const QStringList &files, const File &destination, bool show)
421 272 {
422 273 qDebug("Plotting %d detection file(s) to %s", files.size(), qPrintable(destination));
423   - RPlot p(files, destination, false);
  274 + RPlot p(files, destination);
  275 + p.file.write("\nformatData(type=\"detection\")\n\n");
424 276  
425 277 // Use a br::file for simple storage of plot options
426 278 QMap<QString,File> optMap;
... ... @@ -436,40 +288,26 @@ bool PlotDetection(const QStringList &amp;files, const File &amp;destination, bool show)
436 288 }
437 289 }
438 290  
439   - p.file.write("# Split data into individual plots\n"
440   - "plot_index = which(names(data)==\"Plot\")\n"
441   - "DiscreteROC <- data[grep(\"DiscreteROC\",data$Plot),-c(1)]\n"
442   - "ContinuousROC <- data[grep(\"ContinuousROC\",data$Plot),-c(1)]\n"
443   - "DiscretePR <- data[grep(\"DiscretePR\",data$Plot),-c(1)]\n"
444   - "ContinuousPR <- data[grep(\"ContinuousPR\",data$Plot),-c(1)]\n"
445   - "Overlap <- data[grep(\"Overlap\",data$Plot),-c(1)]\n"
446   - "AverageOverlap <- data[grep(\"AverageOverlap\",data$Plot),-c(1)]\n"
447   - "rm(data)\n"
448   - "\n");
449   -
450 291 QString plotType("line");
451 292 if (filesHaveSinglePoint(files))
452 293 plotType = QString("point");
453 294  
  295 + QString plot = "plot <- plotLine(lineData=%1, options=list(%2), flipY=%3, geometry=%4)\nplot\n";
454 296 foreach (const QString &type, QStringList() << "Discrete" << "Continuous") {
455 297 optMap["rocOptions"].set("title", type);
456   - p.qplot(plotType, type + "ROC", false, optMap["rocOptions"]);
  298 + p.file.write(qPrintable(QString(plot).arg(type + "ROC", toRList(optMap["rocOptions"]), "FALSE", "\"" + plotType + "\"")));
457 299 }
458 300  
459 301 foreach (const QString &type, QStringList() << "Discrete" << "Continuous") {
460 302 optMap["prOptions"].set("title", type);
461   - p.qplot(plotType, type + "PR", false, optMap["prOptions"]);
  303 + p.file.write(qPrintable(QString(plot).arg(type + "PR", toRList(optMap["prOptions"]), "FALSE", "\"" + plotType + "\"")));
462 304 }
463   -
464   - p.file.write(qPrintable(QString("qplot(X, data=Overlap, geom=\"histogram\", position=\"identity\", xlab=\"Overlap\", ylab=\"Frequency\")") +
465   - QString(" + theme_minimal() + scale_x_continuous(minor_breaks=NULL) + scale_y_continuous(minor_breaks=NULL) + theme(axis.text.y=element_blank(), axis.ticks=element_blank(), axis.text.x=element_text(angle=-90, hjust=0))") +
466   - (p.major.size > 1 ? (p.minor.size > 1 ? QString(" + facet_grid(%2 ~ %1, scales=\"free\")").arg(p.minor.header, p.major.header) : QString(" + facet_wrap(~ %1, scales = \"free\")").arg(p.major.header)) : QString()) +
467   - QString(" + theme(aspect.ratio=1, legend.position=\"bottom\")\n\n")));
  305 + p.file.write("plot <- plotOverlap(overlapData=Overlap)\nplot\n");
468 306  
469 307 p.file.write(qPrintable(QString("ggplot(AverageOverlap, aes(x=%1, y=%2, label=round(X,3)), main=\"Average Overlap\") + geom_text() + theme_minimal()").arg(p.minor.size > 1 ? p.minor.header : "'X'", p.major.size > 1 ? p.major.header : "'Y'") +
470 308 QString("%1%2\n\n").arg(p.minor.size > 1 ? "" : " + xlab(NULL)", p.major.size > 1 ? "" : " + ylab(NULL)")));
471 309  
472   - p.file.write(qPrintable(QString("ggplot(AverageOverlap, aes(x=%1, y=%2, fill=X)) + geom_tile() + scale_fill_continuous(\"Average Overlap\") + theme_minimal()").arg(p.minor.size > 1 ? p.minor.header : "'X'", p.major.size > 1 ? p.major.header : "'Y'") +
  310 + p.file.write(qPrintable(QString("ggplot(AverageOverlap, aes(x=%1, y=%2, fill=X)) + geom_tile() + scale_fill_continuous(\"Average Overlap\", guide=FALSE) + theme_minimal()").arg(p.minor.size > 1 ? p.minor.header : "'X'", p.major.size > 1 ? p.major.header : "'Y'") +
473 311 QString("%1%2\n\n").arg(p.minor.size > 1 ? "" : " + xlab(NULL)", p.major.size > 1 ? "" : " + ylab(NULL)")));
474 312  
475 313 return p.finalize(show);
... ... @@ -478,109 +316,12 @@ bool PlotDetection(const QStringList &amp;files, const File &amp;destination, bool show)
478 316 bool PlotLandmarking(const QStringList &files, const File &destination, bool show)
479 317 {
480 318 qDebug("Plotting %d landmarking file(s) to %s", files.size(), qPrintable(destination));
481   - RPlot p(files, destination, false);
482   -
483   - p.file.write(qPrintable(QString("# Split data into individual plots\n"
484   - "plot_index = which(names(data)==\"Plot\")\n"
485   - "Box <- data[grep(\"Box\",data$Plot),-c(1)]\n"
486   - "Box$X <- factor(Box$X, levels = Box$X, ordered = TRUE)\n"
487   - "Sample <- data[grep(\"Sample\",data$Plot),-c(1)]\n"
488   - "Sample$X <- as.character(Sample$X)\n"
489   - "EXT <- data[grep(\"EXT\",data$Plot),-c(1)]\n"
490   - "EXT$X <- as.character(EXT$X)\n"
491   - "EXP <- data[grep(\"EXP\",data$Plot),-c(1)]\n"
492   - "EXP$X <- as.character(EXP$X)\n"
493   - "NormLength <- data[grep(\"NormLength\",data$Plot),-c(1)]\n"
494   - "rm(data)\n"
495   - "\n")));
496   -
497   - p.file.write(qPrintable(QString("summarySE <- function(data=NULL, measurevar, groupvars=NULL, na.rm=FALSE, conf.interval=.95, .drop=TRUE) {\n\t"
498   - "require(plyr)\n\n\tlength2 <- function (x, na.rm=FALSE) {\n\t\tif (na.rm) sum(!is.na(x))\n\t\telse length(x)"
499   - "\n\t}\n\n\tdatac <- ddply(data, groupvars, .drop=.drop, .fun = function(xx, col) {\n\t\t"
500   - "c(N=length2(xx[[col]], na.rm=na.rm), mean=mean(xx[[col]], na.rm=na.rm), sd=sd(xx[[col]], na.rm=na.rm))\n\t\t},"
501   - "\n\t\tmeasurevar\n\t)\n\n\tdatac <- rename(datac, c(\"mean\" = measurevar))\n\tdatac$se <- datac$sd / sqrt(datac$N)"
502   - "\n\tciMult <- qt(conf.interval/2 + .5, datac$N-1)\n\tdatac$ci <- datac$se * ciMult\n\n\treturn(datac)\n}\n")));
503   -
504   -
505   - p.file.write(qPrintable(QString("\nreadData <- function(data) {\n\texamples <- list()\n"
506   - "\tfor (i in 1:nrow(data)) {\n"
507   - "\t\tpath <- data[i,1]\n"
508   - "\t\tvalue <- data[i,2]\n"
509   - "\t\tfile <- unlist(strsplit(path, \"[.]\"))[1]\n"
510   - "\t\text <- unlist(strsplit(path, \"[.]\"))[2]\n"
511   - "\t\tif (ext == \"jpg\" || ext == \"JPEG\" || ext == \"jpeg\" || ext == \"JPG\") {\n"
512   - "\t\t\timg <- readJPEG(path)\n"
513   - "\t\t} else if (ext == \"PNG\" || ext == \"png\") {\n"
514   - "\t\t\timg <- readPNG(path)\n"
515   - "\t\t} else if (ext == \"TIFF\" || ext == \"tiff\" || ext == \"TIF\" || ext == \"tif\") { \n"
516   - "\t\t\timg <- readTIFF(path)\n"
517   - "}else {\n"
518   - "\t\t\tnext\n"
519   - "\t\t}\n"
520   - "\t\texample <- list(file = file, value = value, image = img)\n"
521   - "\t\texamples[[i]] <- example\n"
522   - "\t}\n"
523   - "\treturn(examples)\n"
524   - "}\n")));
525   -
526   - p.file.write(qPrintable(QString("\nlibrary(jpeg)\n"
527   - "library(png)\n"
528   - "library(grid)\n"
529   - "multiplot <- function(..., plotlist=NULL, cols) {\n"
530   - "\trequire(grid)\n"
531   - "\t# Make a list from the ... arguments and plotlist\n"
532   - "\tplots <- c(list(...), plotlist)\n"
533   - "\tnumPlots = length(plots)\n"
534   - "\t# Make the panel\n"
535   - "\tplotCols = cols\n"
536   - "\tplotRows = ceiling(numPlots/plotCols)\n"
537   - "\t# Set up the page\n"
538   - "\tgrid.newpage()\n"
539   - "\tpushViewport(viewport(layout = grid.layout(plotRows, plotCols)))\n"
540   - "\tvplayout <- function(x, y)\n"
541   - "\tviewport(layout.pos.row = x, layout.pos.col = y)\n"
542   - "\t# Make each plot, in the correct location\n"
543   - "\tfor (i in 1:numPlots) {\n"
544   - "\t\tcurRow = ceiling(i/plotCols)\n"
545   - "\t\tcurCol = (i-1) %% plotCols + 1\n"
546   - "\t\tprint(plots[[i]], vp = vplayout(curRow, curCol))\n"
547   - "\t}\n"
548   - "}\n")));
549   -
550   - p.file.write(qPrintable(QString("\nplotImage <- function(image, title=NULL, label=NULL) { \n"
551   - "\tp <- qplot(1:10, 1:10, geom=\"blank\") + annotation_custom(rasterGrob(image$image), xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) + theme(axis.line=element_blank(), axis.title.y=element_blank(), axis.text.x=element_blank(), axis.text.y=element_blank(), line=element_blank(), axis.ticks=element_blank(), panel.background=element_blank()) + labs(title=title) + xlab(label)\n"
552   - "\treturn(p)"
553   - "}\n")));
554   -
555   - p.file.write(qPrintable(QString("\nsample <- readData(Sample) \n"
556   - "rows <- sample[[1]]$value\n"
557   - "algs <- unique(Box$%1)\n"
558   - "algs <- algs[!duplicated(algs)]\n"
559   - "print(plotImage(sample[[1]],\"Sample Landmarks\",sprintf(\"Total Landmarks: %s\",sample[[1]]$value))) \n"
560   - "if (nrow(EXT) != 0 && nrow(EXP)) {\n"
561   - "\tfor (j in 1:length(algs)) {\n"
562   - "\ttruthSample <- readData(EXT[EXT$. == algs[[j]],])\n"
563   - "\tpredictedSample <- readData(EXP[EXP$. == algs[[j]],])\n"
564   - "\t\tfor (i in 1:length(predictedSample)) {\n"
565   - "\t\t\tmultiplot(plotImage(predictedSample[[i]],sprintf(\"%s\\nPredicted Landmarks\",algs[[j]]),sprintf(\"Average Landmark Error: %.3f\",predictedSample[[i]]$value)),plotImage(truthSample[[i]],\"Ground Truth\\nLandmarks\",\"\"),cols=2)\n"
566   - "\t\t}\n"
567   - "\t}\n"
568   - "}\n").arg(p.major.size > 1 ? p.major.header : (p.minor.header.isEmpty() ? p.major.header : p.minor.header))));
569   -
570   - p.file.write(qPrintable(QString("\n"
571   - "# Code to format error table\n"
572   - "StatBox <- summarySE(Box, measurevar=\"Y\", groupvars=c(\"%1\",\"X\"))\n"
573   - "OverallStatBox <- summarySE(Box, measurevar=\"Y\", groupvars=c(\"%1\"))\n"
574   - "mat <- matrix(paste(as.character(round(StatBox$Y, 3)), round(StatBox$ci, 3), sep=\" \\u00b1 \"),nrow=rows,ncol=length(algs),byrow=FALSE)\n"
575   - "mat <- rbind(mat, paste(as.character(round(OverallStatBox$Y, 3)), round(OverallStatBox$ci, 3), sep=\" \\u00b1 \"))\n"
576   - "mat <- rbind(mat, as.character(round(NormLength$Y, 3)))\n"
577   - "colnames(mat) <- algs\n"
578   - "rownames(mat) <- c(seq(0,rows-1),\"Aggregate\",\"Average IPD\")\n"
579   - "ETable <- as.table(mat)\n").arg(p.major.size > 1 ? p.major.header : (p.minor.header.isEmpty() ? p.major.header : p.minor.header))));
580   -
581   - p.file.write(qPrintable(QString("\n"
582   - "print(textplot(ETable))\n"
583   - "print(title(\"Landmarking Error Rates\"))\n")));
  319 + RPlot p(files, destination);
  320 + p.file.write("\nformatData(type=\"landmarking\")\n\n");
  321 + p.file.write(qPrintable(QString("algs <- uniqueBox$%1)\n").arg(p.major.size > 1 ? p.major.header : (p.minor.header.isEmpty() ? p.major.header : p.minor.header))));
  322 + p.file.write("algs <- algs[!duplicated(algs)]\n");
  323 + p.file.write("plotLandmarkSamples(samples=sample, expData=EXP, extData=EXT)\n");
  324 + p.file.write("plotLandmarkTables(tableData=Box)\n");
584 325  
585 326 p.file.write(qPrintable(QString("ggplot(Box, aes(Y,%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(),
586 327 p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) +
... ... @@ -599,7 +340,7 @@ bool PlotMetadata(const QStringList &amp;files, const QString &amp;columns, bool show)
599 340 {
600 341 qDebug("Plotting %d metadata file(s) for columns %s", files.size(), qPrintable(columns));
601 342  
602   - RPlot p(files, "PlotMetadata", false);
  343 + RPlot p(files, "PlotMetadata");
603 344 foreach (const QString &column, columns.split(";"))
604 345 p.file.write(qPrintable(QString("qplot(%1, %2, data=data, geom=\"violin\", fill=%1) + coord_flip() + theme_minimal()\nggsave(\"%2.pdf\")\n").arg(p.major.header, column)));
605 346 return p.finalize(show);
... ...
openbr/openbr_plugin.cpp
... ... @@ -415,6 +415,29 @@ static T findAndRemove(QVariantMap &amp;map, const QString &amp;key, const T &amp;defaultVal
415 415 br_utemplate Template::toUniversalTemplate(const Template &t)
416 416 {
417 417 QVariantMap map = t.file.localMetadata();
  418 +
  419 + // QJsonObject::fromVariantMap (below) fails to convert
  420 + // QRects and QPoints to string, replacing them with null values.
  421 + // so hand-convert these weirdos
  422 + foreach (const QString &k, map.keys()) {
  423 + QVariant v = map[k];
  424 + if (v.canConvert(QVariant::PointF) || v.canConvert(QVariant::RectF)) {
  425 + QString newv = QtUtils::toString(v);
  426 + map[k] = newv;
  427 + }
  428 + // lists of points and rects, too
  429 + else if (v.type() == QVariant::List) {
  430 + QVariantList oldlist = qvariant_cast<QVariantList>(v);
  431 + if (!oldlist.isEmpty() && (oldlist.first().canConvert(QVariant::PointF) || oldlist.first().canConvert(QVariant::RectF))) {
  432 + QVariantList newlist;
  433 + foreach (const QVariant &subv, oldlist) {
  434 + newlist.append(QtUtils::toString(subv));
  435 + }
  436 + map[k] = newlist;
  437 + }
  438 + }
  439 + }
  440 +
418 441 const int32_t algorithmID = findAndRemove<int32_t> (map, "AlgorithmID", 0);
419 442 const int32_t x = findAndRemove<int32_t> (map, "X" , 0);
420 443 const int32_t y = findAndRemove<int32_t> (map, "Y" , 0);
... ... @@ -429,6 +452,49 @@ br_utemplate Template::toUniversalTemplate(const Template &amp;t)
429 452 Template Template::fromUniversalTemplate(br_const_utemplate ut)
430 453 {
431 454 QVariantMap map = QJsonDocument::fromJson(QByteArray((const char*) ut->data)).object().toVariantMap();
  455 +
  456 + // QJsonDocument::fromJson doesn't know about QRects and QPoints
  457 + // so convert any QStrings that can be converted
  458 + foreach (const QString &k, map.keys()) {
  459 + QVariant v = map[k];
  460 + QVariant newv;
  461 + bool istype;
  462 + if (v.type() == QVariant::String) {
  463 + QString vstr = qvariant_cast<QString>(v);
  464 + newv = QtUtils::toRect(vstr, &istype);
  465 + if (!istype) {
  466 + newv = QtUtils::toPoint(vstr, &istype);
  467 + } else if (!istype) {
  468 + newv = v;
  469 + }
  470 + map[k] = newv;
  471 + }
  472 + // convert lists of rects and points, too
  473 + else if (v.type() == QVariant::List) {
  474 + QVariantList oldlist = qvariant_cast<QVariantList>(v);
  475 + if (!oldlist.isEmpty() && oldlist.first().type() == QVariant::String) {
  476 + QString test = qvariant_cast<QString>(oldlist.first());
  477 + QtUtils::toRect(test, &istype);
  478 + QVariantList newlist;
  479 + if (istype) {
  480 + foreach (const QVariant &subv, oldlist) {
  481 + newlist.append(QtUtils::toRect(qvariant_cast<QString>(subv)));
  482 + }
  483 + } else {
  484 + QtUtils::toPoint(test, &istype);
  485 + if (istype) {
  486 + foreach (const QVariant &subv, oldlist) {
  487 + newlist.append(QtUtils::toPoint(qvariant_cast<QString>(subv)));
  488 + }
  489 + } else {
  490 + newlist = oldlist;
  491 + }
  492 + }
  493 + map[k] = newlist;
  494 + }
  495 + }
  496 + }
  497 +
432 498 map.insert("AlgorithmID", ut->algorithmID);
433 499 map.insert("X" , ut->x );
434 500 map.insert("Y" , ut->y );
... ...
openbr/plugins/classification/lda.cpp
... ... @@ -15,9 +15,6 @@
15 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16  
17 17 #include <Eigen/Dense>
18   -#include <opencv2/imgproc/imgproc.hpp>
19   -#include <opencv2/highgui/highgui.hpp>
20   -
21 18 #include <openbr/plugins/openbr_internal.h>
22 19  
23 20 #include <openbr/core/common.h>
... ... @@ -58,7 +55,6 @@ BR_REGISTER(Initializer, EigenInitializer)
58 55 class PCATransform : public Transform
59 56 {
60 57 Q_OBJECT
61   - friend class DFFSTransform;
62 58 friend class LDATransform;
63 59  
64 60 protected:
... ... @@ -224,54 +220,6 @@ BR_REGISTER(Transform, PCATransform)
224 220  
225 221 /*!
226 222 * \ingroup transforms
227   - * \brief Computes Distance From Feature Space (DFFS)
228   - * \br_paper Moghaddam, Baback, and Alex Pentland.
229   - * "Probabilistic visual learning for object representation."
230   - * Pattern Analysis and Machine Intelligence, IEEE Transactions on 19.7 (1997): 696-710.
231   - * \author Josh Klontz \cite jklontz
232   - * \br_property float keep Sets PCA keep property. Default is 0.95.
233   - */
234   -class DFFSTransform : public Transform
235   -{
236   - Q_OBJECT
237   - Q_PROPERTY(float keep READ get_keep WRITE set_keep RESET reset_keep STORED false)
238   - BR_PROPERTY(float, keep, 0.95)
239   -
240   - PCATransform pca;
241   - Transform *cvtFloat;
242   -
243   - void init()
244   - {
245   - pca.keep = keep;
246   - cvtFloat = make("CvtFloat");
247   - }
248   -
249   - void train(const TemplateList &data)
250   - {
251   - pca.train((*cvtFloat)(data));
252   - }
253   -
254   - void project(const Template &src, Template &dst) const
255   - {
256   - dst = src;
257   - dst.file.set("DFFS", sqrt(pca.residualReconstructionError((*cvtFloat)(src))));
258   - }
259   -
260   - void store(QDataStream &stream) const
261   - {
262   - pca.store(stream);
263   - }
264   -
265   - void load(QDataStream &stream)
266   - {
267   - pca.load(stream);
268   - }
269   -};
270   -
271   -BR_REGISTER(Transform, DFFSTransform)
272   -
273   -/*!
274   - * \ingroup transforms
275 223 * \brief Projects input into learned Linear Discriminant Analysis subspace.
276 224 * \author Brendan Klare \cite bklare
277 225 * \author Josh Klontz \cite jklontz
... ... @@ -793,439 +741,6 @@ class WCDATransform : public Transform
793 741  
794 742 BR_REGISTER(Transform, WCDATransform)
795 743  
796   -// For use in BBLDAAlignmentTransform
797   -// A single decision boundary with gaussian distributed responses
798   -struct DecisionBoundary
799   -{
800   - VectorXf function;
801   - float positiveMean, negativeMean, positiveSD, negativeSD;
802   -};
803   -
804   -QDataStream &operator<<(QDataStream &stream, const DecisionBoundary &db)
805   -{
806   - return stream << db.function << db.positiveMean << db.negativeMean << db.positiveSD << db.negativeSD;
807   -}
808   -
809   -QDataStream &operator>>(QDataStream &stream, DecisionBoundary &db)
810   -{
811   - return stream >> db.function >> db.positiveMean >> db.negativeMean >> db.positiveSD >> db.negativeSD;
812   -}
813   -
814   -/*!
815   - * \ingroup transforms
816   - * \brief Boosted Binary LDA classifier for local alignment.
817   - * \author Unknown \cite Unknown
818   - */
819   -class BBLDAAlignmentTransform : public MetaTransform
820   -{
821   - Q_OBJECT
822   - Q_PROPERTY(QList<int> anchors READ get_anchors WRITE set_anchors RESET reset_anchors STORED false) // A list of two indicies to provide initial rotation and scaling of training data
823   - Q_PROPERTY(QString detectionKey READ get_detectionKey WRITE set_detectionKey RESET reset_detectionKey STORED false) // Metadata key containing the detection bounding box
824   - Q_PROPERTY(int iad READ get_iad WRITE set_iad RESET reset_iad STORED false) // Inter-anchor distance in pixels
825   - Q_PROPERTY(float radius READ get_radius WRITE set_radius RESET reset_radius STORED false) // Kernel size as a fraction of IAD
826   - Q_PROPERTY(int resolution READ get_resolution WRITE set_resolution RESET reset_resolution STORED false) // Project resolution for sampling rotation and scale
827   - BR_PROPERTY(QList<int>, anchors, QList<int>())
828   - BR_PROPERTY(QString, detectionKey, "FrontalFace")
829   - BR_PROPERTY(int, iad, 16)
830   - BR_PROPERTY(float, radius, 2)
831   - BR_PROPERTY(int, resolution, 20)
832   -
833   - typedef Matrix<quint8, Dynamic, Dynamic> MatrixX8U;
834   -
835   - struct RegistrationMatrix
836   - {
837   - Mat src;
838   - Point2f center;
839   - double angle, scale;
840   -
841   - RegistrationMatrix(const Mat &src = Mat(), const Point2f &center = Point2f(), double angle = 0, double scale = 1)
842   - : src(src)
843   - , center(center)
844   - , angle(angle)
845   - , scale(scale)
846   - {}
847   -
848   - Mat calculateRotationMatrix2D(int size) const
849   - {
850   - Mat rotationMatrix2D = getRotationMatrix2D(center, angle, scale);
851   - rotationMatrix2D.at<double>(0, 2) -= (center.x - size / 2.0); // Adjust the center to the middle of the dst image
852   - rotationMatrix2D.at<double>(1, 2) -= (center.y - size / 2.0);
853   - return rotationMatrix2D;
854   - }
855   -
856   - Point2f invert(double x, double y, int size) const
857   - {
858   - Mat m;
859   - invertAffineTransform(calculateRotationMatrix2D(size), m);
860   -
861   - Mat src(1, 1, CV_64FC2);
862   - src.ptr<double>()[0] = y; // According to the docs these should be specified in the opposite order ...
863   - src.ptr<double>()[1] = x; // ... however running it seems to suggest this is the correct way
864   -
865   - Mat dst;
866   - transform(src, dst, m);
867   - return Point2f(dst.ptr<double>()[0], dst.ptr<double>()[1]);
868   - }
869   -
870   - MatrixX8U warp(int size) const
871   - {
872   - const Mat rotationMatrix2D = calculateRotationMatrix2D(size);
873   - Mat dst;
874   - warpAffine(src, dst, rotationMatrix2D, Size(size, size), INTER_LINEAR, BORDER_REFLECT);
875   - return Map<MatrixX8U>(dst.ptr<quint8>(), size, size);
876   - }
877   - };
878   -
879   - float rotationMax, scaleMin, scaleMax, xTranslationMax, yTranslationMin, yTranslationMax;
880   - QList<DecisionBoundary> decisionBoundaries;
881   -
882   - static void show(const char *name, const MatrixX8U &data)
883   - {
884   - const Mat mat(data.rows(), data.cols(), CV_8UC1, (void*) data.data());
885   - imshow(name, mat);
886   - waitKey(-1);
887   - }
888   -
889   - static void show(const char *name, MatrixXf data)
890   - {
891   - data.array() -= data.minCoeff();
892   - data.array() *= (255 / data.maxCoeff());
893   - show(name, MatrixX8U(data.cast<quint8>()));
894   - }
895   -
896   - static void show(const char *name, VectorXf data)
897   - {
898   - MatrixXf resized = data;
899   - const int size = int(sqrtf(float(data.rows())));
900   - resized.resize(size, size);
901   - show(name, resized);
902   - }
903   -
904   - static MatrixXf getSample(const MatrixXf &registered, int j, int k, int kernelSize)
905   - {
906   - MatrixXf sample(registered.block(j, k, kernelSize, kernelSize));
907   - const float norm = sample.norm();
908   - if (norm > 0)
909   - sample /= norm;
910   - return sample;
911   - }
912   -
913   - static MatrixXf getSample(const QList<MatrixXf> &registered, int i, int j, int k, int kernelSize)
914   - {
915   - return getSample(registered[i], j, k, kernelSize);
916   - }
917   -
918   - static float IADError(const MatrixXf &responses, const MatrixXf &mask)
919   - {
920   - const int responseSize = sqrtf(responses.cols());
921   - int numImages = 0;
922   - float totalError = 0;
923   - for (int i=0; i<responses.rows(); i++) {
924   - float maxResponse = -std::numeric_limits<float>::max();
925   - int maxX = -1;
926   - int maxY = -1;
927   - for (int j=0; j<responseSize; j++)
928   - for (int k=0; k<responseSize; k++)
929   - if (mask(i, j*responseSize + k) > 0) {
930   - const float response = responses(i, j*responseSize+k);
931   - if (response > maxResponse) {
932   - maxResponse = response;
933   - maxY = j;
934   - maxX = k;
935   - }
936   - }
937   - totalError += sqrtf(powf(maxX - responseSize/2, 2) + powf(maxY - responseSize/2, 2)) / responseSize;
938   - numImages++;
939   - }
940   - return totalError / numImages;
941   - }
942   -
943   - static bool isPositive(int j, int k, int responseSize)
944   - {
945   - return (abs(j - responseSize/2) <= 0) && (abs(k - responseSize/2) <= 0);
946   - }
947   -
948   - static MatrixXf /* output responses */ computeBoundaryRecursive(const QList<MatrixXf> &registered, int kernelSize, const MatrixXf &prior /* input responses */, QList<DecisionBoundary> &boundaries)
949   - {
950   - const int numImages = registered.size();
951   - const int responseSize = registered.first().cols() - kernelSize + 1;
952   - int positiveSamples = 0;
953   - int negativeSamples = 0;
954   -
955   - // Compute weights, a weight of zero operates as a mask
956   - MatrixXf weights(numImages, responseSize*responseSize);
957   - float totalPositiveWeight = 0, totalNegativeWeight = 0;
958   - for (int i=0; i<numImages; i++)
959   - for (int j=0; j<responseSize; j++)
960   - for (int k=0; k<responseSize; k++) {
961   - const float probability = prior(i, j*responseSize+k);
962   - const bool positive = isPositive(j, k, responseSize);
963   - // Weight by probability ...
964   - float weight = positive ? (2 - probability) // Subtracting from a number greater than 1 helps ensure that we don't overfit outliers
965   - : probability;
966   - // ... and by distance
967   - const float distance = sqrtf(powf(j - responseSize/2, 2) + powf(k - responseSize/2, 2));
968   - if (positive) weight *= 1 / (distance + 1);
969   - else weight *= distance;
970   - weights(i, j*responseSize+k) = weight;
971   - if (weight > 0) {
972   - if (positive) { totalPositiveWeight += weight; positiveSamples++; }
973   - else { totalNegativeWeight += weight; negativeSamples++; }
974   - }
975   - }
976   -
977   - // Normalize weights to preserve class sample ratio
978   - const float positiveWeightAdjustment = positiveSamples / totalPositiveWeight;
979   - const float negativeWeightAdjustment = negativeSamples / totalNegativeWeight;
980   - for (int i=0; i<numImages; i++)
981   - for (int j=0; j<responseSize; j++)
982   - for (int k=0; k<responseSize; k++)
983   - weights(i, j*responseSize+k) *= isPositive(j, k, responseSize) ? positiveWeightAdjustment : negativeWeightAdjustment;
984   -
985   - // Compute weighted class means
986   - MatrixXf positiveMean = MatrixXf::Zero(kernelSize, kernelSize);
987   - MatrixXf negativeMean = MatrixXf::Zero(kernelSize, kernelSize);
988   - for (int i=0; i<numImages; i++)
989   - for (int j=0; j<responseSize; j++)
990   - for (int k=0; k<responseSize; k++) {
991   - const MatrixXf sample(getSample(registered, i, j, k, kernelSize));
992   - const float weight = weights(i, j*responseSize+k);
993   - if (weight > 0) {
994   - if (isPositive(j, k, responseSize)) positiveMean += sample * weight;
995   - else negativeMean += sample * weight;
996   - }
997   - }
998   - positiveMean /= positiveSamples;
999   - negativeMean /= negativeSamples;
1000   -
1001   - // Compute weighted scatter matrix and decision boundary
1002   - MatrixXf scatter = MatrixXf::Zero(kernelSize*kernelSize, kernelSize*kernelSize);
1003   - for (int i=0; i<numImages; i++)
1004   - for (int j=0; j<responseSize; j++)
1005   - for (int k=0; k<responseSize; k++) {
1006   - const float weight = weights(i, j*responseSize+k);
1007   - if (weight > 0) {
1008   - const MatrixXf &centeredSampleMatrix = getSample(registered, i, j, k, kernelSize) - (isPositive(j, k, responseSize) ? positiveMean : negativeMean);
1009   - const Map<const VectorXf> centeredSample(centeredSampleMatrix.data(), kernelSize*kernelSize);
1010   - scatter.noalias() += centeredSample * centeredSample.transpose() * weights(i, j*responseSize+k);
1011   - }
1012   - }
1013   - scatter /= (positiveSamples + negativeSamples); // normalize for numerical stability
1014   - DecisionBoundary db;
1015   - db.function = scatter.inverse() * VectorXf(positiveMean - negativeMean); // fisher linear discriminant
1016   -
1017   - // Compute response values to decision boundary
1018   - MatrixXf posterior(numImages, responseSize*responseSize);
1019   - for (int i=0; i<numImages; i++)
1020   - for (int j=0; j<responseSize; j++)
1021   - for (int k=0; k<responseSize; k++)
1022   - if (weights(i, j*responseSize + k) > 0) {
1023   - const MatrixXf sample(getSample(registered, i, j, k, kernelSize));
1024   - posterior(i, j*responseSize + k) = db.function.transpose() * Map<const VectorXf>(sample.data(), kernelSize*kernelSize);
1025   - }
1026   -
1027   - // Compute class response means
1028   - db.positiveMean = 0;
1029   - db.negativeMean = 0;
1030   - for (int i=0; i<numImages; i++)
1031   - for (int j=0; j<responseSize; j++)
1032   - for (int k=0; k<responseSize; k++)
1033   - if (weights(i, j*responseSize + k) > 0) {
1034   - const float response = posterior(i, j*responseSize + k);
1035   - if (isPositive(j, k, responseSize)) db.positiveMean += response;
1036   - else db.negativeMean += response;
1037   - }
1038   - db.positiveMean /= positiveSamples;
1039   - db.negativeMean /= negativeSamples;
1040   -
1041   - // Compute class response standard deviations
1042   - db.positiveSD = 0;
1043   - db.negativeSD = 0;
1044   - for (int i=0; i<numImages; i++)
1045   - for (int j=0; j<responseSize; j++)
1046   - for (int k=0; k<responseSize; k++)
1047   - if (weights(i, j*responseSize + k) > 0) {
1048   - const float response = posterior(i, j*responseSize + k);
1049   - if (isPositive(j, k, responseSize)) db.positiveSD += powf(response-db.positiveMean, 2.f);
1050   - else db.negativeSD += powf(response-db.negativeMean, 2.f);
1051   - }
1052   - db.positiveSD = sqrtf(db.positiveSD / positiveSamples);
1053   - db.negativeSD = sqrtf(db.negativeSD / negativeSamples);
1054   -
1055   - // Normalize responses and propogating prior probabilities
1056   - for (int i=0; i<numImages; i++)
1057   - for (int j=0; j<responseSize; j++)
1058   - for (int k=0; k<responseSize; k++) {
1059   - float &response = posterior(i, j*responseSize + k);
1060   - if (weights(i, j*responseSize + k) > 0) {
1061   - const float positiveDensity = 1 / (sqrtf(2 * CV_PI) * db.positiveSD) * expf(-0.5 * powf((response - db.positiveMean) / db.positiveSD, 2.f));
1062   - const float negativeDensity = 1 / (sqrtf(2 * CV_PI) * db.negativeSD) * expf(-0.5 * powf((response - db.negativeMean) / db.negativeSD, 2.f));
1063   - response = prior(i, j*responseSize+k) * positiveDensity / (positiveDensity + negativeDensity);
1064   - } else {
1065   - response = 0;
1066   - }
1067   - }
1068   - const float previousMeanError = IADError(prior, weights);
1069   - const float meanError = IADError(posterior, weights);
1070   - qDebug() << "Samples positive & negative:" << positiveSamples << negativeSamples;
1071   - qDebug() << "Positive mean & stddev:" << db.positiveMean << db.positiveSD;
1072   - qDebug() << "Negative mean & stddev:" << db.negativeMean << db.negativeSD;
1073   - qDebug() << "Mean error before & after:" << previousMeanError << meanError;
1074   -
1075   - if (meanError < previousMeanError) {
1076   - boundaries.append(db);
1077   - return computeBoundaryRecursive(registered, kernelSize, posterior, boundaries);
1078   - } else {
1079   - return prior;
1080   - }
1081   - }
1082   -
1083   - void train(const TemplateList &data)
1084   - {
1085   - QList<RegistrationMatrix> registrationMatricies;
1086   - {
1087   - if (Globals->verbose)
1088   - qDebug("Preprocessing training data...");
1089   -
1090   - rotationMax = -std::numeric_limits<float>::max();
1091   - scaleMin = std::numeric_limits<float>::max();
1092   - scaleMax = -std::numeric_limits<float>::max();
1093   - xTranslationMax = -std::numeric_limits<float>::max();
1094   - yTranslationMin = std::numeric_limits<float>::max();
1095   - yTranslationMax = -std::numeric_limits<float>::max();
1096   -
1097   - foreach (const Template &t, data) {
1098   - const QRectF detection = t.file.get<QRectF>(detectionKey);
1099   - const QList<QPointF> points = t.file.points();
1100   - const float length = sqrt(pow(points[anchors[1]].x() - points[anchors[0]].x(), 2) +
1101   - pow(points[anchors[1]].y() - points[anchors[0]].y(), 2));
1102   - const float rotation = atan2(points[anchors[1]].y() - points[anchors[0]].y(),
1103   - points[anchors[1]].x() - points[anchors[0]].x()) * 180 / CV_PI;
1104   - const float scale = length / detection.width();
1105   - const float xCenter = (points[anchors[0]].x() + points[anchors[1]].x()) / 2;
1106   - const float yCenter = (points[anchors[0]].y() + points[anchors[1]].y()) / 2;
1107   - const float xTranslation = (xCenter - detection.x() - detection.width() /2) / detection.width();
1108   - const float yTranslation = (yCenter - detection.y() - detection.height()/2) / detection.width();
1109   -
1110   - if (detection.contains(xCenter, yCenter) /* Sometimes the face detector gets the wrong face */) {
1111   - rotationMax = max(rotationMax, fabsf(rotation));
1112   - scaleMin = min(scaleMin, scale);
1113   - scaleMax = max(scaleMax, scale);
1114   - xTranslationMax = max(xTranslationMax, fabsf(xTranslation));
1115   - yTranslationMin = min(yTranslationMin, yTranslation);
1116   - yTranslationMax = max(yTranslationMax, yTranslation);
1117   - }
1118   -
1119   - registrationMatricies.append(RegistrationMatrix(t, Point2f(xCenter, yCenter), rotation, iad / length));
1120   - }
1121   - }
1122   -
1123   - if (Globals->verbose)
1124   - qDebug("Learning affine model ...");
1125   -
1126   - // Construct the registered training data for the landmark
1127   - const int regionSize = 2 * iad * radius; // Train on a search size that is twice to the kernel size
1128   - QList<MatrixXf> registered;
1129   - foreach (const RegistrationMatrix &registrationMatrix, registrationMatricies)
1130   - registered.append(registrationMatrix.warp(regionSize).cast<float>());
1131   -
1132   - // Boosted LDA
1133   - const int numImages = registered.size();
1134   - const int kernelSize = iad * radius;
1135   - const int responseSize = regionSize - kernelSize + 1;
1136   - const MatrixXf prior = MatrixXf::Ones(numImages, responseSize*responseSize);
1137   - const MatrixXf responses = computeBoundaryRecursive(registered, kernelSize, prior, decisionBoundaries);
1138   -
1139   -// for (int i=0; i<numImages; i++) {
1140   -// float maxResponse = -std::numeric_limits<float>::max();
1141   -// int maxX = -1;
1142   -// int maxY = -1;
1143   -// for (int j=0; j<responseSize; j++)
1144   -// for (int k=0; k<responseSize; k++) {
1145   -// const float response = responses(i, j*responseSize+k);
1146   -// if (response > maxResponse) {
1147   -// maxResponse = response;
1148   -// maxY = j;
1149   -// maxX = k;
1150   -// }
1151   -// }
1152   -
1153   -// MatrixXf draw = registered[i];
1154   -// const int r = 3;
1155   -// maxX += kernelSize / 2;
1156   -// maxY += kernelSize / 2;
1157   -// for (int i=-r; i<=r; i++)
1158   -// draw(regionSize/2 + i, regionSize/2) *= 2;
1159   -// for (int i=-r; i<=r; i++)
1160   -// draw(regionSize/2, regionSize/2 + i) *= 2;
1161   -// for (int i=-r; i<=r; i++)
1162   -// draw(maxX + i, maxY) /= 2;
1163   -// for (int i=-r; i<=r; i++)
1164   -// draw(maxX, maxY + i) /= 2;
1165   -// show("draw", draw);
1166   -// show("responses", VectorXf(responses.row(i)));
1167   -// }
1168   -
1169   - if (Globals->verbose)
1170   - qDebug("Learned %d function(s) with error %g", decisionBoundaries.size(), IADError(responses, prior));
1171   - }
1172   -
1173   - void project(const Template &src, Template &dst) const
1174   - {
1175   - dst = src;
1176   - const QRectF detection = src.file.get<QRectF>(detectionKey);
1177   - RegistrationMatrix registrationMatrix(src, OpenCVUtils::toPoint(detection.center()));
1178   -
1179   - const int regionSize = iad * (1 + radius);
1180   - const int kernelSize = iad * radius;
1181   - const int responseSize = regionSize - kernelSize + 1;
1182   - const float rotationStep = (2 * rotationMax) / (resolution - 1);
1183   - const float scaleStep = (scaleMax - scaleMin) / (resolution - 1);
1184   -
1185   - float bestResponse = -std::numeric_limits<float>::max(), bestRotation = 0, bestScale = 0;
1186   - int bestX = 0, bestY =0;
1187   -
1188   - for (float rotation = -rotationMax; rotation <= rotationMax; rotation += rotationStep) {
1189   - registrationMatrix.angle = rotation;
1190   - for (float scale = scaleMin; scale <= scaleMax; scale += scaleStep) {
1191   - registrationMatrix.scale = iad / (scale * detection.width());
1192   - const MatrixXf registered = registrationMatrix.warp(regionSize).cast<float>();
1193   -
1194   - for (int j=0; j<responseSize; j++)
1195   - for (int k=0; k<responseSize; k++) {
1196   - const MatrixXf sample = getSample(registered, j, k, kernelSize);
1197   - float posterior = 1;
1198   - foreach (const DecisionBoundary &db, decisionBoundaries) {
1199   - const float response = db.function.transpose() * Map<const VectorXf>(sample.data(), kernelSize*kernelSize);
1200   - const float positiveDensity = 1 / (sqrtf(2 * CV_PI) * db.positiveSD) * expf(-0.5 * powf((response - db.positiveMean) / db.positiveSD, 2.f));
1201   - const float negativeDensity = 1 / (sqrtf(2 * CV_PI) * db.negativeSD) * expf(-0.5 * powf((response - db.negativeMean) / db.negativeSD, 2.f));
1202   - posterior *= positiveDensity / (positiveDensity + negativeDensity);
1203   - }
1204   - if (posterior > bestResponse) {
1205   - bestResponse = posterior;
1206   - bestX = k + kernelSize/2;
1207   - bestY = j + kernelSize/2;
1208   - bestRotation = rotation;
1209   - bestScale = scale;
1210   - }
1211   - }
1212   - }
1213   - }
1214   - }
1215   -
1216   - void store(QDataStream &stream) const
1217   - {
1218   - stream << rotationMax << scaleMin << scaleMax << xTranslationMax << yTranslationMin << yTranslationMax << decisionBoundaries;
1219   - }
1220   -
1221   - void load(QDataStream &stream)
1222   - {
1223   - stream >> rotationMax >> scaleMin >> scaleMax >> xTranslationMax >> yTranslationMin >> yTranslationMax >> decisionBoundaries;
1224   - }
1225   -};
1226   -
1227   -BR_REGISTER(Transform, BBLDAAlignmentTransform)
1228   -
1229 744 } // namespace br
1230 745  
1231 746 #include "classification/lda.moc"
... ...
openbr/plugins/cmake/dlib.cmake
... ... @@ -2,7 +2,7 @@ set(BR_WITH_DLIB ON CACHE BOOL &quot;Build with DLib&quot;)
2 2  
3 3 if(${BR_WITH_DLIB})
4 4 ExternalProject_Add(dlib
5   - URL http://downloads.sourceforge.net/project/dclib/dlib/v18.16/dlib-18.16.tar.bz2
  5 + URL https://github.com/davisking/dlib/releases/download/v18.16/dlib-18.16.tar.bz2
6 6 URL_MD5 e9e5449bc25370afce2d254327afac99
7 7 SOURCE_DIR "${CMAKE_SOURCE_DIR}/3rdparty/dlib-18.16"
8 8 CONFIGURE_COMMAND ""
... ...
openbr/plugins/core/algorithms.cpp
... ... @@ -34,7 +34,6 @@ class AlgorithmsInitializer : public Initializer
34 34 Globals->abbreviations.insert("FaceRecognition", "FaceDetection+FaceRecognitionRegistration+<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>+SetMetadata(AlgorithmID,-1):Unit(ByteL1)");
35 35 Globals->abbreviations.insert("GenderClassification", "FaceDetection+Expand+FaceClassificationRegistration+Expand+<FaceClassificationExtraction>+<GenderClassifier>+Discard");
36 36 Globals->abbreviations.insert("AgeRegression", "FaceDetection+Expand+FaceClassificationRegistration+Expand+<FaceClassificationExtraction>+<AgeRegressor>+Discard");
37   - Globals->abbreviations.insert("FaceQuality", "Open+Expand+Cascade(FrontalFace)+ASEFEyes+Affine(64,64,0.25,0.35)+ImageQuality+Cvt(Gray)+DFFS+Discard");
38 37 Globals->abbreviations.insert("MedianFace", "Open+Expand+Cascade(FrontalFace)+ASEFEyes+Affine(256,256,0.37,0.45)+Center(Median)");
39 38 Globals->abbreviations.insert("BlurredFaceDetection", "Open+LimitSize(1024)+SkinMask/(Cvt(Gray)+GradientMask)+And+Morph(Erode,16)+LargestConvexArea");
40 39 Globals->abbreviations.insert("DrawFaceDetection", "Open+Cascade(FrontalFace)+Expand+ASEFEyes+Draw(inPlace=true)");
... ...
openbr/plugins/metadata/expandrect.cpp
... ... @@ -23,6 +23,7 @@ namespace br
23 23 * \ingroup transforms
24 24 * \brief Expand the width and height of a Template's rects by input width and height factors.
25 25 * \author Charles Otto \cite caotto
  26 + * \author Brendan Klare \cite bklare
26 27 */
27 28 class ExpandRectTransform : public UntrainableTransform
28 29 {
... ... @@ -31,35 +32,23 @@ class ExpandRectTransform : public UntrainableTransform
31 32 Q_PROPERTY(float heightExpand READ get_heightExpand WRITE set_heightExpand RESET reset_heightExpand STORED false)
32 33 BR_PROPERTY(float, widthExpand, .5)
33 34 BR_PROPERTY(float, heightExpand, .5)
  35 +
34 36 void project(const Template &src, Template &dst) const
35 37 {
36 38 dst = src;
37   - QList<QRectF> rects = dst.file.rects();
  39 + dst.file.clearRects();
  40 + QList<QRectF> rects = src.file.rects();
38 41 for (int i=0;i < rects.size(); i++) {
39   - QRectF rect = rects[i];
40   -
41   - qreal width = rect.width();
42   - qreal height = rect.height();
43   - float half_w_expansion = widthExpand / 2;
44   - float half_h_expansion = heightExpand / 2;
45   -
46   - qreal half_width = width * widthExpand;
47   - qreal quarter_width = width * half_w_expansion;
48   - qreal half_height = height * heightExpand;
49   - qreal quarter_height = height * half_h_expansion;
50   -
51   - rect.setX(std::max(qreal(0),(rect.x() - quarter_width)));
52   - rect.setY(std::max(qreal(0),(rect.y() - quarter_height)));
53   -
54   - qreal x2 = std::min(rect.width() + half_width + rect.x(), qreal(src.m().cols) - 1);
55   - qreal y2 = std::min(rect.height() + half_height + rect.y(), qreal(src.m().rows) - 1);
  42 + qreal widthGrowth = rects[i].width() * widthExpand;
  43 + qreal heightGrowth = rects[i].height() * heightExpand;
56 44  
57   - rect.setWidth(x2 - rect.x());
58   - rect.setHeight(y2 - rect.y());
  45 + qreal x = std::max(qreal(0), rects[i].x() - widthGrowth / 2.);
  46 + qreal y = std::max(qreal(0), rects[i].y() - heightGrowth / 2.);
  47 + dst.file.appendRect(QRectF(x, y,
  48 + std::min(src.m().cols - x - 1, rects[i].width() + widthGrowth),
  49 + std::min(src.m().rows - y - 1, rects[i].height() + heightGrowth)));
59 50  
60   - rects[i] = rect;
61 51 }
62   - dst.file.setRects(rects);
63 52 }
64 53 };
65 54  
... ...
openbr/plugins/metadata/pointstorects.cpp 0 โ†’ 100644
  1 +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2 + * Copyright 2015 Rank One Computing Corporation *
  3 + * *
  4 + * Licensed under the Apache License, Version 2.0 (the "License"); *
  5 + * you may not use this file except in compliance with the License. *
  6 + * You may obtain a copy of the License at *
  7 + * *
  8 + * http://www.apache.org/licenses/LICENSE-2.0 *
  9 + * *
  10 + * Unless required by applicable law or agreed to in writing, software *
  11 + * distributed under the License is distributed on an "AS IS" BASIS, *
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
  13 + * See the License for the specific language governing permissions and *
  14 + * limitations under the License. *
  15 + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  16 +
  17 +#include <openbr/plugins/openbr_internal.h>
  18 +
  19 +namespace br
  20 +{
  21 +
  22 +/*!
  23 + * \ingroup transforms
  24 + * \brief For each point, add a rectangle with radius as a half width
  25 + * \author Brendan Klare \cite bklare
  26 + */
  27 +
  28 +class PointsToRectsTransform : public UntrainableTransform
  29 +{
  30 + Q_OBJECT
  31 + Q_PROPERTY(float radius READ get_radius WRITE set_radius RESET reset_radius STORED false)
  32 + Q_PROPERTY(bool clearRects READ get_clearRects WRITE set_clearRects RESET reset_clearRects STORED false)
  33 + BR_PROPERTY(float, radius, 4)
  34 + BR_PROPERTY(bool, clearRects, true)
  35 +
  36 + void project(const Template &src, Template &dst) const
  37 + {
  38 + dst = src;
  39 +
  40 + if (clearRects)
  41 + dst.file.clearRects();
  42 +
  43 + if (src.file.points().isEmpty()) {
  44 + if (Globals->verbose) qWarning("No landmarks");
  45 + return;
  46 + }
  47 +
  48 + for (int i = 0; i < src.file.points().size(); i++) {
  49 + dst.file.appendRect(QRectF(src.file.points()[i].x() - radius, src.file.points()[i].y() - radius, radius * 2, radius * 2));
  50 + }
  51 + }
  52 +};
  53 +
  54 +BR_REGISTER(Transform, PointsToRectsTransform)
  55 +
  56 +} // namespace br
  57 +
  58 +#include "metadata/pointstorects.moc"
... ...
openbr/plugins/metadata/rectstomats.cpp 0 โ†’ 100644
  1 +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2 + * Copyright 2015 Rank One Computing Corporation *
  3 + * *
  4 + * Licensed under the Apache License, Version 2.0 (the "License"); *
  5 + * you may not use this file except in compliance with the License. *
  6 + * You may obtain a copy of the License at *
  7 + * *
  8 + * http://www.apache.org/licenses/LICENSE-2.0 *
  9 + * *
  10 + * Unless required by applicable law or agreed to in writing, software *
  11 + * distributed under the License is distributed on an "AS IS" BASIS, *
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
  13 + * See the License for the specific language governing permissions and *
  14 + * limitations under the License. *
  15 + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  16 +
  17 +#include <openbr/plugins/openbr_internal.h>
  18 +#include <openbr/core/opencvutils.h>
  19 +
  20 +namespace br
  21 +{
  22 +
  23 +/*!
  24 + * \ingroup transforms
  25 + * \brief For each rectangle bounding box in src, the mat is cropped and appended
  26 + * the the template's list of mats.
  27 + * \author Brendan Klare \cite bklare
  28 + */
  29 +class RectsToMatsTransform : public UntrainableTransform
  30 +{
  31 + Q_OBJECT
  32 +
  33 +private:
  34 + void project(const Template &src, Template &dst) const
  35 + {
  36 + for (int i = 0; i < src.file.rects().size(); i++)
  37 + dst += cv::Mat(src, OpenCVUtils::toRect(src.file.rects()[i]));
  38 + }
  39 +};
  40 +
  41 +BR_REGISTER(Transform, RectsToMatsTransform)
  42 +
  43 +} // namespace br
  44 +
  45 +#include "metadata/rectstomats.moc"
... ...
openbr/plugins/metadata/rectstotemplates.cpp
1   -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2   - * Copyright 2012 The MITRE Corporation *
3   - * *
4   - * Licensed under the Apache License, Version 2.0 (the "License"); *
5   - * you may not use this file except in compliance with the License. *
6   - * You may obtain a copy of the License at *
7   - * *
8   - * http://www.apache.org/licenses/LICENSE-2.0 *
9   - * *
10   - * Unless required by applicable law or agreed to in writing, software *
11   - * distributed under the License is distributed on an "AS IS" BASIS, *
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
13   - * See the License for the specific language governing permissions and *
14   - * limitations under the License. *
15   - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16   -
17 1 #include <openbr/plugins/openbr_internal.h>
18 2 #include <openbr/core/opencvutils.h>
19 3  
... ...
openbr/plugins/metadata/recttokeys.cpp 0 โ†’ 100644
  1 +#include <openbr/plugins/openbr_internal.h>
  2 +
  3 +namespace br
  4 +{
  5 +
  6 +/*!
  7 + * \ingroup transforms
  8 + * \brief Convert a rect to X, Y, Width, and Height. Handy for saving universal templates.
  9 + * \author Austin Blanton \cite imaus10
  10 + */
  11 +class RectToKeysTransform : public UntrainableMetadataTransform
  12 +{
  13 + Q_OBJECT
  14 + Q_PROPERTY(QString key READ get_key WRITE set_key RESET reset_key STORED false)
  15 + BR_PROPERTY(QString, key, "")
  16 +
  17 + void projectMetadata(const File &src, File &dst) const
  18 + {
  19 + dst = src;
  20 + if (src.contains(key)) {
  21 + QRectF r = src.get<QRectF>(key);
  22 + dst.set("Height", r.height());
  23 + dst.set("Width", r.width());
  24 + dst.set("X", r.left());
  25 + dst.set("Y", r.bottom());
  26 + }
  27 + }
  28 +
  29 +};
  30 +
  31 +BR_REGISTER(Transform, RectToKeysTransform)
  32 +
  33 +} // namespace br
  34 +
  35 +#include "metadata/recttokeys.moc"
... ...
share/openbr/plotting/plot_utils.R 0 โ†’ 100644
  1 +# Load libraries
  2 +library("ggplot2")
  3 +library("gplots")
  4 +library("reshape")
  5 +library("scales")
  6 +library("jpeg")
  7 +library("png")
  8 +library("grid")
  9 +
  10 +# Code to format FAR values
  11 +far_names <- list('0.001'="FAR = 0.1%", '0.01'="FAR = 1%")
  12 +far_labeller <- function(variable,value) { return(far_names[as.character(value)]) }
  13 +
  14 +getScale <- function(mode, title, vals) {
  15 + if (vals > 12) return(do.call(paste("scale", mode, "discrete", sep="_"), list(title)))
  16 + else if (vals > 11) return(do.call(paste("scale", mode, "brewer", sep="_"), list(title, palette="Set3")))
  17 + else if (vals > 9) return(do.call(paste("scale", mode, "brewer", sep="_"), list(title, palette="Paired")))
  18 + else return(do.call(paste("scale", mode, "brewer", sep="_"), list(title, palette="Set1")))
  19 +}
  20 +
  21 +plotMetadata <- function(metadata=NULL, title=NULL) {
  22 + MT <- as.data.frame(metadata[c(1, 2, 3, 4, 5),])
  23 + par(mfrow=c(4, 1))
  24 + plot.new()
  25 + print(title(paste(title, date(), sep="\n")))
  26 + mat <- matrix(MT$X[c(1, 2)], ncol=2)
  27 + colnames(mat) <- c("Gallery", "Probe")
  28 + imageTable <- as.table(mat)
  29 + print(textplot(imageTable, show.rownames=FALSE))
  30 + print(title("Images"))
  31 + mat <- matrix(MT$X[c(3, 4, 5)], ncol=3)
  32 + colnames(mat) <- c("Genuine", "Impostor", "Ignored")
  33 + matchTable <- as.table(mat)
  34 + print(textplot(matchTable, show.rownames=FALSE))
  35 + print(title("Matches"))
  36 + plot.new()
  37 + print(title("Gallery * Probe = Genuine + Impostor + Ignored"))
  38 +}
  39 +
  40 +plotTable <- function(tableData=NULL, name=NULL, labels=NULL) {
  41 + if (nrow(tableData) == 0) return()
  42 + if (smooth && confidence != 0) {
  43 + input = paste(as.character(round(tableData$Y, 3)), round(tableData$ci, 3), sep="\u00b1")
  44 + } else {
  45 + input = tableData$Y
  46 + }
  47 + mat <- matrix(input, nrow=length(labels), ncol=length(algs), byrow=FALSE)
  48 + colnames(mat) <- algs
  49 + rownames(mat) <- labels
  50 + table <- as.table(mat)
  51 + if (csv) {
  52 + write.csv(table, file=paste(paste(basename, deparse(substitute(data)), sep="_"), ".csv", sep=""))
  53 + } else {
  54 + print(textplot(table))
  55 + print(title(name))
  56 + }
  57 +}
  58 +
  59 +plotLandmarkTables <- function(tableData=NULL) {
  60 + if(majorSize > 1) {
  61 + var <- majorHeader
  62 + } else {
  63 + if(minorHeader == "") var <- majorHeader else var <- minorHeader
  64 + }
  65 + StatBox <- summarySE(tableData, measurevar="Y", groupvars=c(var,"X"))
  66 + OverallStatBox <- summarySE(tableData, measurevar="Y", groupvars=c(var))
  67 + mat <- matrix(paste(as.character(round(StatBox$Y, 3)), round(StatBox$ci, 3), sep=" \u00b1 "), nrow=rows, ncol=length(algs), byrow=FALSE)
  68 + mat <- rbind(mat, paste(as.character(round(OverallStatBox$Y, 3)), round(OverallStatBox$ci, 3), sep=" \u00b1 "))
  69 + mat <- rbind(mat, as.character(round(NormLength$Y, 3)))
  70 + colnames(mat) <- algs
  71 + rownames(mat) <- c(seq(0, rows-1), "Aggregate","Average IPD")
  72 + ETable <- as.table(mat)
  73 + print(textplot(ETable))
  74 + print(title("Landmarking Error Rates"))
  75 +}
  76 +
  77 +plotLine <- function(lineData=NULL, options=NULL, flipY=FALSE, geometry="line") {
  78 + textSize <- if("textSize" %in% names(options)) as.numeric(options$textSize) else 12
  79 + p <- qplot(X, if(flipY) 1-Y else Y, data=lineData, main=options$title, geom=geometry, size=if("size" %in% names(options)) I(as.numeric(options$size)) else I(.5), colour=if(majorSize > 1) factor(eval(parse(text=majorHeader))) else NULL, linetype=if(minorSize > 1) factor(eval(parse(text=minorHeader))) else NULL, xlab=options$xTitle, ylab=options$yTitle) + theme_minimal()
  80 + if (smooth && deparse(substitute(lineData)) != "CMC" && confidence != 0) p <- p + geom_errorbar(data=lineData[seq(1, NROW(lineData), by = 29),], aes(x=X, ymin=if(flipY) (1-lower) else lower, ymax=if(flipY) (1-upper) else upper), width=0.1, alpha=I(1/2))
  81 + if (majorSize > 1) p <- p + getScale("colour", majorHeader, majorSize)
  82 + if (minorSize > 1) p <- p + scale_linetype_discrete(minorHeader)
  83 +
  84 + # Set log/continuous scales, breaks and labels
  85 + if (options$xLog)
  86 + p <- p + scale_x_log10(labels=if("xLabels" %in% names(options)) eval(parse(text=options$xLabels)) else trans_format("log10", math_format()), breaks=if("xBreaks" %in% names(options)) eval(parse(text=options$xBreaks)) else waiver()) + annotation_logticks(sides="b")
  87 + else
  88 + p <- p + scale_x_continuous(labels=if("xLabels" %in% names(options)) eval(parse(text=options$xLabels)) else percent, breaks=if("xBreaks" %in% names(options)) eval(parse(text=options$xBreaks)) else pretty_breaks(n=10))
  89 + if (options$yLog)
  90 + p <- p + scale_y_log10(labels=if("yLabels" %in% names(options)) eval(parse(text=options$yLabels)) else trans_format("log10", math_format()), breaks=if("yBreaks" %in% names(options)) eval(parse(text=options$yBreaks)) else waiver()) + annotation_logticks(sides="l")
  91 + else
  92 + p <- p + scale_y_continuous(labels=if("yLabels" %in% names(options)) eval(parse(text=options$yLabels)) else percent, breaks=if("yBreaks" %in% names(options)) eval(parse(text=options$yBreaks)) else pretty_breaks(n=10))
  93 +
  94 + if ("xLimits" %in% names(options)) p <- p + xlim(eval(parse(text=options$xLimits)))
  95 + if ("yLimits" %in% names(options)) p <- p + ylim(eval(parse(text=options$yLimits)))
  96 + p <- p + theme(legend.title = element_text(size = textSize), legend.text = element_text(size = textSize), plot.title = element_text(size = textSize), axis.text = element_text(size = textSize), axis.title.x = element_text(size = textSize), axis.title.y = element_text(size = textSize), legend.position=if("legendPosition" %in% names(options)) eval(parse(text=options$legendPosition)) else "bottom", legend.background = element_rect(fill = 'white'), panel.grid.major = element_line(colour = "gray"), panel.grid.minor = element_line(colour = "gray", linetype = "dashed"))
  97 + p <- p + guides(col=guide_legend(ncol=ncol))
  98 + return(p)
  99 +}
  100 +
  101 +plotSD <- function(sdData=NULL) {
  102 + p <- qplot(X, data=sdData, geom="histogram", fill=Y, position="identity", alpha=I(1/2), xlab="Score", ylab="Frequency")
  103 + p <- p + scale_fill_manual("Ground Truth", values=c("blue", "red")) + theme_minimal() + scale_x_continuous(minor_breaks=NULL) + scale_y_continuous(minor_breaks=NULL) + theme(axis.text.y=element_blank(), axis.ticks=element_blank(), axis.text.x=element_text(angle=-90, hjust=0))
  104 + if (majorSize > 1) {
  105 + if (minorSize > 1) {
  106 + if (flip) {
  107 + A <- minorHeader
  108 + B <- majorHeader
  109 + } else {
  110 + A <- majorHeader
  111 + B <- minorHeader
  112 + }
  113 + p <- p + facet_grid(facets=as.formula(paste(A, "~", B)), scales="free")
  114 + } else {
  115 + p <- p + facet_wrap(facets=as.formula(paste("~", majorHeader)), scales="free")
  116 + }
  117 + }
  118 + p <- p + theme(aspect.ratio=1)
  119 + return(p)
  120 +}
  121 +
  122 +plotBC <- function(bcData=NULL) {
  123 + factor <- if (majorSmooth) minorHeader else majorHeader
  124 + plotString <- paste("qplot(factor(", factor, ")", if(smooth) ", Y" else "", ", data=bcData, ", if(smooth) "geom=\"boxplot\"" else "geom=\"bar\", position=\"dodge\", weight=Y", sep="")
  125 + p <- eval(parse(text=paste(plotString, if(majorSize > 1) paste(", fill=factor(", majorHeader, ")", sep="") else "", ", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()", sep="")))
  126 + if(majorSize > 1) p <- p + getScale("fill", majorHeader, majorSize)
  127 + if(minorSize > 1) p <- p + facet_grid(facets=as.formula(paste(minorHeader, "~", "X"))) else p <- p + facet_grid(. ~ X, labeller=far_labeller)
  128 + p <- p + scale_y_continuous(labels=percent) + theme(legend.position="none", axis.text.x=element_text(angle=-90, hjust=0))
  129 + if(!smooth) p <- p + geom_text(data=bcData, aes(label=bcData$Y, y=0.05))
  130 + return(p)
  131 +}
  132 +
  133 +plotERR <- function(errData=NULL) {
  134 + if(flip) {
  135 + if(majorSize > 1) color <- majorHeader
  136 + } else {
  137 + if(minorSize > 1) color <- minorHeader
  138 + }
  139 + p <- qplot(X, Y, data=errData, geom="line", linetype=Error, colour=if(exists("color")) factor(eval(parse(text=color))) else NULL, xlab="Score", ylab="Error Rate") + theme_minimal()
  140 + if(flip) {
  141 + if(majorSize > 1)
  142 + p <- p + getScale("colour", majorHeader, majorSize)
  143 + else if(minorSize > 1)
  144 + p <- p + getScale("colour", minorHeader, minorSize)
  145 + }
  146 + p <- p + scale_y_log10(labels=percent) + annotation_logticks(sides="l")
  147 + if(flip) {
  148 + if(minorSize > 1) {
  149 + facet <- minorHeader
  150 + p <- p + facet_wrap(as.formula(paste("~", facet)), scales="free_x")
  151 + }
  152 + } else {
  153 + if(majorSize >1) {
  154 + facet <- majorHeader
  155 + p <- p + facet_wrap(as.formula(paste("~", facet)), scales="free_x")
  156 + }
  157 + }
  158 + p <- p + theme(aspect.ratio=1)
  159 + return(p)
  160 +}
  161 +
  162 +plotOverlap <- function(overlapData=NULL) {
  163 + p <- qplot(X, data=overlapData, geom="histogram", position="identity", xlab="Overlap", ylab="Frequency")
  164 + p <- p + theme_minimal() + scale_x_continuous(minor_breaks=NULL) + scale_y_continuous(minor_breaks=NULL) + theme(axis.text.y=element_blank(), axis.ticks=element_blank(), axis.text.x=element_text(angle=-90, hjust=0))
  165 + if(majorSize > 1) {
  166 + if(minorSize) {
  167 + p <- p + facet_grid(facets=as.formula(paste(minorHeader, "~", majorHeader)), scales="free")
  168 + } else {
  169 + p <- p + facet_wrap(facets=as.formula(paste("~", majorHeader)), scales="free")
  170 + }
  171 + }
  172 + p <- p + theme(aspect.ratio=1, legend.position="bottom")
  173 + return(p)
  174 +}
  175 +
  176 +formatData <- function(type="eval") {
  177 + if (type == "eval") {
  178 + # Split data into individual plots
  179 + plot_index <<- which(names(data)=="Plot")
  180 + Metadata <<- data[grep("Metadata",data$Plot),-c(1)]
  181 + IM <<- data[grep("IM",data$Plot),-c(1)]
  182 + GM <<- data[grep("GM",data$Plot),-c(1)]
  183 + DET <<- data[grep("DET",data$Plot),-c(1)]
  184 + IET <<- data[grep("IET",data$Plot),-c(1)]
  185 + FAR <- data[grep("FAR",data$Plot),-c(1)]
  186 + FRR <- data[grep("FRR",data$Plot),-c(1)]
  187 + SD <<- data[grep("SD",data$Plot),-c(1)]
  188 + TF <<- data[grep("TF",data$Plot),-c(1)]
  189 + FT <<- data[grep("FT",data$Plot),-c(1)]
  190 + CT <<- data[grep("CT",data$Plot),-c(1)]
  191 + BC <<- data[grep("BC",data$Plot),-c(1)]
  192 + TS <<- data[grep("TS",data$Plot),-c(1)]
  193 + CMC <<- data[grep("CMC",data$Plot),-c(1)]
  194 + FAR$Error <- "FAR"
  195 + FRR$Error <- "FRR"
  196 + ERR <<- rbind(FAR, FRR)
  197 +
  198 + # Format data
  199 + Metadata$Y<-factor(Metadata$Y, levels=c("Genuine", "Impostor", "Ignored", "Gallery", "Probe"))
  200 + IM$Y <<- as.character(IM$Y)
  201 + GM$Y <<- as.character(GM$Y)
  202 + DET$Y <<- as.numeric(as.character(DET$Y))
  203 + IET$Y <<- as.numeric(as.character(IET$Y))
  204 + ERR$Y <<- as.numeric(as.character(ERR$Y))
  205 + SD$Y <<- as.factor(unique(as.character(SD$Y)))
  206 + TF$Y <<- as.numeric(as.character(TF$Y))
  207 + FT$Y <<- as.numeric(as.character(FT$Y))
  208 + CT$Y <<- as.numeric(as.character(CT$Y))
  209 + BC$Y <<- as.numeric(as.character(BC$Y))
  210 + TS$Y <<- as.character(TS$Y)
  211 + CMC$Y <<- as.numeric(as.character(CMC$Y))
  212 + } else if (type == "detection") {
  213 + # Split data into individual plots
  214 + DiscreteROC <<- data[grep("DiscreteROC",data$Plot),-c(1)]
  215 + ContinuousROC <<- data[grep("ContinuousROC",data$Plot),-c(1)]
  216 + DiscretePR <<- data[grep("DiscretePR",data$Plot),-c(1)]
  217 + ContinuousPR <<- data[grep("ContinuousPR",data$Plot),-c(1)]
  218 + Overlap <<- data[grep("Overlap",data$Plot),-c(1)]
  219 + AverageOverlap <<- data[grep("AverageOverlap",data$Plot),-c(1)]
  220 + } else if (type == "landmarking") {
  221 + # Split data into individual plots
  222 + Box <<- data[grep("Box",data$Plot),-c(1)]
  223 + Box$X <<- factor(Box$X, levels = Box$X, ordered = TRUE)
  224 + Sample <<- data[grep("Sample",data$Plot),-c(1)]
  225 + Sample$X <<- as.character(Sample$X)
  226 + EXT <<- data[grep("EXT",data$Plot),-c(1)]
  227 + EXT$X <<- as.character(EXT$X)
  228 + EXP <<- data[grep("EXP",data$Plot),-c(1)]
  229 + EXP$X <<- as.character(EXP$X)
  230 + NormLength <<- data[grep("NormLength",data$Plot),-c(1)]
  231 + sample <<- readImageData(Sample)
  232 + rows <<- sample[[1]]$value
  233 + }
  234 +}
  235 +
  236 +summarySE <- function(data=NULL, measurevar, groupvars=NULL, na.rm=FALSE, conf.interval=0.95, .drop=TRUE) {
  237 + # derived from http://www.cookbook-r.com/Manipulating_data/Summarizing_data/
  238 + require(plyr)
  239 +
  240 + length2 <- function (x, na.rm=FALSE) {
  241 + if (na.rm) sum(!is.na(x))
  242 + else length(x)
  243 + }
  244 +
  245 + datac <- ddply(data, groupvars, .drop=.drop, .fun = function(xx, col) {
  246 + c(N=length2(xx[[col]], na.rm=na.rm), mean=mean(xx[[col]], na.rm=na.rm), sd=sd(xx[[col]], na.rm=na.rm))
  247 + },
  248 + measurevar
  249 + )
  250 +
  251 + datac <- rename(datac, c("mean" = measurevar))
  252 + datac$se <- datac$sd / sqrt(datac$N)
  253 + ciMult <- qt(conf.interval/2 + .5, datac$N-1)
  254 + datac$ci <- datac$se * ciMult
  255 +
  256 + datac$upper <- if(datac[, measurevar] + datac$ci < 1) (datac[, measurevar] + datac$ci) else 1
  257 + datac$lower <- if(datac[, measurevar] - datac$ci > 0) (datac[, measurevar] - datac$ci) else 0
  258 +
  259 + return(datac)
  260 +}
  261 +
  262 +multiplot <- function(..., plotlist=NULL, cols) {
  263 + require(grid)
  264 + # Make a list from the ... arguments and plotlist
  265 + plots <- c(list(...), plotlist)
  266 + numPlots = length(plots)
  267 + # Make the panel
  268 + plotCols = cols
  269 + plotRows = ceiling(numPlots/plotCols)
  270 + # Set up the page
  271 + grid.newpage()
  272 + pushViewport(viewport(layout = grid.layout(plotRows, plotCols)))
  273 + vplayout <- function(x, y)
  274 + viewport(layout.pos.row = x, layout.pos.col = y)
  275 + # Make each plot, in the correct location
  276 + for (i in 1:numPlots) {
  277 + curRow = ceiling(i/plotCols)
  278 + curCol = (i-1) %% plotCols + 1
  279 + print(plots[[i]], vp = vplayout(curRow, curCol))
  280 + }
  281 +}
  282 +
  283 +plotEERSamples <- function(imData=NULL, gmData=NULL) {
  284 + if(nrow(imData) == 0) return()
  285 +
  286 + printImages <- function(images, label) {
  287 + for (i in 1:nrow(images)) {
  288 + score <- images[i,1]
  289 + files <- images[i,2]
  290 + alg <- images[i,3]
  291 + files <- unlist(strsplit(files, "[:]"))
  292 +
  293 + ext1 <- unlist(strsplit(files[2], "[.]"))[2]
  294 + ext2 <- unlist(strsplit(files[4], "[.]"))[2]
  295 + if (ext1 == "jpg" || ext1 == "JPEG" || ext1 == "jpeg" || ext1 == "JPG") {
  296 + img1 <- readJPEG(files[2])
  297 + } else if (ext1 == "PNG" || ext1 == "png") {
  298 + img1 <- readPNG(files[2])
  299 + } else if (ext1 == "TIFF" || ext1 == "tiff" || ext1 == "TIF" || ext1 == "tif") {
  300 + img1 <- readTIFF(files[2])
  301 + } else {
  302 + next
  303 + }
  304 + if (ext2 == "jpg" || ext2 == "JPEG" || ext2 == "jpeg" || ext2 == "JPG") {
  305 + img2 <- readJPEG(files[4])
  306 + } else if (ext2 == "PNG" || ext2 == "png") {
  307 + img2 <- readPNG(files[4])
  308 + } else if (ext2 == "TIFF" || ext2 == "tiff" || ext2 == "TIF" || ext2 == "tif") {
  309 + img2 <- readTIFF(files[4])
  310 + } else {
  311 + next
  312 + }
  313 + name1 <- files[1]
  314 + name2 <- files[3]
  315 +
  316 + g1 <- rasterGrob(img1, interpolate=TRUE)
  317 + g2 <- rasterGrob(img2, interpolate=TRUE)
  318 +
  319 + plot1 <- qplot(1:10, 1:10, geom="blank") + annotation_custom(g1, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) + theme(axis.line=element_blank(), axis.text.x=element_blank(), axis.text.y=element_blank(), axis.ticks=element_blank(), panel.background=element_blank()) + labs(title=alg) + ylab(unlist(strsplit(files[2], "[/]"))[length(unlist(strsplit(files[2], "[/]")))]) + xlab(name1)
  320 + plot2 <- qplot(1:10, 1:10, geom="blank") + annotation_custom(g2, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) + theme(axis.line=element_blank(), axis.text.x=element_blank(), axis.text.y=element_blank(), axis.ticks=element_blank(), panel.background=element_blank()) + labs(title=paste(label, " score =", score)) + ylab(unlist(strsplit(files[4], "[/]"))[length(unlist(strsplit(files[4], "[/]")))]) + xlab(name2)
  321 +
  322 + multiplot(plot1, plot2, cols=2)
  323 + }
  324 + }
  325 + printImages(imData, "Impostor")
  326 + printImages(gmData, "Genuine")
  327 +}
  328 +
  329 +plotLandmarkSamples <- function(samples=NULL, expData=NULL, extData=NULL) {
  330 + print(plotImage(samples[[1]], "Sample Landmarks", sprintf("Total Landmarks: %s", samples[[1]]$value)))
  331 + if (nrow(EXT) != 0 && nrow(EXP)) {
  332 + for (j in 1:length(algs)) {
  333 + truthSample <- readData(EXT[EXT$. == algs[[j]],])
  334 + predictedSample <- readData(EXP[EXP$. == algs[[j]],])
  335 + for (i in 1:length(predictedSample)) {
  336 + multiplot(plotImage(predictedSample[[i]], sprintf("%s\nPredicted Landmarks", algs[[j]]), sprintf("Average Landmark Error: %.3f", predictedSample[[i]]$value)), plotImage(truthSample[[i]], "Ground Truth\nLandmarks", ""), cols=2)
  337 + }
  338 + }
  339 + }
  340 +}
  341 +
  342 +readImageData <- function(data) {
  343 + examples <- list()
  344 + for (i in 1:nrow(data)) {
  345 + path <- data[i,1]
  346 + value <- data[i,2]
  347 + file <- unlist(strsplit(path, "[.]"))[1]
  348 + ext <- unlist(strsplit(path, "[.]"))[2]
  349 + if (ext == "jpg" || ext == "JPEG" || ext == "jpeg" || ext == "JPG") {
  350 + img <- readJPEG(path)
  351 + } else if (ext == "PNG" || ext == "png") {
  352 + img <- readPNG(path)
  353 + } else if (ext == "TIFF" || ext == "tiff" || ext == "TIF" || ext == "tif") {
  354 + img <- readTIFF(path)
  355 + }else {
  356 + next
  357 + }
  358 + example <- list(file = file, value = value, image = img)
  359 + examples[[i]] <- example
  360 + }
  361 + return(examples)
  362 +}
  363 +
  364 +plotImage <- function(image, title=NULL, label=NULL) {
  365 + p <- qplot(1:10, 1:10, geom="blank") + annotation_custom(rasterGrob(image$image), xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf)
  366 + p <- p + theme(axis.line=element_blank(), axis.title.y=element_blank(), axis.text.x=element_blank(), axis.text.y=element_blank(), line=element_blank(), axis.ticks=element_blank(), panel.background=element_blank())
  367 + p <- p + labs(title=title) + xlab(label)
  368 + return(p)
  369 +}
  370 +
... ...