diff --git a/app/br/br.cpp b/app/br/br.cpp index 9b8ae5a..170d651 100644 --- a/app/br/br.cpp +++ b/app/br/br.cpp @@ -144,8 +144,8 @@ public: check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'evalClustering'."); br_eval_clustering(parv[0], parv[1], parc == 3 ? parv[2] : ""); } else if (!strcmp(fun, "evalDetection")) { - check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'evalDetection'."); - br_eval_detection(parv[0], parv[1], parc == 3 ? parv[2] : ""); + check((parc >= 2) && (parc <= 4), "Incorrect parameter count for 'evalDetection'."); + br_eval_detection(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc == 4 ? atoi(parv[3]) : 0); } else if (!strcmp(fun, "evalLandmarking")) { check((parc >= 2) && (parc <= 5), "Incorrect parameter count for 'evalLandmarking'."); br_eval_landmarking(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? atoi(parv[3]) : 0, parc >= 5 ? atoi(parv[4]) : 1); diff --git a/openbr/core/eval.cpp b/openbr/core/eval.cpp index f3bc694..97d015a 100644 --- a/openbr/core/eval.cpp +++ b/openbr/core/eval.cpp @@ -752,18 +752,13 @@ static QMap getDetections(const File &predictedGallery, con return allDetections; } -float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv) +static int associateGroundTruthDetections(QList &resolved, QList &falseNegative, QMap &all, QRectF &offsets) { - qDebug("Evaluating detection of %s against %s", qPrintable(predictedGallery), qPrintable(truthGallery)); + float dLeftTotal = 0.0, dRightTotal = 0.0, dTopTotal = 0.0, dBottomTotal = 0.0; + int count = 0, totalTrueDetections = 0; - // Organized by file, QMap used to preserve order - QMap allDetections = getDetections(predictedGallery, truthGallery); - - QList resolvedDetections, falseNegativeDetections; - int totalTrueDetections = 0; - foreach (Detections detections, allDetections.values()) { // For every file + foreach (Detections detections, all.values()) { totalTrueDetections += detections.truth.size(); - // Try to associate ground truth detections with predicted detections while (!detections.truth.isEmpty() && !detections.predicted.isEmpty()) { const Detection truth = detections.truth.takeFirst(); // Take removes the detection @@ -771,7 +766,16 @@ float EvalDetection(const QString &predictedGallery, const QString &truthGallery float bestOverlap = -std::numeric_limits::max(); // Find the nearest predicted detection to this ground truth detection for (int i=0; i bestOverlap) { bestOverlap = overlap; bestIndex = i; @@ -781,17 +785,67 @@ float EvalDetection(const QString &predictedGallery, const QString &truthGallery // We don't want to associate two ground truth detections with the // same prediction, over vice versa. const Detection predicted = detections.predicted.takeAt(bestIndex); - resolvedDetections.append(ResolvedDetection(predicted.confidence, bestOverlap)); + resolved.append(ResolvedDetection(predicted.confidence, bestOverlap)); + + if (offsets.x() == 0) { + // Add side differences to total only for pairs that meet the overlap threshold. + if (bestOverlap > 0.3) { + count++; + float width = predicted.boundingBox.width(); + dLeftTotal += (truth.boundingBox.left() - predicted.boundingBox.left()) / width; + dRightTotal += (truth.boundingBox.right() - predicted.boundingBox.right()) / width; + dTopTotal += (truth.boundingBox.top() - predicted.boundingBox.top()) / width; + dBottomTotal += (truth.boundingBox.bottom() - predicted.boundingBox.bottom()) / width; + } + } } foreach (const Detection &detection, detections.predicted) - resolvedDetections.append(ResolvedDetection(detection.confidence, 0)); + resolved.append(ResolvedDetection(detection.confidence, 0)); for (int i=0; i::max(), 0)); + falseNegative.append(ResolvedDetection(-std::numeric_limits::max(), 0)); } + if (offsets.x() == 0) { + // Calculate average differences in each direction + float dRight = dRightTotal / count; + float dBottom = dBottomTotal / count; + float dX = dLeftTotal / count; + float dY = dTopTotal / count; + float dWidth = dX - dRight; + float dHeight = dY - dBottom; + + offsets.setX(dX); + offsets.setY(dY); + offsets.setWidth(dWidth); + offsets.setHeight(dHeight); + } + return totalTrueDetections; +} - std::sort(resolvedDetections.begin(), resolvedDetections.end()); +float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv, bool normalize) +{ + qDebug("Evaluating detection of %s against %s", qPrintable(predictedGallery), qPrintable(truthGallery)); + // Organized by file, QMap used to preserve order + QMap allDetections = getDetections(predictedGallery, truthGallery); + QList resolvedDetections, falseNegativeDetections; + QRectF normalizations(0, 0, 0, 0); + + // Associate predictions to ground truth + int totalTrueDetections = associateGroundTruthDetections(resolvedDetections, falseNegativeDetections, allDetections, normalizations); + + // Redo association of ground truth to predictions with boundingBoxes + // resized based on the average differences on each side. + if (normalize) { + qDebug("dX = %.3f", normalizations.x()); + qDebug("dY = %.3f", normalizations.y()); + qDebug("dWidth = %.3f", normalizations.width()); + qDebug("dHeight = %.3f", normalizations.height()); + resolvedDetections.clear(); + falseNegativeDetections.clear(); + totalTrueDetections = associateGroundTruthDetections(resolvedDetections, falseNegativeDetections, allDetections, normalizations); + } + std::sort(resolvedDetections.begin(), resolvedDetections.end()); QStringList lines; lines.append("Plot, X, Y"); lines.append(computeDetectionResults(resolvedDetections, totalTrueDetections, true)); diff --git a/openbr/core/eval.h b/openbr/core/eval.h index b500596..a8c40e6 100644 --- a/openbr/core/eval.h +++ b/openbr/core/eval.h @@ -29,7 +29,7 @@ namespace br float InplaceEval(const QString & simmat, const QString & target, const QString & query, const QString & csv = ""); void EvalClassification(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = ""); - float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv = ""); // Return average overlap + float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", bool normalize = false); // Return average overlap float EvalLandmarking(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", int normalizationIndexA = 0, int normalizationIndexB = 1); // Return average error void EvalRegression(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = ""); } diff --git a/openbr/openbr.cpp b/openbr/openbr.cpp index 34f0b12..0d1c9c8 100644 --- a/openbr/openbr.cpp +++ b/openbr/openbr.cpp @@ -124,9 +124,9 @@ void br_eval_clustering(const char *csv, const char *gallery, const char * truth EvalClustering(csv, gallery, truth_property); } -float br_eval_detection(const char *predicted_gallery, const char *truth_gallery, const char *csv) +float br_eval_detection(const char *predicted_gallery, const char *truth_gallery, const char *csv, bool normalize) { - return EvalDetection(predicted_gallery, truth_gallery, csv); + return EvalDetection(predicted_gallery, truth_gallery, csv, normalize); } float br_eval_landmarking(const char *predicted_gallery, const char *truth_gallery, const char *csv, int normalization_index_a, int normalization_index_b) diff --git a/openbr/openbr.h b/openbr/openbr.h index 4f7f763..33c4a7b 100644 --- a/openbr/openbr.h +++ b/openbr/openbr.h @@ -193,9 +193,10 @@ BR_EXPORT void br_eval_clustering(const char *csv, const char *gallery, const ch * \param predicted_gallery The predicted br::Gallery. * \param truth_gallery The ground truth br::Gallery. * \param csv Optional \c .csv file to contain performance metrics. + * \param normalize Optional \c bool flag to normalize predicted bounding boxes for improved detection. * \return Average detection bounding box overlap. */ -BR_EXPORT float br_eval_detection(const char *predicted_gallery, const char *truth_gallery, const char *csv = ""); +BR_EXPORT float br_eval_detection(const char *predicted_gallery, const char *truth_gallery, const char *csv = "", bool normalize = false); /*! * \brief Evaluates and prints landmarking accuracy to terminal.