Commit e6d1d7b442f289c71965cf1ed5a72df453e8d2b8

Authored by Ben Klein
1 parent d5677d9b

Added flag for normalizing predicted bounding boxes based on average distances f…

…rom ground truth bounding boxes.
app/br/br.cpp
@@ -144,8 +144,8 @@ public: @@ -144,8 +144,8 @@ public:
144 check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'evalClustering'."); 144 check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'evalClustering'.");
145 br_eval_clustering(parv[0], parv[1], parc == 3 ? parv[2] : ""); 145 br_eval_clustering(parv[0], parv[1], parc == 3 ? parv[2] : "");
146 } else if (!strcmp(fun, "evalDetection")) { 146 } else if (!strcmp(fun, "evalDetection")) {
147 - check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'evalDetection'.");  
148 - br_eval_detection(parv[0], parv[1], parc == 3 ? parv[2] : ""); 147 + check((parc >= 2) && (parc <= 4), "Incorrect parameter count for 'evalDetection'.");
  148 + br_eval_detection(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc == 4 ? atoi(parv[3]) : 0);
149 } else if (!strcmp(fun, "evalLandmarking")) { 149 } else if (!strcmp(fun, "evalLandmarking")) {
150 check((parc >= 2) && (parc <= 5), "Incorrect parameter count for 'evalLandmarking'."); 150 check((parc >= 2) && (parc <= 5), "Incorrect parameter count for 'evalLandmarking'.");
151 br_eval_landmarking(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? atoi(parv[3]) : 0, parc >= 5 ? atoi(parv[4]) : 1); 151 br_eval_landmarking(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? atoi(parv[3]) : 0, parc >= 5 ? atoi(parv[4]) : 1);
openbr/core/eval.cpp
@@ -752,18 +752,13 @@ static QMap&lt;QString, Detections&gt; getDetections(const File &amp;predictedGallery, con @@ -752,18 +752,13 @@ static QMap&lt;QString, Detections&gt; getDetections(const File &amp;predictedGallery, con
752 return allDetections; 752 return allDetections;
753 } 753 }
754 754
755 -float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv) 755 +static int associateGroundTruthDetections(QList<ResolvedDetection> &resolved, QList<ResolvedDetection> &falseNegative, QMap<QString, Detections> &all, QRectF &offsets)
756 { 756 {
757 - qDebug("Evaluating detection of %s against %s", qPrintable(predictedGallery), qPrintable(truthGallery)); 757 + float dLeftTotal = 0.0, dRightTotal = 0.0, dTopTotal = 0.0, dBottomTotal = 0.0;
  758 + int count = 0, totalTrueDetections = 0;
758 759
759 - // Organized by file, QMap used to preserve order  
760 - QMap<QString, Detections> allDetections = getDetections(predictedGallery, truthGallery);  
761 -  
762 - QList<ResolvedDetection> resolvedDetections, falseNegativeDetections;  
763 - int totalTrueDetections = 0;  
764 - foreach (Detections detections, allDetections.values()) { // For every file 760 + foreach (Detections detections, all.values()) {
765 totalTrueDetections += detections.truth.size(); 761 totalTrueDetections += detections.truth.size();
766 -  
767 // Try to associate ground truth detections with predicted detections 762 // Try to associate ground truth detections with predicted detections
768 while (!detections.truth.isEmpty() && !detections.predicted.isEmpty()) { 763 while (!detections.truth.isEmpty() && !detections.predicted.isEmpty()) {
769 const Detection truth = detections.truth.takeFirst(); // Take removes the detection 764 const Detection truth = detections.truth.takeFirst(); // Take removes the detection
@@ -771,7 +766,16 @@ float EvalDetection(const QString &amp;predictedGallery, const QString &amp;truthGallery @@ -771,7 +766,16 @@ float EvalDetection(const QString &amp;predictedGallery, const QString &amp;truthGallery
771 float bestOverlap = -std::numeric_limits<float>::max(); 766 float bestOverlap = -std::numeric_limits<float>::max();
772 // Find the nearest predicted detection to this ground truth detection 767 // Find the nearest predicted detection to this ground truth detection
773 for (int i=0; i<detections.predicted.size(); i++) { 768 for (int i=0; i<detections.predicted.size(); i++) {
774 - const float overlap = truth.overlap(detections.predicted[i]); 769 + Detection predicted = detections.predicted[i];
  770 + float predictedWidth = predicted.boundingBox.width();
  771 + float x, y, width, height;
  772 + x = predicted.boundingBox.x() + offsets.x()*predictedWidth;
  773 + y = predicted.boundingBox.y() + offsets.y()*predictedWidth;
  774 + width = predicted.boundingBox.width() - offsets.width()*predictedWidth;
  775 + height = predicted.boundingBox.height() - offsets.height()*predictedWidth;
  776 + Detection newPredicted(QRectF(x, y, width, height), 0.0);
  777 +
  778 + const float overlap = truth.overlap(newPredicted);
775 if (overlap > bestOverlap) { 779 if (overlap > bestOverlap) {
776 bestOverlap = overlap; 780 bestOverlap = overlap;
777 bestIndex = i; 781 bestIndex = i;
@@ -781,17 +785,67 @@ float EvalDetection(const QString &amp;predictedGallery, const QString &amp;truthGallery @@ -781,17 +785,67 @@ float EvalDetection(const QString &amp;predictedGallery, const QString &amp;truthGallery
781 // We don't want to associate two ground truth detections with the 785 // We don't want to associate two ground truth detections with the
782 // same prediction, over vice versa. 786 // same prediction, over vice versa.
783 const Detection predicted = detections.predicted.takeAt(bestIndex); 787 const Detection predicted = detections.predicted.takeAt(bestIndex);
784 - resolvedDetections.append(ResolvedDetection(predicted.confidence, bestOverlap)); 788 + resolved.append(ResolvedDetection(predicted.confidence, bestOverlap));
  789 +
  790 + if (offsets.x() == 0) {
  791 + // Add side differences to total only for pairs that meet the overlap threshold.
  792 + if (bestOverlap > 0.3) {
  793 + count++;
  794 + float width = predicted.boundingBox.width();
  795 + dLeftTotal += (truth.boundingBox.left() - predicted.boundingBox.left()) / width;
  796 + dRightTotal += (truth.boundingBox.right() - predicted.boundingBox.right()) / width;
  797 + dTopTotal += (truth.boundingBox.top() - predicted.boundingBox.top()) / width;
  798 + dBottomTotal += (truth.boundingBox.bottom() - predicted.boundingBox.bottom()) / width;
  799 + }
  800 + }
785 } 801 }
786 802
787 foreach (const Detection &detection, detections.predicted) 803 foreach (const Detection &detection, detections.predicted)
788 - resolvedDetections.append(ResolvedDetection(detection.confidence, 0)); 804 + resolved.append(ResolvedDetection(detection.confidence, 0));
789 for (int i=0; i<detections.truth.size(); i++) 805 for (int i=0; i<detections.truth.size(); i++)
790 - falseNegativeDetections.append(ResolvedDetection(-std::numeric_limits<float>::max(), 0)); 806 + falseNegative.append(ResolvedDetection(-std::numeric_limits<float>::max(), 0));
791 } 807 }
  808 + if (offsets.x() == 0) {
  809 + // Calculate average differences in each direction
  810 + float dRight = dRightTotal / count;
  811 + float dBottom = dBottomTotal / count;
  812 + float dX = dLeftTotal / count;
  813 + float dY = dTopTotal / count;
  814 + float dWidth = dX - dRight;
  815 + float dHeight = dY - dBottom;
  816 +
  817 + offsets.setX(dX);
  818 + offsets.setY(dY);
  819 + offsets.setWidth(dWidth);
  820 + offsets.setHeight(dHeight);
  821 + }
  822 + return totalTrueDetections;
  823 +}
792 824
793 - std::sort(resolvedDetections.begin(), resolvedDetections.end()); 825 +float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv, bool normalize)
  826 +{
  827 + qDebug("Evaluating detection of %s against %s", qPrintable(predictedGallery), qPrintable(truthGallery));
  828 + // Organized by file, QMap used to preserve order
  829 + QMap<QString, Detections> allDetections = getDetections(predictedGallery, truthGallery);
794 830
  831 + QList<ResolvedDetection> resolvedDetections, falseNegativeDetections;
  832 + QRectF normalizations(0, 0, 0, 0);
  833 +
  834 + // Associate predictions to ground truth
  835 + int totalTrueDetections = associateGroundTruthDetections(resolvedDetections, falseNegativeDetections, allDetections, normalizations);
  836 +
  837 + // Redo association of ground truth to predictions with boundingBoxes
  838 + // resized based on the average differences on each side.
  839 + if (normalize) {
  840 + qDebug("dX = %.3f", normalizations.x());
  841 + qDebug("dY = %.3f", normalizations.y());
  842 + qDebug("dWidth = %.3f", normalizations.width());
  843 + qDebug("dHeight = %.3f", normalizations.height());
  844 + resolvedDetections.clear();
  845 + falseNegativeDetections.clear();
  846 + totalTrueDetections = associateGroundTruthDetections(resolvedDetections, falseNegativeDetections, allDetections, normalizations);
  847 + }
  848 + std::sort(resolvedDetections.begin(), resolvedDetections.end());
795 QStringList lines; 849 QStringList lines;
796 lines.append("Plot, X, Y"); 850 lines.append("Plot, X, Y");
797 lines.append(computeDetectionResults(resolvedDetections, totalTrueDetections, true)); 851 lines.append(computeDetectionResults(resolvedDetections, totalTrueDetections, true));
openbr/core/eval.h
@@ -29,7 +29,7 @@ namespace br @@ -29,7 +29,7 @@ namespace br
29 float InplaceEval(const QString & simmat, const QString & target, const QString & query, const QString & csv = ""); 29 float InplaceEval(const QString & simmat, const QString & target, const QString & query, const QString & csv = "");
30 30
31 void EvalClassification(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = ""); 31 void EvalClassification(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = "");
32 - float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv = ""); // Return average overlap 32 + float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", bool normalize = false); // Return average overlap
33 float EvalLandmarking(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", int normalizationIndexA = 0, int normalizationIndexB = 1); // Return average error 33 float EvalLandmarking(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", int normalizationIndexA = 0, int normalizationIndexB = 1); // Return average error
34 void EvalRegression(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = ""); 34 void EvalRegression(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = "");
35 } 35 }
openbr/openbr.cpp
@@ -124,9 +124,9 @@ void br_eval_clustering(const char *csv, const char *gallery, const char * truth @@ -124,9 +124,9 @@ void br_eval_clustering(const char *csv, const char *gallery, const char * truth
124 EvalClustering(csv, gallery, truth_property); 124 EvalClustering(csv, gallery, truth_property);
125 } 125 }
126 126
127 -float br_eval_detection(const char *predicted_gallery, const char *truth_gallery, const char *csv) 127 +float br_eval_detection(const char *predicted_gallery, const char *truth_gallery, const char *csv, bool normalize)
128 { 128 {
129 - return EvalDetection(predicted_gallery, truth_gallery, csv); 129 + return EvalDetection(predicted_gallery, truth_gallery, csv, normalize);
130 } 130 }
131 131
132 float br_eval_landmarking(const char *predicted_gallery, const char *truth_gallery, const char *csv, int normalization_index_a, int normalization_index_b) 132 float br_eval_landmarking(const char *predicted_gallery, const char *truth_gallery, const char *csv, int normalization_index_a, int normalization_index_b)
openbr/openbr.h
@@ -193,9 +193,10 @@ BR_EXPORT void br_eval_clustering(const char *csv, const char *gallery, const ch @@ -193,9 +193,10 @@ BR_EXPORT void br_eval_clustering(const char *csv, const char *gallery, const ch
193 * \param predicted_gallery The predicted br::Gallery. 193 * \param predicted_gallery The predicted br::Gallery.
194 * \param truth_gallery The ground truth br::Gallery. 194 * \param truth_gallery The ground truth br::Gallery.
195 * \param csv Optional \c .csv file to contain performance metrics. 195 * \param csv Optional \c .csv file to contain performance metrics.
  196 + * \param normalize Optional \c bool flag to normalize predicted bounding boxes for improved detection.
196 * \return Average detection bounding box overlap. 197 * \return Average detection bounding box overlap.
197 */ 198 */
198 -BR_EXPORT float br_eval_detection(const char *predicted_gallery, const char *truth_gallery, const char *csv = ""); 199 +BR_EXPORT float br_eval_detection(const char *predicted_gallery, const char *truth_gallery, const char *csv = "", bool normalize = false);
199 200
200 /*! 201 /*!
201 * \brief Evaluates and prints landmarking accuracy to terminal. 202 * \brief Evaluates and prints landmarking accuracy to terminal.