Commit 0721251695f01d27546f85ed299f29193f9a915e

Authored by Scott Klum
1 parent 8be00346

Crudely outputting some example detections in evalDetection, extra verbose output

Showing 1 changed file with 77 additions and 15 deletions
openbr/core/eval.cpp
@@ -19,8 +19,10 @@ @@ -19,8 +19,10 @@
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 "openbr/core/opencvutils.h"
  22 +#include "openbr/core/qtutils.h"
22 #include <QMapIterator> 23 #include <QMapIterator>
23 #include <cmath> 24 #include <cmath>
  25 +#include <opencv2/highgui/highgui.hpp>
24 26
25 using namespace cv; 27 using namespace cv;
26 28
@@ -755,6 +757,7 @@ void EvalClassification(const QString &amp;predictedGallery, const QString &amp;truthGal @@ -755,6 +757,7 @@ void EvalClassification(const QString &amp;predictedGallery, const QString &amp;truthGal
755 struct Detection 757 struct Detection
756 { 758 {
757 QRectF boundingBox; 759 QRectF boundingBox;
  760 + QString filePath;
758 float confidence; 761 float confidence;
759 // The ignore flag is useful when certain faces in an image should be ignored 762 // The ignore flag is useful when certain faces in an image should be ignored
760 // and should not effect detection performance. Predicted detections that overlap 763 // and should not effect detection performance. Predicted detections that overlap
@@ -763,8 +766,8 @@ struct Detection @@ -763,8 +766,8 @@ struct Detection
763 bool ignore; 766 bool ignore;
764 767
765 Detection() {} 768 Detection() {}
766 - Detection(const QRectF &boundingBox_, float confidence_ = -1, bool ignore_ = false)  
767 - : boundingBox(boundingBox_), confidence(confidence_), ignore(ignore_) {} 769 + Detection(const QRectF &boundingBox_, const QString &filePath = QString(), float confidence_ = -1, bool ignore_ = false)
  770 + : boundingBox(boundingBox_), filePath(filePath), confidence(confidence_), ignore(ignore_) {}
768 771
769 float area() const { return boundingBox.width() * boundingBox.height(); } 772 float area() const { return boundingBox.width() * boundingBox.height(); }
770 float overlap(const Detection &other) const 773 float overlap(const Detection &other) const
@@ -791,12 +794,20 @@ struct Detections @@ -791,12 +794,20 @@ struct Detections
791 794
792 struct ResolvedDetection 795 struct ResolvedDetection
793 { 796 {
  797 + QString filePath;
  798 + QRectF boundingBox;
794 float confidence, overlap; 799 float confidence, overlap;
795 ResolvedDetection() : confidence(-1), overlap(-1) {} 800 ResolvedDetection() : confidence(-1), overlap(-1) {}
796 - ResolvedDetection(float confidence_, float overlap_) : confidence(confidence_), overlap(overlap_) {} 801 + ResolvedDetection(const QString &filePath, const QRectF &boundingBox, float confidence_, float overlap_) :
  802 + filePath(filePath), boundingBox(boundingBox), confidence(confidence_), overlap(overlap_) {}
797 inline bool operator<(const ResolvedDetection &other) const { return confidence > other.confidence; } 803 inline bool operator<(const ResolvedDetection &other) const { return confidence > other.confidence; }
798 }; 804 };
799 805
  806 +QDebug operator<<(QDebug dbg, const ResolvedDetection &d)
  807 +{
  808 + return dbg.nospace() << "(FilePath: " << d.filePath << " Bounding Box: " << d.boundingBox << ", Overlap: " << d.overlap << ", Confidence: " << d.confidence << ")";
  809 +}
  810 +
800 struct DetectionOperatingPoint 811 struct DetectionOperatingPoint
801 { 812 {
802 float Recall, FalsePositiveRate, Precision; 813 float Recall, FalsePositiveRate, Precision;
@@ -808,8 +819,10 @@ struct DetectionOperatingPoint @@ -808,8 +819,10 @@ struct DetectionOperatingPoint
808 static QStringList computeDetectionResults(const QList<ResolvedDetection> &detections, int totalTrueDetections, int numImages, bool discrete) 819 static QStringList computeDetectionResults(const QList<ResolvedDetection> &detections, int totalTrueDetections, int numImages, bool discrete)
809 { 820 {
810 QList<DetectionOperatingPoint> points; 821 QList<DetectionOperatingPoint> points;
811 - float TP = 0, FP = 0, prevFP = -1; 822 + float TP = 0, FP = 0, prevFP = -1, prevTP = -1;
812 823
  824 + const int detectionsToKeep = 50;
  825 + QList<ResolvedDetection> topFalsePositives, bottomTruePositives;
813 for (int i=0; i<detections.size(); i++) { 826 for (int i=0; i<detections.size(); i++) {
814 const ResolvedDetection &detection = detections[i]; 827 const ResolvedDetection &detection = detections[i];
815 if (discrete) { 828 if (discrete) {
@@ -822,29 +835,61 @@ static QStringList computeDetectionResults(const QList&lt;ResolvedDetection&gt; &amp;detec @@ -822,29 +835,61 @@ static QStringList computeDetectionResults(const QList&lt;ResolvedDetection&gt; &amp;detec
822 } 835 }
823 if ((i == detections.size()-1) || (detection.confidence > detections[i+1].confidence)) { 836 if ((i == detections.size()-1) || (detection.confidence > detections[i+1].confidence)) {
824 if (FP > prevFP || (i == detections.size()-1)) { 837 if (FP > prevFP || (i == detections.size()-1)) {
825 - if (prevFP / numImages < 0.1 && FP / numImages > 0.1 && discrete) { 838 + if (prevFP / numImages < 1 && FP / numImages >= 1 && discrete) {
  839 + qDebug("TAR @ FAR => %f : 1", TP / totalTrueDetections);
  840 + qDebug("Confidence: %f", detection.confidence);
  841 + qDebug("TP vs. FP: %f to %f", TP, FP);
  842 + } else if (prevFP / numImages < 0.1 && FP / numImages >= 0.1 && discrete) {
826 qDebug("TAR @ FAR => %f : 0.1", TP / totalTrueDetections); 843 qDebug("TAR @ FAR => %f : 0.1", TP / totalTrueDetections);
827 qDebug("Confidence: %f", detection.confidence); 844 qDebug("Confidence: %f", detection.confidence);
828 qDebug("TP vs. FP: %f to %f", TP, FP); 845 qDebug("TP vs. FP: %f to %f", TP, FP);
829 - } else if (prevFP / numImages < 0.01 && FP / numImages > 0.01 && discrete) { 846 + } else if (prevFP / numImages < 0.02 && FP / numImages >= 0.02 && discrete) {
  847 + qDebug("TAR @ FAR => %f : 0.02", TP / totalTrueDetections);
  848 + qDebug("Confidence: %f", detection.confidence);
  849 + qDebug("TP vs. FP: %f to %f", TP, FP);
  850 + } else if (prevFP / numImages < 0.01 && FP / numImages >= 0.01 && discrete) {
830 qDebug("TAR @ FAR => %f : 0.01", TP / totalTrueDetections); 851 qDebug("TAR @ FAR => %f : 0.01", TP / totalTrueDetections);
831 qDebug("Confidence: %f", detection.confidence); 852 qDebug("Confidence: %f", detection.confidence);
832 qDebug("TP vs. FP: %f to %f", TP, FP); 853 qDebug("TP vs. FP: %f to %f", TP, FP);
833 - } else if (prevFP / numImages < 0.001 && FP / numImages > 0.001 && discrete) { 854 + } else if (prevFP / numImages < 0.001 && FP / numImages >= 0.001 && discrete) {
834 qDebug("TAR @ FAR => %f : 0.001", TP / totalTrueDetections); 855 qDebug("TAR @ FAR => %f : 0.001", TP / totalTrueDetections);
835 qDebug("Confidence: %f", detection.confidence); 856 qDebug("Confidence: %f", detection.confidence);
836 qDebug("TP vs. FP: %f to %f", TP, FP); 857 qDebug("TP vs. FP: %f to %f", TP, FP);
837 } 858 }
838 859
  860 + if (detection.overlap < 0.5 && topFalsePositives.size() < detectionsToKeep)
  861 + topFalsePositives.append(detection);
  862 +
839 points.append(DetectionOperatingPoint(TP, FP, totalTrueDetections, numImages)); 863 points.append(DetectionOperatingPoint(TP, FP, totalTrueDetections, numImages));
840 prevFP = FP; 864 prevFP = FP;
841 } 865 }
  866 +
  867 + if (TP > prevTP) {
  868 + bottomTruePositives.append(detection);
  869 + if (bottomTruePositives.size() > detectionsToKeep)
  870 + bottomTruePositives.removeFirst();
  871 + prevTP = TP;
  872 + }
842 } 873 }
843 } 874 }
844 875
845 if (discrete) { 876 if (discrete) {
846 qDebug("Total TP vs. FP: %f to %f", TP, FP); 877 qDebug("Total TP vs. FP: %f to %f", TP, FP);
847 qDebug("Overall Recall (TP vs. possible TP): %f (%f vs. %d)", TP / totalTrueDetections, TP, totalTrueDetections); 878 qDebug("Overall Recall (TP vs. possible TP): %f (%f vs. %d)", TP / totalTrueDetections, TP, totalTrueDetections);
  879 +
  880 + if (Globals->verbose) {
  881 + QtUtils::touchDir(QDir("./falsePos"));
  882 + qDebug("Highest Scoring False Positives:");
  883 + for (int i=0; i<detectionsToKeep; i++) {
  884 + Mat img = imread(qPrintable(Globals->path + "/" + topFalsePositives[i].filePath));
  885 + qDebug() << topFalsePositives[i];
  886 + const Scalar color(0,255,0);
  887 + rectangle(img, OpenCVUtils::toRect(topFalsePositives[i].boundingBox), color, 1);
  888 + imwrite(qPrintable(QString("./falsePos/falsePos%1.jpg").arg(QString::number(i))), img);
  889 + }
  890 + qDebug("Lowest Scoring True Positives:");
  891 + qDebug() << bottomTruePositives;
  892 + }
848 } 893 }
849 894
850 const int keep = qMin(points.size(), Max_Points); 895 const int keep = qMin(points.size(), Max_Points);
@@ -912,6 +957,7 @@ static DetectionKey getDetectKey(const FileList &amp;files) @@ -912,6 +957,7 @@ static DetectionKey getDetectKey(const FileList &amp;files)
912 // return a list of detections independent of the detection key format 957 // return a list of detections independent of the detection key format
913 static QList<Detection> getDetections(const DetectionKey &key, const File &f, bool isTruth) 958 static QList<Detection> getDetections(const DetectionKey &key, const File &f, bool isTruth)
914 { 959 {
  960 + const QString filePath = f.path() + "/" + f.fileName();
915 QList<Detection> dets; 961 QList<Detection> dets;
916 if (key.type == DetectionKey::RectList) { 962 if (key.type == DetectionKey::RectList) {
917 QList<QRectF> rects = f.rects(); 963 QList<QRectF> rects = f.rects();
@@ -920,15 +966,15 @@ static QList&lt;Detection&gt; getDetections(const DetectionKey &amp;key, const File &amp;f, bo @@ -920,15 +966,15 @@ static QList&lt;Detection&gt; getDetections(const DetectionKey &amp;key, const File &amp;f, bo
920 qFatal("You don't have enough confidence. I mean, your detections don't all have confidence measures."); 966 qFatal("You don't have enough confidence. I mean, your detections don't all have confidence measures.");
921 for (int i=0; i<rects.size(); i++) { 967 for (int i=0; i<rects.size(); i++) {
922 if (isTruth) 968 if (isTruth)
923 - dets.append(Detection(rects[i])); 969 + dets.append(Detection(rects[i], filePath));
924 else 970 else
925 - dets.append(Detection(rects[i], confidences[i])); 971 + dets.append(Detection(rects[i], filePath, confidences[i]));
926 } 972 }
927 } else if (key.type == DetectionKey::Rect) { 973 } else if (key.type == DetectionKey::Rect) {
928 - dets.append(Detection(f.get<QRectF>(key), isTruth ? -1 : f.get<float>("Confidence", -1))); 974 + dets.append(Detection(f.get<QRectF>(key), filePath, isTruth ? -1 : f.get<float>("Confidence", -1)));
929 } else if (key.type == DetectionKey::XYWidthHeight) { 975 } else if (key.type == DetectionKey::XYWidthHeight) {
930 const QRectF rect(f.get<float>(key+"_X"), f.get<float>(key+"_Y"), f.get<float>(key+"_Width"), f.get<float>(key+"_Height")); 976 const QRectF rect(f.get<float>(key+"_X"), f.get<float>(key+"_Y"), f.get<float>(key+"_Width"), f.get<float>(key+"_Height"));
931 - dets.append(Detection(rect, isTruth ? -1 : f.get<float>("Confidence", -1), f.get<bool>("Ignore", false))); 977 + dets.append(Detection(rect, filePath, isTruth ? -1 : f.get<float>("Confidence", -1), f.get<bool>("Ignore", false)));
932 } 978 }
933 return dets; 979 return dets;
934 } 980 }
@@ -986,9 +1032,10 @@ static int associateGroundTruthDetections(QList&lt;ResolvedDetection&gt; &amp;resolved, QL @@ -986,9 +1032,10 @@ static int associateGroundTruthDetections(QList&lt;ResolvedDetection&gt; &amp;resolved, QL
986 y = predicted.boundingBox.y() + offsets.y()*predictedWidth; 1032 y = predicted.boundingBox.y() + offsets.y()*predictedWidth;
987 width = predicted.boundingBox.width() - offsets.width()*predictedWidth; 1033 width = predicted.boundingBox.width() - offsets.width()*predictedWidth;
988 height = predicted.boundingBox.height() - offsets.height()*predictedWidth; 1034 height = predicted.boundingBox.height() - offsets.height()*predictedWidth;
989 - Detection newPredicted(QRectF(x, y, width, height), 0.0); 1035 + Detection newPredicted(QRectF(x, y, width, height), predicted.filePath, 0.0);
990 1036
991 const float overlap = truth.overlap(newPredicted); 1037 const float overlap = truth.overlap(newPredicted);
  1038 +
992 if (overlap > 0) 1039 if (overlap > 0)
993 sortedDetections.append(SortedDetection(t, p, overlap)); 1040 sortedDetections.append(SortedDetection(t, p, overlap));
994 } 1041 }
@@ -1006,7 +1053,8 @@ static int associateGroundTruthDetections(QList&lt;ResolvedDetection&gt; &amp;resolved, QL @@ -1006,7 +1053,8 @@ static int associateGroundTruthDetections(QList&lt;ResolvedDetection&gt; &amp;resolved, QL
1006 const Detection truth = detections.truth[detection.truth_idx]; 1053 const Detection truth = detections.truth[detection.truth_idx];
1007 const Detection predicted = detections.predicted[detection.predicted_idx]; 1054 const Detection predicted = detections.predicted[detection.predicted_idx];
1008 1055
1009 - if (!truth.ignore) resolved.append(ResolvedDetection(predicted.confidence, detection.overlap)); 1056 + if (!truth.ignore)
  1057 + resolved.append(ResolvedDetection(predicted.filePath, predicted.boundingBox, predicted.confidence, detection.overlap));
1010 1058
1011 removedTruth.append(detection.truth_idx); 1059 removedTruth.append(detection.truth_idx);
1012 removedPredicted.append(detection.predicted_idx); 1060 removedPredicted.append(detection.predicted_idx);
@@ -1022,9 +1070,9 @@ static int associateGroundTruthDetections(QList&lt;ResolvedDetection&gt; &amp;resolved, QL @@ -1022,9 +1070,9 @@ static int associateGroundTruthDetections(QList&lt;ResolvedDetection&gt; &amp;resolved, QL
1022 } 1070 }
1023 1071
1024 for (int i = 0; i < detections.predicted.size(); i++) 1072 for (int i = 0; i < detections.predicted.size(); i++)
1025 - if (!removedPredicted.contains(i)) resolved.append(ResolvedDetection(detections.predicted[i].confidence, 0)); 1073 + if (!removedPredicted.contains(i)) resolved.append(ResolvedDetection(detections.predicted[i].filePath, detections.predicted[i].boundingBox, detections.predicted[i].confidence, 0));
1026 for (int i = 0; i < detections.truth.size(); i++) 1074 for (int i = 0; i < detections.truth.size(); i++)
1027 - if (!removedTruth.contains(i) && !detections.truth[i].ignore) falseNegative.append(ResolvedDetection(-std::numeric_limits<float>::max(), 0)); 1075 + if (!removedTruth.contains(i) && !detections.truth[i].ignore) falseNegative.append(ResolvedDetection(detections.truth[i].filePath, detections.truth[i].boundingBox, -std::numeric_limits<float>::max(), 0));
1028 } 1076 }
1029 1077
1030 if (offsets.x() == 0) { 1078 if (offsets.x() == 0) {
@@ -1117,6 +1165,20 @@ float EvalDetection(const QString &amp;predictedGallery, const QString &amp;truthGallery @@ -1117,6 +1165,20 @@ float EvalDetection(const QString &amp;predictedGallery, const QString &amp;truthGallery
1117 falseNegativeDetections.clear(); 1165 falseNegativeDetections.clear();
1118 totalTrueDetections = associateGroundTruthDetections(resolvedDetections, falseNegativeDetections, allDetections, normalizations); 1166 totalTrueDetections = associateGroundTruthDetections(resolvedDetections, falseNegativeDetections, allDetections, normalizations);
1119 } 1167 }
  1168 +
  1169 + if (Globals->verbose) {
  1170 + qDebug("Total False negatives:");
  1171 + const int numFalseNegatives = 50;
  1172 + for (int i=0; i<numFalseNegatives; i++) {
  1173 + Mat img = imread(qPrintable(Globals->path + "/" + falseNegativeDetections[i].filePath));
  1174 + qDebug() << falseNegativeDetections[i];
  1175 + const Scalar color(0,255,0);
  1176 + rectangle(img, OpenCVUtils::toRect(falseNegativeDetections[i].boundingBox), color, 1);
  1177 + QtUtils::touchDir(QDir("./falseNegs"));
  1178 + imwrite(qPrintable(QString("./falseNegs/falseNeg%1.jpg").arg(QString::number(i))), img);
  1179 + }
  1180 + }
  1181 +
1120 std::sort(resolvedDetections.begin(), resolvedDetections.end()); 1182 std::sort(resolvedDetections.begin(), resolvedDetections.end());
1121 QStringList lines; 1183 QStringList lines;
1122 lines.append("Plot, X, Y"); 1184 lines.append("Plot, X, Y");