Use average pixel distance to measure error

Use the average pixel distance instead of the L2 norm to measure the
error of the Rotation Vector Crosscheck's estimated test device
position.

The use of the L2 norm results in an error value that is difficult to
interpret. For instance, if there are three error values (3, 4, 5),
then the L2 norm produces an error of 7.07. If there is a fourth error
value of 1, the L2 norm produces a larger error of 7.14. Since the
error depends on the number of values being compared, the L2 norm is
not a good metric to determine the accuracy of the test device
position. Instead, a simple average should be used which is able to be
easily interpreted.

Also, update the asserts in Rotation Vector Crosscheck test to
correctly log the expected values if the test fails.

Bug: 112409976
Test: Verified test passes and fails as expected
Change-Id: I6d8ecdfcde392ef436bd5cdf284535501f67af52
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
index 45439a7..c15d2ac 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
@@ -66,7 +66,7 @@
 
     private static final boolean OUTPUT_DEBUG_IMAGE = false;
     private static final double VALID_FRAME_THRESHOLD = 0.8;
-    private static final double REPROJECTION_THRESHOLD_RATIO = 0.03;
+    private static final double REPROJECTION_THRESHOLD_RATIO = 0.01;
     private static final boolean FORCE_CV_ANALYSIS  = false;
     private static final boolean TRACE_VIDEO_ANALYSIS = false;
     private static final double DECIMATION_FPS_TARGET = 15.0;
@@ -880,13 +880,6 @@
             // reproject points to for evaluation of result accuracy of solvePnP
             Calib3d.projectPoints(grid, rvec, tvec, camMat, coeff, reprojCenters);
 
-            // error is evaluated in norm2, which is real error in pixel distance / sqrt(2)
-            double error = Core.norm(centers, reprojCenters, Core.NORM_L2);
-
-            if (LOCAL_LOGV) {
-                Log.v(TAG, "Found attitude, re-projection error = " + error);
-            }
-
             // Calculate the average distance between opposite corners of the pattern in pixels
             Point[] centerPoints = centers.toArray();
             Point bottomLeftPos = centerPoints[0];
@@ -896,10 +889,27 @@
             double avgPixelDist = (getDistanceBetweenPoints(bottomLeftPos, topRightPos)
                     + getDistanceBetweenPoints(bottomRightPos, topLeftPos)) / 2;
 
+            // Calculate the average pixel error between the circle centers from the video and the
+            // reprojected circle centers based on the estimated camera position. The error provides
+            // a way to estimate how accurate the assumed test device's position is. If the error
+            // is high, then the frame should be discarded to prevent an inaccurate test device's
+            // position from being compared against the rotation vector sample at that time.
+            Point[] reprojectedPointsArray = reprojCenters.toArray();
+            double avgCenterError = 0.0;
+            for (int curCenter = 0; curCenter < reprojectedPointsArray.length; curCenter++) {
+                avgCenterError += getDistanceBetweenPoints(
+                        reprojectedPointsArray[curCenter], centerPoints[curCenter]);
+            }
+            avgCenterError /= reprojectedPointsArray.length;
+
+            if (LOCAL_LOGV) {
+                Log.v(TAG, "Found attitude, re-projection error = " + avgCenterError);
+            }
+
             // if error is reasonable, add it into the results. Use a dynamic threshold based on
             // the pixel distance of opposite corners of the pattern to prevent higher resolution
             // video or the distance between the camera and the test pattern from impacting the test
-            if (error < REPROJECTION_THRESHOLD_RATIO * avgPixelDist) {
+            if (avgCenterError < REPROJECTION_THRESHOLD_RATIO * avgPixelDist) {
                 double [] rv = new double[3];
                 double timestamp;
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java
index 789de68..fec593c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java
@@ -301,10 +301,8 @@
 
         String message = "Test Roll Axis Accuracy";
 
-        Assert.assertEquals("Roll RMS error", 0.0, mReport.roll_rms_error,
-                Criterion.roll_rms_error);
-        Assert.assertEquals("Roll max error", 0.0, mReport.roll_max_error,
-                Criterion.roll_max_error);
+        assertLessThan("Roll RMS error", mReport.roll_rms_error, Criterion.roll_rms_error);
+        assertLessThan("Roll max error", mReport.roll_max_error, Criterion.roll_max_error);
         return message;
     }
 
@@ -316,10 +314,8 @@
 
         String message = "Test Pitch Axis Accuracy";
 
-        Assert.assertEquals("Pitch RMS error", 0.0, mReport.pitch_rms_error,
-                Criterion.pitch_rms_error);
-        Assert.assertEquals("Pitch max error", 0.0, mReport.pitch_max_error,
-                Criterion.pitch_max_error);
+        assertLessThan("Pitch RMS error", mReport.pitch_rms_error, Criterion.pitch_rms_error);
+        assertLessThan("Pitch max error", mReport.pitch_max_error, Criterion.pitch_max_error);
         return message;
     }
 
@@ -331,13 +327,16 @@
 
         String message = "Test Yaw Axis Accuracy";
 
-        Assert.assertEquals("Yaw RMS error", 0.0, mReport.yaw_rms_error,
-                Criterion.yaw_rms_error);
-        Assert.assertEquals("Yaw max error", 0.0, mReport.yaw_max_error,
-                Criterion.yaw_max_error);
+        assertLessThan("Yaw RMS error", mReport.yaw_rms_error, Criterion.yaw_rms_error);
+        assertLessThan("Yaw max error", mReport.yaw_max_error, Criterion.yaw_max_error);
         return message;
     }
 
+    private void assertLessThan(String message, double lhs, double rhs) {
+        Assert.assertTrue(String.format("%s - expected %.4f < %.4f", message, lhs, rhs),
+                lhs < rhs);
+    }
+
     private void loadOpenCVSuccessfulOrSkip() throws SensorTestStateNotSupportedException {
         if (!mOpenCVLoadSuccessful)
             throw new SensorTestStateNotSupportedException("Skipped due to OpenCV cannot be loaded");