Procházet zdrojové kódy

Simplify View, ViewerPose and CameraModel

customisations
alemart před 9 měsíci
rodič
revize
113117d6d7

+ 164
- 111
src/geometry/camera-model.ts Zobrazit soubor

34
 /** A guess of the horizontal field-of-view of a typical camera, in degrees */
34
 /** A guess of the horizontal field-of-view of a typical camera, in degrees */
35
 const HFOV_GUESS = 60; // https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Cameras/Cameras.html
35
 const HFOV_GUESS = 60; // https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Cameras/Cameras.html
36
 
36
 
37
-/** Number of iterations used to refine the estimated pose */
38
-const POSE_ITERATIONS = 30;
39
-
40
 /** Convert degrees to radians */
37
 /** Convert degrees to radians */
41
 const DEG2RAD = 0.017453292519943295; // pi / 180
38
 const DEG2RAD = 0.017453292519943295; // pi / 180
42
 
39
 
47
 const EPSILON = 1e-6;
44
 const EPSILON = 1e-6;
48
 
45
 
49
 /** Index of the horizontal focal length in the camera intrinsics matrix (column-major format) */
46
 /** Index of the horizontal focal length in the camera intrinsics matrix (column-major format) */
50
-export const FX = 0;
47
+const FX = 0;
51
 
48
 
52
 /** Index of the vertical focal length in the camera intrinsics matrix */
49
 /** Index of the vertical focal length in the camera intrinsics matrix */
53
-export const FY = 4;
50
+const FY = 4;
54
 
51
 
55
 /** Index of the horizontal position of the principal point in the camera intrinsics matrix */
52
 /** Index of the horizontal position of the principal point in the camera intrinsics matrix */
56
-export const U0 = 6;
53
+const U0 = 6;
57
 
54
 
58
 /** Index of the vertical position of the principal point in the camera intrinsics matrix */
55
 /** Index of the vertical position of the principal point in the camera intrinsics matrix */
59
-export const V0 = 7;
56
+const V0 = 7;
57
+
58
+/** Number of iterations used to refine the estimated pose */
59
+const POSE_ITERATIONS = 30;
60
 
60
 
61
+/** Maximum number of iterations used when refining the translation vector */
62
+const REFINE_TRANSLATION_ITERATIONS = 15;
63
+
64
+/** Tolerance used to exit early when refining the translation vector */
65
+const REFINE_TRANSLATION_TOLERANCE = 1; // in units compatible with the size of the image sensor
66
+//FIXME make it a percentage?
61
 
67
 
62
 
68
 
63
 
69
 
66
  */
72
  */
67
 export class CameraModel
73
 export class CameraModel
68
 {
74
 {
69
-    /** size of the image sensor, in pixels */
70
-    private _screenSize: SpeedySize;
75
+    /** size of the image */
76
+    private _imageSize: SpeedySize;
71
 
77
 
72
     /** 3x4 camera matrix */
78
     /** 3x4 camera matrix */
73
     private _matrix: SpeedyMatrix;
79
     private _matrix: SpeedyMatrix;
88
      */
94
      */
89
     constructor()
95
     constructor()
90
     {
96
     {
91
-        this._screenSize = Speedy.Size(0, 0);
97
+        this._imageSize = Speedy.Size(0, 0);
92
         this._matrix = Speedy.Matrix.Eye(3, 4);
98
         this._matrix = Speedy.Matrix.Eye(3, 4);
93
         this._intrinsics = [1,0,0,0,1,0,0,0,1]; // 3x3 identity matrix
99
         this._intrinsics = [1,0,0,0,1,0,0,0,1]; // 3x3 identity matrix
94
         this._extrinsics = [1,0,0,0,1,0,0,0,1,0,0,0]; // 3x4 matrix [ R | t ] = [ I | 0 ] no rotation & no translation
100
         this._extrinsics = [1,0,0,0,1,0,0,0,1,0,0,0]; // 3x4 matrix [ R | t ] = [ I | 0 ] no rotation & no translation
97
 
103
 
98
     /**
104
     /**
99
      * Initialize the model
105
      * Initialize the model
100
-     * @param screenSize
106
+     * @param imageSize
101
      */
107
      */
102
-    init(screenSize: SpeedySize): void
108
+    init(imageSize: SpeedySize): void
103
     {
109
     {
104
-        // validate
105
-        if(screenSize.area() == 0)
106
-            throw new IllegalArgumentError(`Can't initialize the camera model with screenSize = ${screenSize.toString()}`);
110
+        // log
111
+        Utils.log(`Initializing the camera model...`);
107
 
112
 
108
-        // set the screen size
109
-        this._screenSize.width = screenSize.width;
110
-        this._screenSize.height = screenSize.height;
113
+        // set the imageSize
114
+        this._imageSize.width = imageSize.width;
115
+        this._imageSize.height = imageSize.height;
111
 
116
 
112
         // reset the model
117
         // reset the model
113
         this.reset();
118
         this.reset();
114
-
115
-        // log
116
-        Utils.log(`Initializing the camera model...`);
117
     }
119
     }
118
 
120
 
119
     /**
121
     /**
128
     /**
130
     /**
129
      * Update the camera model
131
      * Update the camera model
130
      * @param homography 3x3 perspective transform
132
      * @param homography 3x3 perspective transform
131
-     * @param screenSize may change over time (e.g., when going from portrait to landscape or vice-versa)
132
      * @returns promise that resolves to a camera matrix
133
      * @returns promise that resolves to a camera matrix
133
      */
134
      */
134
-    update(homography: SpeedyMatrix, screenSize: SpeedySize): SpeedyPromise<SpeedyMatrix>
135
+    update(homography: SpeedyMatrix): SpeedyPromise<SpeedyMatrix>
135
     {
136
     {
136
         // validate the shape of the homography
137
         // validate the shape of the homography
137
         if(homography.rows != 3 || homography.columns != 3)
138
         if(homography.rows != 3 || homography.columns != 3)
138
             throw new IllegalArgumentError(`Camera model: provide a homography matrix`);
139
             throw new IllegalArgumentError(`Camera model: provide a homography matrix`);
139
 
140
 
140
-        // validate screenSize
141
-        if(screenSize.area() == 0)
142
-            throw new IllegalArgumentError(`Camera model: invalid screenSize = ${screenSize.toString()}`);
143
-
144
-        // changed screen size?
145
-        if(!this._screenSize.equals(screenSize)) {
146
-            Utils.log(`Camera model: detected a change in screen size...`);
147
-
148
-            // update the screen size
149
-            this._screenSize.width = screenSize.width;
150
-            this._screenSize.height = screenSize.height;
151
-
152
-            // reset camera
153
-            this.reset();
154
-        }
155
-
156
         // read the entries of the homography
141
         // read the entries of the homography
157
         const h = homography.read();
142
         const h = homography.read();
158
         const h11 = h[0], h12 = h[3], h13 = h[6],
143
         const h11 = h[0], h12 = h[3], h13 = h[6],
191
     }
176
     }
192
 
177
 
193
     /**
178
     /**
194
-     * The camera matrix that maps the 3D normalized space [-1,1]^3 to the
195
-     * 2D AR screen space (measured in pixels)
196
-     * @returns 3x4 camera matrix
179
+     * The 3x4 camera matrix
197
      */
180
      */
198
     get matrix(): SpeedyMatrix
181
     get matrix(): SpeedyMatrix
199
     {
182
     {
201
     }
184
     }
202
 
185
 
203
     /**
186
     /**
204
-     * Camera intrinsics matrix
205
-     * @returns 3x3 intrinsics matrix in column-major format
187
+     * The aspect ratio of the image
206
      */
188
      */
207
-    get intrinsics(): number[]
189
+    get aspectRatio(): number
208
     {
190
     {
209
-        return this._intrinsics;
191
+        return this._imageSize.width / this._imageSize.height;
210
     }
192
     }
211
 
193
 
212
     /**
194
     /**
213
-     * Camera extrinsics matrix
214
-     * @returns 3x4 extrinsics matrix [ R | t ] in column-major format
195
+     * Focal length in pixels (projection distance in the pinhole camera model)
196
+     * same as (focal length in mm) * (number of pixels per world unit in pixels/mm)
215
      */
197
      */
216
-    get extrinsics(): number[]
198
+    get focalLength(): number
217
     {
199
     {
218
-        return this._extrinsics;
200
+        return this._intrinsics[FX]; // fx == fy
219
     }
201
     }
220
 
202
 
221
     /**
203
     /**
204
+     * Horizontal field-of-view, given in radians
205
+     */
206
+    get fovx(): number
207
+    {
208
+        return 2 * Math.atan(this._intrinsics[U0] / this._intrinsics[FX]);
209
+    }
210
+
211
+    /**
212
+     * Vertical field-of-view, given in radians
213
+     */
214
+    get fovy(): number
215
+    {
216
+        return 2 * Math.atan(this._intrinsics[V0] / this._intrinsics[FY]);
217
+    }
218
+
219
+    /**
220
+     * Principal point
221
+     * @returns principal point
222
+     */
223
+    /*
224
+    principalPoint(): SpeedyPoint2
225
+    {
226
+        return Speedy.Point2(this._intrinsics[U0], this._intrinsics[V0]);
227
+    }
228
+    */
229
+
230
+    /**
222
      * Convert coordinates from normalized space [-1,1]^3 to a
231
      * Convert coordinates from normalized space [-1,1]^3 to a
223
-     * "3D pixel space" based on the dimensions of the AR screen.
232
+     * "3D pixel space" based on the dimensions of the image sensor.
224
      *
233
      *
225
      * We perform a 180-degrees rotation around the x-axis so that
234
      * We perform a 180-degrees rotation around the x-axis so that
226
      * it looks nicer (the y-axis grows downwards in image space).
235
      * it looks nicer (the y-axis grows downwards in image space).
236
      */
245
      */
237
     denormalizer(): SpeedyMatrix
246
     denormalizer(): SpeedyMatrix
238
     {
247
     {
239
-        const w = this._screenSize.width / 2; // half width, in pixels
240
-        const h = this._screenSize.height / 2; // half height, in pixels
248
+        const w = this._imageSize.width / 2; // half width, in pixels
249
+        const h = this._imageSize.height / 2; // half height, in pixels
241
         const d = Math.min(w, h); // virtual unit length, in pixels
250
         const d = Math.min(w, h); // virtual unit length, in pixels
242
 
251
 
243
         /*
252
         /*
258
     }
267
     }
259
 
268
 
260
     /**
269
     /**
261
-     * Size of the AR screen space, in pixels
262
-     * @returns size in pixels
270
+     * Compute the view matrix in AR screen space, measured in pixels.
271
+     * This 4x4 matrix moves 3D points from world space to view space.
272
+     * We assume that the camera is looking in the direction of the
273
+     * negative z-axis (WebGL-friendly)
274
+     * @param camera
275
+     * @returns a 4x4 matrix describing a rotation and a translation
263
      */
276
      */
264
-    get screenSize(): SpeedySize
277
+    computeViewMatrix(): SpeedyMatrix
265
     {
278
     {
266
-        return this._screenSize;
267
-    }
279
+        const E = this._extrinsics;
268
 
280
 
269
-    /**
270
-     * Focal length in pixel units (projection distance in the pinhole camera model)
271
-     * same as (focal length in mm) * (number of pixels per world unit in pixels/mm)
272
-     * @returns focal length
273
-     */
274
-    get focalLength(): number
275
-    {
276
-        return this._intrinsics[FY]; // fx == fy
277
-    }
281
+        /*
278
 
282
 
279
-    /**
280
-     * Horizontal field-of-view, given in radians
281
-     * @returns vertical field-of-view
282
-     */
283
-    get fovx(): number
284
-    {
285
-        return 2 * Math.atan(this._intrinsics[U0] / this._intrinsics[FX]);
286
-    }
283
+        // this is the view matrix in AR screen space, measured in pixels
284
+        // we augment the extrinsics matrix, making it 4x4 by adding a
285
+        // [ 0  0  0  1 ] row. Below, E is a 3x4 extrinsics matrix
286
+        const V = Speedy.Matrix(4, 4, [
287
+            E[0], E[1], E[2], 0,
288
+            E[3], E[4], E[5], 0,
289
+            E[6], E[7], E[8], 0,
290
+            E[9], E[10], E[11], 1
291
+        ]);
287
 
292
 
288
-    /**
289
-     * Vertical field-of-view, given in radians
290
-     * @returns vertical field-of-view
291
-     */
292
-    get fovy(): number
293
-    {
294
-        return 2 * Math.atan(this._intrinsics[V0] / this._intrinsics[FY]);
293
+        // we premultiply V by F, which performs a rotation around the
294
+        // x-axis by 180 degrees, so that we get the 3D objects in front
295
+        // of the camera pointing in the direction of the negative z-axis
296
+        const F = Speedy.Matrix(4, 4, [
297
+            1, 0, 0, 0,
298
+            0,-1, 0, 0,
299
+            0, 0,-1, 0,
300
+            0, 0, 0, 1
301
+        ]);
302
+
303
+        Matrix F * V is matrix V with the second and third rows negated
304
+
305
+        */
306
+
307
+        return Speedy.Matrix(4, 4, [
308
+            E[0],-E[1],-E[2], 0,
309
+            E[3],-E[4],-E[5], 0,
310
+            E[6],-E[7],-E[8], 0,
311
+            E[9],-E[10],-E[11], 1
312
+        ]);
295
     }
313
     }
296
 
314
 
297
     /**
315
     /**
298
-     * Principal point
299
-     * @returns principal point, in pixel coordinates
316
+     * Compute a perspective projection matrix for WebGL
317
+     * @param near distance of the near plane
318
+     * @param far distance of the far plane
300
      */
319
      */
301
-    principalPoint(): SpeedyPoint2
320
+    computeProjectionMatrix(near: number, far: number): SpeedyMatrix
302
     {
321
     {
303
-        return Speedy.Point2(this._intrinsics[U0], this._intrinsics[V0]);
322
+        const K = this._intrinsics;
323
+
324
+        // we assume that the principal point is at the center of the image
325
+        const top = near * (K[V0] / K[FY]);
326
+        const right = near * (K[U0] / K[FX]);
327
+        const bottom = -top, left = -right; // symmetric frustum
328
+
329
+        // a derivation of this projection matrix can be found at
330
+        // https://www.songho.ca/opengl/gl_projectionmatrix.html
331
+        // http://learnwebgl.brown37.net/08_projections/projections_perspective.html
332
+        return Speedy.Matrix(4, 4, [
333
+            2 * near / (right - left), 0, 0, 0,
334
+            0, 2 * near / (top - bottom), 0, 0,
335
+            (right + left) / (right - left), (top + bottom) / (top - bottom), -(far + near) / (far - near), -1,
336
+            0, 0, -2 * far * near / (far - near), 0
337
+        ]);
304
     }
338
     }
305
 
339
 
306
     /**
340
     /**
321
      */
355
      */
322
     private _resetIntrinsics(): void
356
     private _resetIntrinsics(): void
323
     {
357
     {
324
-        const cameraWidth = Math.max(this._screenSize.width, this._screenSize.height); // portrait or landscape?
358
+        const cameraWidth = Math.max(this._imageSize.width, this._imageSize.height); // portrait or landscape?
325
 
359
 
326
-        const u0 = this._screenSize.width / 2;
327
-        const v0 = this._screenSize.height / 2;
360
+        const u0 = this._imageSize.width / 2;
361
+        const v0 = this._imageSize.height / 2;
328
         const fx = (cameraWidth / 2) / Math.tan(DEG2RAD * HFOV_GUESS / 2);
362
         const fx = (cameraWidth / 2) / Math.tan(DEG2RAD * HFOV_GUESS / 2);
329
         const fy = fx;
363
         const fy = fx;
330
 
364
 
401
 
435
 
402
         // sanity check
436
         // sanity check
403
         if(Number.isNaN(scale))
437
         if(Number.isNaN(scale))
404
-            return Speedy.Matrix(3, 3, (new Array(9)).fill(Number.NaN));
438
+            return Speedy.Matrix(3, 3, (new Array<number>(9)).fill(Number.NaN));
405
 
439
 
406
         // recover the rotation
440
         // recover the rotation
407
-        let r = new Array(6) as number[];
441
+        let r = new Array<number>(6);
408
         r[0] = scale * h11;
442
         r[0] = scale * h11;
409
         r[1] = scale * h21;
443
         r[1] = scale * h21;
410
         r[2] = scale * h31;
444
         r[2] = scale * h31;
412
         r[4] = scale * h22;
446
         r[4] = scale * h22;
413
         r[5] = scale * h32;
447
         r[5] = scale * h32;
414
 
448
 
415
-        // refine the rotation
416
-        r = this._refineRotation(r); // r is initially noisy
449
+        // refine the rotation (r is initially noisy)
450
+        r = this._refineRotation(r);
417
 
451
 
418
         /*
452
         /*
419
 
453
 
438
         scale /= h1norm2 + h2norm2;
472
         scale /= h1norm2 + h2norm2;
439
 
473
 
440
         // recover the translation
474
         // recover the translation
441
-        let t = new Array(3) as number[];
475
+        let t = new Array<number>(3);
442
         t[0] = scale * h13;
476
         t[0] = scale * h13;
443
         t[1] = scale * h23;
477
         t[1] = scale * h23;
444
         t[2] = scale * h33;
478
         t[2] = scale * h33;
539
         // compute the Cholesky decomposition LL' of the diagonal matrix D
573
         // compute the Cholesky decomposition LL' of the diagonal matrix D
540
         // whose entries are the two eigenvalues of R'R and then invert L
574
         // whose entries are the two eigenvalues of R'R and then invert L
541
         const s1 = Math.sqrt(eigval1), s2 = Math.sqrt(eigval2); // singular values of R (pick s1 >= s2)
575
         const s1 = Math.sqrt(eigval1), s2 = Math.sqrt(eigval2); // singular values of R (pick s1 >= s2)
576
+
577
+        /*
542
         const Linv = Speedy.Matrix(2, 2, [1/s1, 0, 0, 1/s2]); // L inverse
578
         const Linv = Speedy.Matrix(2, 2, [1/s1, 0, 0, 1/s2]); // L inverse
543
 
579
 
544
         // compute the correction matrix C = Q * Linv * Q', where Q = [q1|q2]
580
         // compute the correction matrix C = Q * Linv * Q', where Q = [q1|q2]
550
         // correct the rotation vectors r1 and r2 using C
586
         // correct the rotation vectors r1 and r2 using C
551
         const R = Speedy.Matrix(3, 2, [r11, r21, r31, r12, r22, r32]);
587
         const R = Speedy.Matrix(3, 2, [r11, r21, r31, r12, r22, r32]);
552
         return Speedy.Matrix(R.times(C)).read();
588
         return Speedy.Matrix(R.times(C)).read();
589
+        */
590
+
591
+        // find C = Q * Linv * Q' manually
592
+        // [ a  b ] is symmetric
593
+        // [ b  c ]
594
+        const a = x1*x1/s1 + x2*x2/s2;
595
+        const b = x1*y1/s1 + x2*y2/s2;
596
+        const c = y1*y1/s1 + y2*y2/s2;
597
+
598
+        // find RC manually
599
+        return [
600
+            a*r11 + b*r12,
601
+            a*r21 + b*r22,
602
+            a*r31 + b*r32,
603
+
604
+            b*r11 + c*r12,
605
+            b*r21 + c*r22,
606
+            b*r31 + c*r32
607
+        ];
553
     }
608
     }
554
 
609
 
555
     /**
610
     /**
587
         const r21 = rot[1], r22 = rot[4];
642
         const r21 = rot[1], r22 = rot[4];
588
         const r31 = rot[2], r32 = rot[5];
643
         const r31 = rot[2], r32 = rot[5];
589
 
644
 
590
-        // sample points [ xi  yi ]' in AR screen space
645
+        // sample points [ xi  yi ]' in screen space
591
         //const x = [ 0.5, 0.0, 1.0, 1.0, 0.0, 0.5, 1.0, 0.5, 0.0 ];
646
         //const x = [ 0.5, 0.0, 1.0, 1.0, 0.0, 0.5, 1.0, 0.5, 0.0 ];
592
         //const y = [ 0.5, 0.0, 0.0, 1.0, 1.0, 0.0, 0.5, 1.0, 0.5 ];
647
         //const y = [ 0.5, 0.0, 0.0, 1.0, 1.0, 0.0, 0.5, 1.0, 0.5 ];
593
         const x = [ 0.5, 0.0, 1.0, 1.0, 0.0 ];
648
         const x = [ 0.5, 0.0, 1.0, 1.0, 0.0 ];
595
         const n = x.length;
650
         const n = x.length;
596
         const n3 = 3*n;
651
         const n3 = 3*n;
597
 
652
 
598
-        const width = this._screenSize.width;
599
-        const height = this._screenSize.height;
653
+        const width = this._imageSize.width;
654
+        const height = this._imageSize.height;
600
         for(let i = 0; i < n; i++) {
655
         for(let i = 0; i < n; i++) {
601
             x[i] *= width;
656
             x[i] *= width;
602
             y[i] *= height;
657
             y[i] *= height;
603
         }
658
         }
604
 
659
 
605
         // set auxiliary values: ai = H [ xi  yi  1 ]'
660
         // set auxiliary values: ai = H [ xi  yi  1 ]'
606
-        const a1 = new Array(n) as number[];
607
-        const a2 = new Array(n) as number[];
608
-        const a3 = new Array(n) as number[];
661
+        const a1 = new Array<number>(n);
662
+        const a2 = new Array<number>(n);
663
+        const a3 = new Array<number>(n);
609
         for(let i = 0; i < n; i++) {
664
         for(let i = 0; i < n; i++) {
610
             a1[i] = x[i] * h11 + y[i] * h12 + h13;
665
             a1[i] = x[i] * h11 + y[i] * h12 + h13;
611
             a2[i] = x[i] * h21 + y[i] * h22 + h23;
666
             a2[i] = x[i] * h21 + y[i] * h22 + h23;
614
 
669
 
615
         // we'll solve M t = v for t with linear least squares
670
         // we'll solve M t = v for t with linear least squares
616
         // M: 3n x 3, v: 3n x 1, t: 3 x 1
671
         // M: 3n x 3, v: 3n x 1, t: 3 x 1
617
-        const m = new Array(3*n * 3) as number[];
618
-        const v = new Array(3*n) as number[];
672
+        const m = new Array<number>(3*n * 3);
673
+        const v = new Array<number>(3*n);
619
         for(let i = 0, k = 0; k < n; i += 3, k++) {
674
         for(let i = 0, k = 0; k < n; i += 3, k++) {
620
             m[i] = m[i+n3+1] = m[i+n3+n3+2] = 0;
675
             m[i] = m[i+n3+1] = m[i+n3+n3+2] = 0;
621
             m[i+n3] = -(m[i+1] = a3[k]);
676
             m[i+n3] = -(m[i+1] = a3[k]);
676
         */
731
         */
677
 
732
 
678
         // gradient descent: super lightweight implementation
733
         // gradient descent: super lightweight implementation
679
-        const r = new Array(3*n) as number[];
680
-        const c = new Array(3) as number[];
681
-        const Mc = new Array(3*n) as number[];
734
+        const r = new Array<number>(3*n);
735
+        const c = new Array<number>(3);
736
+        const Mc = new Array<number>(3*n);
682
 
737
 
683
         // initial guess
738
         // initial guess
684
-        const t = new Array(3) as number[];
739
+        const t = new Array<number>(3);
685
         t[0] = t0[0];
740
         t[0] = t0[0];
686
         t[1] = t0[1];
741
         t[1] = t0[1];
687
         t[2] = t0[2];
742
         t[2] = t0[2];
688
 
743
 
689
         // iterate
744
         // iterate
690
-        const MAX_ITERATIONS = 15;
691
-        const TOLERANCE = 1;
692
-        for(let it = 0; it < MAX_ITERATIONS; it++) {
745
+        for(let it = 0; it < REFINE_TRANSLATION_ITERATIONS; it++) {
693
             //console.log("it",it+1);
746
             //console.log("it",it+1);
694
 
747
 
695
             // compute residual r = Mt - v
748
             // compute residual r = Mt - v
719
             for(let i = 0; i < 3; i++)
772
             for(let i = 0; i < 3; i++)
720
                 num += c[i] * c[i];
773
                 num += c[i] * c[i];
721
             //console.log("c'c=",num);
774
             //console.log("c'c=",num);
722
-            if(num < TOLERANCE)
775
+            if(num < REFINE_TRANSLATION_TOLERANCE)
723
                 break;
776
                 break;
724
 
777
 
725
             // compute (Mc)'(Mc)
778
             // compute (Mc)'(Mc)
778
     }
831
     }
779
 
832
 
780
     /**
833
     /**
781
-     * Estimate the pose [ R | t ] given a homography in AR screen space
834
+     * Estimate the pose [ R | t ] given a homography in sensor space
782
      * @param homography must be valid
835
      * @param homography must be valid
783
      * @returns 3x4 matrix
836
      * @returns 3x4 matrix
784
      */
837
      */

+ 26
- 62
src/geometry/view.ts Zobrazit soubor

23
 
23
 
24
 import Speedy from 'speedy-vision';
24
 import Speedy from 'speedy-vision';
25
 import { SpeedyMatrix } from 'speedy-vision/types/core/speedy-matrix';
25
 import { SpeedyMatrix } from 'speedy-vision/types/core/speedy-matrix';
26
-import { CameraModel, FX, FY, U0, V0 } from './camera-model';
26
+import { CameraModel } from './camera-model';
27
 import { IllegalArgumentError } from '../utils/errors';
27
 import { IllegalArgumentError } from '../utils/errors';
28
 import { Nullable } from '../utils/utils';
28
 import { Nullable } from '../utils/utils';
29
 
29
 
34
 const DEFAULT_FAR = 20000;
34
 const DEFAULT_FAR = 20000;
35
 
35
 
36
 
36
 
37
-
38
 /**
37
 /**
39
  * A view of the 3D world at a moment in time,
38
  * A view of the 3D world at a moment in time,
40
  * featuring the means to project points into clip space
39
  * featuring the means to project points into clip space
48
     readonly _projectionMatrixInverse: SpeedyMatrix;
47
     readonly _projectionMatrixInverse: SpeedyMatrix;
49
 }
48
 }
50
 
49
 
50
+
51
 /**
51
 /**
52
  * A PerspectiveView is a View defining a symmetric frustum around the z-axis
52
  * A PerspectiveView is a View defining a symmetric frustum around the z-axis
53
  * (perspective projection)
53
  * (perspective projection)
54
  */
54
  */
55
 export class PerspectiveView implements View
55
 export class PerspectiveView implements View
56
 {
56
 {
57
-    /** A 4x4 matrix that projects the viewer space into the clip space, i.e., [-1,1]^3 */
58
-    private readonly _projectionMatrix: SpeedyMatrix;
59
-
60
-    /** The inverse of the projection matrix, computed lazily */
61
-    private _inverseProjection: Nullable<SpeedyMatrix>;
62
-
63
-    /** Tangent of the half of the horizontal field-of-view */
64
-    private readonly _tanOfHalfFovx: number;
65
-
66
-    /** Tangent of the half of the vertical field-of-view */
67
-    private readonly _tanOfHalfFovy: number;
68
-
69
-    /** Aspect ratio of the frustum */
70
-    private readonly _aspect: number;
57
+    /** Camera model */
58
+    private readonly _camera: CameraModel;
71
 
59
 
72
     /** Distance of the near plane to the Z = 0 plane in viewer space */
60
     /** Distance of the near plane to the Z = 0 plane in viewer space */
73
     private readonly _near: number;
61
     private readonly _near: number;
75
     /** Distance of the far plane to the Z = 0 plane in viewer space */
63
     /** Distance of the far plane to the Z = 0 plane in viewer space */
76
     private readonly _far: number;
64
     private readonly _far: number;
77
 
65
 
66
+    /** A 4x4 matrix that projects the viewer space into the clip space, i.e., [-1,1]^3 */
67
+    private readonly _projectionMatrix: SpeedyMatrix;
68
+
69
+    /** The inverse of the projection matrix, computed lazily */
70
+    private _inverseProjection: Nullable<SpeedyMatrix>;
78
 
71
 
79
 
72
 
80
 
73
 
86
      */
79
      */
87
     constructor(camera: CameraModel, near: number = DEFAULT_NEAR, far: number = DEFAULT_FAR)
80
     constructor(camera: CameraModel, near: number = DEFAULT_NEAR, far: number = DEFAULT_FAR)
88
     {
81
     {
89
-        const intrinsics = camera.intrinsics;
90
-        const screenSize = camera.screenSize;
91
-
92
         this._near = Math.max(0, +near);
82
         this._near = Math.max(0, +near);
93
         this._far = Math.max(0, +far);
83
         this._far = Math.max(0, +far);
94
 
84
 
95
         if(this._near >= this._far)
85
         if(this._near >= this._far)
96
             throw new IllegalArgumentError(`View expects near < far (found near = ${this._near} and far = ${this._far})`);
86
             throw new IllegalArgumentError(`View expects near < far (found near = ${this._near} and far = ${this._far})`);
97
 
87
 
98
-        this._aspect = screenSize.width / screenSize.height;
99
-        this._tanOfHalfFovx = intrinsics[U0] / intrinsics[FX];
100
-        this._tanOfHalfFovy = intrinsics[V0] / intrinsics[FY];
101
-        this._projectionMatrix = PerspectiveView._computeProjectionMatrix(intrinsics, this._near, this._far);
88
+        this._camera = camera;
89
+        this._projectionMatrix = camera.computeProjectionMatrix(this._near, this._far);
102
         this._inverseProjection = null;
90
         this._inverseProjection = null;
103
     }
91
     }
104
 
92
 
111
     }
99
     }
112
 
100
 
113
     /**
101
     /**
102
+     * The inverse of the projection matrix
103
+     * @internal
104
+     */
105
+    get _projectionMatrixInverse(): SpeedyMatrix
106
+    {
107
+        if(this._inverseProjection === null)
108
+            this._inverseProjection = Speedy.Matrix(this._projectionMatrix.inverse());
109
+
110
+        return this._inverseProjection;
111
+    }
112
+
113
+    /**
114
      * Aspect ratio of the frustum
114
      * Aspect ratio of the frustum
115
      */
115
      */
116
     get aspect(): number
116
     get aspect(): number
117
     {
117
     {
118
-        return this._aspect;
118
+        return this._camera.aspectRatio;
119
     }
119
     }
120
 
120
 
121
     /**
121
     /**
123
      */
123
      */
124
     get fovx(): number
124
     get fovx(): number
125
     {
125
     {
126
-        return 2 * Math.atan(this._tanOfHalfFovx);
126
+        return this._camera.fovx;
127
     }
127
     }
128
 
128
 
129
     /**
129
     /**
131
      */
131
      */
132
     get fovy(): number
132
     get fovy(): number
133
     {
133
     {
134
-        return 2 * Math.atan(this._tanOfHalfFovy);
134
+        return this._camera.fovy;
135
     }
135
     }
136
 
136
 
137
     /**
137
     /**
149
     {
149
     {
150
         return this._far;
150
         return this._far;
151
     }
151
     }
152
-
153
-    /**
154
-     * The inverse of the projection matrix
155
-     * @internal
156
-     */
157
-    get _projectionMatrixInverse(): SpeedyMatrix
158
-    {
159
-        if(this._inverseProjection === null)
160
-            this._inverseProjection = Speedy.Matrix(this._projectionMatrix.inverse());
161
-
162
-        return this._inverseProjection;
163
-    }
164
-
165
-    /**
166
-     * Compute a perspective projection matrix for WebGL
167
-     * @param K camera intrinsics
168
-     * @param near distance of the near plane
169
-     * @param far distance of the far plane
170
-     */
171
-    private static _computeProjectionMatrix(K: number[], near: number, far: number): SpeedyMatrix
172
-    {
173
-        // we assume that the principal point is at the center of the image
174
-        const top = near * (K[V0] / K[FY]);
175
-        const right = near * (K[U0] / K[FX]);
176
-        const bottom = -top, left = -right; // symmetric frustum
177
-
178
-        // a derivation of this projection matrix can be found at
179
-        // https://www.songho.ca/opengl/gl_projectionmatrix.html
180
-        // http://learnwebgl.brown37.net/08_projections/projections_perspective.html
181
-        return Speedy.Matrix(4, 4, [
182
-            2 * near / (right - left), 0, 0, 0,
183
-            0, 2 * near / (top - bottom), 0, 0,
184
-            (right + left) / (right - left), (top + bottom) / (top - bottom), -(far + near) / (far - near), -1,
185
-            0, 0, -2 * far * near / (far - near), 0
186
-        ]);
187
-    }
188
 }
152
 }

+ 7
- 49
src/geometry/viewer-pose.ts Zobrazit soubor

44
      */
44
      */
45
     constructor(camera: CameraModel)
45
     constructor(camera: CameraModel)
46
     {
46
     {
47
-        // compute the view matrix and its inverse in AR screen space
48
-        const viewMatrix = ViewerPose._computeViewMatrix(camera);
49
-        const inverseTransform = new Transform(viewMatrix); // from world space to view space
50
-        const transform = inverseTransform.inverse; // from view space to world space
47
+        const viewMatrix = camera.computeViewMatrix();
48
+        const modelMatrix = Speedy.Matrix(viewMatrix.inverse());
51
 
49
 
50
+        const transform = new Transform(modelMatrix);
52
         super(transform);
51
         super(transform);
52
+
53
         this._viewMatrix = viewMatrix;
53
         this._viewMatrix = viewMatrix;
54
     }
54
     }
55
 
55
 
56
     /**
56
     /**
57
-     * This 4x4 matrix moves 3D points from world space to view space. We
58
-     * assume that the camera is looking in the direction of the negative
59
-     * z-axis (WebGL-friendly)
57
+     * This 4x4 matrix moves 3D points from world space to view space.
58
+     * We assume that the camera is looking in the direction of the
59
+     * negative z-axis (WebGL-friendly)
60
      */
60
      */
61
     get viewMatrix(): SpeedyMatrix
61
     get viewMatrix(): SpeedyMatrix
62
     {
62
     {
63
         return this._viewMatrix;
63
         return this._viewMatrix;
64
     }
64
     }
65
-
66
-    /**
67
-     * Compute the view matrix in AR screen space, measured in pixels
68
-     * @param camera
69
-     * @returns a 4x4 matrix describing a rotation and a translation
70
-     */
71
-    private static _computeViewMatrix(camera: CameraModel): SpeedyMatrix
72
-    {
73
-        /*
74
-
75
-        // this is the view matrix in AR screen space, measured in pixels
76
-        // we augment the extrinsics matrix, making it 4x4 by adding a
77
-        // [ 0  0  0  1 ] row. Below, E is a 3x4 extrinsics matrix
78
-        const V = Speedy.Matrix(4, 4, [
79
-            E[0], E[1], E[2], 0,
80
-            E[3], E[4], E[5], 0,
81
-            E[6], E[7], E[8], 0,
82
-            E[9], E[10], E[11], 1
83
-        ]);
84
-
85
-        // we premultiply V by F, which performs a rotation around the
86
-        // x-axis by 180 degrees, so that we get the 3D objects in front
87
-        // of the camera pointing in the direction of the negative z-axis
88
-        const F = Speedy.Matrix(4, 4, [
89
-            1, 0, 0, 0,
90
-            0,-1, 0, 0,
91
-            0, 0,-1, 0,
92
-            0, 0, 0, 1
93
-        ]);
94
-
95
-        Matrix F * V is matrix V with the second and third rows negated
96
-
97
-        */
98
-
99
-        const E = camera.extrinsics;
100
-        return Speedy.Matrix(4, 4, [
101
-            E[0],-E[1],-E[2], 0,
102
-            E[3],-E[4],-E[5], 0,
103
-            E[6],-E[7],-E[8], 0,
104
-            E[9],-E[10],-E[11], 1
105
-        ]);
106
-    }
107
 }
65
 }

+ 2
- 2
src/trackers/image-tracker/states/tracking.ts Zobrazit soubor

165
         keypointPortalSource.source = templateKeypointPortalSink;
165
         keypointPortalSource.source = templateKeypointPortalSink;
166
 
166
 
167
         // setup camera
167
         // setup camera
168
-        this._camera.init(initialScreenSize);
168
+        this._camera.init(this._initialScreenSize);
169
 
169
 
170
         // emit event
170
         // emit event
171
         const ev = new ImageTrackerEvent('targetfound', referenceImage);
171
         const ev = new ImageTrackerEvent('targetfound', referenceImage);
326
             //console.log("> AF ", Speedy.Matrix(affineMotion).toString());
326
             //console.log("> AF ", Speedy.Matrix(affineMotion).toString());
327
             //console.log("> PF ", Speedy.Matrix(perspectiveMotion).toString());
327
             //console.log("> PF ", Speedy.Matrix(perspectiveMotion).toString());
328
 
328
 
329
-            return this._camera.update(homography, screenSize);
329
+            return this._camera.update(homography);
330
         })
330
         })
331
         .then(() => {
331
         .then(() => {
332
 
332
 

Načítá se…
Zrušit
Uložit