@srsergio/taptapp-ar 1.0.42 → 1.0.50
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/README.md +42 -45
- package/dist/compiler/aframe.js +8 -8
- package/dist/compiler/controller.d.ts +50 -76
- package/dist/compiler/controller.js +72 -116
- package/dist/compiler/detector/detector-lite.js +82 -99
- package/dist/compiler/index.js +3 -3
- package/dist/compiler/matching/hamming-distance.d.ts +8 -0
- package/dist/compiler/matching/hamming-distance.js +35 -16
- package/dist/compiler/matching/hierarchical-clustering.d.ts +9 -0
- package/dist/compiler/matching/hierarchical-clustering.js +76 -56
- package/dist/compiler/matching/matching.js +3 -3
- package/dist/compiler/node-worker.js +144 -18
- package/dist/compiler/offline-compiler.d.ts +34 -83
- package/dist/compiler/offline-compiler.js +92 -96
- package/dist/compiler/simple-ar.d.ts +31 -57
- package/dist/compiler/simple-ar.js +32 -73
- package/dist/compiler/three.d.ts +13 -8
- package/dist/compiler/three.js +6 -6
- package/dist/compiler/tracker/extract.js +17 -14
- package/dist/compiler/utils/images.js +11 -16
- package/dist/compiler/utils/lsh-direct.d.ts +12 -0
- package/dist/compiler/utils/lsh-direct.js +76 -0
- package/dist/compiler/utils/worker-pool.js +10 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/react/types.d.ts +1 -1
- package/dist/react/types.js +1 -1
- package/package.json +2 -1
- package/src/compiler/aframe.js +8 -8
- package/src/compiler/controller.ts +512 -0
- package/src/compiler/detector/detector-lite.js +87 -107
- package/src/compiler/index.js +3 -3
- package/src/compiler/matching/hamming-distance.js +39 -16
- package/src/compiler/matching/hierarchical-clustering.js +85 -57
- package/src/compiler/matching/matching.js +3 -3
- package/src/compiler/node-worker.js +163 -18
- package/src/compiler/offline-compiler.ts +513 -0
- package/src/compiler/{simple-ar.js → simple-ar.ts} +64 -91
- package/src/compiler/three.js +6 -6
- package/src/compiler/tracker/extract.js +18 -15
- package/src/compiler/utils/images.js +11 -21
- package/src/compiler/utils/lsh-direct.js +86 -0
- package/src/compiler/utils/worker-pool.js +9 -1
- package/src/index.ts +2 -2
- package/src/react/types.ts +2 -2
- package/src/compiler/controller.js +0 -554
- package/src/compiler/offline-compiler.js +0 -515
|
@@ -1,554 +0,0 @@
|
|
|
1
|
-
import { Tracker } from "./tracker/tracker.js";
|
|
2
|
-
import { CropDetector } from "./detector/crop-detector.js";
|
|
3
|
-
import { OfflineCompiler as Compiler } from "./offline-compiler.js";
|
|
4
|
-
import { InputLoader } from "./input-loader.js";
|
|
5
|
-
import { OneEuroFilter } from "../libs/one-euro-filter.js";
|
|
6
|
-
|
|
7
|
-
let ControllerWorker;
|
|
8
|
-
|
|
9
|
-
// Conditional import for worker to avoid crash in non-vite environments
|
|
10
|
-
try {
|
|
11
|
-
const workerModule = await import("./controller.worker.js?worker&inline");
|
|
12
|
-
ControllerWorker = workerModule.default;
|
|
13
|
-
} catch (e) {
|
|
14
|
-
// Fallback for tests or other environments
|
|
15
|
-
ControllerWorker = null;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const DEFAULT_FILTER_CUTOFF = 0.1; // Menor cutoff para filtrar más ruidos cuando está quieto
|
|
19
|
-
const DEFAULT_FILTER_BETA = 0.01; // Beta bajo para suavizar movimientos rápidos
|
|
20
|
-
|
|
21
|
-
const DEFAULT_WARMUP_TOLERANCE = 8; // Más frames de calentamiento para asegurar estabilidad inicial
|
|
22
|
-
|
|
23
|
-
const DEFAULT_MISS_TOLERANCE = 2; // Reducido para que el objeto desaparezca más rápido tras pérdida
|
|
24
|
-
|
|
25
|
-
class Controller {
|
|
26
|
-
constructor({
|
|
27
|
-
inputWidth,
|
|
28
|
-
inputHeight,
|
|
29
|
-
onUpdate = null,
|
|
30
|
-
debugMode = false,
|
|
31
|
-
maxTrack = 1,
|
|
32
|
-
warmupTolerance = null,
|
|
33
|
-
missTolerance = null,
|
|
34
|
-
filterMinCF = null,
|
|
35
|
-
filterBeta = null,
|
|
36
|
-
worker = null, // Allow custom worker injection
|
|
37
|
-
}) {
|
|
38
|
-
this.inputWidth = inputWidth;
|
|
39
|
-
this.inputHeight = inputHeight;
|
|
40
|
-
this.maxTrack = maxTrack;
|
|
41
|
-
this.filterMinCF = filterMinCF === null ? DEFAULT_FILTER_CUTOFF : filterMinCF;
|
|
42
|
-
this.filterBeta = filterBeta === null ? DEFAULT_FILTER_BETA : filterBeta;
|
|
43
|
-
this.warmupTolerance = warmupTolerance === null ? DEFAULT_WARMUP_TOLERANCE : warmupTolerance;
|
|
44
|
-
this.missTolerance = missTolerance === null ? DEFAULT_MISS_TOLERANCE : missTolerance;
|
|
45
|
-
this.cropDetector = new CropDetector(this.inputWidth, this.inputHeight, debugMode, true);
|
|
46
|
-
this.inputLoader = new InputLoader(this.inputWidth, this.inputHeight);
|
|
47
|
-
this.markerDimensions = null;
|
|
48
|
-
this.onUpdate = onUpdate;
|
|
49
|
-
this.debugMode = debugMode;
|
|
50
|
-
this.processingVideo = false;
|
|
51
|
-
this.interestedTargetIndex = -1;
|
|
52
|
-
this.trackingStates = [];
|
|
53
|
-
this.worker = worker;
|
|
54
|
-
if (this.worker) this._setupWorkerListener();
|
|
55
|
-
|
|
56
|
-
const near = 10;
|
|
57
|
-
const far = 100000;
|
|
58
|
-
const fovy = (45.0 * Math.PI) / 180;
|
|
59
|
-
const f = this.inputHeight / 2 / Math.tan(fovy / 2);
|
|
60
|
-
|
|
61
|
-
this.projectionTransform = [
|
|
62
|
-
[f, 0, this.inputWidth / 2],
|
|
63
|
-
[0, f, this.inputHeight / 2],
|
|
64
|
-
[0, 0, 1],
|
|
65
|
-
];
|
|
66
|
-
|
|
67
|
-
this.projectionMatrix = this._glProjectionMatrix({
|
|
68
|
-
projectionTransform: this.projectionTransform,
|
|
69
|
-
width: this.inputWidth,
|
|
70
|
-
height: this.inputHeight,
|
|
71
|
-
near: near,
|
|
72
|
-
far: far,
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
_setupWorkerListener() {
|
|
77
|
-
if (!this.worker) return;
|
|
78
|
-
this.worker.onmessage = (e) => {
|
|
79
|
-
if (e.data.type === "matchDone" && this.workerMatchDone !== null) {
|
|
80
|
-
this.workerMatchDone(e.data);
|
|
81
|
-
}
|
|
82
|
-
if (e.data.type === "trackUpdateDone" && this.workerTrackDone !== null) {
|
|
83
|
-
this.workerTrackDone(e.data);
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
_ensureWorker() {
|
|
89
|
-
if (this.worker) return;
|
|
90
|
-
if (ControllerWorker) {
|
|
91
|
-
this.worker = new ControllerWorker();
|
|
92
|
-
this._setupWorkerListener();
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Load image targets from one or multiple .mind files
|
|
98
|
-
* @param {string|string[]} fileURLs - Single URL or array of URLs to .mind files
|
|
99
|
-
* @returns {Promise<{dimensions, matchingDataList, trackingDataList}>}
|
|
100
|
-
*/
|
|
101
|
-
async addImageTargets(fileURLs) {
|
|
102
|
-
const urls = Array.isArray(fileURLs) ? fileURLs : [fileURLs];
|
|
103
|
-
|
|
104
|
-
// Fetch all .mind files in parallel
|
|
105
|
-
const buffers = await Promise.all(
|
|
106
|
-
urls.map(async (url) => {
|
|
107
|
-
const response = await fetch(url);
|
|
108
|
-
return response.arrayBuffer();
|
|
109
|
-
})
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
// Combine all buffers into a single target list
|
|
113
|
-
return this.addImageTargetsFromBuffers(buffers);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Load image targets from multiple ArrayBuffers
|
|
118
|
-
* @param {ArrayBuffer[]} buffers - Array of .mind file buffers
|
|
119
|
-
*/
|
|
120
|
-
addImageTargetsFromBuffers(buffers) {
|
|
121
|
-
const allTrackingData = [];
|
|
122
|
-
const allMatchingData = [];
|
|
123
|
-
const allDimensions = [];
|
|
124
|
-
|
|
125
|
-
for (const buffer of buffers) {
|
|
126
|
-
const compiler = new Compiler();
|
|
127
|
-
const { dataList } = compiler.importData(buffer);
|
|
128
|
-
|
|
129
|
-
for (const item of dataList) {
|
|
130
|
-
allMatchingData.push(item.matchingData);
|
|
131
|
-
allTrackingData.push(item.trackingData);
|
|
132
|
-
allDimensions.push([item.targetImage.width, item.targetImage.height]);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
this.tracker = new Tracker(
|
|
137
|
-
allDimensions,
|
|
138
|
-
allTrackingData,
|
|
139
|
-
this.projectionTransform,
|
|
140
|
-
this.inputWidth,
|
|
141
|
-
this.inputHeight,
|
|
142
|
-
this.debugMode,
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
this._ensureWorker();
|
|
146
|
-
if (this.worker) {
|
|
147
|
-
this.worker.postMessage({
|
|
148
|
-
type: "setup",
|
|
149
|
-
inputWidth: this.inputWidth,
|
|
150
|
-
inputHeight: this.inputHeight,
|
|
151
|
-
projectionTransform: this.projectionTransform,
|
|
152
|
-
debugMode: this.debugMode,
|
|
153
|
-
matchingDataList: allMatchingData,
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
this.markerDimensions = allDimensions;
|
|
158
|
-
this.matchingDataList = allMatchingData; // Store for main-thread fallback
|
|
159
|
-
return { dimensions: allDimensions, matchingDataList: allMatchingData, trackingDataList: allTrackingData };
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Load image targets from a single ArrayBuffer (backward compatible)
|
|
164
|
-
* @param {ArrayBuffer} buffer - Single .mind file buffer
|
|
165
|
-
*/
|
|
166
|
-
addImageTargetsFromBuffer(buffer) {
|
|
167
|
-
return this.addImageTargetsFromBuffers([buffer]);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
dispose() {
|
|
171
|
-
this.stopProcessVideo();
|
|
172
|
-
if (this.worker) {
|
|
173
|
-
this.worker.postMessage({ type: "dispose" });
|
|
174
|
-
this.worker = null;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
dummyRun(input) {
|
|
179
|
-
const inputData = this.inputLoader.loadInput(input);
|
|
180
|
-
this.cropDetector.detect(inputData);
|
|
181
|
-
this.tracker.dummyRun(inputData);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
getProjectionMatrix() {
|
|
185
|
-
return this.projectionMatrix;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
getRotatedZ90Matrix(m) {
|
|
189
|
-
// rotate 90 degree along z-axis
|
|
190
|
-
// rotation matrix
|
|
191
|
-
// | 0 -1 0 0 |
|
|
192
|
-
// | 1 0 0 0 |
|
|
193
|
-
// | 0 0 1 0 |
|
|
194
|
-
// | 0 0 0 1 |
|
|
195
|
-
const rotatedMatrix = [
|
|
196
|
-
-m[1],
|
|
197
|
-
m[0],
|
|
198
|
-
m[2],
|
|
199
|
-
m[3],
|
|
200
|
-
-m[5],
|
|
201
|
-
m[4],
|
|
202
|
-
m[6],
|
|
203
|
-
m[7],
|
|
204
|
-
-m[9],
|
|
205
|
-
m[8],
|
|
206
|
-
m[10],
|
|
207
|
-
m[11],
|
|
208
|
-
-m[13],
|
|
209
|
-
m[12],
|
|
210
|
-
m[14],
|
|
211
|
-
m[15],
|
|
212
|
-
];
|
|
213
|
-
return rotatedMatrix;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
getWorldMatrix(modelViewTransform, targetIndex) {
|
|
217
|
-
return this._glModelViewMatrix(modelViewTransform, targetIndex);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
async _detectAndMatch(inputData, targetIndexes) {
|
|
221
|
-
const { featurePoints } = this.cropDetector.detectMoving(inputData);
|
|
222
|
-
const { targetIndex: matchedTargetIndex, modelViewTransform } = await this._workerMatch(
|
|
223
|
-
featurePoints,
|
|
224
|
-
targetIndexes,
|
|
225
|
-
);
|
|
226
|
-
return { targetIndex: matchedTargetIndex, modelViewTransform };
|
|
227
|
-
}
|
|
228
|
-
async _trackAndUpdate(inputData, lastModelViewTransform, targetIndex) {
|
|
229
|
-
const { worldCoords, screenCoords } = this.tracker.track(
|
|
230
|
-
inputData,
|
|
231
|
-
lastModelViewTransform,
|
|
232
|
-
targetIndex,
|
|
233
|
-
);
|
|
234
|
-
if (worldCoords.length < 6) return null; // Umbral de puntos mínimos para mantener el seguimiento
|
|
235
|
-
const modelViewTransform = await this._workerTrackUpdate(lastModelViewTransform, {
|
|
236
|
-
worldCoords,
|
|
237
|
-
screenCoords,
|
|
238
|
-
});
|
|
239
|
-
return modelViewTransform;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
processVideo(input) {
|
|
243
|
-
if (this.processingVideo) return;
|
|
244
|
-
|
|
245
|
-
this.processingVideo = true;
|
|
246
|
-
|
|
247
|
-
this.trackingStates = [];
|
|
248
|
-
for (let i = 0; i < this.markerDimensions.length; i++) {
|
|
249
|
-
this.trackingStates.push({
|
|
250
|
-
showing: false,
|
|
251
|
-
isTracking: false,
|
|
252
|
-
currentModelViewTransform: null,
|
|
253
|
-
trackCount: 0,
|
|
254
|
-
trackMiss: 0,
|
|
255
|
-
filter: new OneEuroFilter({ minCutOff: this.filterMinCF, beta: this.filterBeta }),
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const startProcessing = async () => {
|
|
260
|
-
while (true) {
|
|
261
|
-
if (!this.processingVideo) break;
|
|
262
|
-
|
|
263
|
-
const inputData = this.inputLoader.loadInput(input);
|
|
264
|
-
|
|
265
|
-
const nTracking = this.trackingStates.reduce((acc, s) => {
|
|
266
|
-
return acc + (!!s.isTracking ? 1 : 0);
|
|
267
|
-
}, 0);
|
|
268
|
-
|
|
269
|
-
// detect and match only if less then maxTrack
|
|
270
|
-
if (nTracking < this.maxTrack) {
|
|
271
|
-
const matchingIndexes = [];
|
|
272
|
-
for (let i = 0; i < this.trackingStates.length; i++) {
|
|
273
|
-
const trackingState = this.trackingStates[i];
|
|
274
|
-
if (trackingState.isTracking === true) continue;
|
|
275
|
-
if (this.interestedTargetIndex !== -1 && this.interestedTargetIndex !== i) continue;
|
|
276
|
-
|
|
277
|
-
matchingIndexes.push(i);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const { targetIndex: matchedTargetIndex, modelViewTransform } =
|
|
281
|
-
await this._detectAndMatch(inputData, matchingIndexes);
|
|
282
|
-
|
|
283
|
-
if (matchedTargetIndex !== -1) {
|
|
284
|
-
this.trackingStates[matchedTargetIndex].isTracking = true;
|
|
285
|
-
this.trackingStates[matchedTargetIndex].currentModelViewTransform = modelViewTransform;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// tracking update
|
|
290
|
-
for (let i = 0; i < this.trackingStates.length; i++) {
|
|
291
|
-
const trackingState = this.trackingStates[i];
|
|
292
|
-
|
|
293
|
-
if (trackingState.isTracking) {
|
|
294
|
-
let modelViewTransform = await this._trackAndUpdate(
|
|
295
|
-
inputData,
|
|
296
|
-
trackingState.currentModelViewTransform,
|
|
297
|
-
i,
|
|
298
|
-
);
|
|
299
|
-
if (modelViewTransform === null) {
|
|
300
|
-
trackingState.isTracking = false;
|
|
301
|
-
} else {
|
|
302
|
-
trackingState.currentModelViewTransform = modelViewTransform;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
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();
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
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 });
|
|
330
|
-
}
|
|
331
|
-
} else {
|
|
332
|
-
trackingState.trackMiss = 0;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
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);
|
|
340
|
-
|
|
341
|
-
let clone = [];
|
|
342
|
-
for (let j = 0; j < trackingState.trackingMatrix.length; j++) {
|
|
343
|
-
clone[j] = trackingState.trackingMatrix[j];
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
const isInputRotated =
|
|
347
|
-
input.width === this.inputHeight && input.height === this.inputWidth;
|
|
348
|
-
if (isInputRotated) {
|
|
349
|
-
clone = this.getRotatedZ90Matrix(clone);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
this.onUpdate &&
|
|
353
|
-
this.onUpdate({ type: "updateMatrix", targetIndex: i, worldMatrix: clone, modelViewTransform: trackingState.currentModelViewTransform });
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
this.onUpdate && this.onUpdate({ type: "processDone" });
|
|
358
|
-
|
|
359
|
-
// Use requestAnimationFrame if available, otherwise just wait briefly
|
|
360
|
-
if (typeof requestAnimationFrame !== "undefined") {
|
|
361
|
-
await new Promise(requestAnimationFrame);
|
|
362
|
-
} else {
|
|
363
|
-
await new Promise(resolve => setTimeout(resolve, 16));
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
};
|
|
367
|
-
startProcessing();
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
stopProcessVideo() {
|
|
371
|
-
this.processingVideo = false;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
async detect(input) {
|
|
375
|
-
const inputData = this.inputLoader.loadInput(input);
|
|
376
|
-
const { featurePoints, debugExtra } = this.cropDetector.detect(inputData);
|
|
377
|
-
return { featurePoints, debugExtra };
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
async match(featurePoints, targetIndex) {
|
|
381
|
-
const { targetIndex: matchedTargetIndex, modelViewTransform, screenCoords, worldCoords, debugExtra } = await this._workerMatch(featurePoints, [
|
|
382
|
-
targetIndex,
|
|
383
|
-
]);
|
|
384
|
-
return { targetIndex: matchedTargetIndex, modelViewTransform, screenCoords, worldCoords, debugExtra };
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
async track(input, modelViewTransform, targetIndex) {
|
|
388
|
-
const inputData = this.inputLoader.loadInput(input);
|
|
389
|
-
const result = this.tracker.track(inputData, modelViewTransform, targetIndex);
|
|
390
|
-
return result;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
async trackUpdate(modelViewTransform, trackFeatures) {
|
|
394
|
-
if (trackFeatures.worldCoords.length < 4) return null;
|
|
395
|
-
const modelViewTransform2 = await this._workerTrackUpdate(modelViewTransform, trackFeatures);
|
|
396
|
-
return modelViewTransform2;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
_workerMatch(featurePoints, targetIndexes) {
|
|
400
|
-
return new Promise((resolve) => {
|
|
401
|
-
// If no worker available, process on main thread
|
|
402
|
-
if (!this.worker) {
|
|
403
|
-
this._matchOnMainThread(featurePoints, targetIndexes).then(resolve);
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
this.workerMatchDone = (data) => {
|
|
408
|
-
resolve({
|
|
409
|
-
targetIndex: data.targetIndex,
|
|
410
|
-
modelViewTransform: data.modelViewTransform,
|
|
411
|
-
screenCoords: data.screenCoords,
|
|
412
|
-
worldCoords: data.worldCoords,
|
|
413
|
-
debugExtra: data.debugExtra,
|
|
414
|
-
});
|
|
415
|
-
};
|
|
416
|
-
this.worker.postMessage({ type: "match", featurePoints: featurePoints, targetIndexes });
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
async _matchOnMainThread(featurePoints, targetIndexes) {
|
|
421
|
-
// Lazy initialize Matcher and Estimator for main thread
|
|
422
|
-
if (!this.mainThreadMatcher) {
|
|
423
|
-
const { Matcher } = await import("./matching/matcher.js");
|
|
424
|
-
const { Estimator } = await import("./estimation/estimator.js");
|
|
425
|
-
this.mainThreadMatcher = new Matcher(this.inputWidth, this.inputHeight, this.debugMode);
|
|
426
|
-
this.mainThreadEstimator = new Estimator(this.projectionTransform);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
let matchedTargetIndex = -1;
|
|
430
|
-
let matchedModelViewTransform = null;
|
|
431
|
-
let matchedScreenCoords = null;
|
|
432
|
-
let matchedWorldCoords = null;
|
|
433
|
-
let matchedDebugExtra = null;
|
|
434
|
-
|
|
435
|
-
for (let i = 0; i < targetIndexes.length; i++) {
|
|
436
|
-
const matchingIndex = targetIndexes[i];
|
|
437
|
-
|
|
438
|
-
const { keyframeIndex, screenCoords, worldCoords, debugExtra } = this.mainThreadMatcher.matchDetection(
|
|
439
|
-
this.matchingDataList[matchingIndex],
|
|
440
|
-
featurePoints,
|
|
441
|
-
);
|
|
442
|
-
matchedDebugExtra = debugExtra;
|
|
443
|
-
|
|
444
|
-
if (keyframeIndex !== -1) {
|
|
445
|
-
const modelViewTransform = this.mainThreadEstimator.estimate({ screenCoords, worldCoords });
|
|
446
|
-
|
|
447
|
-
if (modelViewTransform) {
|
|
448
|
-
matchedTargetIndex = matchingIndex;
|
|
449
|
-
matchedModelViewTransform = modelViewTransform;
|
|
450
|
-
matchedScreenCoords = screenCoords;
|
|
451
|
-
matchedWorldCoords = worldCoords;
|
|
452
|
-
}
|
|
453
|
-
break;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
return {
|
|
458
|
-
targetIndex: matchedTargetIndex,
|
|
459
|
-
modelViewTransform: matchedModelViewTransform,
|
|
460
|
-
screenCoords: matchedScreenCoords,
|
|
461
|
-
worldCoords: matchedWorldCoords,
|
|
462
|
-
debugExtra: matchedDebugExtra,
|
|
463
|
-
};
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
_workerTrackUpdate(modelViewTransform, trackingFeatures) {
|
|
467
|
-
return new Promise((resolve) => {
|
|
468
|
-
// If no worker available, process on main thread
|
|
469
|
-
if (!this.worker) {
|
|
470
|
-
this._trackUpdateOnMainThread(modelViewTransform, trackingFeatures).then(resolve);
|
|
471
|
-
return;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
this.workerTrackDone = (data) => {
|
|
475
|
-
resolve(data.modelViewTransform);
|
|
476
|
-
};
|
|
477
|
-
const { worldCoords, screenCoords } = trackingFeatures;
|
|
478
|
-
this.worker.postMessage({
|
|
479
|
-
type: "trackUpdate",
|
|
480
|
-
modelViewTransform,
|
|
481
|
-
worldCoords,
|
|
482
|
-
screenCoords,
|
|
483
|
-
});
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
async _trackUpdateOnMainThread(modelViewTransform, trackingFeatures) {
|
|
488
|
-
// Lazy initialize Estimator for main thread
|
|
489
|
-
if (!this.mainThreadEstimator) {
|
|
490
|
-
const { Estimator } = await import("./estimation/estimator.js");
|
|
491
|
-
this.mainThreadEstimator = new Estimator(this.projectionTransform);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
const { worldCoords, screenCoords } = trackingFeatures;
|
|
495
|
-
const finalModelViewTransform = this.mainThreadEstimator.refineEstimate({
|
|
496
|
-
initialModelViewTransform: modelViewTransform,
|
|
497
|
-
worldCoords,
|
|
498
|
-
screenCoords,
|
|
499
|
-
});
|
|
500
|
-
return finalModelViewTransform;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
_glModelViewMatrix(modelViewTransform, targetIndex) {
|
|
504
|
-
const height = this.markerDimensions[targetIndex][1];
|
|
505
|
-
|
|
506
|
-
const openGLWorldMatrix = [
|
|
507
|
-
modelViewTransform[0][0],
|
|
508
|
-
-modelViewTransform[1][0],
|
|
509
|
-
-modelViewTransform[2][0],
|
|
510
|
-
0,
|
|
511
|
-
-modelViewTransform[0][1],
|
|
512
|
-
modelViewTransform[1][1],
|
|
513
|
-
modelViewTransform[2][1],
|
|
514
|
-
0,
|
|
515
|
-
-modelViewTransform[0][2],
|
|
516
|
-
modelViewTransform[1][2],
|
|
517
|
-
modelViewTransform[2][2],
|
|
518
|
-
0,
|
|
519
|
-
modelViewTransform[0][1] * height + modelViewTransform[0][3],
|
|
520
|
-
-(modelViewTransform[1][1] * height + modelViewTransform[1][3]),
|
|
521
|
-
-(modelViewTransform[2][1] * height + modelViewTransform[2][3]),
|
|
522
|
-
1,
|
|
523
|
-
];
|
|
524
|
-
return openGLWorldMatrix;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
_glProjectionMatrix({ projectionTransform, width, height, near, far }) {
|
|
528
|
-
const proj = [
|
|
529
|
-
[
|
|
530
|
-
(2 * projectionTransform[0][0]) / width,
|
|
531
|
-
0,
|
|
532
|
-
-((2 * projectionTransform[0][2]) / width - 1),
|
|
533
|
-
0,
|
|
534
|
-
],
|
|
535
|
-
[
|
|
536
|
-
0,
|
|
537
|
-
(2 * projectionTransform[1][1]) / height,
|
|
538
|
-
-((2 * projectionTransform[1][2]) / height - 1),
|
|
539
|
-
0,
|
|
540
|
-
],
|
|
541
|
-
[0, 0, -(far + near) / (far - near), (-2 * far * near) / (far - near)],
|
|
542
|
-
[0, 0, -1, 0],
|
|
543
|
-
];
|
|
544
|
-
const projMatrix = [];
|
|
545
|
-
for (let i = 0; i < 4; i++) {
|
|
546
|
-
for (let j = 0; j < 4; j++) {
|
|
547
|
-
projMatrix.push(proj[j][i]);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
return projMatrix;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
export { Controller };
|