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