Commit bf17972881c559482e56cb89ffc898df48eaeaab

Authored by Josh Klontz
2 parents 453d5f49 fa88ae6a

Merge pull request #283 from biometrics/evaluation_updates

Updates to eval_detection
app/br/br.cpp
@@ -160,8 +160,8 @@ public: @@ -160,8 +160,8 @@ public:
160 check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'evalClustering'."); 160 check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'evalClustering'.");
161 br_eval_clustering(parv[0], parv[1], parc == 3 ? parv[2] : ""); 161 br_eval_clustering(parv[0], parv[1], parc == 3 ? parv[2] : "");
162 } else if (!strcmp(fun, "evalDetection")) { 162 } else if (!strcmp(fun, "evalDetection")) {
163 - check((parc >= 2) && (parc <= 5), "Incorrect parameter count for 'evalDetection'.");  
164 - br_eval_detection(parv[0], parv[1], parc >= 3 ? parv[2] : "", parc >= 4 ? atoi(parv[3]) : 0, parc == 5 ? atoi(parv[4]) : 0); 163 + check((parc >= 2) && (parc <= 6), "Incorrect parameter count for 'evalDetection'.");
  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 } else if (!strcmp(fun, "evalLandmarking")) { 165 } else if (!strcmp(fun, "evalLandmarking")) {
166 check((parc >= 2) && (parc <= 5), "Incorrect parameter count for '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); 167 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
@@ -666,10 +666,15 @@ struct Detection @@ -666,10 +666,15 @@ struct Detection
666 { 666 {
667 QRectF boundingBox; 667 QRectF boundingBox;
668 float confidence; 668 float confidence;
  669 + // The ignore flag is useful when certain faces in an image should be ignored
  670 + // and should not effect detection performance. Predicted detections that overlap
  671 + // with an ignored truth detection will not count as a true positive, false positive,
  672 + // true negative, or false negative, it will simply be ignored.
  673 + bool ignore;
669 674
670 Detection() {} 675 Detection() {}
671 - Detection(const QRectF &boundingBox_, float confidence_ = -1)  
672 - : boundingBox(boundingBox_), confidence(confidence_) {} 676 + Detection(const QRectF &boundingBox_, float confidence_ = -1, bool ignore_ = false)
  677 + : boundingBox(boundingBox_), confidence(confidence_), ignore(ignore_) {}
673 678
674 float area() const { return boundingBox.width() * boundingBox.height(); } 679 float area() const { return boundingBox.width() * boundingBox.height(); }
675 float overlap(const Detection &other) const 680 float overlap(const Detection &other) const
@@ -679,6 +684,16 @@ struct Detection @@ -679,6 +684,16 @@ struct Detection
679 } 684 }
680 }; 685 };
681 686
  687 +struct SortedDetection
  688 +{
  689 + int truth_idx, predicted_idx;
  690 + float overlap;
  691 + SortedDetection() : truth_idx(-1), predicted_idx(-1), overlap(-1) {}
  692 + SortedDetection(int truth_idx_, int predicted_idx_, float overlap_)
  693 + : truth_idx(truth_idx_), predicted_idx(predicted_idx_), overlap(overlap_) {}
  694 + inline bool operator<(const SortedDetection &other) const { return overlap > other.overlap; }
  695 +};
  696 +
682 struct Detections 697 struct Detections
683 { 698 {
684 QList<Detection> predicted, truth; 699 QList<Detection> predicted, truth;
@@ -704,6 +719,7 @@ static QStringList computeDetectionResults(const QList&lt;ResolvedDetection&gt; &amp;detec @@ -704,6 +719,7 @@ static QStringList computeDetectionResults(const QList&lt;ResolvedDetection&gt; &amp;detec
704 { 719 {
705 QList<DetectionOperatingPoint> points; 720 QList<DetectionOperatingPoint> points;
706 float TP = 0, FP = 0, prevFP = -1; 721 float TP = 0, FP = 0, prevFP = -1;
  722 +
707 for (int i=0; i<detections.size(); i++) { 723 for (int i=0; i<detections.size(); i++) {
708 const ResolvedDetection &detection = detections[i]; 724 const ResolvedDetection &detection = detections[i];
709 if (discrete) { 725 if (discrete) {
@@ -716,6 +732,13 @@ static QStringList computeDetectionResults(const QList&lt;ResolvedDetection&gt; &amp;detec @@ -716,6 +732,13 @@ static QStringList computeDetectionResults(const QList&lt;ResolvedDetection&gt; &amp;detec
716 } 732 }
717 if ((i == detections.size()-1) || (detection.confidence > detections[i+1].confidence)) { 733 if ((i == detections.size()-1) || (detection.confidence > detections[i+1].confidence)) {
718 if (FP > prevFP || (i == detections.size()-1)) { 734 if (FP > prevFP || (i == detections.size()-1)) {
  735 + if (prevFP / numImages < 0.1 && FP / numImages > 0.1 && discrete) {
  736 + qDebug("TAR @ FAR => %f : 0.1", TP / totalTrueDetections);
  737 + qDebug("Confidence: %f", detection.confidence);
  738 + } else if (prevFP / numImages < 0.01 && FP / numImages > 0.01 && discrete) {
  739 + qDebug("TAR @ FAR => %f : 0.01", TP / totalTrueDetections);
  740 + qDebug("Confidence: %f", detection.confidence);
  741 + }
719 points.append(DetectionOperatingPoint(TP, FP, totalTrueDetections, numImages)); 742 points.append(DetectionOperatingPoint(TP, FP, totalTrueDetections, numImages));
720 prevFP = FP; 743 prevFP = FP;
721 } 744 }
@@ -803,7 +826,7 @@ static QList&lt;Detection&gt; getDetections(const DetectionKey &amp;key, const File &amp;f, bo @@ -803,7 +826,7 @@ static QList&lt;Detection&gt; getDetections(const DetectionKey &amp;key, const File &amp;f, bo
803 dets.append(Detection(f.get<QRectF>(key), isTruth ? -1 : f.get<float>("Confidence", -1))); 826 dets.append(Detection(f.get<QRectF>(key), isTruth ? -1 : f.get<float>("Confidence", -1)));
804 } else if (key.type == DetectionKey::XYWidthHeight) { 827 } else if (key.type == DetectionKey::XYWidthHeight) {
805 const QRectF rect(f.get<float>(key+"_X"), f.get<float>(key+"_Y"), f.get<float>(key+"_Width"), f.get<float>(key+"_Height")); 828 const QRectF rect(f.get<float>(key+"_X"), f.get<float>(key+"_Y"), f.get<float>(key+"_Width"), f.get<float>(key+"_Height"));
806 - dets.append(Detection(rect, isTruth ? -1 : f.get<float>("Confidence", -1))); 829 + dets.append(Detection(rect, isTruth ? -1 : f.get<float>("Confidence", -1), f.get<bool>("Ignore", false)));
807 } 830 }
808 return dets; 831 return dets;
809 } 832 }
@@ -827,21 +850,16 @@ static QMap&lt;QString, Detections&gt; getDetections(const File &amp;predictedGallery, con @@ -827,21 +850,16 @@ static QMap&lt;QString, Detections&gt; getDetections(const File &amp;predictedGallery, con
827 qPrintable(predictedDetectKey == truthDetectKey ? QString() : "/"+truthDetectKey)); 850 qPrintable(predictedDetectKey == truthDetectKey ? QString() : "/"+truthDetectKey));
828 851
829 QMap<QString, Detections> allDetections; 852 QMap<QString, Detections> allDetections;
830 - foreach (const File &f, predicted)  
831 - allDetections[f.baseName()].predicted.append(getDetections(predictedDetectKey, f, false));  
832 foreach (const File &f, truth) 853 foreach (const File &f, truth)
833 - allDetections[f.baseName()].truth.append(getDetections(truthDetectKey, f, true)); 854 + allDetections[f.name].truth.append(getDetections(truthDetectKey, f, true));
  855 + foreach (const File &f, predicted)
  856 + if (allDetections.contains(f.name)) allDetections[f.name].predicted.append(getDetections(predictedDetectKey, f, false));
834 return allDetections; 857 return allDetections;
835 } 858 }
836 859
837 -static int getNumberOfImages(const File &truthGallery)  
838 -{  
839 - const FileList files = FileList::fromGallery(truthGallery);  
840 -  
841 - QSet<QString> names;  
842 - foreach(const File &file, files)  
843 - names.insert(file.fileName());  
844 - return names.size(); 860 +static inline int getNumberOfImages(const QMap<QString, Detections> detections)
  861 +{
  862 + return detections.keys().size();
845 } 863 }
846 864
847 static int associateGroundTruthDetections(QList<ResolvedDetection> &resolved, QList<ResolvedDetection> &falseNegative, QMap<QString, Detections> &all, QRectF &offsets) 865 static int associateGroundTruthDetections(QList<ResolvedDetection> &resolved, QList<ResolvedDetection> &falseNegative, QMap<QString, Detections> &all, QRectF &offsets)
@@ -852,13 +870,13 @@ static int associateGroundTruthDetections(QList&lt;ResolvedDetection&gt; &amp;resolved, QL @@ -852,13 +870,13 @@ static int associateGroundTruthDetections(QList&lt;ResolvedDetection&gt; &amp;resolved, QL
852 foreach (Detections detections, all.values()) { 870 foreach (Detections detections, all.values()) {
853 totalTrueDetections += detections.truth.size(); 871 totalTrueDetections += detections.truth.size();
854 // Try to associate ground truth detections with predicted detections 872 // Try to associate ground truth detections with predicted detections
855 - while (!detections.truth.isEmpty() && !detections.predicted.isEmpty()) {  
856 - const Detection truth = detections.truth.takeFirst(); // Take removes the detection  
857 - int bestIndex = -1;  
858 - float bestOverlap = -std::numeric_limits<float>::max();  
859 - // Find the nearest predicted detection to this ground truth detection  
860 - for (int i=0; i<detections.predicted.size(); i++) {  
861 - Detection predicted = detections.predicted[i]; 873 +
  874 + QList<SortedDetection> sortedDetections; sortedDetections.reserve(detections.truth.size() * detections.predicted.size());
  875 + for (int t = 0; t < detections.truth.size(); t++) {
  876 + const Detection truth = detections.truth[t];
  877 + for (int p = 0; p < detections.predicted.size(); p++) {
  878 + Detection predicted = detections.predicted[p];
  879 +
862 float predictedWidth = predicted.boundingBox.width(); 880 float predictedWidth = predicted.boundingBox.width();
863 float x, y, width, height; 881 float x, y, width, height;
864 x = predicted.boundingBox.x() + offsets.x()*predictedWidth; 882 x = predicted.boundingBox.x() + offsets.x()*predictedWidth;
@@ -868,35 +886,44 @@ static int associateGroundTruthDetections(QList&lt;ResolvedDetection&gt; &amp;resolved, QL @@ -868,35 +886,44 @@ static int associateGroundTruthDetections(QList&lt;ResolvedDetection&gt; &amp;resolved, QL
868 Detection newPredicted(QRectF(x, y, width, height), 0.0); 886 Detection newPredicted(QRectF(x, y, width, height), 0.0);
869 887
870 const float overlap = truth.overlap(newPredicted); 888 const float overlap = truth.overlap(newPredicted);
871 - if (overlap > bestOverlap) {  
872 - bestOverlap = overlap;  
873 - bestIndex = i;  
874 - } 889 + if (overlap > 0)
  890 + sortedDetections.append(SortedDetection(t, p, overlap));
875 } 891 }
876 - // Removing the detection prevents us from considering it twice.  
877 - // We don't want to associate two ground truth detections with the  
878 - // same prediction, over vice versa.  
879 - const Detection predicted = detections.predicted.takeAt(bestIndex);  
880 - resolved.append(ResolvedDetection(predicted.confidence, bestOverlap));  
881 -  
882 - if (offsets.x() == 0) {  
883 - // Add side differences to total only for pairs that meet the overlap threshold.  
884 - if (bestOverlap > 0.3) {  
885 - count++;  
886 - float width = predicted.boundingBox.width();  
887 - dLeftTotal += (truth.boundingBox.left() - predicted.boundingBox.left()) / width;  
888 - dRightTotal += (truth.boundingBox.right() - predicted.boundingBox.right()) / width;  
889 - dTopTotal += (truth.boundingBox.top() - predicted.boundingBox.top()) / width;  
890 - dBottomTotal += (truth.boundingBox.bottom() - predicted.boundingBox.bottom()) / width;  
891 - } 892 + }
  893 +
  894 + std::sort(sortedDetections.begin(), sortedDetections.end());
  895 +
  896 + QList<int> removedTruth;
  897 + QList<int> removedPredicted;
  898 +
  899 + foreach (const SortedDetection &detection, sortedDetections) {
  900 + if (removedTruth.contains(detection.truth_idx) || removedPredicted.contains(detection.predicted_idx))
  901 + continue;
  902 +
  903 + const Detection truth = detections.truth[detection.truth_idx];
  904 + const Detection predicted = detections.predicted[detection.predicted_idx];
  905 +
  906 + if (!truth.ignore) resolved.append(ResolvedDetection(predicted.confidence, detection.overlap));
  907 +
  908 + removedTruth.append(detection.truth_idx);
  909 + removedPredicted.append(detection.predicted_idx);
  910 +
  911 + if (offsets.x() == 0 && detection.overlap > 0.3) {
  912 + count++;
  913 + float width = predicted.boundingBox.width();
  914 + dLeftTotal += (truth.boundingBox.left() - predicted.boundingBox.left()) / width;
  915 + dRightTotal += (truth.boundingBox.right() - predicted.boundingBox.right()) / width;
  916 + dTopTotal += (truth.boundingBox.top() - predicted.boundingBox.top()) / width;
  917 + dBottomTotal += (truth.boundingBox.bottom() - predicted.boundingBox.bottom()) / width;
892 } 918 }
893 } 919 }
894 920
895 - foreach (const Detection &detection, detections.predicted)  
896 - resolved.append(ResolvedDetection(detection.confidence, 0));  
897 - for (int i=0; i<detections.truth.size(); i++)  
898 - falseNegative.append(ResolvedDetection(-std::numeric_limits<float>::max(), 0)); 921 + for (int i = 0; i < detections.predicted.size(); i++)
  922 + if (!removedPredicted.contains(i)) resolved.append(ResolvedDetection(detections.predicted[i].confidence, 0));
  923 + for (int i = 0; i < detections.truth.size(); i++)
  924 + if (!removedTruth.contains(i) && !detections.truth[i].ignore) falseNegative.append(ResolvedDetection(-std::numeric_limits<float>::max(), 0));
899 } 925 }
  926 +
900 if (offsets.x() == 0) { 927 if (offsets.x() == 0) {
901 // Calculate average differences in each direction 928 // Calculate average differences in each direction
902 float dRight = dRightTotal / count; 929 float dRight = dRightTotal / count;
@@ -914,7 +941,7 @@ static int associateGroundTruthDetections(QList&lt;ResolvedDetection&gt; &amp;resolved, QL @@ -914,7 +941,7 @@ static int associateGroundTruthDetections(QList&lt;ResolvedDetection&gt; &amp;resolved, QL
914 return totalTrueDetections; 941 return totalTrueDetections;
915 } 942 }
916 943
917 -float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv, bool normalize, int minSize) 944 +float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv, bool normalize, int minSize, int maxSize)
918 { 945 {
919 qDebug("Evaluating detection of %s against %s", qPrintable(predictedGallery), qPrintable(truthGallery)); 946 qDebug("Evaluating detection of %s against %s", qPrintable(predictedGallery), qPrintable(truthGallery));
920 // Organized by file, QMap used to preserve order 947 // Organized by file, QMap used to preserve order
@@ -923,12 +950,13 @@ float EvalDetection(const QString &amp;predictedGallery, const QString &amp;truthGallery @@ -923,12 +950,13 @@ float EvalDetection(const QString &amp;predictedGallery, const QString &amp;truthGallery
923 // Remove any bounding boxes with a side smaller than minSize 950 // Remove any bounding boxes with a side smaller than minSize
924 if (minSize > 0) { 951 if (minSize > 0) {
925 qDebug("Removing boxes smaller than %d\n", minSize); 952 qDebug("Removing boxes smaller than %d\n", minSize);
  953 + QMap<QString, Detections> allFilteredDetections;
926 foreach (QString key, allDetections.keys()) { 954 foreach (QString key, allDetections.keys()) {
927 Detections detections = allDetections[key]; 955 Detections detections = allDetections[key];
928 Detections filteredDetections; 956 Detections filteredDetections;
929 for (int i = 0; i < detections.predicted.size(); i++) { 957 for (int i = 0; i < detections.predicted.size(); i++) {
930 QRectF box = detections.predicted[i].boundingBox; 958 QRectF box = detections.predicted[i].boundingBox;
931 - if (min(box.width(), box.height()) > minSize) { 959 + if (min(box.width(), box.height()) > sqrt(0.5 * pow(minSize, 2))) {
932 filteredDetections.predicted.append(detections.predicted[i]); 960 filteredDetections.predicted.append(detections.predicted[i]);
933 } 961 }
934 } 962 }
@@ -939,8 +967,34 @@ float EvalDetection(const QString &amp;predictedGallery, const QString &amp;truthGallery @@ -939,8 +967,34 @@ float EvalDetection(const QString &amp;predictedGallery, const QString &amp;truthGallery
939 filteredDetections.truth.append(detections.truth[i]); 967 filteredDetections.truth.append(detections.truth[i]);
940 } 968 }
941 } 969 }
942 - allDetections.insert(key, filteredDetections); 970 + if (!filteredDetections.truth.empty()) allFilteredDetections[key] = filteredDetections;
  971 + }
  972 + allDetections = allFilteredDetections;
  973 + }
  974 +
  975 + // Remove any bounding boxes with no side smaller than maxSize
  976 + if (maxSize > 0) {
  977 + qDebug("Removing boxes larger than %d\n", maxSize);
  978 + QMap<QString, Detections> allFilteredDetections;
  979 + foreach (QString key, allDetections.keys()) {
  980 + Detections detections = allDetections[key];
  981 + Detections filteredDetections;
  982 + for (int i = 0; i < detections.predicted.size(); i++) {
  983 + QRectF box = detections.predicted[i].boundingBox;
  984 + if (min(box.width(), box.height()) < sqrt(0.5 * pow(maxSize, 2))) {
  985 + filteredDetections.predicted.append(detections.predicted[i]);
  986 + }
  987 + }
  988 +
  989 + for (int i = 0; i < detections.truth.size(); i++) {
  990 + QRectF box = detections.truth[i].boundingBox;
  991 + if (min(box.width(), box.height()) < maxSize) {
  992 + filteredDetections.truth.append(detections.truth[i]);
  993 + }
  994 + }
  995 + if (!filteredDetections.truth.empty()) allFilteredDetections[key] = filteredDetections;
943 } 996 }
  997 + allDetections = allFilteredDetections;
944 } 998 }
945 999
946 QList<ResolvedDetection> resolvedDetections, falseNegativeDetections; 1000 QList<ResolvedDetection> resolvedDetections, falseNegativeDetections;
@@ -963,8 +1017,8 @@ float EvalDetection(const QString &amp;predictedGallery, const QString &amp;truthGallery @@ -963,8 +1017,8 @@ float EvalDetection(const QString &amp;predictedGallery, const QString &amp;truthGallery
963 std::sort(resolvedDetections.begin(), resolvedDetections.end()); 1017 std::sort(resolvedDetections.begin(), resolvedDetections.end());
964 QStringList lines; 1018 QStringList lines;
965 lines.append("Plot, X, Y"); 1019 lines.append("Plot, X, Y");
966 - lines.append(computeDetectionResults(resolvedDetections, totalTrueDetections, getNumberOfImages(truthGallery), true));  
967 - lines.append(computeDetectionResults(resolvedDetections, totalTrueDetections, getNumberOfImages(truthGallery), false)); 1020 + lines.append(computeDetectionResults(resolvedDetections, totalTrueDetections, getNumberOfImages(allDetections), true));
  1021 + lines.append(computeDetectionResults(resolvedDetections, totalTrueDetections, getNumberOfImages(allDetections), false));
968 1022
969 float averageOverlap; 1023 float averageOverlap;
970 { // Overlap Density 1024 { // Overlap Density
openbr/core/eval.h
@@ -30,7 +30,7 @@ namespace br @@ -30,7 +30,7 @@ namespace br
30 float InplaceEval(const QString & simmat, const QString & target, const QString & query, const QString & csv = ""); 30 float InplaceEval(const QString & simmat, const QString & target, const QString & query, const QString & csv = "");
31 31
32 void EvalClassification(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = ""); 32 void EvalClassification(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = "");
33 - float EvalDetection(const QString &predictedGallery, const QString &truthGallery, const QString &csv = "", bool normalize = false, int minSize = 0); // Return average overlap 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); // Return average error
35 void EvalRegression(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = ""); 35 void EvalRegression(const QString &predictedGallery, const QString &truthGallery, QString predictedProperty = "", QString truthProperty = "");
36 } 36 }
openbr/openbr.cpp
@@ -129,9 +129,9 @@ void br_eval_clustering(const char *csv, const char *gallery, const char *truth_ @@ -129,9 +129,9 @@ void br_eval_clustering(const char *csv, const char *gallery, const char *truth_
129 EvalClustering(csv, gallery, truth_property); 129 EvalClustering(csv, gallery, truth_property);
130 } 130 }
131 131
132 -float br_eval_detection(const char *predicted_gallery, const char *truth_gallery, const char *csv, bool normalize, int minSize) 132 +float br_eval_detection(const char *predicted_gallery, const char *truth_gallery, const char *csv, bool normalize, int minSize, int maxSize)
133 { 133 {
134 - return EvalDetection(predicted_gallery, truth_gallery, csv, normalize, minSize); 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)
openbr/openbr.h
@@ -205,7 +205,7 @@ BR_EXPORT void br_eval_clustering(const char *csv, const char *gallery, const ch @@ -205,7 +205,7 @@ BR_EXPORT void br_eval_clustering(const char *csv, const char *gallery, const ch
205 * \param normalize Optional \c bool flag to normalize predicted bounding boxes for improved detection. 205 * \param normalize Optional \c bool flag to normalize predicted bounding boxes for improved detection.
206 * \return Average detection bounding box overlap. 206 * \return Average detection bounding box overlap.
207 */ 207 */
208 -BR_EXPORT float br_eval_detection(const char *predicted_gallery, const char *truth_gallery, const char *csv = "", bool normalize = false, int minSize = 0); 208 +BR_EXPORT float br_eval_detection(const char *predicted_gallery, const char *truth_gallery, const char *csv = "", bool normalize = false, int minSize = 0, int maxSize = 0);
209 209
210 /*! 210 /*!
211 * \brief Evaluates and prints landmarking accuracy to terminal. 211 * \brief Evaluates and prints landmarking accuracy to terminal.