Commit 06a58254a6f517b7c2e444cffc03e061e2bdc742
1 parent
5b53a8c4
Added good and bad examples, lots of cleanup
Showing
2 changed files
with
123 additions
and
97 deletions
openbr/core/eval.cpp
| ... | ... | @@ -1035,15 +1035,15 @@ float EvalDetection(const QString &predictedGallery, const QString &truthGallery |
| 1035 | 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 | - const TemplateList predicted(TemplateList::fromGallery(predictedGallery)); | |
| 1039 | - const TemplateList truth(TemplateList::fromGallery(truthGallery)); | |
| 1040 | - const QStringList predictedNames = File::get<QString>(predicted, "name"); | |
| 1041 | - const QStringList truthNames = File::get<QString>(truth, "name"); | |
| 1038 | + TemplateList predicted(TemplateList::fromGallery(predictedGallery)); | |
| 1039 | + TemplateList truth(TemplateList::fromGallery(truthGallery)); | |
| 1040 | + QStringList predictedNames = File::get<QString>(predicted, "name"); | |
| 1041 | + QStringList truthNames = File::get<QString>(truth, "name"); | |
| 1042 | 1042 | |
| 1043 | 1043 | int skipped = 0; |
| 1044 | 1044 | QList< QList<float> > pointErrors; |
| 1045 | + QList<float> imageErrors; | |
| 1045 | 1046 | QList<float> normalizedLengths; |
| 1046 | - | |
| 1047 | 1047 | for (int i=0; i<predicted.size(); i++) { |
| 1048 | 1048 | const QString &predictedName = predictedNames[i]; |
| 1049 | 1049 | const int truthIndex = truthNames.indexOf(predictedName); |
| ... | ... | @@ -1051,58 +1051,76 @@ float EvalLandmarking(const QString &predictedGallery, const QString &truthGalle |
| 1051 | 1051 | const QList<QPointF> predictedPoints = predicted[i].file.points(); |
| 1052 | 1052 | const QList<QPointF> truthPoints = truth[truthIndex].file.points(); |
| 1053 | 1053 | if (predictedPoints.size() != truthPoints.size()) { |
| 1054 | - skipped++; | |
| 1054 | + predicted.removeAt(i); | |
| 1055 | + predictedNames.removeAt(i); | |
| 1056 | + truth.removeAt(i); | |
| 1057 | + truthNames.removeAt(i); | |
| 1058 | + i--; skipped++; | |
| 1055 | 1059 | continue; |
| 1056 | 1060 | } |
| 1061 | + | |
| 1057 | 1062 | while (pointErrors.size() < predictedPoints.size()) |
| 1058 | 1063 | pointErrors.append(QList<float>()); |
| 1059 | 1064 | |
| 1065 | + // Want to know error for every image. | |
| 1066 | + | |
| 1060 | 1067 | if (normalizationIndexA >= truthPoints.size()) qFatal("Normalization index A is out of range."); |
| 1061 | 1068 | if (normalizationIndexB >= truthPoints.size()) qFatal("Normalization index B is out of range."); |
| 1062 | 1069 | const float normalizedLength = QtUtils::euclideanLength(truthPoints[normalizationIndexB] - truthPoints[normalizationIndexA]); |
| 1063 | 1070 | normalizedLengths.append(normalizedLength); |
| 1071 | + float totalError = 0; | |
| 1064 | 1072 | for (int j=0; j<predictedPoints.size(); j++) { |
| 1065 | - pointErrors[j].append(QtUtils::euclideanLength(predictedPoints[j] - truthPoints[j])/normalizedLength); | |
| 1073 | + float error = QtUtils::euclideanLength(predictedPoints[j] - truthPoints[j])/normalizedLength; | |
| 1074 | + totalError += error; | |
| 1075 | + pointErrors[j].append(error); | |
| 1066 | 1076 | } |
| 1077 | + imageErrors.append(totalError/predictedPoints.size()); | |
| 1067 | 1078 | } |
| 1068 | 1079 | |
| 1069 | 1080 | qDebug() << "Skipped" << skipped << "files due to point size mismatch."; |
| 1070 | 1081 | |
| 1071 | 1082 | QList<float> averagePointErrors; averagePointErrors.reserve(pointErrors.size()); |
| 1072 | 1083 | |
| 1073 | - float normalizedErrorLimit = 1.5; | |
| 1074 | - | |
| 1075 | - QSet<int> worstExamples; | |
| 1076 | - for (int i=0; i<pointErrors.size(); i++) { | |
| 1077 | - if (skipped == 0) { | |
| 1078 | - QList<QPair<float,int> > exampleIndices = Common::Sort(pointErrors[i],true); | |
| 1079 | - for (int j=0; j<exampleIndices.size() && worstExamples.size()<(5*i+1); j++) | |
| 1080 | - if (exampleIndices[j].first < normalizedErrorLimit) | |
| 1081 | - worstExamples.insert(exampleIndices[j].second); | |
| 1082 | - } | |
| 1083 | - std::sort(pointErrors[i].begin(), pointErrors[i].end()); | |
| 1084 | - averagePointErrors.append(Common::Mean(pointErrors[i])); | |
| 1085 | - } | |
| 1086 | - const float averagePointError = Common::Mean(averagePointErrors); | |
| 1087 | - | |
| 1088 | 1084 | QStringList lines; |
| 1089 | 1085 | lines.append("Plot,X,Y"); |
| 1090 | 1086 | |
| 1091 | - // Sample | |
| 1092 | - QFile exampleFile("landmarking_examples"); | |
| 1093 | - QtUtils::touchDir(exampleFile); | |
| 1094 | - lines.append("EX,landmarking_examples/"+truth[sampleIndex].file.fileName()+","+QString::number(truth[sampleIndex].file.points().size())); | |
| 1087 | + // Example | |
| 1088 | + lines.append("Sample,landmarking_examples_truth/"+truth[sampleIndex].file.fileName()+","+QString::number(truth[sampleIndex].file.points().size())); | |
| 1095 | 1089 | |
| 1096 | 1090 | // Alternatively, can we just pass this through a predetermined transform and write? |
| 1097 | - Enroll(truth[sampleIndex],"landmarking_examples"); | |
| 1091 | + Enroll(truth[sampleIndex],"landmarking_examples_truth"); | |
| 1092 | + | |
| 1093 | + // Get best and worst performing examples | |
| 1094 | + QList< QPair<float,int> > exampleIndices = Common::Sort(imageErrors,true); | |
| 1095 | + | |
| 1096 | + const int totalExamples = 10; | |
| 1097 | + for (int i=0; i<totalExamples; i++) { | |
| 1098 | + Enroll(truth[exampleIndices[i].second],"landmarking_examples_truth"); | |
| 1099 | + lines.append("EXT,landmarking_examples_truth/"+truth[exampleIndices[i].second].file.fileName()+","+QString::number(exampleIndices[i].first)); | |
| 1100 | + Enroll(predicted[exampleIndices[i].second],"landmarking_examples_prediced"); | |
| 1101 | + lines.append("EXP,landmarking_examples_prediced/"+predicted[exampleIndices[i].second].file.fileName()+","+QString::number(exampleIndices[i].first)); | |
| 1102 | + | |
| 1103 | + } | |
| 1104 | + | |
| 1105 | + for (int i=exampleIndices.size()-1; i>exampleIndices.size()-totalExamples-1; i--) { | |
| 1106 | + Enroll(truth[exampleIndices[i].second],"landmarking_examples_truth"); | |
| 1107 | + lines.append("EXT,landmarking_examples_truth/"+truth[exampleIndices[i].second].file.fileName()+","+QString::number(exampleIndices[i].first)); | |
| 1108 | + Enroll(predicted[exampleIndices[i].second],"landmarking_examples_prediced"); | |
| 1109 | + lines.append("EXP,landmarking_examples_prediced/"+predicted[exampleIndices[i].second].file.fileName()+","+QString::number(exampleIndices[i].first)); | |
| 1110 | + | |
| 1111 | + } | |
| 1098 | 1112 | |
| 1099 | 1113 | for (int i=0; i<pointErrors.size(); i++) { |
| 1114 | + std::sort(pointErrors[i].begin(), pointErrors[i].end()); | |
| 1115 | + averagePointErrors.append(Common::Mean(pointErrors[i])); | |
| 1100 | 1116 | const QList<float> &pointError = pointErrors[i]; |
| 1101 | 1117 | const int keep = qMin(Max_Points, pointError.size()); |
| 1102 | 1118 | for (int j=0; j<keep; j++) |
| 1103 | 1119 | lines.append(QString("Box,%1,%2").arg(QString::number(i), QString::number(pointError[j*(pointError.size()-1)/(keep-1)]))); |
| 1104 | 1120 | } |
| 1105 | 1121 | |
| 1122 | + const float averagePointError = Common::Mean(averagePointErrors); | |
| 1123 | + | |
| 1106 | 1124 | lines.append(QString("AvgError,0,%1").arg(averagePointError)); |
| 1107 | 1125 | lines.append(QString("NormLength,0,%1").arg(Common::Mean(normalizedLengths))); |
| 1108 | 1126 | ... | ... |
openbr/core/plot.cpp
| ... | ... | @@ -469,77 +469,85 @@ bool PlotLandmarking(const QStringList &files, const File &destination, bool sho |
| 469 | 469 | qDebug("Plotting %d landmarking file(s) to %s", files.size(), qPrintable(destination)); |
| 470 | 470 | RPlot p(files, destination, false); |
| 471 | 471 | |
| 472 | - qDebug() << p.major.header << p.minor.header; | |
| 473 | - | |
| 474 | 472 | p.file.write(qPrintable(QString("# Split data into individual plots\n" |
| 475 | - "plot_index = which(names(data)==\"Plot\")\n" | |
| 476 | - "Box <- data[grep(\"Box\",data$Plot),-c(1)]\n" | |
| 477 | - "Box$X <- factor(Box$X, levels = Box$X, ordered = TRUE)\n" | |
| 478 | - "EX <- data[grep(\"EX\",data$Plot),-c(1)]\n" | |
| 479 | - "NormLength <- data[grep(\"NormLength\",data$Plot),-c(1)]\n" | |
| 480 | - "EX$X <- as.character(EX$X)\n" | |
| 481 | - "EX$Y <- as.character(EX$Y)\n" | |
| 482 | - "\n" | |
| 483 | - "\n\tsummarySE <- function(data=NULL, measurevar, groupvars=NULL, na.rm=FALSE, conf.interval=.95, .drop=TRUE) {\n\t\t" | |
| 484 | - "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)" | |
| 485 | - "\n\t\t}\n\n\t\tdatac <- ddply(data, groupvars, .drop=.drop, .fun = function(xx, col) {\n\t\t\t" | |
| 486 | - "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}," | |
| 487 | - "\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)" | |
| 488 | - "\n\t\tciMult <- qt(conf.interval/2 + .5, datac$N-1)\n\t\tdatac$ci <- datac$se * ciMult\n\n\t\treturn(datac)\n\t}\n\t" | |
| 489 | - "rm(data)\n" | |
| 490 | - "\n"))); | |
| 491 | - | |
| 492 | - p.file.write(qPrintable(QString("if (nrow(EX) != 0) { \ | |
| 493 | - \n\tlibrary(jpeg) \ | |
| 494 | - \n\tlibrary(png) \ | |
| 495 | - \n\tlibrary(grid)\n\t") + | |
| 496 | - | |
| 497 | - QString("multiplot <- function(..., plotlist=NULL, cols) {") + | |
| 498 | - QString("\n\t\t require(grid) \ | |
| 499 | - \n\n\t\t# Make a list from the ... arguments and plotlist \ | |
| 500 | - \n\t\t plots <- c(list(...), plotlist)\n") + | |
| 501 | - | |
| 502 | - QString("\t\tnumPlots = length(plots)\n\n\t\t \ | |
| 503 | - # Make the panel \ | |
| 504 | - \n\t\tplotCols = cols \ | |
| 505 | - \n\t\tplotRows = ceiling(numPlots/plotCols) \ | |
| 506 | - \n\n") + | |
| 507 | - | |
| 508 | - QString("\t\t# Set up the page \ | |
| 509 | - \n\t\tgrid.newpage() \ | |
| 510 | - \n\t\tpushViewport(viewport(layout = grid.layout(plotRows, plotCols))) \ | |
| 511 | - \n\t\tvplayout <- function(x, y) \ | |
| 512 | - \n\t\t\tviewport(layout.pos.row = x, layout.pos.col = y)\n\n") + | |
| 513 | - | |
| 514 | - QString("\t\t# Make each plot, in the correct location \ | |
| 515 | - \n\t\tfor (i in 1:numPlots) { \ | |
| 516 | - \n\t\t\tcurRow = ceiling(i/plotCols)\n\t\t\tcurCol = (i-1) %% plotCols + 1 \ | |
| 517 | - \n\t\t\tprint(plots[[i]], vp = vplayout(curRow, curCol))\n\t\t}\n\t}\n\n"))); | |
| 518 | - | |
| 519 | - p.file.write(qPrintable(QString("\n\n\t# Print genuine matches below the EER \ | |
| 520 | - \n\t \ | |
| 521 | - for (i in 1:nrow(EX)) { \ | |
| 522 | - \n\t\t path <- EX[i,1] \ | |
| 523 | - \n\t\t points <- EX[i,2] \ | |
| 524 | - \n\t\t file <- unlist(strsplit(path, \"[.]\"))[1] \ | |
| 525 | - \n\t\t ext <- unlist(strsplit(path, \"[.]\"))[2]") + | |
| 526 | - | |
| 527 | - // These should be made into a function assuming we can return an image variable regardless of extension | |
| 528 | - QString("\n\t\t\tif (ext == \"jpg\" || ext == \"JPEG\" || ext == \"jpeg\" || ext == \"JPG\") { \ | |
| 529 | - \n\t\t\t img <- readJPEG(path)\n\t\t } \ | |
| 530 | - else if (ext == \"PNG\" || ext == \"png\") { \ | |
| 531 | - \n\t\t\t img <- readPNG(path)\n\t\t} \ | |
| 532 | - else if (ext == \"TIFF\" || ext == \"tiff\" || ext == \"TIF\" || ext == \"tif\") { \ | |
| 533 | - \n\t\t\t img <- readTIFF(path)\n\t\t} \ | |
| 534 | - else {\n\t\t\tnext\n\t\t} ") + | |
| 535 | - | |
| 536 | - QString("\n\t\t name <- file \ | |
| 537 | - \n\n\t\t g <- rasterGrob(img, interpolate=TRUE)\n\n\t\t") + | |
| 538 | - | |
| 539 | - QString("print(qplot(1:10, 1:10, geom=\"blank\") \ | |
| 540 | - + annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) \ | |
| 541 | - + 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()) \ | |
| 542 | - + labs(title=\"Sample Landmarks\") + xlab(sprintf(\"Total Landmarks: %s\",points)))\n\t\t}}\n"))); | |
| 473 | + "plot_index = which(names(data)==\"Plot\")\n" | |
| 474 | + "Box <- data[grep(\"Box\",data$Plot),-c(1)]\n" | |
| 475 | + "Box$X <- factor(Box$X, levels = Box$X, ordered = TRUE)\n" | |
| 476 | + "Sample <- data[grep(\"Sample\",data$Plot),-c(1)]\n" | |
| 477 | + "Sample$X <- as.character(Sample$X)\n" | |
| 478 | + "EXT <- data[grep(\"EXT\",data$Plot),-c(1)]\n" | |
| 479 | + "EXT$X <- as.character(EXT$X)\n" | |
| 480 | + "EXP <- data[grep(\"EXP\",data$Plot),-c(1)]\n" | |
| 481 | + "EXP$X <- as.character(EXP$X)\n" | |
| 482 | + "NormLength <- data[grep(\"NormLength\",data$Plot),-c(1)]\n" | |
| 483 | + "rm(data)\n" | |
| 484 | + "\n"))); | |
| 485 | + | |
| 486 | + p.file.write(qPrintable(QString("summarySE <- function(data=NULL, measurevar, groupvars=NULL, na.rm=FALSE, conf.interval=.95, .drop=TRUE) {\n\t" | |
| 487 | + "require(plyr)\n\n\tlength2 <- function (x, na.rm=FALSE) {\n\t\tif (na.rm) sum(!is.na(x))\n\t\telse length(x)" | |
| 488 | + "\n\t}\n\n\tdatac <- ddply(data, groupvars, .drop=.drop, .fun = function(xx, col) {\n\t\t" | |
| 489 | + "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}," | |
| 490 | + "\n\t\tmeasurevar\n\t)\n\n\tdatac <- rename(datac, c(\"mean\" = measurevar))\n\tdatac$se <- datac$sd / sqrt(datac$N)" | |
| 491 | + "\n\tciMult <- qt(conf.interval/2 + .5, datac$N-1)\n\tdatac$ci <- datac$se * ciMult\n\n\treturn(datac)\n}\n"))); | |
| 492 | + | |
| 493 | + | |
| 494 | + p.file.write(qPrintable(QString("\nreadData <- function(data) {\n\texamples <- list()\n" | |
| 495 | + "\tfor (i in 1:nrow(data)) {\n" | |
| 496 | + "\t\tpath <- data[i,1]\n" | |
| 497 | + "\t\tvalue <- data[i,2]\n" | |
| 498 | + "\t\tfile <- unlist(strsplit(path, \"[.]\"))[1]\n" | |
| 499 | + "\t\text <- unlist(strsplit(path, \"[.]\"))[2]\n" | |
| 500 | + "\t\tif (ext == \"jpg\" || ext == \"JPEG\" || ext == \"jpeg\" || ext == \"JPG\") {\n" | |
| 501 | + "\t\t\timg <- readJPEG(path)\n" | |
| 502 | + "\t\t} else if (ext == \"PNG\" || ext == \"png\") {\n" | |
| 503 | + "\t\t\timg <- readPNG(path)\n" | |
| 504 | + "\t\t} else if (ext == \"TIFF\" || ext == \"tiff\" || ext == \"TIF\" || ext == \"tif\") { \n" | |
| 505 | + "\t\t\timg <- readTIFF(path)\n" | |
| 506 | + "}else {\n" | |
| 507 | + "\t\t\tnext\n" | |
| 508 | + "\t\t}\n" | |
| 509 | + "\t\texample <- list(file = file, value = value, image = img)\n" | |
| 510 | + "\t\texamples[[i]] <- example\n" | |
| 511 | + "\t}\n" | |
| 512 | + "\treturn(examples)\n" | |
| 513 | + "}\n"))); | |
| 514 | + | |
| 515 | + p.file.write(qPrintable(QString("\nlibrary(jpeg)\n" | |
| 516 | + "library(png)\n" | |
| 517 | + "library(grid)\n" | |
| 518 | + "multiplot <- function(..., plotlist=NULL, cols) {\n" | |
| 519 | + "\trequire(grid)\n" | |
| 520 | + "\t# Make a list from the ... arguments and plotlist\n" | |
| 521 | + "\tplots <- c(list(...), plotlist)\n" | |
| 522 | + "\tnumPlots = length(plots)\n" | |
| 523 | + "\t# Make the panel\n" | |
| 524 | + "\tplotCols = cols\n" | |
| 525 | + "\tplotRows = ceiling(numPlots/plotCols)\n" | |
| 526 | + "\t# Set up the page\n" | |
| 527 | + "\tgrid.newpage()\n" | |
| 528 | + "\tpushViewport(viewport(layout = grid.layout(plotRows, plotCols)))\n" | |
| 529 | + "\tvplayout <- function(x, y)\n" | |
| 530 | + "\tviewport(layout.pos.row = x, layout.pos.col = y)\n" | |
| 531 | + "\t# Make each plot, in the correct location\n" | |
| 532 | + "\tfor (i in 1:numPlots) {\n" | |
| 533 | + "\t\tcurRow = ceiling(i/plotCols)\n" | |
| 534 | + "\t\tcurCol = (i-1) %% plotCols + 1\n" | |
| 535 | + "\t\tprint(plots[[i]], vp = vplayout(curRow, curCol))\n" | |
| 536 | + "\t}\n" | |
| 537 | + "}\n"))); | |
| 538 | + | |
| 539 | + p.file.write(qPrintable(QString("\nplotImage <- function(image, title=NULL, label=NULL) { \n" | |
| 540 | + "\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" | |
| 541 | + "\treturn(p)" | |
| 542 | + "}\n"))); | |
| 543 | + | |
| 544 | + p.file.write(qPrintable(QString("\nsample <- readData(Sample) \n" | |
| 545 | + "print(plotImage(sample[[1]],\"Sample Landmarks\",sprintf(\"Total Landmarks: %s\",sample[[1]]$value))) \n" | |
| 546 | + "truthSample <- readData(EXT)\n" | |
| 547 | + "predictedSample <- readData(EXP)\n" | |
| 548 | + "for (i in 1:length(predictedSample)) {\n" | |
| 549 | + "\tmultiplot(plotImage(predictedSample[[i]],\"Predicted Landmarks\",sprintf(\"Average Landmark Error: %.3f\",predictedSample[[i]]$value)),plotImage(truthSample[[i]],\"Ground Truth Landmarks\",\"\"),cols=2)\n" | |
| 550 | + "}\n"))); | |
| 543 | 551 | |
| 544 | 552 | p.file.write(qPrintable(QString("\n" |
| 545 | 553 | "# Code to format error table\n" | ... | ... |