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

Use a different FOV. Remove old auto-calibration code.

customisations
alemart 1 рік тому
джерело
коміт
4895decdc6
1 змінених файлів з 45 додано та 130 видалено
  1. 45
    130
      src/geometry/camera-model.ts

+ 45
- 130
src/geometry/camera-model.ts Переглянути файл

@@ -30,14 +30,8 @@ import { Nullable, Utils } from '../utils/utils';
30 30
 import { Settings } from '../core/settings';
31 31
 import { IllegalOperationError, IllegalArgumentError } from '../utils/errors';
32 32
 
33
-/** Number of samples we'll be keeping to help calibrate the camera */
34
-const INTRISICS_SAMPLES = 401; //201; //31; // odd number
35
-
36
-/** Whether or not to auto-calibrate the camera */
37
-const FOVY_AUTODETECT = false; //true;
38
-
39
-/** A guess of the vertical field-of-view of a generic camera, in degrees */
40
-const FOVY_GUESS = 45; //50; // will be part of the viewing frustum
33
+/** A guess of the horizontal field-of-view of the camera, in degrees */
34
+const HFOV_GUESS = 60; // https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Cameras/Cameras.html
41 35
 
42 36
 /** Number of iterations used to refine the estimated pose */
43 37
 const POSE_ITERATIONS = 30;
@@ -117,12 +111,6 @@ export class CameraModel
117 111
     /** extrinsics matrix, in column-major format */
118 112
     private _extrinsics: number[];
119 113
 
120
-    /** estimates of the focal length */
121
-    private _f: number[];
122
-
123
-    /** index/pointer of _f[] */
124
-    private _fp: number;
125
-
126 114
     /** filter: samples of partial rotation matrix [ r1 | r2 ] */
127 115
     private _partialRotationBuffer: number[][];
128 116
 
@@ -140,8 +128,6 @@ export class CameraModel
140 128
         this._matrix = Speedy.Matrix.Eye(3, 4);
141 129
         this._intrinsics = [1,0,0,0,1,0,0,0,1]; // identity matrix
142 130
         this._extrinsics = [1,0,0,0,1,0,0,0,1,0,0,0]; // no rotation & no translation [ R | t ] = [ I | 0 ]
143
-        this._f = (new Array(INTRISICS_SAMPLES)).fill(this._intrinsics[FY]);
144
-        this._fp = 0;
145 131
         this._partialRotationBuffer = [];
146 132
         this._translationBuffer = [];
147 133
     }
@@ -218,12 +204,6 @@ export class CameraModel
218 204
             return Speedy.Promise.resolve(this._matrix);
219 205
         }
220 206
 
221
-        // estimate the focal length (auto-calibration)
222
-        const f = this._estimateFocal(homography);
223
-        if(f > 0)
224
-            this._storeFocal(f);
225
-        //console.log(this.fovy * RAD2DEG);
226
-
227 207
         // estimate the pose
228 208
         const pose = this._estimatePose(homography);
229 209
         this._storePose(pose);
@@ -379,98 +359,41 @@ export class CameraModel
379 359
      */
380 360
     private _resetIntrinsics(): void
381 361
     {
362
+        const cameraWidth = Math.max(this._screenSize.width, this._screenSize.height); // portrait?
382 363
         const u0 = this._screenSize.width / 2;
383 364
         const v0 = this._screenSize.height / 2;
384
-        const f = v0 / Math.tan(DEG2RAD * FOVY_GUESS / 2);
365
+        const fx = (cameraWidth / 2) / Math.tan(DEG2RAD * HFOV_GUESS / 2);
366
+        const fy = fx;
385 367
 
386
-        this._intrinsics[FX] = f;
387
-        this._intrinsics[FY] = f;
368
+        this._intrinsics[FX] = fx;
369
+        this._intrinsics[FY] = fy;
388 370
         this._intrinsics[U0] = u0;
389 371
         this._intrinsics[V0] = v0;
390
-
391
-        this._f.fill(this._intrinsics[FY]);
392
-        this._fp = 0;
393
-    }
394
-
395
-    /**
396
-     * Estimate the focal length
397
-     * @param homography valid homography
398
-     * @returns estimated focal length, or 0 on error
399
-     */
400
-    private _estimateFocal(homography: SpeedyMatrix): number
401
-    {
402
-        // auto-detect the focal length?
403
-        if(!FOVY_AUTODETECT)
404
-            return 0;
405
-
406
-        // read the entries of the homography
407
-        const h = homography.read();
408
-        const h11 = h[0], h12 = h[3];//, h13 = h[6];
409
-        const h21 = h[1], h22 = h[4];//, h23 = h[7];
410
-        const h31 = h[2], h32 = h[5];//, h33 = h[8];
411
-
412
-        // read the principal point
413
-        const u0 = this._intrinsics[U0];
414
-        const v0 = this._intrinsics[V0];
415
-
416
-        // estimate the focal length based on the orthogonality
417
-        // constraint r1'r2 = 0 of a rotation matrix
418
-        const f2 = -((h11/h31 - u0) * (h12/h32 - u0) + (h21/h31 - v0) * (h22/h32 - v0));
419
-
420
-        // can't estimate it?
421
-        if(f2 < 0)
422
-            return this._intrinsics[FY];
423
-            //return 0;
424
-
425
-        // done!
426
-        return Math.sqrt(f2);
427
-    }
428
-
429
-    /**
430
-     * Store an estimated focal length
431
-     * @param f estimated focal length
432
-     */
433
-    private _storeFocal(f: number): void
434
-    {
435
-        // store the focal length
436
-        this._f[this._fp] = f;
437
-        this._fp = (this._fp + 1) % INTRISICS_SAMPLES;
438
-
439
-        // take the median of the estimated focal lengths
440
-        const sorted = this._f.concat([]).sort((a, b) => a - b);
441
-        const median = sorted[sorted.length >>> 1];
442
-
443
-        // update the intrinsics matrix
444
-        this._intrinsics[FX] = this._intrinsics[FY] = median;
445
-
446
-        /*
447
-        // test
448
-        const u0 = this._intrinsics[U0];
449
-        const v0 = this._intrinsics[V0];
450
-        const fovx = 2 * Math.atan(u0 / median) * RAD2DEG;
451
-        const fovy = 2 * Math.atan(v0 / median) * RAD2DEG;
452
-        console.log('---------------');
453
-        console.log("fov:",fovx,fovy);
454
-        console.log("f:",median);
455
-        */
456 372
     }
457 373
 
458 374
     /**
459 375
      * Compute a normalized homography H' = K^(-1) * H for an
460 376
      * ideal pinhole with f = 1 and principal point = (0,0)
461 377
      * @param homography homography H to be normalized
462
-     * @param f focal length
463 378
      * @returns normalized homography H'
464 379
      */
465
-    private _normalizeHomography(homography: SpeedyMatrix, f: number = this._intrinsics[FY]): SpeedyMatrix
380
+    private _normalizeHomography(homography: SpeedyMatrix): SpeedyMatrix
466 381
     {
467 382
         const h = homography.read();
468 383
         const u0 = this._intrinsics[U0];
469 384
         const v0 = this._intrinsics[V0];
385
+        const fx = this._intrinsics[FX];
386
+        const fy = this._intrinsics[FY];
470 387
 
471
-        const h11 = h[0] - u0 * h[2], h12 = h[3] - u0 * h[5], h13 = h[6] - u0 * h[8];
472
-        const h21 = h[1] - v0 * h[2], h22 = h[4] - v0 * h[5], h23 = h[7] - v0 * h[8];
473
-        const h31 = h[2] * f, h32 = h[5] * f, h33 = h[8] * f;
388
+        const h11 = (h[0] - u0 * h[2]) / fx, h12 = (h[3] - u0 * h[5]) / fx, h13 = (h[6] - u0 * h[8]) / fx;
389
+        const h21 = (h[1] - v0 * h[2]) / fy, h22 = (h[4] - v0 * h[5]) / fy, h23 = (h[7] - v0 * h[8]) / fy;
390
+        const h31 = h[2], h32 = h[5], h33 = h[8];
391
+
392
+        /*console.log([
393
+            h11, h21, h31,
394
+            h12, h22, h32,
395
+            h13, h23, h33,
396
+        ]);*/
474 397
 
475 398
         return Speedy.Matrix(3, 3, [
476 399
             h11, h21, h31,
@@ -495,12 +418,14 @@ export class CameraModel
495 418
         const sign = h33 >= 0 ? 1 : -1;
496 419
 
497 420
         // compute the scale factor
498
-        const h1norm = Math.sqrt(h11 * h11 + h21 * h21 + h31 * h31);
499
-        const h2norm = Math.sqrt(h12 * h12 + h22 * h22 + h32 * h32);
500
-        //const scale = sign * 2 / (h1norm + h2norm);
501
-        //const scale = sign / h1norm;
502
-        //const scale = sign / h2norm;
503
-        const scale = sign / Math.max(h1norm, h2norm); // this seems to work. why?
421
+        const h1norm2 = h11 * h11 + h21 * h21 + h31 * h31;
422
+        const h2norm2 = h12 * h12 + h22 * h22 + h32 * h32;
423
+        const h1norm = Math.sqrt(h1norm2);
424
+        const h2norm = Math.sqrt(h2norm2);
425
+        //const hnorm = (h1norm + h2norm) / 2;
426
+        //const hnorm = Math.sqrt(h1norm * h2norm);
427
+        const hnorm = Math.max(h1norm, h2norm); // this seems to work. why?
428
+        const scale = sign / hnorm;
504 429
 
505 430
         // invalid homography?
506 431
         if(Number.isNaN(scale))
@@ -515,23 +440,24 @@ export class CameraModel
515 440
         //console.log("h1,h2",h1norm,h2norm);
516 441
         //console.log(normalizedHomography.toString());
517 442
 
518
-        // recover the translation and the rotation
519
-        const t1 = scale * h13;
520
-        const t2 = scale * h23;
521
-        const t3 = scale * h33;
522
-
443
+        // recover the pose
523 444
         const r11 = scale * h11;
524 445
         const r21 = scale * h21;
525 446
         const r31 = scale * h31;
526
-
527 447
         const r12 = scale * h12;
528 448
         const r22 = scale * h22;
529 449
         const r32 = scale * h32;
450
+        const r_ = [r11, r21, r31, r12, r22, r32];
451
+
452
+        const t1 = scale * h13;
453
+        const t2 = scale * h23;
454
+        const t3 = scale * h33;
455
+        const t_ = [t1, t2, t3];
530 456
 
531 457
         // refine the pose
532
-        const r = this._refineRotation(r11, r21, r31, r12, r22, r32);
533
-        const t = this._refineTranslation(normalizedHomography, r, [t1, t2, t3]);
534
-        //const t = [t1, t2, t3]; // faster, but less accurate
458
+        const r = this._refineRotation(r_);
459
+        const t = this._refineTranslation(normalizedHomography, r, t_);
460
+        //const t = t_; // faster, but less accurate
535 461
 
536 462
         // done!
537 463
         return Speedy.Matrix(3, 3, r.concat(t)); // this is possibly NaN... why? homography...
@@ -539,16 +465,13 @@ export class CameraModel
539 465
 
540 466
     /**
541 467
      * Make two non-zero and non-parallel input vectors, r1 and r2, orthonormal
542
-     * @param r11 x of r1
543
-     * @param r21 y of r1
544
-     * @param r31 z of r1
545
-     * @param r12 x of r2
546
-     * @param r22 y of r2
547
-     * @param r32 z of r2
468
+     * @param rot rotation vectors [ r1 | r2 ] in column-major format
548 469
      * @returns a 3x2 matrix R such that R'R = I (column-major format)
549 470
      */
550
-    private _refineRotation(r11: number, r21: number, r31: number, r12: number, r22: number, r32: number): number[]
471
+    private _refineRotation(rot: number[]): number[]
551 472
     {
473
+        const [r11, r21, r31, r12, r22, r32] = rot;
474
+
552 475
         /*
553 476
 
554 477
         A little technique I figured out to correct the rotation vectors
@@ -853,7 +776,7 @@ export class CameraModel
853 776
             for(let j = 0; j < 6; j++)
854 777
                 avg[j] += r[j] / n;
855 778
         }
856
-        const r = this._refineRotation(avg[0], avg[1], avg[2], avg[3], avg[4], avg[5]);
779
+        const r = this._refineRotation(avg);
857 780
 
858 781
         // average translations
859 782
         const m = this._translationBuffer.length;
@@ -906,12 +829,11 @@ export class CameraModel
906 829
     /**
907 830
      * Estimate the pose [ R | t ] given a homography in AR screen space
908 831
      * @param homography must be valid
909
-     * @param f focal length
910 832
      * @returns 3x4 matrix
911 833
      */
912
-    private _estimatePose(homography: SpeedyMatrix, f: number = this._intrinsics[FY]): SpeedyMatrix
834
+    private _estimatePose(homography: SpeedyMatrix): SpeedyMatrix
913 835
     {
914
-        const normalizedHomography = this._normalizeHomography(homography, f);
836
+        const normalizedHomography = this._normalizeHomography(homography);
915 837
         const partialPose = Speedy.Matrix.Eye(3);
916 838
 
917 839
         // we want the estimated partial pose [ r1 | r2 | t ] to be as close
@@ -924,6 +846,7 @@ export class CameraModel
924 846
             const rt = this._estimatePartialPose(residual); // rt should converge to the identity matrix
925 847
             partialPose.setToSync(rt.times(partialPose));
926 848
             residual.setToSync(residual.times(rt.inverse()));
849
+            //console.log("rt",rt.toString());
927 850
             //console.log("residual",residual.toString());
928 851
         }
929 852
         //console.log('-----------');
@@ -937,14 +860,6 @@ export class CameraModel
937 860
         console.log("Pose * NORMALIZED HOM^-1", result.toString());
938 861
         */
939 862
 
940
-        /*
941
-        const rt = partialPose.read();
942
-        const r = rt.slice(0, 6);
943
-        const t = this._refineTranslation(normalizedHomography, r, rt.slice(6, 9));
944
-        const refinedPartialPose = Speedy.Matrix(3, 3, r.concat(t));
945
-        const filteredPartialPose = this._filterPartialPose(refinedPartialPose);
946
-        */
947
-
948 863
         // filter the partial pose
949 864
         const filteredPartialPose = this._filterPartialPose(partialPose);
950 865
 

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