瀏覽代碼

Make find6DofHomography() return a promise that resolves to a refined homography

customisations
alemart 6 月之前
父節點
當前提交
0a0146e8b2
共有 2 個檔案被更改,包括 100 行新增80 行删除
  1. 69
    10
      src/geometry/pnp.ts
  2. 31
    70
      src/trackers/image-tracker/image-tracker-utils.ts

+ 69
- 10
src/geometry/pnp.ts 查看文件

@@ -430,6 +430,7 @@ as I explained earlier. Now we've found (R,t) with a quick analytic solution!
430 430
 
431 431
 import Speedy from 'speedy-vision';
432 432
 import { SpeedyMatrix } from 'speedy-vision/types/core/speedy-matrix';
433
+import { SpeedyPromise } from 'speedy-vision/types/core/speedy-promise';
433 434
 import { Utils, Nullable } from '../utils/utils';
434 435
 import { IllegalArgumentError } from '../utils/errors';
435 436
 
@@ -502,14 +503,28 @@ export interface solvePlanarPnPRansacOptions
502 503
     mask?: Nullable<SpeedyMatrix>;
503 504
 }
504 505
 
506
+/** Options for find6DoFHomography() */
507
+export interface find6DoFHomographyOptions extends solvePlanarPnPRansacOptions
508
+{
509
+    /** quality of the refinement: a value in [0,1] */
510
+    refinementQuality?: number;
511
+}
512
+
505 513
 /** Default options for the RANSAC scheme */
506 514
 const DEFAULT_RANSAC_OPTIONS: Required<solvePlanarPnPRansacOptions> = {
507
-    numberOfHypotheses: 512,
515
+    numberOfHypotheses: 100,
508 516
     reprojectionError: 3,
509 517
     acceptablePercentageOfInliers: Number.POSITIVE_INFINITY, // never exit early
510 518
     mask: null,
511 519
 };
512 520
 
521
+/** Default options for find6DoFHomography() */
522
+const DEFAULT_FIND6DOFHOMOGRAPHY_OPTIONS: Required<find6DoFHomographyOptions> = Object.assign(
523
+    {}, DEFAULT_RANSAC_OPTIONS, {
524
+        refinementQuality: 1.0
525
+    }
526
+);
527
+
513 528
 
514 529
 
515 530
 
@@ -960,26 +975,70 @@ export function solvePlanarPnPRansac(referencePoints: SpeedyMatrix, observedPoin
960 975
  * @param referencePoints 2 x n matrix with reference points in a plane parallel to the image plane (Z = z0 > 0)
961 976
  * @param observedPoints 2 x n matrix with the observed points corresponding to the reference points
962 977
  * @param cameraIntrinsics 3x3 matrix
963
- * @param options RANSAC options
978
+ * @param options options
964 979
  * @returns 3x3 homography matrix or a matrix of NaNs if no suitable homography is found
965 980
  */
966
-export function find6DoFHomography(referencePoints: SpeedyMatrix, observedPoints: SpeedyMatrix, cameraIntrinsics: SpeedyMatrix, options: solvePlanarPnPRansacOptions = {}): SpeedyMatrix
981
+export function find6DoFHomography(referencePoints: SpeedyMatrix, observedPoints: SpeedyMatrix, cameraIntrinsics: SpeedyMatrix, options: find6DoFHomographyOptions = {}): SpeedyPromise<SpeedyMatrix>
967 982
 {
968
-    const extrinsics = solvePlanarPnPRansac(referencePoints, observedPoints, cameraIntrinsics, options);
969
-    const homography = buildHomography(cameraIntrinsics, extrinsics);
970
-    const entries = Array.from<number>(homography);
983
+    const n = referencePoints.columns;
984
+    const settings = Object.assign({}, DEFAULT_FIND6DOFHOMOGRAPHY_OPTIONS, options);
985
+    const refinementQuality = Math.max(0, Math.min(settings.refinementQuality, 1));
986
+    const reprojectionError = settings.reprojectionError;
987
+    const mask = settings.mask;
988
+
989
+    // validate
990
+    if(n < 4)
991
+        return Speedy.Promise.reject(new IllegalArgumentError('find6DofHomography() requires at least 4 points'));
992
+    else if(referencePoints.columns != observedPoints.columns || referencePoints.rows != 2 || observedPoints.rows != 2)
993
+        return Speedy.Promise.reject(new IllegalArgumentError('Bad input'));
994
+    else if(cameraIntrinsics.columns != 3 || cameraIntrinsics.rows != 3)
995
+        return Speedy.Promise.reject(new IllegalArgumentError('Bad intrinsics'));
996
+
997
+    // find a pose. 6DoF: 3 for rotation + 3 for translation
998
+    const pose = solvePlanarPnPRansac(referencePoints, observedPoints, cameraIntrinsics, options);
971 999
 
972 1000
     /*
973 1001
     DEBUG && print({
974 1002
         find6DoFHomography: {
975
-            R: extrinsics.block(0, 2, 0, 2).toString(),
976
-            t: extrinsics.block(0, 2, 3, 3).toString(),
977
-            //q: new Quaternion()._fromRotationMatrix(extrinsics.block(0, 2, 0, 2)).toString(),
1003
+            R: pose.block(0, 2, 0, 2).toString(),
1004
+            t: pose.block(0, 2, 3, 3).toString(),
1005
+            //q: new Quaternion()._fromRotationMatrix(pose.block(0, 2, 0, 2)).toString(),
978 1006
         }
979 1007
     });
980 1008
     */
981 1009
 
982
-    return Speedy.Matrix(3, 3, entries);
1010
+    // build an initial homography
1011
+    const hom = buildHomography(cameraIntrinsics, pose);
1012
+    const entries = Array.from<number>(hom);
1013
+    const homography = Speedy.Matrix(3, 3, entries);
1014
+
1015
+    // quit without refinement (for testing purposes)
1016
+    // we can expect a coarse estimate of the camera intrinsics
1017
+    if(refinementQuality == 0 || Number.isNaN(entries[0]))
1018
+        return Speedy.Promise.resolve(homography);
1019
+
1020
+    // refine the homography with DLT + RANSAC
1021
+    const src = referencePoints, dest = observedPoints;
1022
+    const intermediate = Speedy.Matrix.Zeros(2, n);
1023
+
1024
+    return Speedy.Matrix.applyPerspectiveTransform(intermediate, src, homography)
1025
+    .then(intermediate =>
1026
+        Speedy.Matrix.findHomography(
1027
+            Speedy.Matrix.Zeros(3),
1028
+            intermediate,
1029
+            dest,
1030
+            {
1031
+                method: 'pransac',
1032
+                numberOfHypotheses: Math.ceil(512 * refinementQuality), // XXX we can reduce this number without compromising quality
1033
+                bundleSize: Math.ceil(128 * refinementQuality),
1034
+                reprojectionError: reprojectionError,
1035
+                mask,
1036
+            }
1037
+        )
1038
+    )
1039
+    .then(adjustment =>
1040
+        adjustment.setTo(adjustment.times(homography))
1041
+    );
983 1042
 }
984 1043
 
985 1044
 /*

+ 31
- 70
src/trackers/image-tracker/image-tracker-utils.ts 查看文件

@@ -32,7 +32,7 @@ import { ReferenceImageWithMedia } from './reference-image';
32 32
 import { Utils } from '../../utils/utils';
33 33
 import { IllegalOperationError, IllegalArgumentError, NumericalError } from '../../utils/errors';
34 34
 import { NIS_SIZE, TRACK_GRID_GRANULARITY } from './settings';
35
-import { find6DoFHomography, solvePlanarPnPRansacOptions } from '../../geometry/pnp';
35
+import { find6DoFHomography, find6DoFHomographyOptions } from '../../geometry/pnp';
36 36
 
37 37
 /*
38 38
 
@@ -231,10 +231,10 @@ export class ImageTrackerUtils
231 231
      * find a 6 DoF perspective warp (homography) from src to dest in NDC
232 232
      * @param cameraIntrinsics 3x3 camera intrinsics
233 233
      * @param points compiled pairs of keypoints in NDC
234
-     * @param options to be passed to pnp
234
+     * @param options to be passed to find6DofHomography
235 235
      * @returns a pair [ 3x3 transformation matrix, quality score ]
236 236
      */
237
-    static find6DoFHomographyNDC(cameraIntrinsics: SpeedyMatrix, points: SpeedyMatrix, options: solvePlanarPnPRansacOptions): SpeedyPromise<[SpeedyMatrix,number]>
237
+    static find6DoFHomographyNDC(cameraIntrinsics: SpeedyMatrix, points: SpeedyMatrix, options: find6DoFHomographyOptions): SpeedyPromise<[SpeedyMatrix,number]>
238 238
     {
239 239
         // too few data points?
240 240
         const n = points.columns / 2;
@@ -247,34 +247,19 @@ export class ImageTrackerUtils
247 247
         // compute a homography
248 248
         const src = points.block(0, 1, 0, n-1);
249 249
         const dest = points.block(0, 1, n, 2*n-1);
250
-        const homography = find6DoFHomography(src, dest, cameraIntrinsics, options);
251
-        //console.log('homography',homography.toString(), src.toString(), dest.toString());
252
-
253
-        // quit without refinement (test)
254
-        // we use a coarse estimate of the camera intrinsics
255
-        //return Speedy.Promise.resolve([homography, 0]);
256
-
257
-        const mask = Speedy.Matrix.Zeros(1, n);
258
-        const intermediate = Speedy.Matrix.Zeros(2, n);
259
-
260
-        // refine the result of find6DoFHomography() with DLT + RANSAC
261
-        return Speedy.Matrix.applyPerspectiveTransform(intermediate, src, homography)
262
-        .then(intermediate =>
263
-            Speedy.Matrix.findHomography(
264
-                Speedy.Matrix.Zeros(3),
265
-                intermediate,
266
-                dest,
267
-                {
268
-                    method: 'pransac',
269
-                    numberOfHypotheses: 512, // XXX we can reduce this number without compromising quality
270
-                    bundleSize: 128, // maybe make it a parameter in case we need an extra performance boost?
271
-                    reprojectionError: options.reprojectionError,
272
-                    mask,
273
-                }
274
-            )
275
-        )
276
-        .then(adjustment => adjustment.setTo(adjustment.times(homography)))
277
-        .then(newHomography => {
250
+        const mask = options.mask || Speedy.Matrix.Zeros(1, n);
251
+
252
+        return find6DoFHomography(
253
+            src,
254
+            dest,
255
+            cameraIntrinsics,
256
+            Object.assign({ mask }, options)
257
+        ).then(homography => {
258
+
259
+            // check if this is a valid homography
260
+            const a00 = homography.at(0,0);
261
+            if(Number.isNaN(a00))
262
+                throw new NumericalError(`Can't compute a perspective warp: bad keypoints`);
278 263
 
279 264
             // count inliers
280 265
             let m = 0;
@@ -282,33 +267,9 @@ export class ImageTrackerUtils
282 267
             for(let i = 0; i < n; i++)
283 268
                 m += inliers[i];
284 269
 
285
-            /*
286
-            // count and collect inliers
287
-            let m = 0;
288
-            const _mask = mask.read(), _src = src.read(), _dest = dest.read();
289
-            const _isrc = new Array<number>(2*n), _idest = new Array<number>(2*n);
290
-            for(let i = 0; i < n; i++) {
291
-                if(_mask[i]) {
292
-                    const j = m++;
293
-                    _isrc[2*j] = _src[2*i];
294
-                    _isrc[2*j+1] = _src[2*i+1];
295
-                    _idest[2*j] = _dest[2*i];
296
-                    _idest[2*j+1] = _dest[2*i+1];
297
-                }
298
-            }
299
-            _isrc.length = _idest.length = 2*m;
300
-
301
-            // refine homography
302
-            if(m > 0) {
303
-                const isrc = Speedy.Matrix(2, m, _isrc);
304
-                const idest = Speedy.Matrix(2, m, _idest);
305
-                newHomography = refineHomography(newHomography, isrc, idest);
306
-            }
307
-            */
308
-
309 270
             // done!
310 271
             const score = m / n;
311
-            return [newHomography, score];
272
+            return [homography, score];
312 273
 
313 274
         });
314 275
     }
@@ -333,7 +294,7 @@ export class ImageTrackerUtils
333 294
         // compute a homography
334 295
         const src = points.block(0, 1, 0, n-1);
335 296
         const dest = points.block(0, 1, n, 2*n-1);
336
-        const mask = Speedy.Matrix.Zeros(1, n);
297
+        const mask = ((options as any).mask as SpeedyMatrix | null | undefined) || Speedy.Matrix.Zeros(1, n);
337 298
 
338 299
         return Speedy.Matrix.findHomography(
339 300
             Speedy.Matrix.Zeros(3),
@@ -347,15 +308,15 @@ export class ImageTrackerUtils
347 308
             if(Number.isNaN(a00))
348 309
                 throw new NumericalError(`Can't compute a perspective warp: bad keypoints`);
349 310
 
350
-            // count the number of inliers
311
+            // count inliers
312
+            let m = 0;
351 313
             const inliers = mask.read();
352
-            let inlierCount = 0;
353
-            for(let i = inliers.length - 1; i >= 0; i--)
354
-                inlierCount += inliers[i];
355
-            const score = inlierCount / inliers.length;
314
+            for(let i = 0; i < n; i++)
315
+                m += inliers[i];
356 316
 
357 317
             // done!
358
-            return [ homography, score ];
318
+            const score = m / n;
319
+            return [homography, score];
359 320
 
360 321
         });
361 322
     }
@@ -382,7 +343,7 @@ export class ImageTrackerUtils
382 343
         const model = Speedy.Matrix.Eye(3);
383 344
         const src = points.block(0, 1, 0, n-1);
384 345
         const dest = points.block(0, 1, n, 2*n-1);
385
-        const mask = Speedy.Matrix.Zeros(1, n);
346
+        const mask = ((options as any).mask as SpeedyMatrix | null | undefined) || Speedy.Matrix.Zeros(1, n);
386 347
 
387 348
         return Speedy.Matrix.findAffineTransform(
388 349
             model.block(0, 1, 0, 2), // 2x3 submatrix
@@ -396,15 +357,15 @@ export class ImageTrackerUtils
396 357
             if(Number.isNaN(a00))
397 358
                 throw new NumericalError(`Can't compute an affine warp: bad keypoints`);
398 359
 
399
-            // count the number of inliers
360
+            // count inliers
361
+            let m = 0;
400 362
             const inliers = mask.read();
401
-            let inlierCount = 0;
402
-            for(let i = inliers.length - 1; i >= 0; i--)
403
-                inlierCount += inliers[i];
404
-            const score = inlierCount / inliers.length;
363
+            for(let i = 0; i < n; i++)
364
+                m += inliers[i];
405 365
 
406 366
             // done!
407
-            return [ model, score ];
367
+            const score = m / n;
368
+            return [model, score];
408 369
 
409 370
         });
410 371
     }

Loading…
取消
儲存