Commit d6ae28496be88d981d27f70a2a326344a61c05e9

Authored by Scott Klum
1 parent 31008334

Added output of sample landmarks, added index to control which image is used as sample

app/br/br.cpp
... ... @@ -163,8 +163,8 @@ public:
163 163 check((parc >= 2) && (parc <= 6), "Incorrect parameter count for 'evalDetection'.");
164 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 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 <= 6), "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);
168 168 } else if (!strcmp(fun, "evalRegression")) {
169 169 check(parc >= 2 && parc <= 4, "Incorrect parameter count for 'evalRegression'.");
170 170 br_eval_regression(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? parv[3] : "");
... ...
openbr/core/eval.cpp
... ... @@ -1032,7 +1032,7 @@ float EvalDetection(const QString &amp;predictedGallery, const QString &amp;truthGallery
1032 1032 return averageOverlap;
1033 1033 }
1034 1034  
1035   -float EvalLandmarking(const QString &predictedGallery, const QString &truthGallery, const QString &csv, int normalizationIndexA, int normalizationIndexB)
  1035 +float EvalLandmarking(const QString &predictedGallery, const QString &truthGallery, const QString &csv, int normalizationIndexA, int normalizationIndexB, int sampleIndex)
1036 1036 {
1037 1037 qDebug("Evaluating landmarking of %s against %s", qPrintable(predictedGallery), qPrintable(truthGallery));
1038 1038 const TemplateList predicted(TemplateList::fromGallery(predictedGallery));
... ... @@ -1042,6 +1042,8 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle
1042 1042  
1043 1043 int skipped = 0;
1044 1044 QList< QList<float> > pointErrors;
  1045 + QList<float> normalizedLengths;
  1046 +
1045 1047 for (int i=0; i<predicted.size(); i++) {
1046 1048 const QString &predictedName = predictedNames[i];
1047 1049 const int truthIndex = truthNames.indexOf(predictedName);
... ... @@ -1057,20 +1059,49 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle
1057 1059 if (normalizationIndexA >= truthPoints.size()) qFatal("Normalization index A is out of range.");
1058 1060 if (normalizationIndexB >= truthPoints.size()) qFatal("Normalization index B is out of range.");
1059 1061 const float normalizedLength = QtUtils::euclideanLength(truthPoints[normalizationIndexB] - truthPoints[normalizationIndexA]);
1060   - for (int j=0; j<predictedPoints.size(); j++)
  1062 + normalizedLengths.append(normalizedLength);
  1063 + for (int j=0; j<predictedPoints.size(); j++) {
1061 1064 pointErrors[j].append(QtUtils::euclideanLength(predictedPoints[j] - truthPoints[j])/normalizedLength);
  1065 + }
1062 1066 }
1063   - qDebug() << "Skipped " << skipped << " files due to point size mismatch.";
  1067 +
  1068 + qDebug() << "Skipped" << skipped << "files due to point size mismatch.";
1064 1069  
1065 1070 QList<float> averagePointErrors; averagePointErrors.reserve(pointErrors.size());
  1071 + QList<float> medianPointErrors; medianPointErrors.reserve(pointErrors.size());
  1072 + QList<float> stddevPointErrors; stddevPointErrors.reserve(pointErrors.size());
  1073 +
  1074 + float normalizedErrorLimit = 1.5;
  1075 +
  1076 + QSet<int> worstExamples;
1066 1077 for (int i=0; i<pointErrors.size(); i++) {
  1078 + if (skipped == 0) {
  1079 + QList<QPair<float,int> > exampleIndices = Common::Sort(pointErrors[i],true);
  1080 + for (int j=0; j<exampleIndices.size() && worstExamples.size()<(5*i+1); j++)
  1081 + if (exampleIndices[j].first < normalizedErrorLimit)
  1082 + worstExamples.insert(exampleIndices[j].second);
  1083 + }
1067 1084 std::sort(pointErrors[i].begin(), pointErrors[i].end());
1068   - averagePointErrors.append(Common::Mean(pointErrors[i]));
  1085 + double mean, stddev;
  1086 + Common::MeanStdDev(pointErrors[i],&mean,&stddev);
  1087 + averagePointErrors.append(mean);
  1088 + stddevPointErrors.append(stddev);
  1089 + medianPointErrors.append(Common::Median(pointErrors[i]));
1069 1090 }
1070 1091 const float averagePointError = Common::Mean(averagePointErrors);
  1092 + const float medianPointError = Common::Mean(medianPointErrors);
  1093 + const float stddevPointError = Common::Mean(stddevPointErrors);
1071 1094  
1072 1095 QStringList lines;
1073 1096 lines.append("Plot,X,Y");
  1097 +
  1098 + QFile exampleFile("landmarking_examples");
  1099 + QtUtils::touchDir(exampleFile);
  1100 + lines.append("EX,landmarking_examples/"+truth[sampleIndex].file.fileName()+","+QString::number(truth[sampleIndex].file.points().size()));
  1101 +
  1102 + // Alternatively, can we just pass this through a predetermined transform and write?
  1103 + Enroll(truth[sampleIndex],"landmarking_examples");
  1104 +
1074 1105 for (int i=0; i<pointErrors.size(); i++) {
1075 1106 const QList<float> &pointError = pointErrors[i];
1076 1107 const int keep = qMin(Max_Points, pointError.size());
... ... @@ -1081,7 +1112,18 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle
1081 1112 lines.append(QString("AvgError,0,%1").arg(averagePointError));
1082 1113  
1083 1114 QtUtils::writeFile(csv, lines);
1084   - qDebug("Average Error: %.3f", averagePointError);
  1115 +
  1116 + for (int i=0; i<averagePointErrors.size(); i++) {
  1117 + qDebug("Average Error for point %d: %.3f", i, averagePointErrors[i]);
  1118 + qDebug("Median Error for point %d: %.3f", i, medianPointErrors[i]);
  1119 + qDebug("Standard Deviation of Error for point %d: %.3f", i, stddevPointErrors[i]);
  1120 + }
  1121 +
  1122 + qDebug("Average Error for all Points: %.3f", averagePointError);
  1123 + qDebug("Average Median Error for all Points: %.3f", medianPointError);
  1124 + qDebug("Average Standard Deviation of Error for all Points: %.3f", stddevPointError);
  1125 + qDebug("Average Normalization Length (pixels): %.3f", Common::Mean(normalizedLengths));
  1126 +
1085 1127 return averagePointError;
1086 1128 }
1087 1129  
... ...
openbr/core/eval.h
... ... @@ -31,7 +31,7 @@ namespace br
31 31  
32 32 void EvalClassification(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = "");
33 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); // Return average error
35 35 void EvalRegression(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = "");
36 36 }
37 37  
... ...
openbr/core/plot.cpp
... ... @@ -472,13 +472,72 @@ bool PlotLandmarking(const QStringList &amp;files, const File &amp;destination, bool sho
472 472 p.file.write("# Split data into individual plots\n"
473 473 "plot_index = which(names(data)==\"Plot\")\n"
474 474 "Box <- data[grep(\"Box\",data$Plot),-c(1)]\n"
  475 + "EX <- data[grep(\"EX\",data$Plot),-c(1)]\n"
  476 + "EX$X <- as.character(EX$X)\n"
  477 + "EX$Y <- as.character(EX$Y)\n"
475 478 "rm(data)\n"
476 479 "\n");
477 480  
478   - 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()) +
  481 + // Load in the relevant libraries
  482 + p.file.write(qPrintable(QString("if (nrow(EX) != 0) { \
  483 + \n\tlibrary(jpeg) \
  484 + \n\tlibrary(png) \
  485 + \n\tlibrary(grid)\n\t") +
  486 +
  487 + QString("multiplot <- function(..., plotlist=NULL, cols) {") +
  488 + QString("\n\t\t require(grid) \
  489 + \n\n\t\t# Make a list from the ... arguments and plotlist \
  490 + \n\t\t plots <- c(list(...), plotlist)\n") +
  491 +
  492 + QString("\t\tnumPlots = length(plots)\n\n\t\t \
  493 + # Make the panel \
  494 + \n\t\tplotCols = cols \
  495 + \n\t\tplotRows = ceiling(numPlots/plotCols) \
  496 + \n\n") +
  497 +
  498 + QString("\t\t# Set up the page \
  499 + \n\t\tgrid.newpage() \
  500 + \n\t\tpushViewport(viewport(layout = grid.layout(plotRows, plotCols))) \
  501 + \n\t\tvplayout <- function(x, y) \
  502 + \n\t\t\tviewport(layout.pos.row = x, layout.pos.col = y)\n\n") +
  503 +
  504 + QString("\t\t# Make each plot, in the correct location \
  505 + \n\t\tfor (i in 1:numPlots) { \
  506 + \n\t\t\tcurRow = ceiling(i/plotCols)\n\t\t\tcurCol = (i-1) %% plotCols + 1 \
  507 + \n\t\t\tprint(plots[[i]], vp = vplayout(curRow, curCol))\n\t\t}\n\t}\n\n")));
  508 +
  509 + p.file.write(qPrintable(QString("\n\n\t# Print genuine matches below the EER \
  510 + \n\t \
  511 + for (i in 1:nrow(EX)) { \
  512 + \n\t\t path <- EX[i,1] \
  513 + \n\t\t points <- EX[i,2] \
  514 + \n\t\t file <- unlist(strsplit(path, \"[.]\"))[1] \
  515 + \n\t\t ext <- unlist(strsplit(path, \"[.]\"))[2]") +
  516 +
  517 + // These should be made into a function assuming we can return an image variable regardless of extension
  518 + QString("\n\t\t\tif (ext == \"jpg\" || ext == \"JPEG\" || ext == \"jpeg\" || ext == \"JPG\") { \
  519 + \n\t\t\t img <- readJPEG(path)\n\t\t } \
  520 + else if (ext == \"PNG\" || ext == \"png\") { \
  521 + \n\t\t\t img <- readPNG(path)\n\t\t} \
  522 + else if (ext == \"TIFF\" || ext == \"tiff\" || ext == \"TIF\" || ext == \"tif\") { \
  523 + \n\t\t\t img <- readTIFF(path)\n\t\t} \
  524 + else {\n\t\t\tnext\n\t\t} ") +
  525 +
  526 + QString("\n\t\t name <- file \
  527 + \n\n\t\t g <- rasterGrob(img, interpolate=TRUE)\n\n\t\t") +
  528 +
  529 + QString("print(qplot(1:10, 1:10, geom=\"blank\") \
  530 + + annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) \
  531 + + 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()) \
  532 + + labs(title=\"Sample Landmarks\") + xlab(sprintf(\"Total Landmarks: %s\",points)))\n\t\t}}\n")));
  533 +
  534 + p.file.write(qPrintable(QString("ggplot(Box, aes(Y,%1%2))").arg(p.major.size > 1 ? QString(", colour=%1").arg(p.major.header) : QString(),
  535 + p.minor.size > 1 ? QString(", linetype=%1").arg(p.minor.header) : QString()) +
479 536 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")));
  537 +
480 538 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()) +
481 539 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")));
  540 +
482 541 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()) +
483 542 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")));
484 543  
... ...
openbr/openbr.cpp
... ... @@ -134,9 +134,9 @@ float br_eval_detection(const char *predicted_gallery, const char *truth_gallery
134 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)
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);
140 140 }
141 141  
142 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,9 @@ BR_EXPORT float br_eval_detection(const char *predicted_gallery, const char *tru
214 214 * \param csv Optional \c .csv file to contain performance metrics.
215 215 * \param normalization_index_a Optional first index in the list of points to use for normalization.
216 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.
217 218 */
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);
  219 +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);
219 220  
220 221 /*!
221 222 * \brief Evaluates regression accuracy to disk.
... ...