@srsergio/taptapp-ar 1.0.35 → 1.0.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compiler/offline-compiler.js +2 -2
- package/dist/compiler/simple-ar.js +51 -42
- package/dist/compiler/tracker/tracker.d.ts +1 -9
- package/dist/compiler/tracker/tracker.js +26 -11
- package/package.json +1 -1
- package/src/compiler/offline-compiler.js +2 -2
- package/src/compiler/simple-ar.js +49 -50
- package/src/compiler/tracker/tracker.js +29 -12
|
@@ -217,9 +217,9 @@ export class OfflineCompiler {
|
|
|
217
217
|
}
|
|
218
218
|
const dataList = this.data.map((item) => {
|
|
219
219
|
const matchingData = item.matchingData.map((kf) => this._packKeyframe(kf));
|
|
220
|
-
const trackingData =
|
|
220
|
+
const trackingData = item.trackingData.map((td) => {
|
|
221
221
|
const count = td.points.length;
|
|
222
|
-
//
|
|
222
|
+
// Packed Coords - Float32 for now as in current import logic
|
|
223
223
|
const px = new Float32Array(count);
|
|
224
224
|
const py = new Float32Array(count);
|
|
225
225
|
for (let i = 0; i < count; i++) {
|
|
@@ -192,60 +192,69 @@ class SimpleAR {
|
|
|
192
192
|
const needsRotation = isPortrait && isVideoLandscape;
|
|
193
193
|
// 3. Get intrinsic projection from controller
|
|
194
194
|
const proj = this.controller.projectionTransform;
|
|
195
|
-
//
|
|
196
|
-
// We convert the OpenGL World Matrix to a CSS matrix3d.
|
|
197
|
-
// The OpenGL matrix is column-major. CSS matrix3d is also column-major.
|
|
198
|
-
const m = this.controller.getWorldMatrix(mVT, targetIndex);
|
|
199
|
-
// Map OpenGL coords to Screen Pixels using the projection logic
|
|
200
|
-
const vW = needsRotation ? videoH : videoW;
|
|
201
|
-
const vH = needsRotation ? videoW : videoH;
|
|
202
|
-
const perspectiveScale = Math.max(containerRect.width / vW, containerRect.height / vH);
|
|
203
|
-
const displayW = vW * perspectiveScale;
|
|
204
|
-
const displayH = vH * perspectiveScale;
|
|
205
|
-
const offsetX = (containerRect.width - displayW) / 2;
|
|
206
|
-
const offsetY = (containerRect.height - displayH) / 2;
|
|
207
|
-
// Adjust for centered marker and scaleMultiplier
|
|
208
|
-
const s = finalScale; // We still need the base scale factor for the pixel-to-marker mapping
|
|
209
|
-
// However, a cleaner way is to use the world matrix directly and map it.
|
|
210
|
-
// Actually, the simpler way to do 3D in CSS while keeping my projection logic is:
|
|
211
|
-
// Project the 4 corners and find the homography, OR
|
|
212
|
-
// Use the OpenGL matrix directly with a perspective mapping.
|
|
213
|
-
// Let's use the points projection to maintain the "needsRotation" logic compatibility
|
|
214
|
-
const pMid = projectToScreen(markerW / 2, markerH / 2, 0, mVT, proj, videoW, videoH, containerRect, needsRotation);
|
|
195
|
+
// 3. Project 4 corners to determine a full 3D perspective (homography)
|
|
215
196
|
const pUL = projectToScreen(0, 0, 0, mVT, proj, videoW, videoH, containerRect, needsRotation);
|
|
216
197
|
const pUR = projectToScreen(markerW, 0, 0, mVT, proj, videoW, videoH, containerRect, needsRotation);
|
|
217
198
|
const pLL = projectToScreen(0, markerH, 0, mVT, proj, videoW, videoH, containerRect, needsRotation);
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
199
|
+
const pLR = projectToScreen(markerW, markerH, 0, mVT, proj, videoW, videoH, containerRect, needsRotation);
|
|
200
|
+
// Helper to solve for 2D Homography (maps 0..1 square to pUL, pUR, pLL, pLR)
|
|
201
|
+
const solveHomography = (w, h, p1, p2, p3, p4) => {
|
|
202
|
+
const x1 = p1.sx, y1 = p1.sy;
|
|
203
|
+
const x2 = p2.sx, y2 = p2.sy;
|
|
204
|
+
const x3 = p3.sx, y3 = p3.sy;
|
|
205
|
+
const x4 = p4.sx, y4 = p4.sy;
|
|
206
|
+
const dx1 = x2 - x4, dx2 = x3 - x4, dx3 = x1 - x2 + x4 - x3;
|
|
207
|
+
const dy1 = y2 - y4, dy2 = y3 - y4, dy3 = y1 - y2 + y4 - y3;
|
|
208
|
+
let a, b, c, d, e, f, g, h_coeff;
|
|
209
|
+
if (dx3 === 0 && dy3 === 0) {
|
|
210
|
+
a = x2 - x1;
|
|
211
|
+
b = x3 - x1;
|
|
212
|
+
c = x1;
|
|
213
|
+
d = y2 - y1;
|
|
214
|
+
e = y3 - y1;
|
|
215
|
+
f = y1;
|
|
216
|
+
g = 0;
|
|
217
|
+
h_coeff = 0;
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
const det = dx1 * dy2 - dx2 * dy1;
|
|
221
|
+
g = (dx3 * dy2 - dx2 * dy3) / det;
|
|
222
|
+
h_coeff = (dx1 * dy3 - dx3 * dy1) / det;
|
|
223
|
+
a = x2 - x1 + g * x2;
|
|
224
|
+
b = x3 - x1 + h_coeff * x3;
|
|
225
|
+
c = x1;
|
|
226
|
+
d = y2 - y1 + g * y2;
|
|
227
|
+
e = y3 - y1 + h_coeff * y3;
|
|
228
|
+
f = y1;
|
|
229
|
+
}
|
|
230
|
+
// This maps unit square (0..1) to the quadrilateral.
|
|
231
|
+
// We need to scale it by 1/w and 1/h to map (0..w, 0..h)
|
|
232
|
+
return [
|
|
233
|
+
a / w, d / w, 0, g / w,
|
|
234
|
+
b / h, e / h, 0, h_coeff / h,
|
|
235
|
+
0, 0, 1, 0,
|
|
236
|
+
c, f, 0, 1
|
|
237
|
+
];
|
|
238
|
+
};
|
|
239
|
+
const matrix = solveHomography(markerW, markerH, pUL, pUR, pLL, pLR);
|
|
240
|
+
// Apply styles
|
|
233
241
|
this.overlay.style.maxWidth = 'none';
|
|
234
242
|
this.overlay.style.width = `${markerW}px`;
|
|
235
243
|
this.overlay.style.height = `${markerH}px`;
|
|
236
244
|
this.overlay.style.position = 'absolute';
|
|
237
|
-
this.overlay.style.transformOrigin = '0 0';
|
|
245
|
+
this.overlay.style.transformOrigin = '0 0';
|
|
238
246
|
this.overlay.style.left = '0';
|
|
239
247
|
this.overlay.style.top = '0';
|
|
240
248
|
this.overlay.style.display = 'block';
|
|
241
|
-
//
|
|
242
|
-
//
|
|
249
|
+
// Apply 3D transform with matrix3d
|
|
250
|
+
// We also apply the user's custom scaleMultiplier AFTER the perspective transform
|
|
251
|
+
// but since we want to scale around the marker center, we apply it as a prefix/suffix
|
|
252
|
+
// Scale around top-left (0,0) is easy. Scale around center requires offset.
|
|
243
253
|
this.overlay.style.transform = `
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
${(pLL.sx - pUL.sx) / markerH}, ${(pLL.sy - pUL.sy) / markerH},
|
|
247
|
-
0, 0)
|
|
254
|
+
matrix3d(${matrix.join(',')})
|
|
255
|
+
translate(${markerW / 2}px, ${markerH / 2}px)
|
|
248
256
|
scale(${this.scaleMultiplier})
|
|
257
|
+
translate(${-markerW / 2}px, ${-markerH / 2}px)
|
|
249
258
|
`;
|
|
250
259
|
}
|
|
251
260
|
// Unified projection logic moved to ./utils/projection.js
|
|
@@ -7,15 +7,7 @@ export class Tracker {
|
|
|
7
7
|
inputHeight: any;
|
|
8
8
|
debugMode: boolean;
|
|
9
9
|
trackingKeyframeList: any[];
|
|
10
|
-
prebuiltData:
|
|
11
|
-
px: Float32Array<any>;
|
|
12
|
-
py: Float32Array<any>;
|
|
13
|
-
data: Uint8Array<any>;
|
|
14
|
-
width: any;
|
|
15
|
-
height: any;
|
|
16
|
-
scale: any;
|
|
17
|
-
projectedImage: Float32Array<ArrayBuffer>;
|
|
18
|
-
}[];
|
|
10
|
+
prebuiltData: any[];
|
|
19
11
|
templateBuffer: Float32Array<ArrayBuffer>;
|
|
20
12
|
dummyRun(inputData: any): void;
|
|
21
13
|
track(inputData: any, lastModelViewTransform: any, targetIndex: any): {
|
|
@@ -13,15 +13,12 @@ class Tracker {
|
|
|
13
13
|
this.inputWidth = inputWidth;
|
|
14
14
|
this.inputHeight = inputHeight;
|
|
15
15
|
this.debugMode = debugMode;
|
|
16
|
-
this.trackingKeyframeList = [];
|
|
16
|
+
this.trackingKeyframeList = []; // All octaves for all targets: [targetIndex][octaveIndex]
|
|
17
|
+
this.prebuiltData = []; // [targetIndex][octaveIndex]
|
|
17
18
|
for (let i = 0; i < trackingDataList.length; i++) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.prebuiltData = [];
|
|
22
|
-
for (let i = 0; i < this.trackingKeyframeList.length; i++) {
|
|
23
|
-
const keyframe = this.trackingKeyframeList[i];
|
|
24
|
-
this.prebuiltData[i] = {
|
|
19
|
+
const targetOctaves = trackingDataList[i];
|
|
20
|
+
this.trackingKeyframeList[i] = targetOctaves;
|
|
21
|
+
this.prebuiltData[i] = targetOctaves.map(keyframe => ({
|
|
25
22
|
px: new Float32Array(keyframe.px),
|
|
26
23
|
py: new Float32Array(keyframe.py),
|
|
27
24
|
data: new Uint8Array(keyframe.d),
|
|
@@ -30,7 +27,7 @@ class Tracker {
|
|
|
30
27
|
scale: keyframe.s,
|
|
31
28
|
// Recyclable projected image buffer
|
|
32
29
|
projectedImage: new Float32Array(keyframe.w * keyframe.h)
|
|
33
|
-
};
|
|
30
|
+
}));
|
|
34
31
|
}
|
|
35
32
|
// Pre-allocate template data buffer to avoid garbage collection
|
|
36
33
|
const templateOneSize = AR2_DEFAULT_TS;
|
|
@@ -49,14 +46,31 @@ class Tracker {
|
|
|
49
46
|
}
|
|
50
47
|
track(inputData, lastModelViewTransform, targetIndex) {
|
|
51
48
|
let debugExtra = {};
|
|
49
|
+
// Select the best octave based on current estimated distance/scale
|
|
50
|
+
// We want the octave where the marker size is closest to its projected size on screen
|
|
52
51
|
const modelViewProjectionTransform = buildModelViewProjectionTransform(this.projectionTransform, lastModelViewTransform);
|
|
53
|
-
|
|
52
|
+
// Estimate current marker width on screen
|
|
53
|
+
const [mW, mH] = this.markerDimensions[targetIndex];
|
|
54
|
+
const p0 = computeScreenCoordiate(modelViewProjectionTransform, 0, 0);
|
|
55
|
+
const p1 = computeScreenCoordiate(modelViewProjectionTransform, mW, 0);
|
|
56
|
+
const screenW = Math.sqrt((p1.x - p0.x) ** 2 + (p1.y - p0.y) ** 2);
|
|
57
|
+
// Select octave whose image width is closest to screenW
|
|
58
|
+
let octaveIndex = 0;
|
|
59
|
+
let minDiff = Infinity;
|
|
60
|
+
for (let i = 0; i < this.prebuiltData[targetIndex].length; i++) {
|
|
61
|
+
const diff = Math.abs(this.prebuiltData[targetIndex][i].width - screenW);
|
|
62
|
+
if (diff < minDiff) {
|
|
63
|
+
minDiff = diff;
|
|
64
|
+
octaveIndex = i;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const prebuilt = this.prebuiltData[targetIndex][octaveIndex];
|
|
54
68
|
// 1. Compute Projection (Warping)
|
|
55
69
|
this._computeProjection(modelViewProjectionTransform, inputData, prebuilt);
|
|
56
70
|
const projectedImage = prebuilt.projectedImage;
|
|
57
71
|
// 2. Compute Matching (NCC)
|
|
58
72
|
const { matchingPoints, sim } = this._computeMatching(prebuilt, projectedImage);
|
|
59
|
-
const trackingFrame = this.trackingKeyframeList[targetIndex];
|
|
73
|
+
const trackingFrame = this.trackingKeyframeList[targetIndex][octaveIndex];
|
|
60
74
|
const worldCoords = [];
|
|
61
75
|
const screenCoords = [];
|
|
62
76
|
const goodTrack = [];
|
|
@@ -75,6 +89,7 @@ class Tracker {
|
|
|
75
89
|
}
|
|
76
90
|
if (this.debugMode) {
|
|
77
91
|
debugExtra = {
|
|
92
|
+
octaveIndex,
|
|
78
93
|
projectedImage: Array.from(projectedImage),
|
|
79
94
|
matchingPoints,
|
|
80
95
|
goodTrack,
|
package/package.json
CHANGED
|
@@ -260,9 +260,9 @@ export class OfflineCompiler {
|
|
|
260
260
|
const dataList = this.data.map((item) => {
|
|
261
261
|
const matchingData = item.matchingData.map((kf) => this._packKeyframe(kf));
|
|
262
262
|
|
|
263
|
-
const trackingData =
|
|
263
|
+
const trackingData = item.trackingData.map((td) => {
|
|
264
264
|
const count = td.points.length;
|
|
265
|
-
//
|
|
265
|
+
// Packed Coords - Float32 for now as in current import logic
|
|
266
266
|
const px = new Float32Array(count);
|
|
267
267
|
const py = new Float32Array(count);
|
|
268
268
|
for (let i = 0; i < count; i++) {
|
|
@@ -222,71 +222,70 @@ class SimpleAR {
|
|
|
222
222
|
// 3. Get intrinsic projection from controller
|
|
223
223
|
const proj = this.controller.projectionTransform;
|
|
224
224
|
|
|
225
|
-
//
|
|
226
|
-
// We convert the OpenGL World Matrix to a CSS matrix3d.
|
|
227
|
-
// The OpenGL matrix is column-major. CSS matrix3d is also column-major.
|
|
228
|
-
const m = this.controller.getWorldMatrix(mVT, targetIndex);
|
|
229
|
-
|
|
230
|
-
// Map OpenGL coords to Screen Pixels using the projection logic
|
|
231
|
-
const vW = needsRotation ? videoH : videoW;
|
|
232
|
-
const vH = needsRotation ? videoW : videoH;
|
|
233
|
-
const perspectiveScale = Math.max(containerRect.width / vW, containerRect.height / vH);
|
|
234
|
-
const displayW = vW * perspectiveScale;
|
|
235
|
-
const displayH = vH * perspectiveScale;
|
|
236
|
-
const offsetX = (containerRect.width - displayW) / 2;
|
|
237
|
-
const offsetY = (containerRect.height - displayH) / 2;
|
|
238
|
-
|
|
239
|
-
// Adjust for centered marker and scaleMultiplier
|
|
240
|
-
const s = finalScale; // We still need the base scale factor for the pixel-to-marker mapping
|
|
241
|
-
// However, a cleaner way is to use the world matrix directly and map it.
|
|
242
|
-
|
|
243
|
-
// Actually, the simpler way to do 3D in CSS while keeping my projection logic is:
|
|
244
|
-
// Project the 4 corners and find the homography, OR
|
|
245
|
-
// Use the OpenGL matrix directly with a perspective mapping.
|
|
246
|
-
|
|
247
|
-
// Let's use the points projection to maintain the "needsRotation" logic compatibility
|
|
248
|
-
const pMid = projectToScreen(markerW / 2, markerH / 2, 0, mVT, proj, videoW, videoH, containerRect, needsRotation);
|
|
225
|
+
// 3. Project 4 corners to determine a full 3D perspective (homography)
|
|
249
226
|
const pUL = projectToScreen(0, 0, 0, mVT, proj, videoW, videoH, containerRect, needsRotation);
|
|
250
227
|
const pUR = projectToScreen(markerW, 0, 0, mVT, proj, videoW, videoH, containerRect, needsRotation);
|
|
251
228
|
const pLL = projectToScreen(0, markerH, 0, mVT, proj, videoW, videoH, containerRect, needsRotation);
|
|
229
|
+
const pLR = projectToScreen(markerW, markerH, 0, mVT, proj, videoW, videoH, containerRect, needsRotation);
|
|
230
|
+
|
|
231
|
+
// Helper to solve for 2D Homography (maps 0..1 square to pUL, pUR, pLL, pLR)
|
|
232
|
+
const solveHomography = (w, h, p1, p2, p3, p4) => {
|
|
233
|
+
const x1 = p1.sx, y1 = p1.sy;
|
|
234
|
+
const x2 = p2.sx, y2 = p2.sy;
|
|
235
|
+
const x3 = p3.sx, y3 = p3.sy;
|
|
236
|
+
const x4 = p4.sx, y4 = p4.sy;
|
|
237
|
+
|
|
238
|
+
const dx1 = x2 - x4, dx2 = x3 - x4, dx3 = x1 - x2 + x4 - x3;
|
|
239
|
+
const dy1 = y2 - y4, dy2 = y3 - y4, dy3 = y1 - y2 + y4 - y3;
|
|
240
|
+
|
|
241
|
+
let a, b, c, d, e, f, g, h_coeff;
|
|
242
|
+
|
|
243
|
+
if (dx3 === 0 && dy3 === 0) {
|
|
244
|
+
a = x2 - x1; b = x3 - x1; c = x1;
|
|
245
|
+
d = y2 - y1; e = y3 - y1; f = y1;
|
|
246
|
+
g = 0; h_coeff = 0;
|
|
247
|
+
} else {
|
|
248
|
+
const det = dx1 * dy2 - dx2 * dy1;
|
|
249
|
+
g = (dx3 * dy2 - dx2 * dy3) / det;
|
|
250
|
+
h_coeff = (dx1 * dy3 - dx3 * dy1) / det;
|
|
251
|
+
a = x2 - x1 + g * x2;
|
|
252
|
+
b = x3 - x1 + h_coeff * x3;
|
|
253
|
+
c = x1;
|
|
254
|
+
d = y2 - y1 + g * y2;
|
|
255
|
+
e = y3 - y1 + h_coeff * y3;
|
|
256
|
+
f = y1;
|
|
257
|
+
}
|
|
258
|
+
// This maps unit square (0..1) to the quadrilateral.
|
|
259
|
+
// We need to scale it by 1/w and 1/h to map (0..w, 0..h)
|
|
260
|
+
return [
|
|
261
|
+
a / w, d / w, 0, g / w,
|
|
262
|
+
b / h, e / h, 0, h_coeff / h,
|
|
263
|
+
0, 0, 1, 0,
|
|
264
|
+
c, f, 0, 1
|
|
265
|
+
];
|
|
266
|
+
};
|
|
252
267
|
|
|
253
|
-
|
|
254
|
-
const dx = pUR.sx - pUL.sx;
|
|
255
|
-
const dy = pUR.sy - pUL.sy;
|
|
256
|
-
const dz = pUR.sx - pLL.sx; // Not really Z but used for slant
|
|
257
|
-
|
|
258
|
-
const angle = Math.atan2(dy, dx);
|
|
259
|
-
const scaleX = Math.sqrt(dx * dx + dy * dy) / markerW;
|
|
260
|
-
const scaleY = Math.sqrt((pLL.sx - pUL.sx) ** 2 + (pLL.sy - pUL.sy) ** 2) / markerH;
|
|
261
|
-
|
|
262
|
-
// For true 3D tilt, we'll use the projection of the axes
|
|
263
|
-
const screenX = pMid.sx;
|
|
264
|
-
const screenY = pMid.sy;
|
|
265
|
-
|
|
266
|
-
// Final Transform applying 3D perspective via matrix3d derived from projected points
|
|
267
|
-
// NOTE: For full 3D we'd need a homography solver, but for "tilt" we can use the
|
|
268
|
-
// original modelViewTransform if we convert it carefully.
|
|
269
|
-
|
|
270
|
-
const openGLWorldMatrix = this.controller.getWorldMatrix(mVT, targetIndex);
|
|
271
|
-
// We need to apply the same scaling and offsets as projectToScreen to the matrix
|
|
268
|
+
const matrix = solveHomography(markerW, markerH, pUL, pUR, pLL, pLR);
|
|
272
269
|
|
|
270
|
+
// Apply styles
|
|
273
271
|
this.overlay.style.maxWidth = 'none';
|
|
274
272
|
this.overlay.style.width = `${markerW}px`;
|
|
275
273
|
this.overlay.style.height = `${markerH}px`;
|
|
276
274
|
this.overlay.style.position = 'absolute';
|
|
277
|
-
this.overlay.style.transformOrigin = '0 0';
|
|
275
|
+
this.overlay.style.transformOrigin = '0 0';
|
|
278
276
|
this.overlay.style.left = '0';
|
|
279
277
|
this.overlay.style.top = '0';
|
|
280
278
|
this.overlay.style.display = 'block';
|
|
281
279
|
|
|
282
|
-
//
|
|
283
|
-
//
|
|
280
|
+
// Apply 3D transform with matrix3d
|
|
281
|
+
// We also apply the user's custom scaleMultiplier AFTER the perspective transform
|
|
282
|
+
// but since we want to scale around the marker center, we apply it as a prefix/suffix
|
|
283
|
+
// Scale around top-left (0,0) is easy. Scale around center requires offset.
|
|
284
284
|
this.overlay.style.transform = `
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
${(pLL.sx - pUL.sx) / markerH}, ${(pLL.sy - pUL.sy) / markerH},
|
|
288
|
-
0, 0)
|
|
285
|
+
matrix3d(${matrix.join(',')})
|
|
286
|
+
translate(${markerW / 2}px, ${markerH / 2}px)
|
|
289
287
|
scale(${this.scaleMultiplier})
|
|
288
|
+
translate(${-markerW / 2}px, ${-markerH / 2}px)
|
|
290
289
|
`;
|
|
291
290
|
}
|
|
292
291
|
|
|
@@ -24,16 +24,13 @@ class Tracker {
|
|
|
24
24
|
this.inputHeight = inputHeight;
|
|
25
25
|
this.debugMode = debugMode;
|
|
26
26
|
|
|
27
|
-
this.trackingKeyframeList = [];
|
|
28
|
-
|
|
29
|
-
this.trackingKeyframeList.push(trackingDataList[i][TRACKING_KEYFRAME]);
|
|
30
|
-
}
|
|
27
|
+
this.trackingKeyframeList = []; // All octaves for all targets: [targetIndex][octaveIndex]
|
|
28
|
+
this.prebuiltData = []; // [targetIndex][octaveIndex]
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
this.prebuiltData[i] = {
|
|
30
|
+
for (let i = 0; i < trackingDataList.length; i++) {
|
|
31
|
+
const targetOctaves = trackingDataList[i];
|
|
32
|
+
this.trackingKeyframeList[i] = targetOctaves;
|
|
33
|
+
this.prebuiltData[i] = targetOctaves.map(keyframe => ({
|
|
37
34
|
px: new Float32Array(keyframe.px),
|
|
38
35
|
py: new Float32Array(keyframe.py),
|
|
39
36
|
data: new Uint8Array(keyframe.d),
|
|
@@ -42,7 +39,7 @@ class Tracker {
|
|
|
42
39
|
scale: keyframe.s,
|
|
43
40
|
// Recyclable projected image buffer
|
|
44
41
|
projectedImage: new Float32Array(keyframe.w * keyframe.h)
|
|
45
|
-
};
|
|
42
|
+
}));
|
|
46
43
|
}
|
|
47
44
|
|
|
48
45
|
// Pre-allocate template data buffer to avoid garbage collection
|
|
@@ -65,12 +62,31 @@ class Tracker {
|
|
|
65
62
|
track(inputData, lastModelViewTransform, targetIndex) {
|
|
66
63
|
let debugExtra = {};
|
|
67
64
|
|
|
65
|
+
// Select the best octave based on current estimated distance/scale
|
|
66
|
+
// We want the octave where the marker size is closest to its projected size on screen
|
|
68
67
|
const modelViewProjectionTransform = buildModelViewProjectionTransform(
|
|
69
68
|
this.projectionTransform,
|
|
70
69
|
lastModelViewTransform,
|
|
71
70
|
);
|
|
72
71
|
|
|
73
|
-
|
|
72
|
+
// Estimate current marker width on screen
|
|
73
|
+
const [mW, mH] = this.markerDimensions[targetIndex];
|
|
74
|
+
const p0 = computeScreenCoordiate(modelViewProjectionTransform, 0, 0);
|
|
75
|
+
const p1 = computeScreenCoordiate(modelViewProjectionTransform, mW, 0);
|
|
76
|
+
const screenW = Math.sqrt((p1.x - p0.x) ** 2 + (p1.y - p0.y) ** 2);
|
|
77
|
+
|
|
78
|
+
// Select octave whose image width is closest to screenW
|
|
79
|
+
let octaveIndex = 0;
|
|
80
|
+
let minDiff = Infinity;
|
|
81
|
+
for (let i = 0; i < this.prebuiltData[targetIndex].length; i++) {
|
|
82
|
+
const diff = Math.abs(this.prebuiltData[targetIndex][i].width - screenW);
|
|
83
|
+
if (diff < minDiff) {
|
|
84
|
+
minDiff = diff;
|
|
85
|
+
octaveIndex = i;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const prebuilt = this.prebuiltData[targetIndex][octaveIndex];
|
|
74
90
|
|
|
75
91
|
// 1. Compute Projection (Warping)
|
|
76
92
|
this._computeProjection(
|
|
@@ -87,7 +103,7 @@ class Tracker {
|
|
|
87
103
|
projectedImage
|
|
88
104
|
);
|
|
89
105
|
|
|
90
|
-
const trackingFrame = this.trackingKeyframeList[targetIndex];
|
|
106
|
+
const trackingFrame = this.trackingKeyframeList[targetIndex][octaveIndex];
|
|
91
107
|
const worldCoords = [];
|
|
92
108
|
const screenCoords = [];
|
|
93
109
|
const goodTrack = [];
|
|
@@ -113,6 +129,7 @@ class Tracker {
|
|
|
113
129
|
|
|
114
130
|
if (this.debugMode) {
|
|
115
131
|
debugExtra = {
|
|
132
|
+
octaveIndex,
|
|
116
133
|
projectedImage: Array.from(projectedImage),
|
|
117
134
|
matchingPoints,
|
|
118
135
|
goodTrack,
|