Browse Source

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

customisations
alemart 1 year ago
parent
commit
4895decdc6
1 changed files with 45 additions and 130 deletions
  1. 45
    130
      src/geometry/camera-model.ts

+ 45
- 130
src/geometry/camera-model.ts View File

30
 import { Settings } from '../core/settings';
30
 import { Settings } from '../core/settings';
31
 import { IllegalOperationError, IllegalArgumentError } from '../utils/errors';
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
 /** Number of iterations used to refine the estimated pose */
36
 /** Number of iterations used to refine the estimated pose */
43
 const POSE_ITERATIONS = 30;
37
 const POSE_ITERATIONS = 30;
117
     /** extrinsics matrix, in column-major format */
111
     /** extrinsics matrix, in column-major format */
118
     private _extrinsics: number[];
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
     /** filter: samples of partial rotation matrix [ r1 | r2 ] */
114
     /** filter: samples of partial rotation matrix [ r1 | r2 ] */
127
     private _partialRotationBuffer: number[][];
115
     private _partialRotationBuffer: number[][];
128
 
116
 
140
         this._matrix = Speedy.Matrix.Eye(3, 4);
128
         this._matrix = Speedy.Matrix.Eye(3, 4);
141
         this._intrinsics = [1,0,0,0,1,0,0,0,1]; // identity matrix
129
         this._intrinsics = [1,0,0,0,1,0,0,0,1]; // identity matrix
142
         this._extrinsics = [1,0,0,0,1,0,0,0,1,0,0,0]; // no rotation & no translation [ R | t ] = [ I | 0 ]
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
         this._partialRotationBuffer = [];
131
         this._partialRotationBuffer = [];
146
         this._translationBuffer = [];
132
         this._translationBuffer = [];
147
     }
133
     }
218
             return Speedy.Promise.resolve(this._matrix);
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
         // estimate the pose
207
         // estimate the pose
228
         const pose = this._estimatePose(homography);
208
         const pose = this._estimatePose(homography);
229
         this._storePose(pose);
209
         this._storePose(pose);
379
      */
359
      */
380
     private _resetIntrinsics(): void
360
     private _resetIntrinsics(): void
381
     {
361
     {
362
+        const cameraWidth = Math.max(this._screenSize.width, this._screenSize.height); // portrait?
382
         const u0 = this._screenSize.width / 2;
363
         const u0 = this._screenSize.width / 2;
383
         const v0 = this._screenSize.height / 2;
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
         this._intrinsics[U0] = u0;
370
         this._intrinsics[U0] = u0;
389
         this._intrinsics[V0] = v0;
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
      * Compute a normalized homography H' = K^(-1) * H for an
375
      * Compute a normalized homography H' = K^(-1) * H for an
460
      * ideal pinhole with f = 1 and principal point = (0,0)
376
      * ideal pinhole with f = 1 and principal point = (0,0)
461
      * @param homography homography H to be normalized
377
      * @param homography homography H to be normalized
462
-     * @param f focal length
463
      * @returns normalized homography H'
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
         const h = homography.read();
382
         const h = homography.read();
468
         const u0 = this._intrinsics[U0];
383
         const u0 = this._intrinsics[U0];
469
         const v0 = this._intrinsics[V0];
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
         return Speedy.Matrix(3, 3, [
398
         return Speedy.Matrix(3, 3, [
476
             h11, h21, h31,
399
             h11, h21, h31,
495
         const sign = h33 >= 0 ? 1 : -1;
418
         const sign = h33 >= 0 ? 1 : -1;
496
 
419
 
497
         // compute the scale factor
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
         // invalid homography?
430
         // invalid homography?
506
         if(Number.isNaN(scale))
431
         if(Number.isNaN(scale))
515
         //console.log("h1,h2",h1norm,h2norm);
440
         //console.log("h1,h2",h1norm,h2norm);
516
         //console.log(normalizedHomography.toString());
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
         const r11 = scale * h11;
444
         const r11 = scale * h11;
524
         const r21 = scale * h21;
445
         const r21 = scale * h21;
525
         const r31 = scale * h31;
446
         const r31 = scale * h31;
526
-
527
         const r12 = scale * h12;
447
         const r12 = scale * h12;
528
         const r22 = scale * h22;
448
         const r22 = scale * h22;
529
         const r32 = scale * h32;
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
         // refine the pose
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
         // done!
462
         // done!
537
         return Speedy.Matrix(3, 3, r.concat(t)); // this is possibly NaN... why? homography...
463
         return Speedy.Matrix(3, 3, r.concat(t)); // this is possibly NaN... why? homography...
539
 
465
 
540
     /**
466
     /**
541
      * Make two non-zero and non-parallel input vectors, r1 and r2, orthonormal
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
      * @returns a 3x2 matrix R such that R'R = I (column-major format)
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
         A little technique I figured out to correct the rotation vectors
477
         A little technique I figured out to correct the rotation vectors
853
             for(let j = 0; j < 6; j++)
776
             for(let j = 0; j < 6; j++)
854
                 avg[j] += r[j] / n;
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
         // average translations
781
         // average translations
859
         const m = this._translationBuffer.length;
782
         const m = this._translationBuffer.length;
906
     /**
829
     /**
907
      * Estimate the pose [ R | t ] given a homography in AR screen space
830
      * Estimate the pose [ R | t ] given a homography in AR screen space
908
      * @param homography must be valid
831
      * @param homography must be valid
909
-     * @param f focal length
910
      * @returns 3x4 matrix
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
         const partialPose = Speedy.Matrix.Eye(3);
837
         const partialPose = Speedy.Matrix.Eye(3);
916
 
838
 
917
         // we want the estimated partial pose [ r1 | r2 | t ] to be as close
839
         // we want the estimated partial pose [ r1 | r2 | t ] to be as close
924
             const rt = this._estimatePartialPose(residual); // rt should converge to the identity matrix
846
             const rt = this._estimatePartialPose(residual); // rt should converge to the identity matrix
925
             partialPose.setToSync(rt.times(partialPose));
847
             partialPose.setToSync(rt.times(partialPose));
926
             residual.setToSync(residual.times(rt.inverse()));
848
             residual.setToSync(residual.times(rt.inverse()));
849
+            //console.log("rt",rt.toString());
927
             //console.log("residual",residual.toString());
850
             //console.log("residual",residual.toString());
928
         }
851
         }
929
         //console.log('-----------');
852
         //console.log('-----------');
937
         console.log("Pose * NORMALIZED HOM^-1", result.toString());
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
         // filter the partial pose
863
         // filter the partial pose
949
         const filteredPartialPose = this._filterPartialPose(partialPose);
864
         const filteredPartialPose = this._filterPartialPose(partialPose);
950
 
865
 

Loading…
Cancel
Save