|
@@ -227,6 +227,88 @@ export class ImageTrackerUtils
|
227
|
227
|
}
|
228
|
228
|
|
229
|
229
|
/**
|
|
230
|
+ * Interpolation filter for homographies
|
|
231
|
+ * In its simplest form, it's similar to linear interpolation: src (1-alpha) + dest alpha
|
|
232
|
+ * @param src homography
|
|
233
|
+ * @param dest homography
|
|
234
|
+ * @param alpha interpolation factor in [0,1]
|
|
235
|
+ * @param beta correction strength for noisy corners (optional)
|
|
236
|
+ * @param tau translation factor (optional)
|
|
237
|
+ * @returns interpolated homography
|
|
238
|
+ */
|
|
239
|
+ static interpolateHomographies(src: SpeedyMatrix, dest: SpeedyMatrix, alpha: number, beta: number = 0, tau: number = 0): SpeedyPromise<SpeedyMatrix>
|
|
240
|
+ {
|
|
241
|
+ const d = new Array<number>(4), q = new Array<number>(8), p = [
|
|
242
|
+ // NDC
|
|
243
|
+ -1, 1,
|
|
244
|
+ 1, 1,
|
|
245
|
+ 1, -1,
|
|
246
|
+ -1, -1,
|
|
247
|
+ ];
|
|
248
|
+
|
|
249
|
+ const ha = src.read(), hb = dest.read();
|
|
250
|
+ for(let i = 0, j = 0; i < 4; i++, j += 2) {
|
|
251
|
+ const x = p[j], y = p[j+1];
|
|
252
|
+
|
|
253
|
+ const hax = ha[0] * x + ha[3] * y + ha[6];
|
|
254
|
+ const hay = ha[1] * x + ha[4] * y + ha[7];
|
|
255
|
+ const haz = ha[2] * x + ha[5] * y + ha[8];
|
|
256
|
+
|
|
257
|
+ const hbx = hb[0] * x + hb[3] * y + hb[6];
|
|
258
|
+ const hby = hb[1] * x + hb[4] * y + hb[7];
|
|
259
|
+ const hbz = hb[2] * x + hb[5] * y + hb[8];
|
|
260
|
+
|
|
261
|
+ const dx = hbx/hbz - hax/haz;
|
|
262
|
+ const dy = hby/hbz - hay/haz;
|
|
263
|
+ d[i] = dx*dx + dy*dy;
|
|
264
|
+ }
|
|
265
|
+
|
|
266
|
+ let tx = 0, ty = 0;
|
|
267
|
+ const max = Math.max(d[0], d[1], d[2], d[3]); // max = Math.max(...d)
|
|
268
|
+ const min = Math.min(d[0], d[1], d[2], d[3]);
|
|
269
|
+ for(let i = 0, j = 0; i < 4; i++, j += 2) {
|
|
270
|
+ const x = p[j], y = p[j+1];
|
|
271
|
+
|
|
272
|
+ const hax = ha[0] * x + ha[3] * y + ha[6];
|
|
273
|
+ const hay = ha[1] * x + ha[4] * y + ha[7];
|
|
274
|
+ const haz = ha[2] * x + ha[5] * y + ha[8];
|
|
275
|
+
|
|
276
|
+ const hbx = hb[0] * x + hb[3] * y + hb[6];
|
|
277
|
+ const hby = hb[1] * x + hb[4] * y + hb[7];
|
|
278
|
+ const hbz = hb[2] * x + hb[5] * y + hb[8];
|
|
279
|
+
|
|
280
|
+ if(d[i] == min) {
|
|
281
|
+ // we take the min for the translation
|
|
282
|
+ // because there may be noisy corners
|
|
283
|
+ tx = hbx/hbz - hax/haz;
|
|
284
|
+ ty = hby/hbz - hay/haz;
|
|
285
|
+ }
|
|
286
|
+
|
|
287
|
+ // compute the interpolation factor t = t(alpha, beta)
|
|
288
|
+ // t is alpha if beta is zero
|
|
289
|
+ const f = 1 - Math.sqrt(d[i] / max); // f is zero when d[i] is max (hence, it minimizes t and contributes to src)
|
|
290
|
+ const g = alpha * (1 + beta * f);
|
|
291
|
+ const t = Math.max(0, Math.min(g, 1)); // clamp
|
|
292
|
+ const _t = 1 - t;
|
|
293
|
+
|
|
294
|
+ // a (1-t) + b t
|
|
295
|
+ q[ j ] = (hax/haz) * _t + (hbx/hbz) * t;
|
|
296
|
+ q[j+1] = (hay/haz) * _t + (hby/hbz) * t;
|
|
297
|
+ }
|
|
298
|
+
|
|
299
|
+ for(let j = 0; j < 8; j += 2) {
|
|
300
|
+ q[ j ] += tx * tau;
|
|
301
|
+ q[j+1] += ty * tau;
|
|
302
|
+ }
|
|
303
|
+
|
|
304
|
+ return Speedy.Matrix.perspective(
|
|
305
|
+ Speedy.Matrix.Zeros(3),
|
|
306
|
+ Speedy.Matrix(2, 4, p),
|
|
307
|
+ Speedy.Matrix(2, 4, q)
|
|
308
|
+ );
|
|
309
|
+ }
|
|
310
|
+
|
|
311
|
+ /**
|
230
|
312
|
* Given n > 0 pairs of keypoints in NDC as a 2 x 2n [ src | dest ] matrix,
|
231
|
313
|
* find a 6 DoF perspective warp (homography) from src to dest in NDC
|
232
|
314
|
* @param cameraIntrinsics 3x3 camera intrinsics
|