Commit 1bff5f2f8623f1c1355ca90228940def28ab5944

Authored by Josh Klontz
1 parent 2929d5f1

EvalLandmarking now corrects for systematic landmark bias

openbr/core/eval.cpp
... ... @@ -850,7 +850,7 @@ float EvalLandmarking(const QString &predictedGallery, const QString &truthGalle
850 850 QStringList truthNames = File::get<QString>(truth, "name");
851 851  
852 852 int skipped = 0;
853   - QList< QList<float> > pointErrors;
  853 + QList< QList<float> > pointErrorMagnitudes, pointErrorOrientations;
854 854 QList<float> imageErrors;
855 855 QList<float> normalizedLengths;
856 856 for (int i=0; i<predicted.size(); i++) {
... ... @@ -876,6 +876,7 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle
876 876 if (normalizationIndexA >= truthPoints.size()) qFatal("Normalization index A is out of range.");
877 877 if (normalizationIndexB >= truthPoints.size()) qFatal("Normalization index B is out of range.");
878 878 const float normalizedLength = QtUtils::euclideanLength(truthPoints[normalizationIndexB] - truthPoints[normalizationIndexA]);
  879 + const float normalizedOrientation = QtUtils::orientation(truthPoints[normalizationIndexB], truthPoints[normalizationIndexA]);
879 880  
880 881 if (predictedPoints.size() != truthPoints.size() || qIsNaN(normalizedLength)) {
881 882 predicted.removeAt(i);
... ... @@ -886,8 +887,10 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle
886 887 continue;
887 888 }
888 889  
889   - while (pointErrors.size() < predictedPoints.size())
890   - pointErrors.append(QList<float>());
  890 + while (pointErrorMagnitudes.size() < predictedPoints.size()) {
  891 + pointErrorMagnitudes.append(QList<float>());
  892 + pointErrorOrientations.append(QList<float>());
  893 + }
891 894  
892 895 // Want to know error for every image.
893 896 normalizedLengths.append(normalizedLength);
... ... @@ -897,7 +900,8 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle
897 900 const float error = QtUtils::euclideanLength(predictedPoints[j] - truthPoints[j])/normalizedLength;
898 901 if (!qIsNaN(error)) {
899 902 totalError += error;
900   - pointErrors[j].append(error);
  903 + pointErrorMagnitudes[j].append(error);
  904 + pointErrorOrientations[j].append(QtUtils::orientation(predictedPoints[j], truthPoints[j]) - normalizedOrientation);
901 905 totalCount++;
902 906 }
903 907 }
... ... @@ -906,7 +910,47 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle
906 910  
907 911 qDebug() << "Skipped" << skipped << "files due to point size mismatch or NaN normalized length.";
908 912  
909   - QList<float> averagePointErrors; averagePointErrors.reserve(pointErrors.size());
  913 + // Adjust the point error to not penalize for systematic biases...
  914 + // ... by first calculating the average bias for each point
  915 + QList<QPointF> averagePointBiases;
  916 + for (int i=0; i<pointErrorMagnitudes.size(); i++) {
  917 + const QList<float> &magnitudes = pointErrorMagnitudes[i];
  918 + const QList<float> &orientations = pointErrorOrientations[i];
  919 + QPointF cumulativePointBias;
  920 + for (int j=0; j<magnitudes.size(); j++) {
  921 + const float m = magnitudes[j];
  922 + const float o = orientations[j];
  923 + cumulativePointBias += QPointF(m*cos(o), m*sin(o));
  924 + }
  925 + averagePointBiases.append(cumulativePointBias / magnitudes.size());
  926 + }
  927 +
  928 + // ... and then subtracting the average bias from each individual error.
  929 + for (int i=0; i<pointErrorMagnitudes.size(); i++) {
  930 + QList<float> &magnitudes = pointErrorMagnitudes[i];
  931 + QList<float> &orientations = pointErrorOrientations[i];
  932 + const QPointF &bias = averagePointBiases[i];
  933 + for (int j=0; j<magnitudes.size(); j++) {
  934 + float &m = magnitudes[j];
  935 + float &o = orientations[j];
  936 + QPointF error(m*cos(o), m*sin(o));
  937 + error -= bias;
  938 + // At this point if we added up all the `error` vectors for a
  939 + // landmark they would sum to zero. Josh confirmed this when
  940 + // implementing the bias normalization correction, but removed it
  941 + // from the final implementation.
  942 +
  943 + // Update the error magnitude for reporting MAE
  944 + // m = QtUtils::euclideanLength(error);
  945 +
  946 + // We don't need to update orientation because we don't use it
  947 + // again, but we do so anyway in the interest of pedantic
  948 + // correctness.
  949 + o = QtUtils::orientation(QPointF(0.f,0.f), error);
  950 + }
  951 + }
  952 +
  953 + QList<float> averagePointErrors; averagePointErrors.reserve(pointErrorMagnitudes.size());
910 954  
911 955 QStringList lines;
912 956 lines.append("Plot,X,Y");
... ... @@ -948,8 +992,8 @@ float EvalLandmarking(const QString &amp;predictedGallery, const QString &amp;truthGalle
948 992 lines.append("EXP,"+filePath+":"+predicted[exampleIndices[i].second].file.name+","+QString::number(exampleIndices[i].first));
949 993 }
950 994  
951   - for (int i=0; i<pointErrors.size(); i++) {
952   - QList<float> &pointError = pointErrors[i];
  995 + for (int i=0; i<pointErrorMagnitudes.size(); i++) {
  996 + QList<float> &pointError = pointErrorMagnitudes[i];
953 997 std::sort(pointError.begin(), pointError.end());
954 998 averagePointErrors.append(Common::Mean(pointError));
955 999 const int keep = qMin(Max_Points, pointError.size());
... ...
openbr/core/qtutils.cpp
... ... @@ -462,6 +462,11 @@ float euclideanLength(const QPointF &amp;point)
462 462 return sqrt(pow(point.x(), 2) + pow(point.y(), 2));
463 463 }
464 464  
  465 +float orientation(const QPointF &pointA, const QPointF &pointB)
  466 +{
  467 + return atan2(pointB.y() - pointA.y(), pointB.x() - pointA.x());
  468 +}
  469 +
465 470 float overlap(const QRectF &r, const QRectF &s) {
466 471 QRectF intersection = r & s;
467 472  
... ...
openbr/core/qtutils.h
... ... @@ -90,6 +90,7 @@ namespace QtUtils
90 90  
91 91 /**** Point Utilities ****/
92 92 float euclideanLength(const QPointF &point);
  93 + float orientation(const QPointF &pointA, const QPointF &pointB);
93 94  
94 95 /**** Rect Utilities ****/
95 96 float overlap(const QRectF &r, const QRectF &s);
... ...