@srsergio/taptapp-ar 1.0.101 → 1.1.2
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/node-worker.js +1 -197
- package/dist/compiler/offline-compiler.js +1 -207
- package/dist/core/constants.js +1 -38
- package/dist/core/detector/crop-detector.js +1 -88
- package/dist/core/detector/detector-lite.js +1 -455
- package/dist/core/detector/freak.js +1 -89
- package/dist/core/estimation/estimate.js +1 -16
- package/dist/core/estimation/estimator.js +1 -30
- package/dist/core/estimation/morph-refinement.js +1 -116
- package/dist/core/estimation/non-rigid-refine.js +1 -70
- package/dist/core/estimation/pnp-solver.js +1 -109
- package/dist/core/estimation/refine-estimate.js +1 -311
- package/dist/core/estimation/utils.js +1 -67
- package/dist/core/features/auto-rotation-feature.js +1 -30
- package/dist/core/features/crop-detection-feature.js +1 -26
- package/dist/core/features/feature-base.js +1 -1
- package/dist/core/features/feature-manager.js +1 -55
- package/dist/core/features/one-euro-filter-feature.js +1 -44
- package/dist/core/features/temporal-filter-feature.js +1 -57
- package/dist/core/image-list.js +1 -54
- package/dist/core/input-loader.js +1 -87
- package/dist/core/matching/hamming-distance.js +1 -66
- package/dist/core/matching/hdc.js +1 -102
- package/dist/core/matching/hierarchical-clustering.js +1 -130
- package/dist/core/matching/hough.js +1 -170
- package/dist/core/matching/matcher.js +1 -66
- package/dist/core/matching/matching.js +1 -401
- package/dist/core/matching/ransacHomography.js +1 -132
- package/dist/core/perception/bio-inspired-engine.js +1 -232
- package/dist/core/perception/foveal-attention.js +1 -280
- package/dist/core/perception/index.js +1 -17
- package/dist/core/perception/predictive-coding.js +1 -278
- package/dist/core/perception/saccadic-controller.js +1 -269
- package/dist/core/perception/saliency-map.js +1 -254
- package/dist/core/perception/scale-orchestrator.js +1 -68
- package/dist/core/protocol.js +1 -254
- package/dist/core/tracker/extract-utils.js +1 -29
- package/dist/core/tracker/extract.js +1 -306
- package/dist/core/tracker/tracker.js +1 -352
- package/dist/core/utils/cumsum.js +1 -37
- package/dist/core/utils/delaunay.js +1 -125
- package/dist/core/utils/geometry.js +1 -101
- package/dist/core/utils/gpu-compute.js +1 -231
- package/dist/core/utils/homography.js +1 -138
- package/dist/core/utils/images.js +1 -108
- package/dist/core/utils/lsh-binarizer.js +1 -37
- package/dist/core/utils/lsh-direct.js +1 -76
- package/dist/core/utils/projection.js +1 -51
- package/dist/core/utils/randomizer.js +1 -25
- package/dist/core/utils/worker-pool.js +1 -89
- package/dist/index.js +1 -7
- package/dist/libs/one-euro-filter.js +1 -70
- package/dist/react/TaptappAR.js +1 -151
- package/dist/react/types.js +1 -16
- package/dist/react/use-ar.js +1 -118
- package/dist/runtime/aframe.js +1 -272
- package/dist/runtime/bio-inspired-controller.js +1 -358
- package/dist/runtime/controller.js +1 -592
- package/dist/runtime/controller.worker.js +1 -93
- package/dist/runtime/index.js +1 -5
- package/dist/runtime/three.js +1 -304
- package/dist/runtime/track.js +1 -381
- package/package.json +9 -3
|
@@ -1,358 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Bio-Inspired Controller Adapter
|
|
3
|
-
*
|
|
4
|
-
* Wraps the standard Controller with Bio-Inspired Perception capabilities.
|
|
5
|
-
* Provides significant performance improvements while maintaining API compatibility.
|
|
6
|
-
*
|
|
7
|
-
* Key features:
|
|
8
|
-
* - Foveal attention: Processes only regions of interest at full resolution
|
|
9
|
-
* - Predictive coding: Skips processing when scene is static
|
|
10
|
-
* - Saccadic sampling: Strategic "glances" at high-saliency regions
|
|
11
|
-
*
|
|
12
|
-
* Usage:
|
|
13
|
-
* ```javascript
|
|
14
|
-
* import { BioInspiredController } from './bio-inspired-controller.js';
|
|
15
|
-
*
|
|
16
|
-
* const controller = new BioInspiredController({
|
|
17
|
-
* inputWidth: 640,
|
|
18
|
-
* inputHeight: 480,
|
|
19
|
-
* onUpdate: (data) => console.log(data),
|
|
20
|
-
* bioInspired: {
|
|
21
|
-
* enabled: true,
|
|
22
|
-
* aggressiveSkipping: true,
|
|
23
|
-
* }
|
|
24
|
-
* });
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
import { Controller } from './controller.js';
|
|
28
|
-
import { BioInspiredEngine } from '../core/perception/index.js';
|
|
29
|
-
/**
|
|
30
|
-
* Bio-Inspired Controller
|
|
31
|
-
*
|
|
32
|
-
* Extends the standard Controller with bio-inspired perception capabilities.
|
|
33
|
-
*/
|
|
34
|
-
class BioInspiredController extends Controller {
|
|
35
|
-
bioEngine = null;
|
|
36
|
-
bioEnabled = true;
|
|
37
|
-
bioMetricsInterval = null;
|
|
38
|
-
lastBioResult = null;
|
|
39
|
-
constructor(options) {
|
|
40
|
-
super(options);
|
|
41
|
-
const bioOptions = options.bioInspired || {};
|
|
42
|
-
this.bioEnabled = bioOptions.enabled !== false;
|
|
43
|
-
if (this.bioEnabled) {
|
|
44
|
-
// Initialize Bio-Inspired Engine
|
|
45
|
-
const bioConfig = {};
|
|
46
|
-
if (bioOptions.foveaRadiusRatio !== undefined) {
|
|
47
|
-
bioConfig.FOVEA_RADIUS_RATIO = bioOptions.foveaRadiusRatio;
|
|
48
|
-
}
|
|
49
|
-
if (bioOptions.maxSaccades !== undefined) {
|
|
50
|
-
bioConfig.MAX_SACCADES_PER_FRAME = bioOptions.maxSaccades;
|
|
51
|
-
}
|
|
52
|
-
if (bioOptions.aggressiveSkipping !== undefined) {
|
|
53
|
-
bioConfig.ENABLE_SKIP_FRAMES = bioOptions.aggressiveSkipping;
|
|
54
|
-
if (bioOptions.aggressiveSkipping) {
|
|
55
|
-
bioConfig.CHANGE_THRESHOLD = 0.03; // More aggressive
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
this.bioEngine = new BioInspiredEngine(options.inputWidth, options.inputHeight, bioConfig);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Override processVideo to add bio-inspired perception
|
|
63
|
-
*/
|
|
64
|
-
processVideo(input) {
|
|
65
|
-
if (!this.bioEnabled || !this.bioEngine) {
|
|
66
|
-
return super.processVideo(input);
|
|
67
|
-
}
|
|
68
|
-
if (this.processingVideo)
|
|
69
|
-
return;
|
|
70
|
-
this.processingVideo = true;
|
|
71
|
-
// Reset tracking states
|
|
72
|
-
this.trackingStates = [];
|
|
73
|
-
for (let i = 0; i < (this.markerDimensions?.length || 0); i++) {
|
|
74
|
-
this.trackingStates.push({
|
|
75
|
-
showing: false,
|
|
76
|
-
isTracking: false,
|
|
77
|
-
currentModelViewTransform: null,
|
|
78
|
-
trackCount: 0,
|
|
79
|
-
trackMiss: 0,
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
const startProcessing = async () => {
|
|
83
|
-
while (this.processingVideo) {
|
|
84
|
-
const inputData = this.inputLoader.loadInput(input);
|
|
85
|
-
// Get current tracking state for bio engine
|
|
86
|
-
const activeTracking = this.trackingStates.find(s => s.isTracking);
|
|
87
|
-
const trackingState = activeTracking ? {
|
|
88
|
-
isTracking: true,
|
|
89
|
-
activeOctave: activeTracking.lastOctaveIndex, // Tracked octave index
|
|
90
|
-
worldMatrix: activeTracking.currentModelViewTransform
|
|
91
|
-
? this._flattenMatrix(activeTracking.currentModelViewTransform)
|
|
92
|
-
: null
|
|
93
|
-
} : null;
|
|
94
|
-
// Process through bio-inspired engine
|
|
95
|
-
const bioResult = this.bioEngine.process(inputData, trackingState || undefined);
|
|
96
|
-
this.lastBioResult = bioResult;
|
|
97
|
-
// If bio engine says we can skip, use prediction
|
|
98
|
-
if (bioResult.skipped && activeTracking?.isTracking) {
|
|
99
|
-
// Use predicted state
|
|
100
|
-
this._handleSkippedFrame(activeTracking, bioResult);
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
// Normal processing with attention regions
|
|
104
|
-
await this._processWithAttention(input, inputData, bioResult);
|
|
105
|
-
}
|
|
106
|
-
// Wait for next frame
|
|
107
|
-
if (typeof requestAnimationFrame !== 'undefined') {
|
|
108
|
-
await new Promise(requestAnimationFrame);
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
await new Promise(resolve => setTimeout(resolve, 16));
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
startProcessing();
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Handle a skipped frame using prediction
|
|
119
|
-
* @private
|
|
120
|
-
*/
|
|
121
|
-
_handleSkippedFrame(trackingState, bioResult) {
|
|
122
|
-
// Use predicted matrix
|
|
123
|
-
if (bioResult.prediction && bioResult.prediction.worldMatrix) {
|
|
124
|
-
trackingState.currentModelViewTransform = this._unflattenMatrix(bioResult.prediction.worldMatrix);
|
|
125
|
-
}
|
|
126
|
-
// Notify with skipped status
|
|
127
|
-
const worldMatrix = trackingState.currentModelViewTransform
|
|
128
|
-
? this._glModelViewMatrix(trackingState.currentModelViewTransform, 0)
|
|
129
|
-
: null;
|
|
130
|
-
this.onUpdate?.({
|
|
131
|
-
type: 'updateMatrix',
|
|
132
|
-
targetIndex: 0,
|
|
133
|
-
worldMatrix: worldMatrix ? this.featureManager.applyWorldMatrixFilters(0, worldMatrix, { stability: 0.9 }) : null,
|
|
134
|
-
skipped: true,
|
|
135
|
-
bioMetrics: this.bioEngine?.getMetrics(),
|
|
136
|
-
});
|
|
137
|
-
this.onUpdate?.({ type: 'processDone' });
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Process frame using bio-inspired attention regions
|
|
141
|
-
* @private
|
|
142
|
-
*/
|
|
143
|
-
async _processWithAttention(input, inputData, bioResult) {
|
|
144
|
-
const nTracking = this.trackingStates.reduce((acc, s) => acc + (s.isTracking ? 1 : 0), 0);
|
|
145
|
-
// Detection phase - use primary attention region for efficiency
|
|
146
|
-
if (nTracking < this.maxTrack) {
|
|
147
|
-
const matchingIndexes = this.trackingStates
|
|
148
|
-
.map((s, i) => ({ state: s, index: i }))
|
|
149
|
-
.filter(({ state, index }) => !state.isTracking &&
|
|
150
|
-
(this.interestedTargetIndex === -1 || this.interestedTargetIndex === index))
|
|
151
|
-
.map(({ index }) => index);
|
|
152
|
-
if (matchingIndexes.length > 0) {
|
|
153
|
-
// Use full input for detection (bio engine already optimized upstream processing)
|
|
154
|
-
const { targetIndex: matchedTargetIndex, modelViewTransform, featurePoints } = await this._detectAndMatch(inputData, matchingIndexes, bioResult.octavesToProcess || null);
|
|
155
|
-
if (matchedTargetIndex !== -1) {
|
|
156
|
-
this.trackingStates[matchedTargetIndex].isTracking = true;
|
|
157
|
-
this.trackingStates[matchedTargetIndex].currentModelViewTransform = modelViewTransform;
|
|
158
|
-
// Update bio engine fovea to focus on detected target
|
|
159
|
-
if (bioResult.attentionRegions?.[0]) {
|
|
160
|
-
this.bioEngine?.reset();
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
this.onUpdate?.({ type: 'featurePoints', featurePoints });
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
// Tracking phase
|
|
167
|
-
for (let i = 0; i < this.trackingStates.length; i++) {
|
|
168
|
-
const trackingState = this.trackingStates[i];
|
|
169
|
-
if (trackingState.isTracking) {
|
|
170
|
-
const result = await this._trackAndUpdate(inputData, trackingState.currentModelViewTransform, i);
|
|
171
|
-
if (!result || !result.modelViewTransform) {
|
|
172
|
-
trackingState.isTracking = false;
|
|
173
|
-
trackingState.screenCoords = result?.screenCoords || [];
|
|
174
|
-
trackingState.reliabilities = result?.reliabilities || [];
|
|
175
|
-
trackingState.stabilities = result?.stabilities || [];
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
trackingState.currentModelViewTransform = result.modelViewTransform;
|
|
179
|
-
trackingState.screenCoords = result.screenCoords;
|
|
180
|
-
trackingState.reliabilities = result.reliabilities;
|
|
181
|
-
trackingState.stabilities = result.stabilities;
|
|
182
|
-
trackingState.deformedMesh = result.deformedMesh;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
const wasShowing = trackingState.showing;
|
|
186
|
-
trackingState.showing = this.featureManager.shouldShow(i, trackingState.isTracking);
|
|
187
|
-
if (wasShowing && !trackingState.showing) {
|
|
188
|
-
trackingState.trackingMatrix = null;
|
|
189
|
-
this.featureManager.notifyUpdate({ type: 'reset', targetIndex: i });
|
|
190
|
-
}
|
|
191
|
-
// Emit update
|
|
192
|
-
if (trackingState.showing || trackingState.screenCoords?.length > 0 || (wasShowing && !trackingState.showing)) {
|
|
193
|
-
const worldMatrix = trackingState.showing
|
|
194
|
-
? this._glModelViewMatrix(trackingState.currentModelViewTransform, i)
|
|
195
|
-
: null;
|
|
196
|
-
let finalMatrix = null;
|
|
197
|
-
if (worldMatrix) {
|
|
198
|
-
const stabilities = trackingState.stabilities || [];
|
|
199
|
-
const avgStability = stabilities.length > 0
|
|
200
|
-
? stabilities.reduce((a, b) => a + b, 0) / stabilities.length
|
|
201
|
-
: 0;
|
|
202
|
-
finalMatrix = this.featureManager.applyWorldMatrixFilters(i, worldMatrix, { stability: avgStability });
|
|
203
|
-
trackingState.trackingMatrix = finalMatrix;
|
|
204
|
-
const isInputRotated = input.width === this.inputHeight && input.height === this.inputWidth;
|
|
205
|
-
if (isInputRotated) {
|
|
206
|
-
const rotationFeature = this.featureManager.getFeature('auto-rotation');
|
|
207
|
-
if (rotationFeature) {
|
|
208
|
-
finalMatrix = rotationFeature.rotate(finalMatrix);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
this.onUpdate?.({
|
|
213
|
-
type: 'updateMatrix',
|
|
214
|
-
targetIndex: i,
|
|
215
|
-
worldMatrix: finalMatrix,
|
|
216
|
-
modelViewTransform: trackingState.currentModelViewTransform,
|
|
217
|
-
screenCoords: trackingState.screenCoords,
|
|
218
|
-
reliabilities: trackingState.reliabilities,
|
|
219
|
-
stabilities: trackingState.stabilities,
|
|
220
|
-
deformedMesh: trackingState.deformedMesh,
|
|
221
|
-
bioMetrics: this.bioEngine?.getMetrics(),
|
|
222
|
-
foveaCenter: bioResult.foveaCenter,
|
|
223
|
-
pixelsSaved: bioResult.pixelsSaved,
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
this.onUpdate?.({ type: 'processDone' });
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Detect and match features, optionally limited to specific octaves
|
|
231
|
-
*/
|
|
232
|
-
async _detectAndMatch(inputData, targetIndexes, octavesToProcess = null) {
|
|
233
|
-
// 🚀 NANITE-STYLE: Estimate scale for filtered matching
|
|
234
|
-
let predictedScale = undefined;
|
|
235
|
-
for (const state of this.trackingStates) {
|
|
236
|
-
if (state.isTracking && state.currentModelViewTransform) {
|
|
237
|
-
const m = state.currentModelViewTransform;
|
|
238
|
-
predictedScale = Math.sqrt(m[0][0] ** 2 + m[1][0] ** 2 + m[2][0] ** 2);
|
|
239
|
-
break;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
const { targetIndex, modelViewTransform, screenCoords, worldCoords, featurePoints } = await this._workerMatch(null, // No feature points, worker will detect from inputData
|
|
243
|
-
targetIndexes, inputData, predictedScale, octavesToProcess);
|
|
244
|
-
return { targetIndex, modelViewTransform, screenCoords, worldCoords, featurePoints };
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* Communicate with worker for matching phase
|
|
248
|
-
*/
|
|
249
|
-
_workerMatch(featurePoints, targetIndexes, inputData = null, expectedScale, octavesToProcess = null) {
|
|
250
|
-
return new Promise((resolve) => {
|
|
251
|
-
if (!this.worker) {
|
|
252
|
-
// If no feature points but we have input data, detect first
|
|
253
|
-
let fpPromise;
|
|
254
|
-
if (!featurePoints && inputData) {
|
|
255
|
-
fpPromise = Promise.resolve(this.fullDetector.detect(inputData, { octavesToProcess }).featurePoints);
|
|
256
|
-
}
|
|
257
|
-
else {
|
|
258
|
-
fpPromise = Promise.resolve(featurePoints);
|
|
259
|
-
}
|
|
260
|
-
fpPromise.then(fp => {
|
|
261
|
-
this._matchOnMainThread(fp, targetIndexes, expectedScale).then(resolve);
|
|
262
|
-
}).catch(() => resolve({ targetIndex: -1 }));
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
const timeout = setTimeout(() => {
|
|
266
|
-
this.workerMatchDone = null;
|
|
267
|
-
resolve({ targetIndex: -1 });
|
|
268
|
-
}, 1000);
|
|
269
|
-
this.workerMatchDone = (data) => {
|
|
270
|
-
clearTimeout(timeout);
|
|
271
|
-
this.workerMatchDone = null;
|
|
272
|
-
resolve(data);
|
|
273
|
-
};
|
|
274
|
-
if (inputData) {
|
|
275
|
-
this.worker.postMessage({ type: "match", inputData, targetIndexes, octavesToProcess, expectedScale });
|
|
276
|
-
}
|
|
277
|
-
else {
|
|
278
|
-
this.worker.postMessage({ type: "match", featurePoints: featurePoints, targetIndexes, expectedScale });
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
/**
|
|
283
|
-
* Override _trackAndUpdate to capture active octave for the next frame's orchestration
|
|
284
|
-
*/
|
|
285
|
-
async _trackAndUpdate(inputData, lastModelViewTransform, targetIndex) {
|
|
286
|
-
const result = await super._trackAndUpdate(inputData, lastModelViewTransform, targetIndex);
|
|
287
|
-
if (result && result.octaveIndex !== undefined) {
|
|
288
|
-
this.trackingStates[targetIndex].lastOctaveIndex = result.octaveIndex;
|
|
289
|
-
}
|
|
290
|
-
return result;
|
|
291
|
-
}
|
|
292
|
-
/**
|
|
293
|
-
* Flatten a 3x4 matrix to Float32Array
|
|
294
|
-
* @private
|
|
295
|
-
*/
|
|
296
|
-
_flattenMatrix(matrix) {
|
|
297
|
-
const result = new Float32Array(16);
|
|
298
|
-
for (let i = 0; i < 3; i++) {
|
|
299
|
-
for (let j = 0; j < 4; j++) {
|
|
300
|
-
result[i * 4 + j] = matrix[i][j];
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
result[12] = 0;
|
|
304
|
-
result[13] = 0;
|
|
305
|
-
result[14] = 0;
|
|
306
|
-
result[15] = 1;
|
|
307
|
-
return result;
|
|
308
|
-
}
|
|
309
|
-
/**
|
|
310
|
-
* Unflatten Float32Array to 3x4 matrix
|
|
311
|
-
* @private
|
|
312
|
-
*/
|
|
313
|
-
_unflattenMatrix(flat) {
|
|
314
|
-
return [
|
|
315
|
-
[flat[0], flat[1], flat[2], flat[3]],
|
|
316
|
-
[flat[4], flat[5], flat[6], flat[7]],
|
|
317
|
-
[flat[8], flat[9], flat[10], flat[11]],
|
|
318
|
-
];
|
|
319
|
-
}
|
|
320
|
-
/**
|
|
321
|
-
* Get bio-inspired engine metrics
|
|
322
|
-
*/
|
|
323
|
-
getBioMetrics() {
|
|
324
|
-
return this.bioEngine?.getMetrics() || null;
|
|
325
|
-
}
|
|
326
|
-
/**
|
|
327
|
-
* Get last bio processing result
|
|
328
|
-
*/
|
|
329
|
-
getLastBioResult() {
|
|
330
|
-
return this.lastBioResult;
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* Enable/disable bio-inspired processing dynamically
|
|
334
|
-
*/
|
|
335
|
-
setBioEnabled(enabled) {
|
|
336
|
-
this.bioEnabled = enabled;
|
|
337
|
-
if (enabled && !this.bioEngine) {
|
|
338
|
-
this.bioEngine = new BioInspiredEngine(this.inputWidth, this.inputHeight);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
|
-
* Configure bio-inspired engine at runtime
|
|
343
|
-
*/
|
|
344
|
-
configureBio(options) {
|
|
345
|
-
this.bioEngine?.configure(options);
|
|
346
|
-
}
|
|
347
|
-
/**
|
|
348
|
-
* Override dispose to clean up bio engine
|
|
349
|
-
*/
|
|
350
|
-
dispose() {
|
|
351
|
-
super.dispose();
|
|
352
|
-
this.bioEngine = null;
|
|
353
|
-
if (this.bioMetricsInterval) {
|
|
354
|
-
clearInterval(this.bioMetricsInterval);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
export { BioInspiredController };
|
|
1
|
+
import{Controller as e}from"./controller.js";import{BioInspiredEngine as t}from"../core/perception/index.js";class i extends e{bioEngine=null;bioEnabled=!0;bioMetricsInterval=null;lastBioResult=null;constructor(e){super(e);const i=e.bioInspired||{};if(this.bioEnabled=!1!==i.enabled,this.bioEnabled){const r={};void 0!==i.foveaRadiusRatio&&(r.FOVEA_RADIUS_RATIO=i.foveaRadiusRatio),void 0!==i.maxSaccades&&(r.MAX_SACCADES_PER_FRAME=i.maxSaccades),void 0!==i.aggressiveSkipping&&(r.ENABLE_SKIP_FRAMES=i.aggressiveSkipping,i.aggressiveSkipping&&(r.CHANGE_THRESHOLD=.03)),this.bioEngine=new t(e.inputWidth,e.inputHeight,r)}}processVideo(e){if(!this.bioEnabled||!this.bioEngine)return super.processVideo(e);if(!this.processingVideo){this.processingVideo=!0,this.trackingStates=[];for(let e=0;e<(this.markerDimensions?.length||0);e++)this.trackingStates.push({showing:!1,isTracking:!1,currentModelViewTransform:null,trackCount:0,trackMiss:0});(async()=>{for(;this.processingVideo;){const t=this.inputLoader.loadInput(e),i=this.trackingStates.find(e=>e.isTracking),r=i?{isTracking:!0,activeOctave:i.lastOctaveIndex,worldMatrix:i.currentModelViewTransform?this._flattenMatrix(i.currentModelViewTransform):null}:null,s=this.bioEngine.process(t,r||void 0);this.lastBioResult=s,s.skipped&&i?.isTracking?this._handleSkippedFrame(i,s):await this._processWithAttention(e,t,s),"undefined"!=typeof requestAnimationFrame?await new Promise(requestAnimationFrame):await new Promise(e=>setTimeout(e,16))}})()}}_handleSkippedFrame(e,t){t.prediction&&t.prediction.worldMatrix&&(e.currentModelViewTransform=this._unflattenMatrix(t.prediction.worldMatrix));const i=e.currentModelViewTransform?this._glModelViewMatrix(e.currentModelViewTransform,0):null;this.onUpdate?.({type:"updateMatrix",targetIndex:0,worldMatrix:i?this.featureManager.applyWorldMatrixFilters(0,i,{stability:.9}):null,skipped:!0,bioMetrics:this.bioEngine?.getMetrics()}),this.onUpdate?.({type:"processDone"})}async _processWithAttention(e,t,i){if(this.trackingStates.reduce((e,t)=>e+(t.isTracking?1:0),0)<this.maxTrack){const e=this.trackingStates.map((e,t)=>({state:e,index:t})).filter(({state:e,index:t})=>!e.isTracking&&(-1===this.interestedTargetIndex||this.interestedTargetIndex===t)).map(({index:e})=>e);if(e.length>0){const{targetIndex:r,modelViewTransform:s,featurePoints:n}=await this._detectAndMatch(t,e,i.octavesToProcess||null);-1!==r&&(this.trackingStates[r].isTracking=!0,this.trackingStates[r].currentModelViewTransform=s,i.attentionRegions?.[0]&&this.bioEngine?.reset()),this.onUpdate?.({type:"featurePoints",featurePoints:n})}}for(let r=0;r<this.trackingStates.length;r++){const s=this.trackingStates[r];if(s.isTracking){const e=await this._trackAndUpdate(t,s.currentModelViewTransform,r);e&&e.modelViewTransform?(s.currentModelViewTransform=e.modelViewTransform,s.screenCoords=e.screenCoords,s.reliabilities=e.reliabilities,s.stabilities=e.stabilities,s.deformedMesh=e.deformedMesh):(s.isTracking=!1,s.screenCoords=e?.screenCoords||[],s.reliabilities=e?.reliabilities||[],s.stabilities=e?.stabilities||[])}const n=s.showing;if(s.showing=this.featureManager.shouldShow(r,s.isTracking),n&&!s.showing&&(s.trackingMatrix=null,this.featureManager.notifyUpdate({type:"reset",targetIndex:r})),s.showing||s.screenCoords?.length>0||n&&!s.showing){const t=s.showing?this._glModelViewMatrix(s.currentModelViewTransform,r):null;let n=null;if(t){const i=s.stabilities||[],a=i.length>0?i.reduce((e,t)=>e+t,0)/i.length:0;if(n=this.featureManager.applyWorldMatrixFilters(r,t,{stability:a}),s.trackingMatrix=n,e.width===this.inputHeight&&e.height===this.inputWidth){const e=this.featureManager.getFeature("auto-rotation");e&&(n=e.rotate(n))}}this.onUpdate?.({type:"updateMatrix",targetIndex:r,worldMatrix:n,modelViewTransform:s.currentModelViewTransform,screenCoords:s.screenCoords,reliabilities:s.reliabilities,stabilities:s.stabilities,deformedMesh:s.deformedMesh,bioMetrics:this.bioEngine?.getMetrics(),foveaCenter:i.foveaCenter,pixelsSaved:i.pixelsSaved})}}this.onUpdate?.({type:"processDone"})}async _detectAndMatch(e,t,i=null){let r;for(const e of this.trackingStates)if(e.isTracking&&e.currentModelViewTransform){const t=e.currentModelViewTransform;r=Math.sqrt(t[0][0]**2+t[1][0]**2+t[2][0]**2);break}const{targetIndex:s,modelViewTransform:n,screenCoords:a,worldCoords:o,featurePoints:l}=await this._workerMatch(null,t,e,r,i);return{targetIndex:s,modelViewTransform:n,screenCoords:a,worldCoords:o,featurePoints:l}}_workerMatch(e,t,i=null,r,s=null){return new Promise(n=>{if(!this.worker){let a;return a=!e&&i?Promise.resolve(this.fullDetector.detect(i,{octavesToProcess:s}).featurePoints):Promise.resolve(e),void a.then(e=>{this._matchOnMainThread(e,t,r).then(n)}).catch(()=>n({targetIndex:-1}))}const a=setTimeout(()=>{this.workerMatchDone=null,n({targetIndex:-1})},1e3);this.workerMatchDone=e=>{clearTimeout(a),this.workerMatchDone=null,n(e)},i?this.worker.postMessage({type:"match",inputData:i,targetIndexes:t,octavesToProcess:s,expectedScale:r}):this.worker.postMessage({type:"match",featurePoints:e,targetIndexes:t,expectedScale:r})})}async _trackAndUpdate(e,t,i){const r=await super._trackAndUpdate(e,t,i);return r&&void 0!==r.octaveIndex&&(this.trackingStates[i].lastOctaveIndex=r.octaveIndex),r}_flattenMatrix(e){const t=new Float32Array(16);for(let i=0;i<3;i++)for(let r=0;r<4;r++)t[4*i+r]=e[i][r];return t[12]=0,t[13]=0,t[14]=0,t[15]=1,t}_unflattenMatrix(e){return[[e[0],e[1],e[2],e[3]],[e[4],e[5],e[6],e[7]],[e[8],e[9],e[10],e[11]]]}getBioMetrics(){return this.bioEngine?.getMetrics()||null}getLastBioResult(){return this.lastBioResult}setBioEnabled(e){this.bioEnabled=e,e&&!this.bioEngine&&(this.bioEngine=new t(this.inputWidth,this.inputHeight))}configureBio(e){this.bioEngine?.configure(e)}dispose(){super.dispose(),this.bioEngine=null,this.bioMetricsInterval&&clearInterval(this.bioMetricsInterval)}}export{i as BioInspiredController};
|