@srsergio/taptapp-ar 1.0.40 → 1.0.41
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/controller.d.ts +3 -8
- package/dist/compiler/controller.js +47 -83
- package/dist/compiler/tracker/tracker.d.ts +0 -8
- package/dist/compiler/tracker/tracker.js +10 -31
- package/package.json +1 -1
- package/src/compiler/controller.js +54 -84
- package/src/compiler/tracker/tracker.js +11 -30
|
@@ -65,16 +65,12 @@ export class Controller {
|
|
|
65
65
|
dummyRun(input: any): void;
|
|
66
66
|
getProjectionMatrix(): number[];
|
|
67
67
|
getRotatedZ90Matrix(m: any): any[];
|
|
68
|
-
getWorldMatrix(modelViewTransform: any, targetIndex: any): any[]
|
|
68
|
+
getWorldMatrix(modelViewTransform: any, targetIndex: any): any[];
|
|
69
69
|
_detectAndMatch(inputData: any, targetIndexes: any): Promise<{
|
|
70
70
|
targetIndex: any;
|
|
71
71
|
modelViewTransform: any;
|
|
72
72
|
}>;
|
|
73
|
-
_trackAndUpdate(inputData: any, lastModelViewTransform: any, targetIndex: any): Promise<
|
|
74
|
-
modelViewTransform: any;
|
|
75
|
-
inliers: number;
|
|
76
|
-
octaveIndex: number;
|
|
77
|
-
} | null>;
|
|
73
|
+
_trackAndUpdate(inputData: any, lastModelViewTransform: any, targetIndex: any): Promise<any>;
|
|
78
74
|
processVideo(input: any): void;
|
|
79
75
|
stopProcessVideo(): void;
|
|
80
76
|
detect(input: any): Promise<{
|
|
@@ -102,7 +98,6 @@ export class Controller {
|
|
|
102
98
|
x: number;
|
|
103
99
|
y: number;
|
|
104
100
|
}[];
|
|
105
|
-
octaveIndex: number;
|
|
106
101
|
debugExtra: {};
|
|
107
102
|
}>;
|
|
108
103
|
trackUpdate(modelViewTransform: any, trackFeatures: any): Promise<any>;
|
|
@@ -129,7 +124,7 @@ export class Controller {
|
|
|
129
124
|
_workerTrackUpdate(modelViewTransform: any, trackingFeatures: any): Promise<any>;
|
|
130
125
|
workerTrackDone: ((data: any) => void) | undefined;
|
|
131
126
|
_trackUpdateOnMainThread(modelViewTransform: any, trackingFeatures: any): Promise<never[][] | null>;
|
|
132
|
-
_glModelViewMatrix(modelViewTransform: any, targetIndex: any): any[]
|
|
127
|
+
_glModelViewMatrix(modelViewTransform: any, targetIndex: any): any[];
|
|
133
128
|
_glProjectionMatrix({ projectionTransform, width, height, near, far }: {
|
|
134
129
|
projectionTransform: any;
|
|
135
130
|
width: any;
|
|
@@ -181,20 +181,14 @@ class Controller {
|
|
|
181
181
|
return { targetIndex: matchedTargetIndex, modelViewTransform };
|
|
182
182
|
}
|
|
183
183
|
async _trackAndUpdate(inputData, lastModelViewTransform, targetIndex) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const result = this.tracker.track(inputData, lastModelViewTransform, targetIndex);
|
|
187
|
-
if (result.worldCoords.length < 6)
|
|
184
|
+
const { worldCoords, screenCoords } = this.tracker.track(inputData, lastModelViewTransform, targetIndex);
|
|
185
|
+
if (worldCoords.length < 6)
|
|
188
186
|
return null; // Umbral de puntos mínimos para mantener el seguimiento
|
|
189
187
|
const modelViewTransform = await this._workerTrackUpdate(lastModelViewTransform, {
|
|
190
|
-
worldCoords
|
|
191
|
-
screenCoords
|
|
188
|
+
worldCoords,
|
|
189
|
+
screenCoords,
|
|
192
190
|
});
|
|
193
|
-
return
|
|
194
|
-
modelViewTransform,
|
|
195
|
-
inliers: result.worldCoords.length,
|
|
196
|
-
octaveIndex: result.octaveIndex
|
|
197
|
-
};
|
|
191
|
+
return modelViewTransform;
|
|
198
192
|
}
|
|
199
193
|
processVideo(input) {
|
|
200
194
|
if (this.processingVideo)
|
|
@@ -208,7 +202,6 @@ class Controller {
|
|
|
208
202
|
currentModelViewTransform: null,
|
|
209
203
|
trackCount: 0,
|
|
210
204
|
trackMiss: 0,
|
|
211
|
-
stabilityCount: 0, // Nuevo: Contador para Live Adaptation
|
|
212
205
|
filter: new OneEuroFilter({ minCutOff: this.filterMinCF, beta: this.filterBeta }),
|
|
213
206
|
});
|
|
214
207
|
}
|
|
@@ -221,22 +214,18 @@ class Controller {
|
|
|
221
214
|
return acc + (!!s.isTracking ? 1 : 0);
|
|
222
215
|
}, 0);
|
|
223
216
|
// detect and match only if less then maxTrack
|
|
224
|
-
// BUG FIX: Only match if we are NOT in a "ghosting" period for a target
|
|
225
|
-
// to prevent the "found but immediately lost" loop that keeps opacity at 1.
|
|
226
217
|
if (nTracking < this.maxTrack) {
|
|
227
218
|
const matchingIndexes = [];
|
|
228
219
|
for (let i = 0; i < this.trackingStates.length; i++) {
|
|
229
220
|
const trackingState = this.trackingStates[i];
|
|
230
221
|
if (trackingState.isTracking === true)
|
|
231
222
|
continue;
|
|
232
|
-
if (trackingState.showing === true)
|
|
233
|
-
continue; // Don't try to re-detect if we are still buffers-showing the last position
|
|
234
223
|
if (this.interestedTargetIndex !== -1 && this.interestedTargetIndex !== i)
|
|
235
224
|
continue;
|
|
236
225
|
matchingIndexes.push(i);
|
|
237
226
|
}
|
|
238
227
|
const { targetIndex: matchedTargetIndex, modelViewTransform } = await this._detectAndMatch(inputData, matchingIndexes);
|
|
239
|
-
if (matchedTargetIndex !== -1
|
|
228
|
+
if (matchedTargetIndex !== -1) {
|
|
240
229
|
this.trackingStates[matchedTargetIndex].isTracking = true;
|
|
241
230
|
this.trackingStates[matchedTargetIndex].currentModelViewTransform = modelViewTransform;
|
|
242
231
|
}
|
|
@@ -245,80 +234,57 @@ class Controller {
|
|
|
245
234
|
for (let i = 0; i < this.trackingStates.length; i++) {
|
|
246
235
|
const trackingState = this.trackingStates[i];
|
|
247
236
|
if (trackingState.isTracking) {
|
|
248
|
-
|
|
237
|
+
let modelViewTransform = await this._trackAndUpdate(inputData, trackingState.currentModelViewTransform, i);
|
|
238
|
+
if (modelViewTransform === null) {
|
|
249
239
|
trackingState.isTracking = false;
|
|
250
|
-
trackingState.stabilityCount = 0;
|
|
251
240
|
}
|
|
252
241
|
else {
|
|
253
|
-
|
|
254
|
-
if (result === null) {
|
|
255
|
-
trackingState.isTracking = false;
|
|
256
|
-
trackingState.stabilityCount = 0;
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
trackingState.currentModelViewTransform = result.modelViewTransform;
|
|
260
|
-
// --- LIVE MODEL ADAPTATION LOGIC ---
|
|
261
|
-
// Si el tracking es muy sólido (muchos inliers) y estable, refinamos el modelo
|
|
262
|
-
// Requisito: > 35 inliers (muy exigente) para evitar polución por ruido
|
|
263
|
-
if (result.inliers > 35) {
|
|
264
|
-
trackingState.stabilityCount++;
|
|
265
|
-
if (trackingState.stabilityCount > 30) { // 30 frames (~1s) de estabilidad absoluta
|
|
266
|
-
this.tracker.applyLiveFeedback(i, result.octaveIndex, 0.05); // Menor alpha (5%) para ser más conservador
|
|
267
|
-
if (this.debugMode)
|
|
268
|
-
console.log(`✨ Live Reification: Target ${i} (Octave ${result.octaveIndex}) updated.`);
|
|
269
|
-
trackingState.stabilityCount = 0;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
else {
|
|
273
|
-
trackingState.stabilityCount = Math.max(0, trackingState.stabilityCount - 1);
|
|
274
|
-
}
|
|
275
|
-
// -----------------------------------
|
|
276
|
-
}
|
|
242
|
+
trackingState.currentModelViewTransform = modelViewTransform;
|
|
277
243
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
244
|
+
}
|
|
245
|
+
// if not showing, then show it once it reaches warmup number of frames
|
|
246
|
+
if (!trackingState.showing) {
|
|
247
|
+
if (trackingState.isTracking) {
|
|
248
|
+
trackingState.trackMiss = 0;
|
|
249
|
+
trackingState.trackCount += 1;
|
|
250
|
+
if (trackingState.trackCount > this.warmupTolerance) {
|
|
251
|
+
trackingState.showing = true;
|
|
252
|
+
trackingState.trackingMatrix = null;
|
|
253
|
+
trackingState.filter.reset();
|
|
288
254
|
}
|
|
289
255
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
else {
|
|
303
|
-
trackingState.trackMiss = 0;
|
|
256
|
+
}
|
|
257
|
+
// if showing, then count miss, and hide it when reaches tolerance
|
|
258
|
+
if (trackingState.showing) {
|
|
259
|
+
if (!trackingState.isTracking) {
|
|
260
|
+
trackingState.trackCount = 0;
|
|
261
|
+
trackingState.trackMiss += 1;
|
|
262
|
+
if (trackingState.trackMiss > this.missTolerance) {
|
|
263
|
+
trackingState.showing = false;
|
|
264
|
+
trackingState.trackingMatrix = null;
|
|
265
|
+
this.onUpdate &&
|
|
266
|
+
this.onUpdate({ type: "updateMatrix", targetIndex: i, worldMatrix: null });
|
|
304
267
|
}
|
|
305
268
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
const worldMatrix = this._glModelViewMatrix(trackingState.currentModelViewTransform, i);
|
|
309
|
-
trackingState.trackingMatrix = trackingState.filter.filter(Date.now(), worldMatrix);
|
|
310
|
-
let clone = [];
|
|
311
|
-
for (let j = 0; j < trackingState.trackingMatrix.length; j++) {
|
|
312
|
-
clone[j] = trackingState.trackingMatrix[j];
|
|
313
|
-
}
|
|
314
|
-
const isInputRotated = input.width === this.inputHeight && input.height === this.inputWidth;
|
|
315
|
-
if (isInputRotated) {
|
|
316
|
-
clone = this.getRotatedZ90Matrix(clone);
|
|
317
|
-
}
|
|
318
|
-
this.onUpdate &&
|
|
319
|
-
this.onUpdate({ type: "updateMatrix", targetIndex: i, worldMatrix: clone, modelViewTransform: trackingState.currentModelViewTransform });
|
|
269
|
+
else {
|
|
270
|
+
trackingState.trackMiss = 0;
|
|
320
271
|
}
|
|
321
272
|
}
|
|
273
|
+
// if showing, then call onUpdate, with world matrix
|
|
274
|
+
if (trackingState.showing) {
|
|
275
|
+
const worldMatrix = this._glModelViewMatrix(trackingState.currentModelViewTransform, i);
|
|
276
|
+
trackingState.trackingMatrix = trackingState.filter.filter(Date.now(), worldMatrix);
|
|
277
|
+
let clone = [];
|
|
278
|
+
for (let j = 0; j < trackingState.trackingMatrix.length; j++) {
|
|
279
|
+
clone[j] = trackingState.trackingMatrix[j];
|
|
280
|
+
}
|
|
281
|
+
const isInputRotated = input.width === this.inputHeight && input.height === this.inputWidth;
|
|
282
|
+
if (isInputRotated) {
|
|
283
|
+
clone = this.getRotatedZ90Matrix(clone);
|
|
284
|
+
}
|
|
285
|
+
this.onUpdate &&
|
|
286
|
+
this.onUpdate({ type: "updateMatrix", targetIndex: i, worldMatrix: clone, modelViewTransform: trackingState.currentModelViewTransform });
|
|
287
|
+
}
|
|
322
288
|
}
|
|
323
289
|
this.onUpdate && this.onUpdate({ type: "processDone" });
|
|
324
290
|
// Use requestAnimationFrame if available, otherwise just wait briefly
|
|
@@ -446,8 +412,6 @@ class Controller {
|
|
|
446
412
|
return finalModelViewTransform;
|
|
447
413
|
}
|
|
448
414
|
_glModelViewMatrix(modelViewTransform, targetIndex) {
|
|
449
|
-
if (!modelViewTransform)
|
|
450
|
-
return null;
|
|
451
415
|
const height = this.markerDimensions[targetIndex][1];
|
|
452
416
|
const openGLWorldMatrix = [
|
|
453
417
|
modelViewTransform[0][0],
|
|
@@ -20,7 +20,6 @@ export class Tracker {
|
|
|
20
20
|
x: number;
|
|
21
21
|
y: number;
|
|
22
22
|
}[];
|
|
23
|
-
octaveIndex: number;
|
|
24
23
|
debugExtra: {};
|
|
25
24
|
};
|
|
26
25
|
/**
|
|
@@ -34,11 +33,4 @@ export class Tracker {
|
|
|
34
33
|
* Pure JS implementation of Bilinear Warping
|
|
35
34
|
*/
|
|
36
35
|
_computeProjection(M: any, inputData: any, prebuilt: any): void;
|
|
37
|
-
/**
|
|
38
|
-
* Refines the target data (Living Mind Map) using actual camera feedback
|
|
39
|
-
* @param {number} targetIndex
|
|
40
|
-
* @param {number} octaveIndex
|
|
41
|
-
* @param {number} alpha - Blending factor (e.g. 0.1 for 10% new data)
|
|
42
|
-
*/
|
|
43
|
-
applyLiveFeedback(targetIndex: number, octaveIndex: number, alpha: number): void;
|
|
44
36
|
}
|
|
@@ -46,8 +46,6 @@ class Tracker {
|
|
|
46
46
|
}
|
|
47
47
|
track(inputData, lastModelViewTransform, targetIndex) {
|
|
48
48
|
let debugExtra = {};
|
|
49
|
-
if (!lastModelViewTransform)
|
|
50
|
-
return { worldCoords: [], screenCoords: [], octaveIndex: 0, debugExtra };
|
|
51
49
|
// Select the best octave based on current estimated distance/scale
|
|
52
50
|
// We want the octave where the marker size is closest to its projected size on screen
|
|
53
51
|
const modelViewProjectionTransform = buildModelViewProjectionTransform(this.projectionTransform, lastModelViewTransform);
|
|
@@ -89,7 +87,16 @@ class Tracker {
|
|
|
89
87
|
});
|
|
90
88
|
}
|
|
91
89
|
}
|
|
92
|
-
|
|
90
|
+
if (this.debugMode) {
|
|
91
|
+
debugExtra = {
|
|
92
|
+
octaveIndex,
|
|
93
|
+
projectedImage: Array.from(projectedImage),
|
|
94
|
+
matchingPoints,
|
|
95
|
+
goodTrack,
|
|
96
|
+
trackedPoints: screenCoords,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return { worldCoords, screenCoords, debugExtra };
|
|
93
100
|
}
|
|
94
101
|
/**
|
|
95
102
|
* Pure JS implementation of NCC matching
|
|
@@ -225,33 +232,5 @@ class Tracker {
|
|
|
225
232
|
}
|
|
226
233
|
}
|
|
227
234
|
}
|
|
228
|
-
/**
|
|
229
|
-
* Refines the target data (Living Mind Map) using actual camera feedback
|
|
230
|
-
* @param {number} targetIndex
|
|
231
|
-
* @param {number} octaveIndex
|
|
232
|
-
* @param {number} alpha - Blending factor (e.g. 0.1 for 10% new data)
|
|
233
|
-
*/
|
|
234
|
-
applyLiveFeedback(targetIndex, octaveIndex, alpha) {
|
|
235
|
-
if (targetIndex === undefined || octaveIndex === undefined)
|
|
236
|
-
return;
|
|
237
|
-
const targetPrebuilts = this.prebuiltData[targetIndex];
|
|
238
|
-
if (!targetPrebuilts)
|
|
239
|
-
return;
|
|
240
|
-
const prebuilt = targetPrebuilts[octaveIndex];
|
|
241
|
-
if (!prebuilt || !prebuilt.projectedImage || !prebuilt.data)
|
|
242
|
-
return;
|
|
243
|
-
const markerPixels = prebuilt.data;
|
|
244
|
-
const projectedPixels = prebuilt.projectedImage;
|
|
245
|
-
const count = markerPixels.length;
|
|
246
|
-
// Blend the projected (camera-sourced) pixels into the marker reference data
|
|
247
|
-
// This allows the NCC matching to adapt to real-world lighting and print quality
|
|
248
|
-
for (let i = 0; i < count; i++) {
|
|
249
|
-
const val = projectedPixels[i];
|
|
250
|
-
if (isNaN(val))
|
|
251
|
-
continue; // Don't pollute with NaN
|
|
252
|
-
// Simple linear blend
|
|
253
|
-
markerPixels[i] = (1 - alpha) * markerPixels[i] + alpha * val;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
235
|
}
|
|
257
236
|
export { Tracker };
|
package/package.json
CHANGED
|
@@ -226,22 +226,17 @@ class Controller {
|
|
|
226
226
|
return { targetIndex: matchedTargetIndex, modelViewTransform };
|
|
227
227
|
}
|
|
228
228
|
async _trackAndUpdate(inputData, lastModelViewTransform, targetIndex) {
|
|
229
|
-
|
|
230
|
-
const result = this.tracker.track(
|
|
229
|
+
const { worldCoords, screenCoords } = this.tracker.track(
|
|
231
230
|
inputData,
|
|
232
231
|
lastModelViewTransform,
|
|
233
232
|
targetIndex,
|
|
234
233
|
);
|
|
235
|
-
if (
|
|
234
|
+
if (worldCoords.length < 6) return null; // Umbral de puntos mínimos para mantener el seguimiento
|
|
236
235
|
const modelViewTransform = await this._workerTrackUpdate(lastModelViewTransform, {
|
|
237
|
-
worldCoords
|
|
238
|
-
screenCoords
|
|
236
|
+
worldCoords,
|
|
237
|
+
screenCoords,
|
|
239
238
|
});
|
|
240
|
-
return
|
|
241
|
-
modelViewTransform,
|
|
242
|
-
inliers: result.worldCoords.length,
|
|
243
|
-
octaveIndex: result.octaveIndex
|
|
244
|
-
};
|
|
239
|
+
return modelViewTransform;
|
|
245
240
|
}
|
|
246
241
|
|
|
247
242
|
processVideo(input) {
|
|
@@ -257,7 +252,6 @@ class Controller {
|
|
|
257
252
|
currentModelViewTransform: null,
|
|
258
253
|
trackCount: 0,
|
|
259
254
|
trackMiss: 0,
|
|
260
|
-
stabilityCount: 0, // Nuevo: Contador para Live Adaptation
|
|
261
255
|
filter: new OneEuroFilter({ minCutOff: this.filterMinCF, beta: this.filterBeta }),
|
|
262
256
|
});
|
|
263
257
|
}
|
|
@@ -273,14 +267,11 @@ class Controller {
|
|
|
273
267
|
}, 0);
|
|
274
268
|
|
|
275
269
|
// detect and match only if less then maxTrack
|
|
276
|
-
// BUG FIX: Only match if we are NOT in a "ghosting" period for a target
|
|
277
|
-
// to prevent the "found but immediately lost" loop that keeps opacity at 1.
|
|
278
270
|
if (nTracking < this.maxTrack) {
|
|
279
271
|
const matchingIndexes = [];
|
|
280
272
|
for (let i = 0; i < this.trackingStates.length; i++) {
|
|
281
273
|
const trackingState = this.trackingStates[i];
|
|
282
274
|
if (trackingState.isTracking === true) continue;
|
|
283
|
-
if (trackingState.showing === true) continue; // Don't try to re-detect if we are still buffers-showing the last position
|
|
284
275
|
if (this.interestedTargetIndex !== -1 && this.interestedTargetIndex !== i) continue;
|
|
285
276
|
|
|
286
277
|
matchingIndexes.push(i);
|
|
@@ -289,7 +280,7 @@ class Controller {
|
|
|
289
280
|
const { targetIndex: matchedTargetIndex, modelViewTransform } =
|
|
290
281
|
await this._detectAndMatch(inputData, matchingIndexes);
|
|
291
282
|
|
|
292
|
-
if (matchedTargetIndex !== -1
|
|
283
|
+
if (matchedTargetIndex !== -1) {
|
|
293
284
|
this.trackingStates[matchedTargetIndex].isTracking = true;
|
|
294
285
|
this.trackingStates[matchedTargetIndex].currentModelViewTransform = modelViewTransform;
|
|
295
286
|
}
|
|
@@ -300,89 +291,69 @@ class Controller {
|
|
|
300
291
|
const trackingState = this.trackingStates[i];
|
|
301
292
|
|
|
302
293
|
if (trackingState.isTracking) {
|
|
303
|
-
|
|
294
|
+
let modelViewTransform = await this._trackAndUpdate(
|
|
295
|
+
inputData,
|
|
296
|
+
trackingState.currentModelViewTransform,
|
|
297
|
+
i,
|
|
298
|
+
);
|
|
299
|
+
if (modelViewTransform === null) {
|
|
304
300
|
trackingState.isTracking = false;
|
|
305
|
-
trackingState.stabilityCount = 0;
|
|
306
301
|
} else {
|
|
307
|
-
|
|
308
|
-
inputData,
|
|
309
|
-
trackingState.currentModelViewTransform,
|
|
310
|
-
i,
|
|
311
|
-
);
|
|
312
|
-
if (result === null) {
|
|
313
|
-
trackingState.isTracking = false;
|
|
314
|
-
trackingState.stabilityCount = 0;
|
|
315
|
-
} else {
|
|
316
|
-
trackingState.currentModelViewTransform = result.modelViewTransform;
|
|
317
|
-
|
|
318
|
-
// --- LIVE MODEL ADAPTATION LOGIC ---
|
|
319
|
-
// Si el tracking es muy sólido (muchos inliers) y estable, refinamos el modelo
|
|
320
|
-
// Requisito: > 35 inliers (muy exigente) para evitar polución por ruido
|
|
321
|
-
if (result.inliers > 35) {
|
|
322
|
-
trackingState.stabilityCount++;
|
|
323
|
-
if (trackingState.stabilityCount > 30) { // 30 frames (~1s) de estabilidad absoluta
|
|
324
|
-
this.tracker.applyLiveFeedback(i, result.octaveIndex, 0.05); // Menor alpha (5%) para ser más conservador
|
|
325
|
-
if (this.debugMode) console.log(`✨ Live Reification: Target ${i} (Octave ${result.octaveIndex}) updated.`);
|
|
326
|
-
trackingState.stabilityCount = 0;
|
|
327
|
-
}
|
|
328
|
-
} else {
|
|
329
|
-
trackingState.stabilityCount = Math.max(0, trackingState.stabilityCount - 1);
|
|
330
|
-
}
|
|
331
|
-
// -----------------------------------
|
|
332
|
-
}
|
|
302
|
+
trackingState.currentModelViewTransform = modelViewTransform;
|
|
333
303
|
}
|
|
304
|
+
}
|
|
334
305
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}
|
|
306
|
+
// if not showing, then show it once it reaches warmup number of frames
|
|
307
|
+
if (!trackingState.showing) {
|
|
308
|
+
if (trackingState.isTracking) {
|
|
309
|
+
trackingState.trackMiss = 0;
|
|
310
|
+
trackingState.trackCount += 1;
|
|
311
|
+
if (trackingState.trackCount > this.warmupTolerance) {
|
|
312
|
+
trackingState.showing = true;
|
|
313
|
+
trackingState.trackingMatrix = null;
|
|
314
|
+
trackingState.filter.reset();
|
|
345
315
|
}
|
|
346
316
|
}
|
|
317
|
+
}
|
|
347
318
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}
|
|
360
|
-
} else {
|
|
361
|
-
trackingState.trackMiss = 0;
|
|
319
|
+
// if showing, then count miss, and hide it when reaches tolerance
|
|
320
|
+
if (trackingState.showing) {
|
|
321
|
+
if (!trackingState.isTracking) {
|
|
322
|
+
trackingState.trackCount = 0;
|
|
323
|
+
trackingState.trackMiss += 1;
|
|
324
|
+
|
|
325
|
+
if (trackingState.trackMiss > this.missTolerance) {
|
|
326
|
+
trackingState.showing = false;
|
|
327
|
+
trackingState.trackingMatrix = null;
|
|
328
|
+
this.onUpdate &&
|
|
329
|
+
this.onUpdate({ type: "updateMatrix", targetIndex: i, worldMatrix: null });
|
|
362
330
|
}
|
|
331
|
+
} else {
|
|
332
|
+
trackingState.trackMiss = 0;
|
|
363
333
|
}
|
|
334
|
+
}
|
|
364
335
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
let clone = [];
|
|
371
|
-
for (let j = 0; j < trackingState.trackingMatrix.length; j++) {
|
|
372
|
-
clone[j] = trackingState.trackingMatrix[j];
|
|
373
|
-
}
|
|
336
|
+
// if showing, then call onUpdate, with world matrix
|
|
337
|
+
if (trackingState.showing) {
|
|
338
|
+
const worldMatrix = this._glModelViewMatrix(trackingState.currentModelViewTransform, i);
|
|
339
|
+
trackingState.trackingMatrix = trackingState.filter.filter(Date.now(), worldMatrix);
|
|
374
340
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}
|
|
341
|
+
let clone = [];
|
|
342
|
+
for (let j = 0; j < trackingState.trackingMatrix.length; j++) {
|
|
343
|
+
clone[j] = trackingState.trackingMatrix[j];
|
|
344
|
+
}
|
|
380
345
|
|
|
381
|
-
|
|
382
|
-
|
|
346
|
+
const isInputRotated =
|
|
347
|
+
input.width === this.inputHeight && input.height === this.inputWidth;
|
|
348
|
+
if (isInputRotated) {
|
|
349
|
+
clone = this.getRotatedZ90Matrix(clone);
|
|
383
350
|
}
|
|
351
|
+
|
|
352
|
+
this.onUpdate &&
|
|
353
|
+
this.onUpdate({ type: "updateMatrix", targetIndex: i, worldMatrix: clone, modelViewTransform: trackingState.currentModelViewTransform });
|
|
384
354
|
}
|
|
385
355
|
}
|
|
356
|
+
|
|
386
357
|
this.onUpdate && this.onUpdate({ type: "processDone" });
|
|
387
358
|
|
|
388
359
|
// Use requestAnimationFrame if available, otherwise just wait briefly
|
|
@@ -530,7 +501,6 @@ class Controller {
|
|
|
530
501
|
}
|
|
531
502
|
|
|
532
503
|
_glModelViewMatrix(modelViewTransform, targetIndex) {
|
|
533
|
-
if (!modelViewTransform) return null;
|
|
534
504
|
const height = this.markerDimensions[targetIndex][1];
|
|
535
505
|
|
|
536
506
|
const openGLWorldMatrix = [
|
|
@@ -61,7 +61,6 @@ class Tracker {
|
|
|
61
61
|
|
|
62
62
|
track(inputData, lastModelViewTransform, targetIndex) {
|
|
63
63
|
let debugExtra = {};
|
|
64
|
-
if (!lastModelViewTransform) return { worldCoords: [], screenCoords: [], octaveIndex: 0, debugExtra };
|
|
65
64
|
|
|
66
65
|
// Select the best octave based on current estimated distance/scale
|
|
67
66
|
// We want the octave where the marker size is closest to its projected size on screen
|
|
@@ -128,7 +127,17 @@ class Tracker {
|
|
|
128
127
|
}
|
|
129
128
|
}
|
|
130
129
|
|
|
131
|
-
|
|
130
|
+
if (this.debugMode) {
|
|
131
|
+
debugExtra = {
|
|
132
|
+
octaveIndex,
|
|
133
|
+
projectedImage: Array.from(projectedImage),
|
|
134
|
+
matchingPoints,
|
|
135
|
+
goodTrack,
|
|
136
|
+
trackedPoints: screenCoords,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return { worldCoords, screenCoords, debugExtra };
|
|
132
141
|
}
|
|
133
142
|
|
|
134
143
|
/**
|
|
@@ -291,34 +300,6 @@ class Tracker {
|
|
|
291
300
|
}
|
|
292
301
|
}
|
|
293
302
|
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Refines the target data (Living Mind Map) using actual camera feedback
|
|
297
|
-
* @param {number} targetIndex
|
|
298
|
-
* @param {number} octaveIndex
|
|
299
|
-
* @param {number} alpha - Blending factor (e.g. 0.1 for 10% new data)
|
|
300
|
-
*/
|
|
301
|
-
applyLiveFeedback(targetIndex, octaveIndex, alpha) {
|
|
302
|
-
if (targetIndex === undefined || octaveIndex === undefined) return;
|
|
303
|
-
const targetPrebuilts = this.prebuiltData[targetIndex];
|
|
304
|
-
if (!targetPrebuilts) return;
|
|
305
|
-
|
|
306
|
-
const prebuilt = targetPrebuilts[octaveIndex];
|
|
307
|
-
if (!prebuilt || !prebuilt.projectedImage || !prebuilt.data) return;
|
|
308
|
-
|
|
309
|
-
const markerPixels = prebuilt.data;
|
|
310
|
-
const projectedPixels = prebuilt.projectedImage;
|
|
311
|
-
const count = markerPixels.length;
|
|
312
|
-
|
|
313
|
-
// Blend the projected (camera-sourced) pixels into the marker reference data
|
|
314
|
-
// This allows the NCC matching to adapt to real-world lighting and print quality
|
|
315
|
-
for (let i = 0; i < count; i++) {
|
|
316
|
-
const val = projectedPixels[i];
|
|
317
|
-
if (isNaN(val)) continue; // Don't pollute with NaN
|
|
318
|
-
// Simple linear blend
|
|
319
|
-
markerPixels[i] = (1 - alpha) * markerPixels[i] + alpha * val;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
303
|
}
|
|
323
304
|
|
|
324
305
|
export { Tracker };
|