Commit ee5f23d444c62549eb99a5bb1873ca0e62c6219c

Authored by bhklein
1 parent b2ca8dda

expose axis types, bounds and ticks. Output tables to csv.

Showing 1 changed file with 150 additions and 173 deletions
openbr/core/plot.cpp
@@ -42,20 +42,6 @@ static QString getScale(const QString &mode, const QString &title, int vals) @@ -42,20 +42,6 @@ static QString getScale(const QString &mode, const QString &title, int vals)
42 // Custom sorting method to ensure datasets are ordered nicely 42 // Custom sorting method to ensure datasets are ordered nicely
43 static bool sortFiles(const QString &fileA, const QString &fileB) 43 static bool sortFiles(const QString &fileA, const QString &fileB)
44 { 44 {
45 - QFileInfo fileInfoA(fileA);  
46 - QFileInfo fileInfoB(fileB);  
47 -  
48 - if (fileInfoA.fileName().contains("Good")) return true;  
49 - if (fileInfoB.fileName().contains("Good")) return false;  
50 - if (fileInfoA.fileName().contains("Bad")) return true;  
51 - if (fileInfoB.fileName().contains("Bad")) return false;  
52 - if (fileInfoA.fileName().contains("Ugly")) return true;  
53 - if (fileInfoB.fileName().contains("Ugly")) return false;  
54 - if (fileInfoA.fileName().contains("MEDS")) return true;  
55 - if (fileInfoB.fileName().contains("MEDS")) return false;  
56 - if (fileInfoA.fileName().contains("PCSO")) return true;  
57 - if (fileInfoB.fileName().contains("PCSO")) return false;  
58 -  
59 return fileA < fileB; 45 return fileA < fileB;
60 } 46 }
61 47
@@ -65,7 +51,8 @@ struct RPlot @@ -65,7 +51,8 @@ struct RPlot
65 QFile file; 51 QFile file;
66 QStringList pivotHeaders; 52 QStringList pivotHeaders;
67 QVector< QSet<QString> > pivotItems; 53 QVector< QSet<QString> > pivotItems;
68 - float confidence; 54 + float confidence; // confidence interval for plotting across splits
  55 + int ncol; // Number of columns for plot legends
69 56
70 bool flip; 57 bool flip;
71 58
@@ -124,6 +111,7 @@ struct RPlot @@ -124,6 +111,7 @@ struct RPlot
124 } 111 }
125 file.write("data <- rbind(data, tmp)\n"); 112 file.write("data <- rbind(data, tmp)\n");
126 } 113 }
  114 +
127 for (int i=0; i<pivotItems.size(); i++) { 115 for (int i=0; i<pivotItems.size(); i++) {
128 const int size = pivotItems[i].size(); 116 const int size = pivotItems[i].size();
129 if (size > major.size) { 117 if (size > major.size) {
@@ -133,13 +121,10 @@ struct RPlot @@ -133,13 +121,10 @@ struct RPlot
133 minor = Pivot(i, size, pivotHeaders[i]); 121 minor = Pivot(i, size, pivotHeaders[i]);
134 } 122 }
135 } 123 }
  124 +
136 const QString &smooth = destination.get<QString>("smooth", ""); 125 const QString &smooth = destination.get<QString>("smooth", "");
137 - if (destination.contains(QString("confidence"))) {  
138 - const QString &CI = destination.get<QString>("confidence");  
139 - confidence = !CI.isEmpty() ? CI.toFloat()/100.0 : 0.95;  
140 - } else {  
141 - confidence = 0.95;  
142 - } 126 + confidence = destination.get<float>("confidence", 95) / 100.0;
  127 +
143 major.smooth = !smooth.isEmpty() && (major.header == smooth) && (major.size > 1); 128 major.smooth = !smooth.isEmpty() && (major.header == smooth) && (major.size > 1);
144 minor.smooth = !smooth.isEmpty() && (minor.header == smooth) && (minor.size > 1); 129 minor.smooth = !smooth.isEmpty() && (minor.header == smooth) && (minor.size > 1);
145 if (major.smooth) major.size = 1; 130 if (major.smooth) major.size = 1;
@@ -147,6 +132,7 @@ struct RPlot @@ -147,6 +132,7 @@ struct RPlot
147 if (major.size < minor.size) 132 if (major.size < minor.size)
148 std::swap(major, minor); 133 std::swap(major, minor);
149 134
  135 + ncol = destination.get<int>("ncol", major.size > 1 ? major.size : (minor.header.isEmpty() ? major.size : minor.size));
150 flip = minor.header == "Algorithm"; 136 flip = minor.header == "Algorithm";
151 // Format data 137 // Format data
152 if (isEvalFormat) 138 if (isEvalFormat)
@@ -187,7 +173,7 @@ struct RPlot @@ -187,7 +173,7 @@ struct RPlot
187 "TS$Y <- as.character(TS$Y)\n" 173 "TS$Y <- as.character(TS$Y)\n"
188 "CMC$Y <- as.numeric(as.character(CMC$Y))\n" 174 "CMC$Y <- as.numeric(as.character(CMC$Y))\n"
189 "\n" 175 "\n"
190 - "if (%1) {\n\tsummarySE <- function(data=NULL, measurevar, groupvars=NULL, na.rm=FALSE, conf.interval=%7, .drop=TRUE) {\n\t\t" 176 + "if (%1) {\n\tsummarySE <- function(data=NULL, measurevar, groupvars=NULL, na.rm=FALSE, conf.interval=%3, .drop=TRUE) {\n\t\t"
191 "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)" 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)"
192 "\n\t\t}\n\n\t\tdatac <- ddply(data, groupvars, .drop=.drop, .fun = function(xx, col) {\n\t\t\t" 178 "\n\t\t}\n\n\t\tdatac <- ddply(data, groupvars, .drop=.drop, .fun = function(xx, col) {\n\t\t\t"
193 "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}," 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},"
@@ -197,6 +183,7 @@ struct RPlot @@ -197,6 +183,7 @@ struct RPlot
197 "datac$lower <- if(datac[, measurevar] - datac$ci > 0) (datac[, measurevar] - datac$ci) else 0\n\n\t\treturn(datac)\n\t}\n\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"
198 "DET <- summarySE(DET, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"))\n\t" 184 "DET <- summarySE(DET, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"))\n\t"
199 "IET <- summarySE(IET, 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"
200 "ERR <- summarySE(ERR, measurevar=\"X\", groupvars=c(\"Error\", \"%2\", \"Y\"))\n\t" 187 "ERR <- summarySE(ERR, measurevar=\"X\", groupvars=c(\"Error\", \"%2\", \"Y\"))\n\t"
201 "TF <- summarySE(TF, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"))\n\t" 188 "TF <- summarySE(TF, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"))\n\t"
202 "FT <- summarySE(FT, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"))\n\t" 189 "FT <- summarySE(FT, measurevar=\"Y\", groupvars=c(\"%2\", \"X\"))\n\t"
@@ -204,82 +191,112 @@ struct RPlot @@ -204,82 +191,112 @@ struct RPlot
204 "# Code to format FAR values\n" 191 "# Code to format FAR values\n"
205 "far_names <- list('0.001'=\"FAR = 0.1%\", '0.01'=\"FAR = 1%\")\n" 192 "far_names <- list('0.001'=\"FAR = 0.1%\", '0.01'=\"FAR = 1%\")\n"
206 "far_labeller <- function(variable,value) { return(far_names[as.character(value)]) }\n" 193 "far_labeller <- function(variable,value) { return(far_names[as.character(value)]) }\n"
207 - "\n"  
208 - "# Code to format TAR@FAR table\n"  
209 - "algs <- unique(%6)\n"  
210 - "algs <- algs[!duplicated(algs)]\n"  
211 - "mat <- matrix(%3,nrow=6,ncol=length(algs),byrow=FALSE)\n"  
212 - "colnames(mat) <- algs \n"  
213 - "rownames(mat) <- c(\"FAR = 1e-06\", \"FAR = 1e-05\", \"FAR = 1e-04\", \"FAR = 1e-03\", \"FAR = 1e-02\", \"FAR = 1e-01\")\n"  
214 - "FTtable <- as.table(mat)\n"  
215 - "\n"  
216 - "# Code to format FAR@TAR table\n"  
217 - "mat <- matrix(%4,nrow=6,ncol=length(algs),byrow=FALSE)\n"  
218 - "colnames(mat) <- algs \n"  
219 - "rownames(mat) <- c(\"TAR = 0.40\", \"TAR = 0.50\", \"TAR = 0.65\", \"TAR = 0.75\", \"TAR = 0.85\", \"TAR = 0.95\")\n"  
220 - "F_at_Ttable <- as.table(mat)\n"  
221 - "\n"  
222 - "# Code to format CMC Table\n"  
223 - "mat <- matrix(%5,nrow=6,ncol=length(algs),byrow=FALSE)\n"  
224 - "colnames(mat) <- algs \n"  
225 - "rownames(mat) <- c(\" Rank 1\", \"Rank 5\", \"Rank 10\", \"Rank 20\", \"Rank 50\", \"Rank 100\")\n"  
226 - "CMCtable <- as.table(mat)\n"  
227 - "\n"  
228 - "# Code to format Template Size Table\n"  
229 - "if (nrow(TS) != 0) {\n\t"  
230 - "mat <- matrix(TS$Y,nrow=1,ncol=length(algs),byrow=FALSE)\n\t"  
231 - "colnames(mat) <- algs\n\t"  
232 - "rownames(mat) <- c(\"Template Size (bytes):\")\n\t"  
233 - "TStable <- as.table(mat)\n}\n").arg(((major.smooth || minor.smooth) ? "TRUE" : "FALSE"), major.size > 1 ? major.header : (minor.header.isEmpty() ? major.header : minor.header),  
234 - (major.smooth || minor.smooth) && confidence != 0 ? "paste(as.character(round(TF$Y, 3)), round(TF$ci, 3), sep=\"\\u00b1\")" : "TF$Y",  
235 - (major.smooth || minor.smooth) && confidence != 0 ? "paste(as.character(round(FT$Y, 3)), round(FT$ci, 3), sep=\"\\u00b1\")" : "FT$Y",  
236 - (major.smooth || minor.smooth) && confidence != 0 ? "paste(as.character(round(CT$Y, 3)), round(CT$ci, 3), sep=\"\\u00b1\")" : "CT$Y",  
237 - (major.size > 1 && minor.size > 1) && !(major.smooth || minor.smooth) ? QString("paste(TF$%1, TF$%2, sep=\"_\")").arg(major.header, minor.header) : QString("TF$%1").arg(major.size > 1 ? major.header : (minor.header.isEmpty() ? major.header : minor.header)),  
238 - QString::number(confidence)))); 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))));
239 197
240 // Open output device 198 // Open output device
241 file.write(qPrintable(QString("\n" 199 file.write(qPrintable(QString("\n"
242 "# Open output device\n" 200 "# Open output device\n"
243 - "%1(\"%2.%1\")\n").arg(suffix, basename)));  
244 -  
245 - // Write metadata table  
246 - if ((suffix == "pdf") && isEvalFormat) {  
247 - file.write("\n"  
248 - "# Write metadata table\n");  
249 - QString textplot = "MT <- as.data.frame(Metadata[c(1,2,3,4,5),])\n"  
250 - "par(mfrow=c(4,1))\n"  
251 - "plot.new()\n"  
252 - "print(title(paste(\"%1 - %2\",date(),sep=\"\\n\")))\n"  
253 - "mat <- matrix(MT$X[c(1,2)],ncol=2)\n"  
254 - "colnames(mat) <- c(\"Gallery\", \"Probe\")\n"  
255 - "imageTable <- as.table(mat)\n"  
256 - "print(textplot(imageTable,show.rownames=FALSE))\n"  
257 - "print(title(\"Images\"))\n"  
258 - "mat <- matrix(MT$X[c(3,4,5)],ncol=3)\n"  
259 - "colnames(mat) <- c(\"Genuine\", \"Impostor\", \"Ignored\")\n"  
260 - "matchTable <- as.table(mat)\n"  
261 - "print(textplot(matchTable,show.rownames=FALSE))\n"  
262 - "print(title(\"Matches\"))\n"  
263 - "plot.new()\n"  
264 - "print(title(\"Gallery * Probe = Genuine + Impostor + Ignored\"))\n"  
265 - "plot.new()\n"  
266 - "print(textplot(FTtable))\n"  
267 - "print(title(\"Table of True Accept Rates at various False Accept Rates\"))\n"  
268 - "print(textplot(F_at_Ttable))\n"  
269 - "print(title(\"Table of False Accept Rates at various True Accept Rates\"))\n"  
270 - "print(textplot(CMCtable))\n"  
271 - "print(title(\"Table of retrieval rate at various ranks\"))\n"  
272 - "if (nrow(TS) != 0) {\n\t"  
273 - "print(textplot(TStable, cex=1.15))\n\t"  
274 - "print(title(\"Template Size by Algorithm\"))\n}\n";  
275 - file.write(qPrintable(textplot.arg(PRODUCT_NAME, PRODUCT_VERSION)));  
276 - } 201 + "%1(\"%2.%1\"%3)\n").arg(suffix, basename, suffix != "pdf" ? ", width=1000, height=1000" : "")));
277 202
278 // Write figures 203 // Write figures
279 file.write("\n" 204 file.write("\n"
280 "# Write figures\n"); 205 "# Write figures\n");
281 } 206 }
282 207
  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>("xLab"), opts.get<QString>("yLab"))) +
  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) + annotation_logticks(sides=\"b\")").arg(data == "CMC" ? "c(1,5,10,50,100), breaks=c(1,5,10,50,100)" : "trans_format(\"log10\", math_format())")
  290 + : QString(" + scale_x_continuous(labels=%1, breaks=pretty_breaks(n=%2))").arg(data == "CMC" ? "waiver()" : "percent", opts.get<QString>("xTicks", "10"))) +
  291 + (opts.getBool("yLog") ? " + scale_y_log10(labels=trans_format(\"log10\", math_format())) + annotation_logticks(sides=\"l\")"
  292 + : QString(" + scale_y_continuous(labels=percent, breaks=pretty_breaks(n=%1))").arg(opts.get<QString>("yTicks", "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 +
283 bool finalize(bool show = false) 300 bool finalize(bool show = false)
284 { 301 {
285 file.write("dev.off()\n"); 302 file.write("dev.off()\n");
@@ -297,66 +314,31 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show) @@ -297,66 +314,31 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
297 { 314 {
298 qDebug("Plotting %d file(s) to %s", files.size(), qPrintable(destination)); 315 qDebug("Plotting %d file(s) to %s", files.size(), qPrintable(destination));
299 316
300 - const bool minimalist = destination.getBool("minimalist");  
301 - const bool uncertainty = destination.get<bool>("uncertainty"); 317 + RPlot p(files, destination);
302 318
303 // Use a br::file for simple storage of plot options 319 // Use a br::file for simple storage of plot options
304 - File cmcOpts;  
305 - const QStringList cmcOptions = destination.get<QStringList>("cmcOptions", QStringList());  
306 - foreach (const QString& option, cmcOptions) {  
307 - QStringList words = QtUtils::parse(option, '=');  
308 - QtUtils::checkArgsSize(words[0],words,1,2);  
309 - cmcOpts.set(words[0],words[1]);  
310 - }  
311 -  
312 - File rocOpts;  
313 - const QStringList rocOptions = destination.get<QStringList>("rocOptions", QStringList());  
314 - foreach (const QString& option, rocOptions) {  
315 - QStringList words = QtUtils::parse(option, '=');  
316 - QtUtils::checkArgsSize(words[0],words,1,2);  
317 - rocOpts.set(words[0],words[1]); 320 + QMap<QString,File> optMap;
  321 + optMap.insert("rocOptions", File(QString("[xLab=False Accept Rate,yLab=True Accept Rate,xLog=true,yLog=false]")));
  322 + optMap.insert("detOptions", File(QString("[xLab=False Accept Rate,yLab=False Reject Rate,xLog=true,yLog=true]")));
  323 + optMap.insert("ietOptions", File(QString("[xLab=False Positive Identification Rate (FPIR),yLab=False Negative Identification Rate (FNIR),xLog=true,yLog=true]")));
  324 + optMap.insert("cmcOptions", File(QString("[xLab=Rank,yLab=Retrieval Rate,xLog=true,yLog=false,size=1]")));
  325 +
  326 + foreach (const QString &key, optMap.keys()) {
  327 + const QStringList options = destination.get<QStringList>(key, QStringList());
  328 + foreach (const QString &option, options) {
  329 + QStringList words = QtUtils::parse(option, '=');
  330 + QtUtils::checkArgsSize(words[0], words, 1, 2);
  331 + optMap[key].set(words[0], words[1]);
  332 + }
318 } 333 }
319 334
320 -  
321 - RPlot p(files, destination);  
322 -  
323 - p.file.write(qPrintable(QString("qplot(X, 1-Y, data=DET, geom=\"line\", main=\"%1\"").arg(rocOpts.get<QString>("title",QString())) +  
324 - (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) +  
325 - (p.minor.size > 1 ? QString(", linetype=factor(%1)").arg(p.minor.header) : QString()) +  
326 - QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") +  
327 - ((p.major.smooth || p.minor.smooth) && p.confidence != 0 ? " + geom_errorbar(data=DET[seq(1, NROW(DET), by = 29),], aes(x=X, ymin=(1-lower), ymax=(1-upper)), width=0.1, alpha=I(1/2))" : QString()) +  
328 - (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) +  
329 - (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) +  
330 - QString(" + scale_x_log10(labels=trans_format(\"log10\", math_format()))") +  
331 - (rocOpts.contains("yLimits") ? QString(" + scale_y_continuous(labels=percent) + coord_cartesian(ylim=%1)").arg("c"+QtUtils::toString(rocOpts.get<QPointF>("yLimits",QPointF()))) : QString(" + scale_y_continuous(labels=percent)")) +  
332 - QString(" + annotation_logticks(sides=\"b\")") +  
333 - QString(" + theme(legend.title = 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),"  
334 - " legend.position=%2, legend.background = element_rect(fill = 'white'), panel.grid.major = element_line(colour = \"gray\"), panel.grid.minor = element_line(colour = \"gray\", linetype = \"dashed\"), legend.text = element_text(size = %1))").arg(QString::number(rocOpts.get<float>("textSize",12)), rocOpts.contains("legendPosition") ? "c"+QtUtils::toString(rocOpts.get<QPointF>("legendPosition")) : "'bottom'") +  
335 - QString(" + guides(col=guide_legend(ncol=%1))\n\n").arg(destination.get<int>("ncol", p.major.size > 1 ? p.major.size : (p.minor.header.isEmpty() ? p.major.size : p.minor.size)))));  
336 -  
337 - p.file.write(qPrintable(QString("qplot(X, Y, data=DET, geom=\"line\"") +  
338 - (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) +  
339 - (p.minor.size > 1 ? QString(", linetype=factor(%1)").arg(p.minor.header) : QString()) +  
340 - QString(", xlab=\"False Accept Rate\", ylab=\"False Reject Rate\") + geom_abline(alpha=0.5, colour=\"grey\", linetype=\"dashed\") + theme_minimal()") +  
341 - ((p.major.smooth || p.minor.smooth) && p.confidence != 0 ? " + geom_errorbar(data=DET[seq(1, NROW(DET), by = 29),], aes(x=X, ymin=lower, ymax=upper), width=0.1, alpha=I(1/2))" : QString()) +  
342 - (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) +  
343 - (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) +  
344 - QString(" + theme(legend.title = 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),"  
345 - " legend.position=%2, legend.background = element_rect(fill = 'white'), panel.grid.major = element_line(colour = \"gray\"), panel.grid.minor = element_line(colour = \"gray\", linetype = \"dashed\"), legend.text = element_text(size = %1))").arg(QString::number(rocOpts.get<float>("textSize",12)), rocOpts.contains("legendPosition") ? "c"+QtUtils::toString(rocOpts.get<QPointF>("legendPosition")) : "'bottom'") +  
346 - QString(" + scale_x_log10(labels=trans_format(\"log10\", math_format())) + scale_y_log10(labels=trans_format(\"log10\", math_format())) + annotation_logticks()") +  
347 - QString(" + guides(col=guide_legend(ncol=%1))\n\n").arg(destination.get<int>("ncol", p.major.size > 1 ? p.major.size : (p.minor.header.isEmpty() ? p.major.size : p.minor.size)))));  
348 -  
349 - p.file.write(qPrintable(QString("qplot(X, Y, data=IET, geom=\"line\"") +  
350 - (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) +  
351 - (p.minor.size > 1 ? QString(", linetype=factor(%1)").arg(p.minor.header) : QString()) +  
352 - QString(", xlab=\"False Positive Identification Rate (FPIR)\", ylab=\"False Negative Identification Rate (FNIR)\") + theme_minimal()") +  
353 - ((p.major.smooth || p.minor.smooth) && p.confidence != 0 ? " + geom_errorbar(data=IET[seq(1, NROW(IET), by = 29),], aes(x=X, ymin=lower, ymax=upper), width=0.1, alpha=I(1/2))" : QString()) +  
354 - (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) +  
355 - (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) +  
356 - QString(" + theme(legend.title = 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),"  
357 - " legend.position=%2, legend.background = element_rect(fill = 'white'), panel.grid.major = element_line(colour = \"gray\"), panel.grid.minor = element_line(colour = \"gray\", linetype = \"dashed\"), legend.text = element_text(size = %1))").arg(QString::number(rocOpts.get<float>("textSize",12)), rocOpts.contains("legendPosition") ? "c"+QtUtils::toString(rocOpts.get<QPointF>("legendPosition")) : "'bottom'") +  
358 - QString(" + scale_x_log10(labels=trans_format(\"log10\", math_format())) + scale_y_log10(labels=trans_format(\"log10\", math_format())) + annotation_logticks()") +  
359 - QString(" + guides(col=guide_legend(ncol=%1))\n\n").arg(destination.get<int>("ncol", p.major.size > 1 ? p.major.size : (p.minor.header.isEmpty() ? p.major.size : p.minor.size))))); 335 + // 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"]);
360 342
361 p.file.write(qPrintable(QString("qplot(X, data=SD, geom=\"histogram\", fill=Y, position=\"identity\", alpha=I(1/2)") + 343 p.file.write(qPrintable(QString("qplot(X, data=SD, geom=\"histogram\", fill=Y, position=\"identity\", alpha=I(1/2)") +
362 QString(", xlab=\"Score\", ylab=\"Frequency\"") + 344 QString(", xlab=\"Score\", ylab=\"Frequency\"") +
@@ -364,18 +346,7 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show) @@ -364,18 +346,7 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
364 (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()) + 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()) +
365 QString(" + theme(aspect.ratio=1)\n\n"))); 347 QString(" + theme(aspect.ratio=1)\n\n")));
366 348
367 - p.file.write(qPrintable(QString("ggplot(CMC, aes(x=X, y=Y%1%2)) + ggtitle(\"%3\") + xlab(\"Rank\") + ylab(\"Retrieval Rate\")").arg(p.major.size > 1 ? QString(" ,colour=factor(%1)").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=factor(%1)").arg(p.minor.header) : QString(), cmcOpts.get<QString>("title",QString())) +  
368 - QString(((p.major.smooth || p.minor.smooth) ? (!uncertainty ? " + stat_summary(geom=\"line\", fun.y=mean, size=%1)" : " + stat_summary(geom=\"line\", fun.y=min, aes(linetype=\"Min/Max\"), size=%1) + stat_summary(geom=\"line\", "  
369 - "fun.y=max, aes(linetype=\"Min/Max\"), size=%1) + stat_summary(geom=\"line\", fun.y=mean, aes(linetype=\"Mean\"), size=%1) + scale_linetype_manual(\"Legend\", values=c(\"Mean\"=1, \"Min/Max\"=2))") : " + geom_line(size=%1)")).arg(QString::number(cmcOpts.get<float>("thickness",1))) +  
370 - (minimalist ? "" : " + scale_x_log10(labels=c(1,5,10,50,100), breaks=c(1,5,10,50,100)) + annotation_logticks(sides=\"b\")") +  
371 - (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) +  
372 - (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) +  
373 - (cmcOpts.contains("yLimits") ? QString(" + scale_y_continuous(labels=percent) + coord_cartesian(ylim=%1)").arg("c"+QtUtils::toString(cmcOpts.get<QPointF>("yLimits",QPointF()))) : QString(" + scale_y_continuous(labels=percent)")) +  
374 - QString(" + theme_minimal() + theme(legend.title = 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),"  
375 - " legend.position=%2, legend.background = element_rect(fill = 'white'), panel.grid.major = element_line(colour = \"gray\"), panel.grid.minor = element_line(colour = \"gray\", linetype = \"dashed\"), legend.text = element_text(size = %1))").arg(QString::number(cmcOpts.get<float>("textSize",12)), cmcOpts.contains("legendPosition") ? "c"+QtUtils::toString(cmcOpts.get<QPointF>("legendPosition")) : "'bottom'") +  
376 - QString(" + guides(col=guide_legend(ncol=%1))\n\n").arg(destination.get<int>("ncol", p.major.size > 1 ? p.major.size : (p.minor.header.isEmpty() ? p.major.size : p.minor.size)))));  
377 -  
378 - 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") + 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") +
379 (p.major.size > 1 ? QString(", fill=factor(%1)").arg(p.major.header) : QString()) + 350 (p.major.size > 1 ? QString(", fill=factor(%1)").arg(p.major.header) : QString()) +
380 QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") + 351 QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") +
381 (p.major.size > 1 ? getScale("fill", p.major.header, p.major.size) : QString()) + 352 (p.major.size > 1 ? getScale("fill", p.major.header, p.major.size) : QString()) +
@@ -451,6 +422,20 @@ bool PlotDetection(const QStringList &amp;files, const File &amp;destination, bool show) @@ -451,6 +422,20 @@ bool PlotDetection(const QStringList &amp;files, const File &amp;destination, bool show)
451 qDebug("Plotting %d detection file(s) to %s", files.size(), qPrintable(destination)); 422 qDebug("Plotting %d detection file(s) to %s", files.size(), qPrintable(destination));
452 RPlot p(files, destination, false); 423 RPlot p(files, destination, false);
453 424
  425 + // Use a br::file for simple storage of plot options
  426 + QMap<QString,File> optMap;
  427 + optMap.insert("rocOptions", File(QString("[xLab=False Accepts Per Image,yLab=True Accept Rate,xLog=true,yLog=false]")));
  428 + optMap.insert("prOptions", File(QString("[xLab=False Accept Rate,yLab=False Reject Rate,xLog=true,yLog=true]")));
  429 +
  430 + foreach (const QString &key, optMap.keys()) {
  431 + const QStringList options = destination.get<QStringList>(key, QStringList());
  432 + foreach (const QString &option, options) {
  433 + QStringList words = QtUtils::parse(option, '=');
  434 + QtUtils::checkArgsSize(words[0], words, 1, 2);
  435 + optMap[key].set(words[0], words[1]);
  436 + }
  437 + }
  438 +
454 p.file.write("# Split data into individual plots\n" 439 p.file.write("# Split data into individual plots\n"
455 "plot_index = which(names(data)==\"Plot\")\n" 440 "plot_index = which(names(data)==\"Plot\")\n"
456 "DiscreteROC <- data[grep(\"DiscreteROC\",data$Plot),-c(1)]\n" 441 "DiscreteROC <- data[grep(\"DiscreteROC\",data$Plot),-c(1)]\n"
@@ -466,23 +451,15 @@ bool PlotDetection(const QStringList &amp;files, const File &amp;destination, bool show) @@ -466,23 +451,15 @@ bool PlotDetection(const QStringList &amp;files, const File &amp;destination, bool show)
466 if (filesHaveSinglePoint(files)) 451 if (filesHaveSinglePoint(files))
467 plotType = QString("point"); 452 plotType = QString("point");
468 453
469 - foreach (const QString &type, QStringList() << "Discrete" << "Continuous")  
470 - p.file.write(qPrintable(QString("qplot(X, Y, data=%1ROC%2").arg(type, (p.major.smooth || p.minor.smooth) ? ", geom=\"smooth\", method=loess, level=0.99" : QString(", geom=\"%1\"").arg(plotType)) +  
471 - (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) +  
472 - (p.minor.size > 1 ? QString(", linetype=factor(%1)").arg(p.minor.header) : QString()) +  
473 - QString(", xlab=\"False Accepts Per Image\", ylab=\"True Accept Rate\") + theme_minimal()") +  
474 - (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) +  
475 - (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) +  
476 - QString(" + scale_x_log10() + scale_y_continuous(labels=percent, limits=c(0,1)) + annotation_logticks(sides=\"b\") + ggtitle(\"%1\") + theme(legend.position=\"bottom\")\n\n").arg(type)));  
477 -  
478 - foreach (const QString &type, QStringList() << "Discrete" << "Continuous")  
479 - p.file.write(qPrintable(QString("qplot(X, Y, data=%1PR%2").arg(type, (p.major.smooth || p.minor.smooth) ? ", geom=\"smooth\", method=loess, level=0.99" : QString(", geom=\"%1\"").arg(plotType)) +  
480 - (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) +  
481 - (p.minor.size > 1 ? QString(", linetype=factor(%1)").arg(p.minor.header) : QString()) +  
482 - QString(", xlab=\"Recall\", ylab=\"Precision\") + theme_minimal()") +  
483 - (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) +  
484 - (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) +  
485 - QString(" + scale_x_continuous(limits=c(0,1)) + scale_y_continuous(labels=percent, limits=c(0,1)) + ggtitle(\"%1\") + theme(legend.position=\"bottom\")\n\n").arg(type))); 454 + foreach (const QString &type, QStringList() << "Discrete" << "Continuous") {
  455 + optMap["rocOptions"].set("title", type);
  456 + p.qplot(plotType, type + "ROC", false, optMap["rocOptions"]);
  457 + }
  458 +
  459 + foreach (const QString &type, QStringList() << "Discrete" << "Continuous") {
  460 + optMap["prOptions"].set("title", type);
  461 + p.qplot(plotType, type + "PR", false, optMap["prOptions"]);
  462 + }
486 463
487 p.file.write(qPrintable(QString("qplot(X, data=Overlap, geom=\"histogram\", position=\"identity\", xlab=\"Overlap\", ylab=\"Frequency\")") + 464 p.file.write(qPrintable(QString("qplot(X, data=Overlap, geom=\"histogram\", position=\"identity\", xlab=\"Overlap\", ylab=\"Frequency\")") +
488 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))") + 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))") +