Commit 3122cac3a97f03b34b0b1fab226e40aeb0c78cb8
Merge pull request #310 from biometrics/eval_landmark_improvements
Eval landmark improvements
Showing
7 changed files
with
203 additions
and
32 deletions
app/br/br.cpp
| @@ -163,8 +163,8 @@ public: | @@ -163,8 +163,8 @@ public: | ||
| 163 | check((parc >= 2) && (parc <= 6), "Incorrect parameter count for 'evalDetection'."); | 163 | check((parc >= 2) && (parc <= 6), "Incorrect parameter count for 'evalDetection'."); |
| 164 | br_eval_detection(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? atoi(parv[3]) : 0, parc >= 5 ? atoi(parv[4]) : 0, parc == 6 ? atoi(parv[5]) : 0); | 164 | br_eval_detection(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? atoi(parv[3]) : 0, parc >= 5 ? atoi(parv[4]) : 0, parc == 6 ? atoi(parv[5]) : 0); |
| 165 | } else if (!strcmp(fun, "evalLandmarking")) { | 165 | } else if (!strcmp(fun, "evalLandmarking")) { |
| 166 | - check((parc >= 2) && (parc <= 5), "Incorrect parameter count for 'evalLandmarking'."); | ||
| 167 | - br_eval_landmarking(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? atoi(parv[3]) : 0, parc >= 5 ? atoi(parv[4]) : 1); | 166 | + check((parc >= 2) && (parc <= 7), "Incorrect parameter count for 'evalLandmarking'."); |
| 167 | + br_eval_landmarking(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? atoi(parv[3]) : 0, parc >= 5 ? atoi(parv[4]) : 1, parc >= 6 ? atoi(parv[5]) : 0, parc >= 7 ? atoi(parv[6]) : 5); | ||
| 168 | } else if (!strcmp(fun, "evalRegression")) { | 168 | } else if (!strcmp(fun, "evalRegression")) { |
| 169 | check(parc >= 2 && parc <= 4, "Incorrect parameter count for 'evalRegression'."); | 169 | check(parc >= 2 && parc <= 4, "Incorrect parameter count for 'evalRegression'."); |
| 170 | br_eval_regression(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? parv[3] : ""); | 170 | br_eval_regression(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? parv[3] : ""); |
| @@ -264,7 +264,7 @@ private: | @@ -264,7 +264,7 @@ private: | ||
| 264 | "-evalClassification <predicted_gallery> <truth_gallery> <predicted property name> <ground truth proprty name>\n" | 264 | "-evalClassification <predicted_gallery> <truth_gallery> <predicted property name> <ground truth proprty name>\n" |
| 265 | "-evalClustering <clusters> <gallery>\n" | 265 | "-evalClustering <clusters> <gallery>\n" |
| 266 | "-evalDetection <predicted_gallery> <truth_gallery> [{csv}] [{normalize}] [{minSize}]\n" | 266 | "-evalDetection <predicted_gallery> <truth_gallery> [{csv}] [{normalize}] [{minSize}]\n" |
| 267 | - "-evalLandmarking <predicted_gallery> <truth_gallery> [{csv} [<normalization_index_a> <normalization_index_b>]]\n" | 267 | + "-evalLandmarking <predicted_gallery> <truth_gallery> [{csv} [<normalization_index_a> <normalization_index_b>] [sample_index] [total_examples]]\n" |
| 268 | "-evalRegression <predicted_gallery> <truth_gallery> <predicted property name> <ground truth property name>\n" | 268 | "-evalRegression <predicted_gallery> <truth_gallery> <predicted property name> <ground truth property name>\n" |
| 269 | "-assertEval <simmat> <mask> <accuracy>\n" | 269 | "-assertEval <simmat> <mask> <accuracy>\n" |
| 270 | "-plotDetection <file> ... <file> {destination}\n" | 270 | "-plotDetection <file> ... <file> {destination}\n" |
openbr/core/eval.cpp
| @@ -18,6 +18,7 @@ | @@ -18,6 +18,7 @@ | ||
| 18 | #include "eval.h" | 18 | #include "eval.h" |
| 19 | #include "openbr/core/common.h" | 19 | #include "openbr/core/common.h" |
| 20 | #include "openbr/core/qtutils.h" | 20 | #include "openbr/core/qtutils.h" |
| 21 | +#include "openbr/core/opencvutils.h" | ||
| 21 | #include <QMapIterator> | 22 | #include <QMapIterator> |
| 22 | 23 | ||
| 23 | using namespace cv; | 24 | using namespace cv; |
| @@ -1068,16 +1069,25 @@ float EvalDetection(const QString &predictedGallery, const QString &truthGallery | @@ -1068,16 +1069,25 @@ float EvalDetection(const QString &predictedGallery, const QString &truthGallery | ||
| 1068 | return averageOverlap; | 1069 | return averageOverlap; |
| 1069 | } | 1070 | } |
| 1070 | 1071 | ||
| 1071 | -float EvalLandmarking(const QString &predictedGallery, const QString &truthGallery, const QString &csv, int normalizationIndexA, int normalizationIndexB) | 1072 | +static void projectAndWrite(Transform *t, const Template &src, const QString &filePath) |
| 1073 | +{ | ||
| 1074 | + Template dst; | ||
| 1075 | + t->project(src,dst); | ||
| 1076 | + OpenCVUtils::saveImage(dst.m(),filePath); | ||
| 1077 | +} | ||
| 1078 | + | ||
| 1079 | +float EvalLandmarking(const QString &predictedGallery, const QString &truthGallery, const QString &csv, int normalizationIndexA, int normalizationIndexB, int sampleIndex, int totalExamples) | ||
| 1072 | { | 1080 | { |
| 1073 | qDebug("Evaluating landmarking of %s against %s", qPrintable(predictedGallery), qPrintable(truthGallery)); | 1081 | qDebug("Evaluating landmarking of %s against %s", qPrintable(predictedGallery), qPrintable(truthGallery)); |
| 1074 | - const TemplateList predicted(TemplateList::fromGallery(predictedGallery)); | ||
| 1075 | - const TemplateList truth(TemplateList::fromGallery(truthGallery)); | ||
| 1076 | - const QStringList predictedNames = File::get<QString>(predicted, "name"); | ||
| 1077 | - const QStringList truthNames = File::get<QString>(truth, "name"); | 1082 | + TemplateList predicted(TemplateList::fromGallery(predictedGallery)); |
| 1083 | + TemplateList truth(TemplateList::fromGallery(truthGallery)); | ||
| 1084 | + QStringList predictedNames = File::get<QString>(predicted, "name"); | ||
| 1085 | + QStringList truthNames = File::get<QString>(truth, "name"); | ||
| 1078 | 1086 | ||
| 1079 | int skipped = 0; | 1087 | int skipped = 0; |
| 1080 | QList< QList<float> > pointErrors; | 1088 | QList< QList<float> > pointErrors; |
| 1089 | + QList<float> imageErrors; | ||
| 1090 | + QList<float> normalizedLengths; | ||
| 1081 | for (int i=0; i<predicted.size(); i++) { | 1091 | for (int i=0; i<predicted.size(); i++) { |
| 1082 | const QString &predictedName = predictedNames[i]; | 1092 | const QString &predictedName = predictedNames[i]; |
| 1083 | const int truthIndex = truthNames.indexOf(predictedName); | 1093 | const int truthIndex = truthNames.indexOf(predictedName); |
| @@ -1085,39 +1095,94 @@ float EvalLandmarking(const QString &predictedGallery, const QString &truthGalle | @@ -1085,39 +1095,94 @@ float EvalLandmarking(const QString &predictedGallery, const QString &truthGalle | ||
| 1085 | const QList<QPointF> predictedPoints = predicted[i].file.points(); | 1095 | const QList<QPointF> predictedPoints = predicted[i].file.points(); |
| 1086 | const QList<QPointF> truthPoints = truth[truthIndex].file.points(); | 1096 | const QList<QPointF> truthPoints = truth[truthIndex].file.points(); |
| 1087 | if (predictedPoints.size() != truthPoints.size() || truthPoints.contains(QPointF(-1,-1))) { | 1097 | if (predictedPoints.size() != truthPoints.size() || truthPoints.contains(QPointF(-1,-1))) { |
| 1088 | - skipped++; | 1098 | + predicted.removeAt(i); |
| 1099 | + predictedNames.removeAt(i); | ||
| 1100 | + truth.removeAt(i); | ||
| 1101 | + truthNames.removeAt(i); | ||
| 1102 | + i--; skipped++; | ||
| 1089 | continue; | 1103 | continue; |
| 1090 | } | 1104 | } |
| 1105 | + | ||
| 1091 | while (pointErrors.size() < predictedPoints.size()) | 1106 | while (pointErrors.size() < predictedPoints.size()) |
| 1092 | pointErrors.append(QList<float>()); | 1107 | pointErrors.append(QList<float>()); |
| 1108 | + | ||
| 1109 | + // Want to know error for every image. | ||
| 1110 | + | ||
| 1093 | if (normalizationIndexA >= truthPoints.size()) qFatal("Normalization index A is out of range."); | 1111 | if (normalizationIndexA >= truthPoints.size()) qFatal("Normalization index A is out of range."); |
| 1094 | if (normalizationIndexB >= truthPoints.size()) qFatal("Normalization index B is out of range."); | 1112 | if (normalizationIndexB >= truthPoints.size()) qFatal("Normalization index B is out of range."); |
| 1095 | const float normalizedLength = QtUtils::euclideanLength(truthPoints[normalizationIndexB] - truthPoints[normalizationIndexA]); | 1113 | const float normalizedLength = QtUtils::euclideanLength(truthPoints[normalizationIndexB] - truthPoints[normalizationIndexA]); |
| 1096 | - for (int j=0; j<predictedPoints.size(); j++) | ||
| 1097 | - pointErrors[j].append(QtUtils::euclideanLength(predictedPoints[j] - truthPoints[j])/normalizedLength); | 1114 | + normalizedLengths.append(normalizedLength); |
| 1115 | + float totalError = 0; | ||
| 1116 | + for (int j=0; j<predictedPoints.size(); j++) { | ||
| 1117 | + float error = QtUtils::euclideanLength(predictedPoints[j] - truthPoints[j])/normalizedLength; | ||
| 1118 | + totalError += error; | ||
| 1119 | + pointErrors[j].append(error); | ||
| 1120 | + } | ||
| 1121 | + imageErrors.append(totalError/predictedPoints.size()); | ||
| 1098 | } | 1122 | } |
| 1099 | - qDebug() << "Skipped " << skipped << " files due to point size mismatch."; | 1123 | + |
| 1124 | + qDebug() << "Skipped" << skipped << "files due to point size mismatch."; | ||
| 1100 | 1125 | ||
| 1101 | QList<float> averagePointErrors; averagePointErrors.reserve(pointErrors.size()); | 1126 | QList<float> averagePointErrors; averagePointErrors.reserve(pointErrors.size()); |
| 1102 | - for (int i=0; i<pointErrors.size(); i++) { | ||
| 1103 | - std::sort(pointErrors[i].begin(), pointErrors[i].end()); | ||
| 1104 | - averagePointErrors.append(Common::Mean(pointErrors[i])); | ||
| 1105 | - } | ||
| 1106 | - const float averagePointError = Common::Mean(averagePointErrors); | ||
| 1107 | 1127 | ||
| 1108 | QStringList lines; | 1128 | QStringList lines; |
| 1109 | lines.append("Plot,X,Y"); | 1129 | lines.append("Plot,X,Y"); |
| 1130 | + | ||
| 1131 | + QtUtils::touchDir(QDir("landmarking_examples_truth")); | ||
| 1132 | + QtUtils::touchDir(QDir("landmarking_examples_predicted")); | ||
| 1133 | + | ||
| 1134 | + // Example | ||
| 1135 | + { | ||
| 1136 | + QScopedPointer<Transform> t(Transform::make("Open+Draw(verbose,rects=false,location=false)",NULL)); | ||
| 1137 | + | ||
| 1138 | + QString filePath = "landmarking_examples_truth/"+truth[sampleIndex].file.fileName(); | ||
| 1139 | + projectAndWrite(t.data(), truth[sampleIndex],filePath); | ||
| 1140 | + lines.append("Sample,"+filePath+","+QString::number(truth[sampleIndex].file.points().size())); | ||
| 1141 | + } | ||
| 1142 | + | ||
| 1143 | + // Get best and worst performing examples | ||
| 1144 | + QList< QPair<float,int> > exampleIndices = Common::Sort(imageErrors,true); | ||
| 1145 | + | ||
| 1146 | + QScopedPointer<Transform> t(Transform::make("Open+Draw(rects=false)",NULL)); | ||
| 1147 | + | ||
| 1148 | + for (int i=0; i<totalExamples; i++) { | ||
| 1149 | + QString filePath = "landmarking_examples_truth/"+truth[exampleIndices[i].second].file.fileName(); | ||
| 1150 | + projectAndWrite(t.data(), truth[exampleIndices[i].second],filePath); | ||
| 1151 | + lines.append("EXT,"+filePath+","+QString::number(exampleIndices[i].first)); | ||
| 1152 | + | ||
| 1153 | + filePath = "landmarking_examples_predicted/"+predicted[exampleIndices[i].second].file.fileName(); | ||
| 1154 | + projectAndWrite(t.data(), predicted[exampleIndices[i].second],filePath); | ||
| 1155 | + lines.append("EXP,"+filePath+","+QString::number(exampleIndices[i].first)); | ||
| 1156 | + } | ||
| 1157 | + | ||
| 1158 | + for (int i=exampleIndices.size()-1; i>exampleIndices.size()-totalExamples-1; i--) { | ||
| 1159 | + QString filePath = "landmarking_examples_truth/"+truth[exampleIndices[i].second].file.fileName(); | ||
| 1160 | + projectAndWrite(t.data(), truth[exampleIndices[i].second],filePath); | ||
| 1161 | + lines.append("EXT,"+filePath+","+QString::number(exampleIndices[i].first)); | ||
| 1162 | + | ||
| 1163 | + filePath = "landmarking_examples_predicted/"+predicted[exampleIndices[i].second].file.fileName(); | ||
| 1164 | + projectAndWrite(t.data(), predicted[exampleIndices[i].second],filePath); | ||
| 1165 | + lines.append("EXP,"+filePath+","+QString::number(exampleIndices[i].first)); | ||
| 1166 | + } | ||
| 1167 | + | ||
| 1110 | for (int i=0; i<pointErrors.size(); i++) { | 1168 | for (int i=0; i<pointErrors.size(); i++) { |
| 1169 | + std::sort(pointErrors[i].begin(), pointErrors[i].end()); | ||
| 1170 | + averagePointErrors.append(Common::Mean(pointErrors[i])); | ||
| 1111 | const QList<float> &pointError = pointErrors[i]; | 1171 | const QList<float> &pointError = pointErrors[i]; |
| 1112 | const int keep = qMin(Max_Points, pointError.size()); | 1172 | const int keep = qMin(Max_Points, pointError.size()); |
| 1113 | for (int j=0; j<keep; j++) | 1173 | for (int j=0; j<keep; j++) |
| 1114 | lines.append(QString("Box,%1,%2").arg(QString::number(i), QString::number(pointError[j*(pointError.size()-1)/(keep-1)]))); | 1174 | lines.append(QString("Box,%1,%2").arg(QString::number(i), QString::number(pointError[j*(pointError.size()-1)/(keep-1)]))); |
| 1115 | } | 1175 | } |
| 1116 | 1176 | ||
| 1177 | + const float averagePointError = Common::Mean(averagePointErrors); | ||
| 1178 | + | ||
| 1117 | lines.append(QString("AvgError,0,%1").arg(averagePointError)); | 1179 | lines.append(QString("AvgError,0,%1").arg(averagePointError)); |
| 1180 | + lines.append(QString("NormLength,0,%1").arg(Common::Mean(normalizedLengths))); | ||
| 1118 | 1181 | ||
| 1119 | QtUtils::writeFile(csv, lines); | 1182 | QtUtils::writeFile(csv, lines); |
| 1120 | - qDebug("Average Error: %.3f", averagePointError); | 1183 | + |
| 1184 | + qDebug("Average Error for all Points: %.3f", averagePointError); | ||
| 1185 | + | ||
| 1121 | return averagePointError; | 1186 | return averagePointError; |
| 1122 | } | 1187 | } |
| 1123 | 1188 |
openbr/core/eval.h
| @@ -31,7 +31,7 @@ namespace br | @@ -31,7 +31,7 @@ namespace br | ||
| 31 | 31 | ||
| 32 | void EvalClassification(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = ""); | 32 | void EvalClassification(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = ""); |
| 33 | float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", bool normalize = false, int minSize = 0, int maxSize = 0); // Return average overlap | 33 | float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", bool normalize = false, int minSize = 0, int maxSize = 0); // Return average overlap |
| 34 | - float EvalLandmarking(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", int normalizationIndexA = 0, int normalizationIndexB = 1); // Return average error | 34 | + float EvalLandmarking(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", int normalizationIndexA = 0, int normalizationIndexB = 1, int sampleIndex = 0, int totalExamples = 5); // Return average error |
| 35 | void EvalRegression(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = ""); | 35 | void EvalRegression(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = ""); |
| 36 | } | 36 | } |
| 37 | 37 |
openbr/core/plot.cpp
| @@ -482,16 +482,115 @@ bool PlotLandmarking(const QStringList &files, const File &destination, bool sho | @@ -482,16 +482,115 @@ bool PlotLandmarking(const QStringList &files, const File &destination, bool sho | ||
| 482 | qDebug("Plotting %d landmarking file(s) to %s", files.size(), qPrintable(destination)); | 482 | qDebug("Plotting %d landmarking file(s) to %s", files.size(), qPrintable(destination)); |
| 483 | RPlot p(files, destination, false); | 483 | RPlot p(files, destination, false); |
| 484 | 484 | ||
| 485 | - p.file.write("# Split data into individual plots\n" | ||
| 486 | - "plot_index = which(names(data)==\"Plot\")\n" | ||
| 487 | - "Box <- data[grep(\"Box\",data$Plot),-c(1)]\n" | ||
| 488 | - "rm(data)\n" | ||
| 489 | - "\n"); | ||
| 490 | - | ||
| 491 | - p.file.write(qPrintable(QString("ggplot(Box, aes(Y,%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) + | 485 | + p.file.write(qPrintable(QString("# Split data into individual plots\n" |
| 486 | + "plot_index = which(names(data)==\"Plot\")\n" | ||
| 487 | + "Box <- data[grep(\"Box\",data$Plot),-c(1)]\n" | ||
| 488 | + "Box$X <- factor(Box$X, levels = Box$X, ordered = TRUE)\n" | ||
| 489 | + "Sample <- data[grep(\"Sample\",data$Plot),-c(1)]\n" | ||
| 490 | + "Sample$X <- as.character(Sample$X)\n" | ||
| 491 | + "EXT <- data[grep(\"EXT\",data$Plot),-c(1)]\n" | ||
| 492 | + "EXT$X <- as.character(EXT$X)\n" | ||
| 493 | + "EXP <- data[grep(\"EXP\",data$Plot),-c(1)]\n" | ||
| 494 | + "EXP$X <- as.character(EXP$X)\n" | ||
| 495 | + "NormLength <- data[grep(\"NormLength\",data$Plot),-c(1)]\n" | ||
| 496 | + "rm(data)\n" | ||
| 497 | + "\n"))); | ||
| 498 | + | ||
| 499 | + p.file.write(qPrintable(QString("summarySE <- function(data=NULL, measurevar, groupvars=NULL, na.rm=FALSE, conf.interval=.95, .drop=TRUE) {\n\t" | ||
| 500 | + "require(plyr)\n\n\tlength2 <- function (x, na.rm=FALSE) {\n\t\tif (na.rm) sum(!is.na(x))\n\t\telse length(x)" | ||
| 501 | + "\n\t}\n\n\tdatac <- ddply(data, groupvars, .drop=.drop, .fun = function(xx, col) {\n\t\t" | ||
| 502 | + "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}," | ||
| 503 | + "\n\t\tmeasurevar\n\t)\n\n\tdatac <- rename(datac, c(\"mean\" = measurevar))\n\tdatac$se <- datac$sd / sqrt(datac$N)" | ||
| 504 | + "\n\tciMult <- qt(conf.interval/2 + .5, datac$N-1)\n\tdatac$ci <- datac$se * ciMult\n\n\treturn(datac)\n}\n"))); | ||
| 505 | + | ||
| 506 | + | ||
| 507 | + p.file.write(qPrintable(QString("\nreadData <- function(data) {\n\texamples <- list()\n" | ||
| 508 | + "\tfor (i in 1:nrow(data)) {\n" | ||
| 509 | + "\t\tpath <- data[i,1]\n" | ||
| 510 | + "\t\tvalue <- data[i,2]\n" | ||
| 511 | + "\t\tfile <- unlist(strsplit(path, \"[.]\"))[1]\n" | ||
| 512 | + "\t\text <- unlist(strsplit(path, \"[.]\"))[2]\n" | ||
| 513 | + "\t\tif (ext == \"jpg\" || ext == \"JPEG\" || ext == \"jpeg\" || ext == \"JPG\") {\n" | ||
| 514 | + "\t\t\timg <- readJPEG(path)\n" | ||
| 515 | + "\t\t} else if (ext == \"PNG\" || ext == \"png\") {\n" | ||
| 516 | + "\t\t\timg <- readPNG(path)\n" | ||
| 517 | + "\t\t} else if (ext == \"TIFF\" || ext == \"tiff\" || ext == \"TIF\" || ext == \"tif\") { \n" | ||
| 518 | + "\t\t\timg <- readTIFF(path)\n" | ||
| 519 | + "}else {\n" | ||
| 520 | + "\t\t\tnext\n" | ||
| 521 | + "\t\t}\n" | ||
| 522 | + "\t\texample <- list(file = file, value = value, image = img)\n" | ||
| 523 | + "\t\texamples[[i]] <- example\n" | ||
| 524 | + "\t}\n" | ||
| 525 | + "\treturn(examples)\n" | ||
| 526 | + "}\n"))); | ||
| 527 | + | ||
| 528 | + p.file.write(qPrintable(QString("\nlibrary(jpeg)\n" | ||
| 529 | + "library(png)\n" | ||
| 530 | + "library(grid)\n" | ||
| 531 | + "multiplot <- function(..., plotlist=NULL, cols) {\n" | ||
| 532 | + "\trequire(grid)\n" | ||
| 533 | + "\t# Make a list from the ... arguments and plotlist\n" | ||
| 534 | + "\tplots <- c(list(...), plotlist)\n" | ||
| 535 | + "\tnumPlots = length(plots)\n" | ||
| 536 | + "\t# Make the panel\n" | ||
| 537 | + "\tplotCols = cols\n" | ||
| 538 | + "\tplotRows = ceiling(numPlots/plotCols)\n" | ||
| 539 | + "\t# Set up the page\n" | ||
| 540 | + "\tgrid.newpage()\n" | ||
| 541 | + "\tpushViewport(viewport(layout = grid.layout(plotRows, plotCols)))\n" | ||
| 542 | + "\tvplayout <- function(x, y)\n" | ||
| 543 | + "\tviewport(layout.pos.row = x, layout.pos.col = y)\n" | ||
| 544 | + "\t# Make each plot, in the correct location\n" | ||
| 545 | + "\tfor (i in 1:numPlots) {\n" | ||
| 546 | + "\t\tcurRow = ceiling(i/plotCols)\n" | ||
| 547 | + "\t\tcurCol = (i-1) %% plotCols + 1\n" | ||
| 548 | + "\t\tprint(plots[[i]], vp = vplayout(curRow, curCol))\n" | ||
| 549 | + "\t}\n" | ||
| 550 | + "}\n"))); | ||
| 551 | + | ||
| 552 | + p.file.write(qPrintable(QString("\nplotImage <- function(image, title=NULL, label=NULL) { \n" | ||
| 553 | + "\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" | ||
| 554 | + "\treturn(p)" | ||
| 555 | + "}\n"))); | ||
| 556 | + | ||
| 557 | + p.file.write(qPrintable(QString("\nsample <- readData(Sample) \n" | ||
| 558 | + "rows <- sample[[1]]$value\n" | ||
| 559 | + "algs <- unique(Box$%1)\n" | ||
| 560 | + "algs <- algs[!duplicated(algs)]\n" | ||
| 561 | + "print(plotImage(sample[[1]],\"Sample Landmarks\",sprintf(\"Total Landmarks: %s\",sample[[1]]$value))) \n" | ||
| 562 | + "if (nrow(EXT) != 0 && nrow(EXP)) {\n" | ||
| 563 | + "\tfor (j in 1:length(algs)) {\n" | ||
| 564 | + "\ttruthSample <- readData(EXT[EXT$. == algs[[j]],])\n" | ||
| 565 | + "\tpredictedSample <- readData(EXP[EXP$. == algs[[j]],])\n" | ||
| 566 | + "\t\tfor (i in 1:length(predictedSample)) {\n" | ||
| 567 | + "\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" | ||
| 568 | + "\t\t}\n" | ||
| 569 | + "\t}\n" | ||
| 570 | + "}\n").arg(p.major.size > 1 ? p.major.header : (p.minor.header.isEmpty() ? p.major.header : p.minor.header)))); | ||
| 571 | + | ||
| 572 | + p.file.write(qPrintable(QString("\n" | ||
| 573 | + "# Code to format error table\n" | ||
| 574 | + "StatBox <- summarySE(Box, measurevar=\"Y\", groupvars=c(\"%1\",\"X\"))\n" | ||
| 575 | + "OverallStatBox <- summarySE(Box, measurevar=\"Y\", groupvars=c(\"%1\"))\n" | ||
| 576 | + "mat <- matrix(paste(as.character(round(StatBox$Y, 3)), round(StatBox$ci, 3), sep=\" \\u00b1 \"),nrow=rows,ncol=length(algs),byrow=FALSE)\n" | ||
| 577 | + "mat <- rbind(mat, paste(as.character(round(OverallStatBox$Y, 3)), round(OverallStatBox$ci, 3), sep=\" \\u00b1 \"))\n" | ||
| 578 | + "mat <- rbind(mat, as.character(round(NormLength$Y, 3)))\n" | ||
| 579 | + "colnames(mat) <- algs\n" | ||
| 580 | + "rownames(mat) <- c(seq(0,rows-1),\"Aggregate\",\"Average IPD\")\n" | ||
| 581 | + "ETable <- as.table(mat)\n").arg(p.major.size > 1 ? p.major.header : (p.minor.header.isEmpty() ? p.major.header : p.minor.header)))); | ||
| 582 | + | ||
| 583 | + p.file.write(qPrintable(QString("\n" | ||
| 584 | + "print(textplot(ETable))\n" | ||
| 585 | + "print(title(\"Landmarking Error Rates\"))\n"))); | ||
| 586 | + | ||
| 587 | + p.file.write(qPrintable(QString("ggplot(Box, aes(Y,%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), | ||
| 588 | + p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) + | ||
| 492 | QString(" + annotation_logticks(sides=\"b\") + stat_ecdf() + scale_x_log10(\"Normalized Error\", breaks=c(0.001,0.01,0.1,1,10)) + scale_y_continuous(\"Cumulative Density\", label=percent) + theme_minimal()\n\n"))); | 589 | QString(" + annotation_logticks(sides=\"b\") + stat_ecdf() + scale_x_log10(\"Normalized Error\", breaks=c(0.001,0.01,0.1,1,10)) + scale_y_continuous(\"Cumulative Density\", label=percent) + theme_minimal()\n\n"))); |
| 590 | + | ||
| 493 | p.file.write(qPrintable(QString("ggplot(Box, aes(factor(X), Y%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) + | 591 | p.file.write(qPrintable(QString("ggplot(Box, aes(factor(X), Y%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) + |
| 494 | - QString("+ annotation_logticks(sides=\"l\") + geom_boxplot(alpha=0.5) + geom_jitter(size=1, alpha=0.5) + scale_x_discrete(\"Landmark\") + scale_y_log10(\"Normalized Error\", breaks=c(0.01,0.1,1,10)) + theme_minimal()\n\n"))); | 592 | + QString("+ annotation_logticks(sides=\"l\") + geom_boxplot(alpha=0.5) + geom_jitter(size=1, alpha=0.5) + scale_x_discrete(\"Landmark\") + scale_y_log10(\"Normalized Error\", breaks=c(0.001,0.01,0.1,1,10)) + theme_minimal()\n\n"))); |
| 593 | + | ||
| 495 | p.file.write(qPrintable(QString("ggplot(Box, aes(factor(X), Y%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) + | 594 | p.file.write(qPrintable(QString("ggplot(Box, aes(factor(X), Y%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(), p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) + |
| 496 | QString("+ annotation_logticks(sides=\"l\") + geom_violin(alpha=0.5) + scale_x_discrete(\"Landmark\") + scale_y_log10(\"Normalized Error\", breaks=c(0.001,0.01,0.1,1,10))\n\n"))); | 595 | QString("+ annotation_logticks(sides=\"l\") + geom_violin(alpha=0.5) + scale_x_discrete(\"Landmark\") + scale_y_log10(\"Normalized Error\", breaks=c(0.001,0.01,0.1,1,10))\n\n"))); |
| 497 | 596 |
openbr/openbr.cpp
| @@ -134,9 +134,9 @@ float br_eval_detection(const char *predicted_gallery, const char *truth_gallery | @@ -134,9 +134,9 @@ float br_eval_detection(const char *predicted_gallery, const char *truth_gallery | ||
| 134 | return EvalDetection(predicted_gallery, truth_gallery, csv, normalize, minSize, maxSize); | 134 | return EvalDetection(predicted_gallery, truth_gallery, csv, normalize, minSize, maxSize); |
| 135 | } | 135 | } |
| 136 | 136 | ||
| 137 | -float br_eval_landmarking(const char *predicted_gallery, const char *truth_gallery, const char *csv, int normalization_index_a, int normalization_index_b) | 137 | +float br_eval_landmarking(const char *predicted_gallery, const char *truth_gallery, const char *csv, int normalization_index_a, int normalization_index_b, int sample_index, int total_examples) |
| 138 | { | 138 | { |
| 139 | - return EvalLandmarking(predicted_gallery, truth_gallery, csv, normalization_index_a, normalization_index_b); | 139 | + return EvalLandmarking(predicted_gallery, truth_gallery, csv, normalization_index_a, normalization_index_b, sample_index, total_examples); |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | void br_eval_regression(const char *predicted_gallery, const char *truth_gallery, const char *predicted_property, const char *truth_property) | 142 | void br_eval_regression(const char *predicted_gallery, const char *truth_gallery, const char *predicted_property, const char *truth_property) |
openbr/openbr.h
| @@ -214,8 +214,10 @@ BR_EXPORT float br_eval_detection(const char *predicted_gallery, const char *tru | @@ -214,8 +214,10 @@ BR_EXPORT float br_eval_detection(const char *predicted_gallery, const char *tru | ||
| 214 | * \param csv Optional \c .csv file to contain performance metrics. | 214 | * \param csv Optional \c .csv file to contain performance metrics. |
| 215 | * \param normalization_index_a Optional first index in the list of points to use for normalization. | 215 | * \param normalization_index_a Optional first index in the list of points to use for normalization. |
| 216 | * \param normalization_index_b Optional second index in the list of points to use for normalization. | 216 | * \param normalization_index_b Optional second index in the list of points to use for normalization. |
| 217 | + * \param sample_index Optional index for sample landmark image in ground truth gallery. | ||
| 218 | + * \param total_examples Optional number of accurate and inaccurate examples to display. | ||
| 217 | */ | 219 | */ |
| 218 | -BR_EXPORT float br_eval_landmarking(const char *predicted_gallery, const char *truth_gallery, const char *csv = "", int normalization_index_a = 0, int normalization_index_b = 1); | 220 | +BR_EXPORT float br_eval_landmarking(const char *predicted_gallery, const char *truth_gallery, const char *csv = "", int normalization_index_a = 0, int normalization_index_b = 1, int sample_index = 0, int total_examples = 5); |
| 219 | 221 | ||
| 220 | /*! | 222 | /*! |
| 221 | * \brief Evaluates regression accuracy to disk. | 223 | * \brief Evaluates regression accuracy to disk. |
openbr/plugins/draw.cpp
| @@ -42,11 +42,15 @@ class DrawTransform : public UntrainableTransform | @@ -42,11 +42,15 @@ class DrawTransform : public UntrainableTransform | ||
| 42 | Q_PROPERTY(bool rects READ get_rects WRITE set_rects RESET reset_rects STORED false) | 42 | Q_PROPERTY(bool rects READ get_rects WRITE set_rects RESET reset_rects STORED false) |
| 43 | Q_PROPERTY(bool inPlace READ get_inPlace WRITE set_inPlace RESET reset_inPlace STORED false) | 43 | Q_PROPERTY(bool inPlace READ get_inPlace WRITE set_inPlace RESET reset_inPlace STORED false) |
| 44 | Q_PROPERTY(int lineThickness READ get_lineThickness WRITE set_lineThickness RESET reset_lineThickness STORED false) | 44 | Q_PROPERTY(int lineThickness READ get_lineThickness WRITE set_lineThickness RESET reset_lineThickness STORED false) |
| 45 | + Q_PROPERTY(bool named READ get_named WRITE set_named RESET reset_named STORED false) | ||
| 46 | + Q_PROPERTY(bool location READ get_location WRITE set_location RESET reset_location STORED false) | ||
| 45 | BR_PROPERTY(bool, verbose, false) | 47 | BR_PROPERTY(bool, verbose, false) |
| 46 | BR_PROPERTY(bool, points, true) | 48 | BR_PROPERTY(bool, points, true) |
| 47 | BR_PROPERTY(bool, rects, true) | 49 | BR_PROPERTY(bool, rects, true) |
| 48 | BR_PROPERTY(bool, inPlace, false) | 50 | BR_PROPERTY(bool, inPlace, false) |
| 49 | BR_PROPERTY(int, lineThickness, 1) | 51 | BR_PROPERTY(int, lineThickness, 1) |
| 52 | + BR_PROPERTY(bool, named, true) | ||
| 53 | + BR_PROPERTY(bool, location, true) | ||
| 50 | 54 | ||
| 51 | void project(const Template &src, Template &dst) const | 55 | void project(const Template &src, Template &dst) const |
| 52 | { | 56 | { |
| @@ -55,11 +59,12 @@ class DrawTransform : public UntrainableTransform | @@ -55,11 +59,12 @@ class DrawTransform : public UntrainableTransform | ||
| 55 | dst.m() = inPlace ? src.m() : src.m().clone(); | 59 | dst.m() = inPlace ? src.m() : src.m().clone(); |
| 56 | 60 | ||
| 57 | if (points) { | 61 | if (points) { |
| 58 | - const QList<Point2f> pointsList = OpenCVUtils::toPoints(src.file.namedPoints() + src.file.points()); | 62 | + const QList<Point2f> pointsList = (named) ? OpenCVUtils::toPoints(src.file.points()+src.file.namedPoints()) : OpenCVUtils::toPoints(src.file.points()); |
| 59 | for (int i=0; i<pointsList.size(); i++) { | 63 | for (int i=0; i<pointsList.size(); i++) { |
| 60 | const Point2f &point = pointsList[i]; | 64 | const Point2f &point = pointsList[i]; |
| 61 | circle(dst, point, 3, color, -1); | 65 | circle(dst, point, 3, color, -1); |
| 62 | - if (verbose) putText(dst, QString("%1,(%2,%3)").arg(QString::number(i),QString::number(point.x),QString::number(point.y)).toStdString(), point, FONT_HERSHEY_SIMPLEX, 0.5, verboseColor, 1); | 66 | + QString label = (location) ? QString("%1,(%2,%3)").arg(QString::number(i),QString::number(point.x),QString::number(point.y)) : QString("%1").arg(QString::number(i)); |
| 67 | + if (verbose) putText(dst, label.toStdString(), point, FONT_HERSHEY_SIMPLEX, 0.5, verboseColor, 1); | ||
| 63 | } | 68 | } |
| 64 | } | 69 | } |
| 65 | if (rects) { | 70 | if (rects) { |