Переглянути джерело

Performance tweaks

customisations
alemart 6 місяці тому
джерело
коміт
0e81ac7dbd
1 змінених файлів з 66 додано та 138 видалено
  1. 66
    138
      src/geometry/pnp.ts

+ 66
- 138
src/geometry/pnp.ts Переглянути файл

@@ -836,40 +836,16 @@ export function solvePlanarPnPRansac(referencePoints: SpeedyMatrix, observedPoin
836 836
     if(n < 4)
837 837
         throw new IllegalArgumentError('solvePlanarPnP requires at least 4 points');
838 838
 
839
+    const K = mat3(cameraIntrinsics.read()), E = mat3x4(0);
839 840
     const mask = new Array<number>(n);
840 841
     mask.fill(0);
841 842
 
842
-    const referencePointsEntries = referencePoints.read();
843
-    const observedPointsEntries = observedPoints.read();
844
-    const inliers = new Array<number>(n);
843
+    const src = referencePoints.read();
844
+    const dest = observedPoints.read();
845 845
     const permutation = Utils.range(n); // vector of indices
846 846
 
847
-    //const p = new Array<number>(2*n), q = new Array<number>(2*n);
848
-    //let mp = Speedy.Matrix.Zeros(2, n), mq = Speedy.Matrix.Zeros(2, n);
849 847
     const p = new Array<number>(2*4), q = new Array<number>(2*4);
850 848
     let mp = Speedy.Matrix.Zeros(2, 4), mq = Speedy.Matrix.Zeros(2, 4);
851
-    const reorderPoints = (permutation: number[], n: number) => {
852
-        // resize the matrices to change the number of points
853
-        //p.length = q.length = 2*n;
854
-        //Utils.assert(n == 4);
855
-
856
-        // reorder elements according to the permutation
857
-        for(let j = 0; j < n; j++) {
858
-            const k = permutation[j];
859
-            for(let a = 0; a < 2; a++) {
860
-                p[j*2 + a] = referencePointsEntries[k*2 + a];
861
-                q[j*2 + a] = observedPointsEntries[k*2 + a];
862
-            }
863
-        }
864
-
865
-        // entries are changed and matrices may be resized
866
-        //mp = Speedy.Matrix(2, n, p);
867
-        //mq = Speedy.Matrix(2, n, q);
868
-
869
-        // matrices may NOT be resized
870
-        mp.data.set(p);
871
-        mq.data.set(q);
872
-    };
873 849
 
874 850
     let bestError = Number.POSITIVE_INFINITY;
875 851
     let bestPose = INVALID_POSE;
@@ -879,16 +855,15 @@ export function solvePlanarPnPRansac(referencePoints: SpeedyMatrix, observedPoin
879 855
         // Preprocessing
880 856
         // Set p = shuffled referencePoints and q = shuffled observedPoints
881 857
         Utils.shuffle(permutation);
882
-        reorderPoints(permutation, 4);
858
+        reorderPoints4(mp, mq, p, q, src, dest, permutation);
883 859
 
884 860
         // Generate a model with 4 points only
885 861
         const pose = solvePlanarPnP(mp, mq, cameraIntrinsics);
886
-        const homography = buildHomography(cameraIntrinsics, pose);
862
+        E.set(pose.read()); //E.set(pose.data);
863
+        const homography = buildHomography(K, E);
887 864
 
888 865
         // Evaluate the model against the entire dataset
889
-        //reorderPoints(permutation, n);
890
-        //const error = computeReprojectionError(homography, p, q, threshold, mask);
891
-        const error = computeReprojectionError(homography, referencePointsEntries, observedPointsEntries, threshold, mask);
866
+        const error = computeReprojectionError(homography, src, dest, threshold, mask);
892 867
 
893 868
         /*
894 869
         DEBUG && print({
@@ -901,13 +876,10 @@ export function solvePlanarPnPRansac(referencePoints: SpeedyMatrix, observedPoin
901 876
         });
902 877
         */
903 878
 
904
-        // Count and collect the inliers
879
+        // Count the inliers
905 880
         let count = 0;
906
-        for(let j = 0; j < n; j++) {
907
-            if(mask[j] != 0)
908
-                inliers[count++] = permutation[j];
909
-                //inliers[count++] = j;
910
-        }
881
+        for(let j = 0; j < n; j++)
882
+            count += mask[j];
911 883
 
912 884
         /*
913 885
         DEBUG && print({
@@ -923,18 +895,10 @@ export function solvePlanarPnPRansac(referencePoints: SpeedyMatrix, observedPoin
923 895
         // Insufficient inliers? Discard the model
924 896
         if(count < 4)
925 897
             continue;
926
-
927
-        // Generate a new model with all the inliers
928
-        reorderPoints(inliers, count);
929
-        const newPose = solvePlanarPnP(mp, mq, cameraIntrinsics);
930
-        const newHomography = buildHomography(cameraIntrinsics, newPose);
931
-
932
-        // Evaluate the new model against the set of inliers
933
-        const newError = computeReprojectionError(newHomography, p, q, threshold, mask);
934 898
         */
935 899
 
936
-        // If we generate a new model with all the inliers, we may end up with
937
-        // a bad normal vector, which would adversely affect the entire pose.
900
+        // We don't generate a new model with all the inliers. We can expect a
901
+        // coarse estimate of the intrinsics K. The model will need refinement.
938 902
         // We're just using n = 4. We'll refine the homography later.
939 903
         const newPose = pose;
940 904
         const newError = error;
@@ -1011,7 +975,9 @@ export function find6DoFHomography(referencePoints: SpeedyMatrix, observedPoints
1011 975
     */
1012 976
 
1013 977
     // build an initial homography
1014
-    const hom = buildHomography(cameraIntrinsics, pose);
978
+    const K = mat3(cameraIntrinsics.read());
979
+    const E = mat3x4(pose.read());
980
+    const hom = buildHomography(K, E);
1015 981
     const entries = Array.from<number>(hom);
1016 982
     const homography = Speedy.Matrix(3, 3, entries);
1017 983
 
@@ -1044,79 +1010,6 @@ export function find6DoFHomography(referencePoints: SpeedyMatrix, observedPoints
1044 1010
     );
1045 1011
 }
1046 1012
 
1047
-/*
1048
-// refine homography. pass only inliers!
1049
-export function refineHomography(homography: SpeedyMatrix, referencePoints: SpeedyMatrix, observedPoints: SpeedyMatrix): SpeedyMatrix
1050
-{
1051
-    //return homography;
1052
-    const n = referencePoints.columns;
1053
-    const P = referencePoints.read();
1054
-    const Q = observedPoints.read();
1055
-    const h = mat3(0);
1056
-    const grad = mat3(0); // or 9x1 vector
1057
-    const delta = mat3(0);
1058
-    const rate = 0.002;
1059
-    const threshold = 1e-4;
1060
-    const maxIterations = 100;
1061
-    const thr2 = threshold * threshold;
1062
-    let it = 0, error = 0;
1063
-
1064
-    h.set(homography.read());
1065
-
1066
-    // gradient descent
1067
-    // TODO Gauss-Newton / OLS ?
1068
-    for(it = 0; it < maxIterations; it++) {
1069
-
1070
-        error = 0;
1071
-        grad.fill(0);
1072
-
1073
-        for(let j = 0; j < n; j += 2) {
1074
-            const px = P[j], py = P[j+1], qx = Q[j], qy = Q[j+1];
1075
-            const num1 = h[0] * px + h[3] * py + h[6];
1076
-            const num2 = h[1] * px + h[4] * py + h[7];
1077
-            const den = h[2] * px + h[5] * py + h[8];
1078
-            const den2 = den * den;
1079
-            const dx = num1 / den - qx;
1080
-            const dy = num2 / den - qy;
1081
-
1082
-            grad[0] += dx * (px / den);
1083
-            grad[1] += dy * (px / den);
1084
-            grad[2] += (dx * num1 + dy * num2) * px / den2;
1085
-            grad[3] += dx * (py / den);
1086
-            grad[4] += dy * (py / den);
1087
-            grad[5] += (dx * num1 + dy * num2) * py / den2;
1088
-            grad[6] += dx / den;
1089
-            grad[7] += dy / den;
1090
-            grad[8] += (dx * num1 + dy * num2) / den2;
1091
-
1092
-            error += dx*dx + dy*dy;
1093
-        }
1094
-
1095
-        grad[2] = -grad[2];
1096
-        grad[5] = -grad[5];
1097
-        grad[8] = -grad[8];
1098
-        error *= 0.5 / n;
1099
-
1100
-        if(error < thr2)
1101
-            break;
1102
-
1103
-        scale(delta, grad, rate);
1104
-        sub(h, h, delta);
1105
-
1106
-    }
1107
-
1108
-    DEBUG && print({
1109
-        refineHomography: {
1110
-            iterations: it,
1111
-            error: Math.sqrt(error),
1112
-            grad: Math.sqrt(grad.reduce((s,x) => s+x*x, 0)),
1113
-        }
1114
-    });
1115
-
1116
-    return Speedy.Matrix(3, 3, Array.from(h));
1117
-}
1118
-*/
1119
-
1120 1013
 
1121 1014
 
1122 1015
 
@@ -1129,13 +1022,12 @@ export function refineHomography(homography: SpeedyMatrix, referencePoints: Spee
1129 1022
 
1130 1023
 /**
1131 1024
  * Build a homography matrix
1132
- * @param cameraIntrinsics 3x3 intrinsics
1133
- * @param cameraExtrinsics 3x4 extrinsics
1025
+ * @param K 3x3 intrinsics
1026
+ * @param E 3x4 extrinsics
1134 1027
  * @returns 3x3 homography as a TinyMatrix
1135 1028
  */
1136
-function buildHomography(cameraIntrinsics: SpeedyMatrix, cameraExtrinsics: SpeedyMatrix): TinyMatrix
1029
+function buildHomography(K: TinyMatrix, E: TinyMatrix): TinyMatrix
1137 1030
 {
1138
-    const K = mat3(cameraIntrinsics.read());
1139 1031
     const fx = K[0], fy = K[4], cx = K[6], cy = K[7];
1140 1032
     const z0 = getZ0(fx);
1141 1033
 
@@ -1173,7 +1065,6 @@ function buildHomography(cameraIntrinsics: SpeedyMatrix, cameraExtrinsics: Speed
1173 1065
     A4[15] = 1;
1174 1066
 
1175 1067
     // compose with the pose. The result M is 3x4
1176
-    const E = mat3x4(cameraExtrinsics.read());
1177 1068
     mul(M, E, A4);
1178 1069
 
1179 1070
     // remove the third column from the extrinsics matrix
@@ -1204,17 +1095,17 @@ function buildHomography(cameraIntrinsics: SpeedyMatrix, cameraExtrinsics: Speed
1204 1095
 /**
1205 1096
  * Given a homography matrix and n correspondences (pi, qi), compute the reprojection error
1206 1097
  * @param homography 3x3 homography matrix
1207
- * @param p reference points as a 2 x n matrix
1208
- * @param q observed points as a 2 x n matrix
1098
+ * @param src reference points as a 2 x n matrix
1099
+ * @param dest observed points as a 2 x n matrix
1209 1100
  * @param threshold for a point to be considered an outlier
1210 1101
  * @param mask inliers mask (output)
1211 1102
  * @returns a measure of the error (less is better)
1212 1103
  */
1213
-function computeReprojectionError(homography: TinyMatrix, p: number[], q: number[], threshold: number = 3, mask: number[] = []): number
1104
+function computeReprojectionError(homography: TinyMatrix, src: number[], dest: number[], threshold: number = 3, mask: number[] = []): number
1214 1105
 {
1215 1106
     const [ h11, h21, h31, h12, h22, h32, h13, h23, h33 ] = homography;
1216 1107
     const [ ih11, ih21, ih31, ih12, ih22, ih32, ih13, ih23, ih33 ] = inverse(invH, homography);
1217
-    const n = p.length / 2;
1108
+    const n = src.length / 2;
1218 1109
     const thr2 = threshold * threshold;
1219 1110
     let totalError = 0;
1220 1111
 
@@ -1227,12 +1118,12 @@ function computeReprojectionError(homography: TinyMatrix, p: number[], q: number
1227 1118
 
1228 1119
     // for each point correspondence (p,q)
1229 1120
     for(let i = 0, j = 0; i < n; i++, j += 2) {
1230
-        const px = p[j+0];
1231
-        const py = p[j+1];
1121
+        const px = src[j+0];
1122
+        const py = src[j+1];
1232 1123
         const pz = 1; // homogeneous
1233 1124
 
1234
-        const qx = q[j+0];
1235
-        const qy = q[j+1];
1125
+        const qx = dest[j+0];
1126
+        const qy = dest[j+1];
1236 1127
         const qz = 1;
1237 1128
 
1238 1129
         // compute the reprojection error H*p - q
@@ -1259,8 +1150,7 @@ function computeReprojectionError(homography: TinyMatrix, p: number[], q: number
1259 1150
 
1260 1151
         // accumulate
1261 1152
         totalError += error2 + ierror2;
1262
-        if(error2 < thr2 && ierror2 < thr2)
1263
-            mask[i] = 1; // this is an inlier
1153
+        mask[i] = +(error2 < thr2 && ierror2 < thr2); // 1 if this is an inlier
1264 1154
 
1265 1155
         /*
1266 1156
         DEBUG && mask[i] && print({
@@ -1284,7 +1174,8 @@ function computeReprojectionError(homography: TinyMatrix, p: number[], q: number
1284 1174
     }
1285 1175
 
1286 1176
     // return the average error
1287
-    return Math.sqrt(totalError / n);
1177
+    //return Math.sqrt(totalError / n);
1178
+    return totalError / n;
1288 1179
 }
1289 1180
 
1290 1181
 /**
@@ -1491,6 +1382,43 @@ function getZ0(fx: number, targetWidthInMeters: number = DEFAULT_TARGET_WIDTH_IN
1491 1382
     return z0;
1492 1383
 }
1493 1384
 
1385
+/**
1386
+ * Reorder n = 4 points according to a permutation
1387
+ * @param mp output
1388
+ * @param mq output
1389
+ * @param p temporary
1390
+ * @param q temporary
1391
+ * @param src reference points as a 2 x n matrix
1392
+ * @param dest observed points as a 2 x n matrix
1393
+ * @param permutation permutation of n indices
1394
+ */
1395
+function reorderPoints4(mp: SpeedyMatrix, mq: SpeedyMatrix, p: number[], q: number[], src: number[], dest: number[], permutation: number[])
1396
+{
1397
+    const n = 4;
1398
+
1399
+    // validate
1400
+    Utils.assert(
1401
+        permutation.length >= n &&
1402
+        p.length == n*2 && q.length == n*2 &&
1403
+        mp.rows == 2 && mp.columns == n &&
1404
+        mq.rows == 2 && mq.columns == n
1405
+    );
1406
+
1407
+    // reorder elements according to the permutation
1408
+    for(let j = 0; j < n; j++) {
1409
+        const k = permutation[j];
1410
+        for(let a = 0; a < 2; a++) {
1411
+            p[j*2 + a] = src[k*2 + a];
1412
+            q[j*2 + a] = dest[k*2 + a];
1413
+        }
1414
+    }
1415
+
1416
+    // matrices may NOT be resized
1417
+    mp.data.set(p);
1418
+    mq.data.set(q);
1419
+}
1420
+
1421
+
1494 1422
 
1495 1423
 
1496 1424
 

Завантаження…
Відмінити
Зберегти