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,6 +142,7 @@ endif()
142 # Look for extensions to OpenBR 142 # Look for extensions to OpenBR
143 set(BR_THIRDPARTY_PLUGINS_DIR CACHE PATH "") 143 set(BR_THIRDPARTY_PLUGINS_DIR CACHE PATH "")
144 set(BR_THIRDPARTY_APPS_DIR CACHE PATH "") 144 set(BR_THIRDPARTY_APPS_DIR CACHE PATH "")
  145 +set(BR_THIRDPARTY_DIR CACHE PATH "")
145 mark_as_advanced(BR_THIRDPARTY_PLUGINS_DIR) 146 mark_as_advanced(BR_THIRDPARTY_PLUGINS_DIR)
146 mark_as_advanced(BR_THIRDPARTY_APPS_DIR) 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,4 +8,4 @@ Identify the latest [tagged release](https://github.com/biometrics/openbr/releas
8 $ git submodule init 8 $ git submodule init
9 $ git submodule update 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,8 +635,8 @@ textSize | float | Size of text for title, legend and axes
635 xTitle/yTitle | [QString] | Title for x/y axis 635 xTitle/yTitle | [QString] | Title for x/y axis
636 xLog/yLog | bool | Plot log scale for x/y axis 636 xLog/yLog | bool | Plot log scale for x/y axis
637 xLimits/yLimits | [QPointF] | Set x/y axis limits, ex. xLimits=(lower,upper) 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 If specifying plot options it is a good idea to wrap the destination file in single quotes to avoid parsing errors. 641 If specifying plot options it is a good idea to wrap the destination file in single quotes to avoid parsing errors.
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. 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,6 +23,25 @@ using namespace cv;
23 namespace br 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 static QStringList getPivots(const QString &file, bool headers) 45 static QStringList getPivots(const QString &file, bool headers)
27 { 46 {
28 QString str; 47 QString str;
@@ -31,14 +50,6 @@ static QStringList getPivots(const QString &file, bool headers) @@ -31,14 +50,6 @@ static QStringList getPivots(const QString &file, bool headers)
31 return str.split("_"); 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 // Custom sorting method to ensure datasets are ordered nicely 53 // Custom sorting method to ensure datasets are ordered nicely
43 static bool sortFiles(const QString &fileA, const QString &fileB) 54 static bool sortFiles(const QString &fileA, const QString &fileB)
44 { 55 {
@@ -51,10 +62,6 @@ struct RPlot @@ -51,10 +62,6 @@ struct RPlot
51 QFile file; 62 QFile file;
52 QStringList pivotHeaders; 63 QStringList pivotHeaders;
53 QVector< QSet<QString> > pivotItems; 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 struct Pivot 66 struct Pivot
60 { 67 {
@@ -68,7 +75,7 @@ struct RPlot @@ -68,7 +75,7 @@ struct RPlot
68 75
69 Pivot major, minor; 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 if (files.isEmpty()) qFatal("Empty file list."); 80 if (files.isEmpty()) qFatal("Empty file list.");
74 qSort(files.begin(), files.end(), sortFiles); 81 qSort(files.begin(), files.end(), sortFiles);
@@ -83,13 +90,9 @@ struct RPlot @@ -83,13 +90,9 @@ struct RPlot
83 bool success = file.open(QFile::WriteOnly); 90 bool success = file.open(QFile::WriteOnly);
84 if (!success) qFatal("Failed to open %s for writing.", qPrintable(file.fileName())); 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 "data <- NULL\n"); 96 "data <- NULL\n");
94 97
95 // Read files and retrieve pivots 98 // Read files and retrieve pivots
@@ -123,8 +126,6 @@ struct RPlot @@ -123,8 +126,6 @@ struct RPlot
123 } 126 }
124 127
125 const QString &smooth = destination.get<QString>("smooth", ""); 128 const QString &smooth = destination.get<QString>("smooth", "");
126 - confidence = destination.get<float>("confidence", 95) / 100.0;  
127 -  
128 major.smooth = !smooth.isEmpty() && (major.header == smooth) && (major.size > 1); 129 major.smooth = !smooth.isEmpty() && (major.header == smooth) && (major.size > 1);
129 minor.smooth = !smooth.isEmpty() && (minor.header == smooth) && (minor.size > 1); 130 minor.smooth = !smooth.isEmpty() && (minor.header == smooth) && (minor.size > 1);
130 if (major.smooth) major.size = 1; 131 if (major.smooth) major.size = 1;
@@ -132,68 +133,19 @@ struct RPlot @@ -132,68 +133,19 @@ struct RPlot
132 if (major.size < minor.size) 133 if (major.size < minor.size)
133 std::swap(major, minor); 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 // Open output device 150 // Open output device
199 file.write(qPrintable(QString("\n" 151 file.write(qPrintable(QString("\n"
@@ -205,98 +157,6 @@ struct RPlot @@ -205,98 +157,6 @@ struct RPlot
205 "# Write figures\n"); 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 bool finalize(bool show = false) 160 bool finalize(bool show = false)
301 { 161 {
302 file.write("dev.off()\n"); 162 file.write("dev.off()\n");
@@ -315,13 +175,27 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show) @@ -315,13 +175,27 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
315 qDebug("Plotting %d file(s) to %s", files.size(), qPrintable(destination)); 175 qDebug("Plotting %d file(s) to %s", files.size(), qPrintable(destination));
316 176
317 RPlot p(files, destination); 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 // Use a br::file for simple storage of plot options 193 // Use a br::file for simple storage of plot options
320 QMap<QString,File> optMap; 194 QMap<QString,File> optMap;
321 optMap.insert("rocOptions", File(QString("[xTitle=False Accept Rate,yTitle=True Accept Rate,xLog=true,yLog=false]"))); 195 optMap.insert("rocOptions", File(QString("[xTitle=False Accept Rate,yTitle=True Accept Rate,xLog=true,yLog=false]")));
322 optMap.insert("detOptions", File(QString("[xTitle=False Accept Rate,yTitle=False Reject Rate,xLog=true,yLog=true]"))); 196 optMap.insert("detOptions", File(QString("[xTitle=False Accept Rate,yTitle=False Reject Rate,xLog=true,yLog=true]")));
323 optMap.insert("ietOptions", File(QString("[xTitle=False Positive Identification Rate (FPIR),yTitle=False Negative Identification Rate (FNIR),xLog=true,yLog=true]"))); 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 foreach (const QString &key, optMap.keys()) { 200 foreach (const QString &key, optMap.keys()) {
327 const QStringList options = destination.get<QStringList>(key, QStringList()); 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,56 +207,33 @@ bool Plot(const QStringList &amp;files, const File &amp;destination, bool show)
333 } 207 }
334 208
335 // optional plot metadata and accuracy tables 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 return p.finalize(show); 238 return p.finalize(show);
388 } 239 }
@@ -420,7 +271,8 @@ bool filesHaveSinglePoint(const QStringList &amp;files) { @@ -420,7 +271,8 @@ bool filesHaveSinglePoint(const QStringList &amp;files) {
420 bool PlotDetection(const QStringList &files, const File &destination, bool show) 271 bool PlotDetection(const QStringList &files, const File &destination, bool show)
421 { 272 {
422 qDebug("Plotting %d detection file(s) to %s", files.size(), qPrintable(destination)); 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 // Use a br::file for simple storage of plot options 277 // Use a br::file for simple storage of plot options
426 QMap<QString,File> optMap; 278 QMap<QString,File> optMap;
@@ -436,40 +288,26 @@ bool PlotDetection(const QStringList &amp;files, const File &amp;destination, bool show) @@ -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 QString plotType("line"); 291 QString plotType("line");
451 if (filesHaveSinglePoint(files)) 292 if (filesHaveSinglePoint(files))
452 plotType = QString("point"); 293 plotType = QString("point");
453 294
  295 + QString plot = "plot <- plotLine(lineData=%1, options=list(%2), flipY=%3, geometry=%4)\nplot\n";
454 foreach (const QString &type, QStringList() << "Discrete" << "Continuous") { 296 foreach (const QString &type, QStringList() << "Discrete" << "Continuous") {
455 optMap["rocOptions"].set("title", type); 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 foreach (const QString &type, QStringList() << "Discrete" << "Continuous") { 301 foreach (const QString &type, QStringList() << "Discrete" << "Continuous") {
460 optMap["prOptions"].set("title", type); 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 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'") + 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 QString("%1%2\n\n").arg(p.minor.size > 1 ? "" : " + xlab(NULL)", p.major.size > 1 ? "" : " + ylab(NULL)"))); 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 QString("%1%2\n\n").arg(p.minor.size > 1 ? "" : " + xlab(NULL)", p.major.size > 1 ? "" : " + ylab(NULL)"))); 311 QString("%1%2\n\n").arg(p.minor.size > 1 ? "" : " + xlab(NULL)", p.major.size > 1 ? "" : " + ylab(NULL)")));
474 312
475 return p.finalize(show); 313 return p.finalize(show);
@@ -478,109 +316,12 @@ bool PlotDetection(const QStringList &amp;files, const File &amp;destination, bool show) @@ -478,109 +316,12 @@ bool PlotDetection(const QStringList &amp;files, const File &amp;destination, bool show)
478 bool PlotLandmarking(const QStringList &files, const File &destination, bool show) 316 bool PlotLandmarking(const QStringList &files, const File &destination, bool show)
479 { 317 {
480 qDebug("Plotting %d landmarking file(s) to %s", files.size(), qPrintable(destination)); 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 p.file.write(qPrintable(QString("ggplot(Box, aes(Y,%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), 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 p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) + 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,7 +340,7 @@ bool PlotMetadata(const QStringList &amp;files, const QString &amp;columns, bool show)
599 { 340 {
600 qDebug("Plotting %d metadata file(s) for columns %s", files.size(), qPrintable(columns)); 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 foreach (const QString &column, columns.split(";")) 344 foreach (const QString &column, columns.split(";"))
604 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))); 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 return p.finalize(show); 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,6 +415,29 @@ static T findAndRemove(QVariantMap &amp;map, const QString &amp;key, const T &amp;defaultVal
415 br_utemplate Template::toUniversalTemplate(const Template &t) 415 br_utemplate Template::toUniversalTemplate(const Template &t)
416 { 416 {
417 QVariantMap map = t.file.localMetadata(); 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 const int32_t algorithmID = findAndRemove<int32_t> (map, "AlgorithmID", 0); 441 const int32_t algorithmID = findAndRemove<int32_t> (map, "AlgorithmID", 0);
419 const int32_t x = findAndRemove<int32_t> (map, "X" , 0); 442 const int32_t x = findAndRemove<int32_t> (map, "X" , 0);
420 const int32_t y = findAndRemove<int32_t> (map, "Y" , 0); 443 const int32_t y = findAndRemove<int32_t> (map, "Y" , 0);
@@ -429,6 +452,49 @@ br_utemplate Template::toUniversalTemplate(const Template &amp;t) @@ -429,6 +452,49 @@ br_utemplate Template::toUniversalTemplate(const Template &amp;t)
429 Template Template::fromUniversalTemplate(br_const_utemplate ut) 452 Template Template::fromUniversalTemplate(br_const_utemplate ut)
430 { 453 {
431 QVariantMap map = QJsonDocument::fromJson(QByteArray((const char*) ut->data)).object().toVariantMap(); 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 map.insert("AlgorithmID", ut->algorithmID); 498 map.insert("AlgorithmID", ut->algorithmID);
433 map.insert("X" , ut->x ); 499 map.insert("X" , ut->x );
434 map.insert("Y" , ut->y ); 500 map.insert("Y" , ut->y );
openbr/plugins/classification/lda.cpp
@@ -15,9 +15,6 @@ @@ -15,9 +15,6 @@
15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 16
17 #include <Eigen/Dense> 17 #include <Eigen/Dense>
18 -#include <opencv2/imgproc/imgproc.hpp>  
19 -#include <opencv2/highgui/highgui.hpp>  
20 -  
21 #include <openbr/plugins/openbr_internal.h> 18 #include <openbr/plugins/openbr_internal.h>
22 19
23 #include <openbr/core/common.h> 20 #include <openbr/core/common.h>
@@ -58,7 +55,6 @@ BR_REGISTER(Initializer, EigenInitializer) @@ -58,7 +55,6 @@ BR_REGISTER(Initializer, EigenInitializer)
58 class PCATransform : public Transform 55 class PCATransform : public Transform
59 { 56 {
60 Q_OBJECT 57 Q_OBJECT
61 - friend class DFFSTransform;  
62 friend class LDATransform; 58 friend class LDATransform;
63 59
64 protected: 60 protected:
@@ -224,54 +220,6 @@ BR_REGISTER(Transform, PCATransform) @@ -224,54 +220,6 @@ BR_REGISTER(Transform, PCATransform)
224 220
225 /*! 221 /*!
226 * \ingroup transforms 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 * \brief Projects input into learned Linear Discriminant Analysis subspace. 223 * \brief Projects input into learned Linear Discriminant Analysis subspace.
276 * \author Brendan Klare \cite bklare 224 * \author Brendan Klare \cite bklare
277 * \author Josh Klontz \cite jklontz 225 * \author Josh Klontz \cite jklontz
@@ -793,439 +741,6 @@ class WCDATransform : public Transform @@ -793,439 +741,6 @@ class WCDATransform : public Transform
793 741
794 BR_REGISTER(Transform, WCDATransform) 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 } // namespace br 744 } // namespace br
1230 745
1231 #include "classification/lda.moc" 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,7 +2,7 @@ set(BR_WITH_DLIB ON CACHE BOOL &quot;Build with DLib&quot;)
2 2
3 if(${BR_WITH_DLIB}) 3 if(${BR_WITH_DLIB})
4 ExternalProject_Add(dlib 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 URL_MD5 e9e5449bc25370afce2d254327afac99 6 URL_MD5 e9e5449bc25370afce2d254327afac99
7 SOURCE_DIR "${CMAKE_SOURCE_DIR}/3rdparty/dlib-18.16" 7 SOURCE_DIR "${CMAKE_SOURCE_DIR}/3rdparty/dlib-18.16"
8 CONFIGURE_COMMAND "" 8 CONFIGURE_COMMAND ""
openbr/plugins/core/algorithms.cpp
@@ -34,7 +34,6 @@ class AlgorithmsInitializer : public Initializer @@ -34,7 +34,6 @@ class AlgorithmsInitializer : public Initializer
34 Globals->abbreviations.insert("FaceRecognition", "FaceDetection+FaceRecognitionRegistration+<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>+SetMetadata(AlgorithmID,-1):Unit(ByteL1)"); 34 Globals->abbreviations.insert("FaceRecognition", "FaceDetection+FaceRecognitionRegistration+<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>+SetMetadata(AlgorithmID,-1):Unit(ByteL1)");
35 Globals->abbreviations.insert("GenderClassification", "FaceDetection+Expand+FaceClassificationRegistration+Expand+<FaceClassificationExtraction>+<GenderClassifier>+Discard"); 35 Globals->abbreviations.insert("GenderClassification", "FaceDetection+Expand+FaceClassificationRegistration+Expand+<FaceClassificationExtraction>+<GenderClassifier>+Discard");
36 Globals->abbreviations.insert("AgeRegression", "FaceDetection+Expand+FaceClassificationRegistration+Expand+<FaceClassificationExtraction>+<AgeRegressor>+Discard"); 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 Globals->abbreviations.insert("MedianFace", "Open+Expand+Cascade(FrontalFace)+ASEFEyes+Affine(256,256,0.37,0.45)+Center(Median)"); 37 Globals->abbreviations.insert("MedianFace", "Open+Expand+Cascade(FrontalFace)+ASEFEyes+Affine(256,256,0.37,0.45)+Center(Median)");
39 Globals->abbreviations.insert("BlurredFaceDetection", "Open+LimitSize(1024)+SkinMask/(Cvt(Gray)+GradientMask)+And+Morph(Erode,16)+LargestConvexArea"); 38 Globals->abbreviations.insert("BlurredFaceDetection", "Open+LimitSize(1024)+SkinMask/(Cvt(Gray)+GradientMask)+And+Morph(Erode,16)+LargestConvexArea");
40 Globals->abbreviations.insert("DrawFaceDetection", "Open+Cascade(FrontalFace)+Expand+ASEFEyes+Draw(inPlace=true)"); 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,6 +23,7 @@ namespace br
23 * \ingroup transforms 23 * \ingroup transforms
24 * \brief Expand the width and height of a Template's rects by input width and height factors. 24 * \brief Expand the width and height of a Template's rects by input width and height factors.
25 * \author Charles Otto \cite caotto 25 * \author Charles Otto \cite caotto
  26 + * \author Brendan Klare \cite bklare
26 */ 27 */
27 class ExpandRectTransform : public UntrainableTransform 28 class ExpandRectTransform : public UntrainableTransform
28 { 29 {
@@ -31,35 +32,23 @@ class ExpandRectTransform : public UntrainableTransform @@ -31,35 +32,23 @@ class ExpandRectTransform : public UntrainableTransform
31 Q_PROPERTY(float heightExpand READ get_heightExpand WRITE set_heightExpand RESET reset_heightExpand STORED false) 32 Q_PROPERTY(float heightExpand READ get_heightExpand WRITE set_heightExpand RESET reset_heightExpand STORED false)
32 BR_PROPERTY(float, widthExpand, .5) 33 BR_PROPERTY(float, widthExpand, .5)
33 BR_PROPERTY(float, heightExpand, .5) 34 BR_PROPERTY(float, heightExpand, .5)
  35 +
34 void project(const Template &src, Template &dst) const 36 void project(const Template &src, Template &dst) const
35 { 37 {
36 dst = src; 38 dst = src;
37 - QList<QRectF> rects = dst.file.rects(); 39 + dst.file.clearRects();
  40 + QList<QRectF> rects = src.file.rects();
38 for (int i=0;i < rects.size(); i++) { 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 #include <openbr/plugins/openbr_internal.h> 1 #include <openbr/plugins/openbr_internal.h>
18 #include <openbr/core/opencvutils.h> 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 +