@srsergio/taptapp-ar 1.0.77 → 1.0.79
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 +2 -6
- package/dist/compiler/controller.js +43 -44
- package/dist/compiler/features/auto-rotation-feature.d.ts +15 -0
- package/dist/compiler/features/auto-rotation-feature.js +30 -0
- package/dist/compiler/features/crop-detection-feature.d.ts +18 -0
- package/dist/compiler/features/crop-detection-feature.js +26 -0
- package/dist/compiler/features/feature-base.d.ts +46 -0
- package/dist/compiler/features/feature-base.js +1 -0
- package/dist/compiler/features/feature-manager.d.ts +12 -0
- package/dist/compiler/features/feature-manager.js +55 -0
- package/dist/compiler/features/one-euro-filter-feature.d.ts +15 -0
- package/dist/compiler/features/one-euro-filter-feature.js +37 -0
- package/dist/compiler/features/temporal-filter-feature.d.ts +19 -0
- package/dist/compiler/features/temporal-filter-feature.js +57 -0
- package/dist/compiler/offline-compiler.d.ts +92 -8
- package/dist/compiler/offline-compiler.js +3 -86
- package/dist/react/TaptappAR.js +3 -0
- package/dist/react/use-ar.js +1 -0
- package/package.json +1 -1
- package/src/compiler/FEATURES.md +40 -0
- package/src/compiler/controller.ts +54 -42
- package/src/compiler/features/auto-rotation-feature.ts +36 -0
- package/src/compiler/features/crop-detection-feature.ts +31 -0
- package/src/compiler/features/feature-base.ts +48 -0
- package/src/compiler/features/feature-manager.ts +65 -0
- package/src/compiler/features/one-euro-filter-feature.ts +44 -0
- package/src/compiler/features/temporal-filter-feature.ts +68 -0
- package/src/compiler/offline-compiler.ts +3 -94
- package/src/react/TaptappAR.tsx +3 -0
- package/src/react/use-ar.ts +1 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Tracker } from "./tracker/tracker.js";
|
|
2
|
-
import { CropDetector } from "./detector/crop-detector.js";
|
|
3
2
|
import { InputLoader } from "./input-loader.js";
|
|
3
|
+
import { FeatureManager } from "./features/feature-manager.js";
|
|
4
4
|
export interface ControllerOptions {
|
|
5
5
|
inputWidth: number;
|
|
6
6
|
inputHeight: number;
|
|
@@ -17,11 +17,6 @@ declare class Controller {
|
|
|
17
17
|
inputWidth: number;
|
|
18
18
|
inputHeight: number;
|
|
19
19
|
maxTrack: number;
|
|
20
|
-
filterMinCF: number;
|
|
21
|
-
filterBeta: number;
|
|
22
|
-
warmupTolerance: number;
|
|
23
|
-
missTolerance: number;
|
|
24
|
-
cropDetector: CropDetector;
|
|
25
20
|
inputLoader: InputLoader;
|
|
26
21
|
markerDimensions: any[] | null;
|
|
27
22
|
onUpdate: ((data: any) => void) | null;
|
|
@@ -38,6 +33,7 @@ declare class Controller {
|
|
|
38
33
|
workerTrackDone: ((data: any) => void) | null;
|
|
39
34
|
mainThreadMatcher: any;
|
|
40
35
|
mainThreadEstimator: any;
|
|
36
|
+
featureManager: FeatureManager;
|
|
41
37
|
constructor({ inputWidth, inputHeight, onUpdate, debugMode, maxTrack, warmupTolerance, missTolerance, filterMinCF, filterBeta, worker, }: ControllerOptions);
|
|
42
38
|
_setupWorkerListener(): void;
|
|
43
39
|
_ensureWorker(): void;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { Tracker } from "./tracker/tracker.js";
|
|
2
|
-
import { CropDetector } from "./detector/crop-detector.js";
|
|
3
2
|
import { OfflineCompiler as Compiler } from "./offline-compiler.js";
|
|
4
3
|
import { InputLoader } from "./input-loader.js";
|
|
5
|
-
import {
|
|
4
|
+
import { FeatureManager } from "./features/feature-manager.js";
|
|
5
|
+
import { OneEuroFilterFeature } from "./features/one-euro-filter-feature.js";
|
|
6
|
+
import { TemporalFilterFeature } from "./features/temporal-filter-feature.js";
|
|
7
|
+
import { AutoRotationFeature } from "./features/auto-rotation-feature.js";
|
|
8
|
+
import { CropDetectionFeature } from "./features/crop-detection-feature.js";
|
|
6
9
|
let ControllerWorker;
|
|
7
10
|
// Conditional import for worker to avoid crash in non-vite environments
|
|
8
11
|
const getControllerWorker = async () => {
|
|
@@ -26,11 +29,6 @@ class Controller {
|
|
|
26
29
|
inputWidth;
|
|
27
30
|
inputHeight;
|
|
28
31
|
maxTrack;
|
|
29
|
-
filterMinCF;
|
|
30
|
-
filterBeta;
|
|
31
|
-
warmupTolerance;
|
|
32
|
-
missTolerance;
|
|
33
|
-
cropDetector;
|
|
34
32
|
inputLoader;
|
|
35
33
|
markerDimensions = null;
|
|
36
34
|
onUpdate;
|
|
@@ -47,21 +45,28 @@ class Controller {
|
|
|
47
45
|
workerTrackDone = null;
|
|
48
46
|
mainThreadMatcher;
|
|
49
47
|
mainThreadEstimator;
|
|
48
|
+
featureManager;
|
|
50
49
|
constructor({ inputWidth, inputHeight, onUpdate = null, debugMode = false, maxTrack = 1, warmupTolerance = null, missTolerance = null, filterMinCF = null, filterBeta = null, worker = null, }) {
|
|
51
50
|
this.inputWidth = inputWidth;
|
|
52
51
|
this.inputHeight = inputHeight;
|
|
53
52
|
this.maxTrack = maxTrack;
|
|
54
|
-
this.
|
|
55
|
-
this.
|
|
56
|
-
this.warmupTolerance
|
|
57
|
-
this.
|
|
58
|
-
this.
|
|
53
|
+
this.featureManager = new FeatureManager();
|
|
54
|
+
this.featureManager.addFeature(new OneEuroFilterFeature(filterMinCF === null ? DEFAULT_FILTER_CUTOFF : filterMinCF, filterBeta === null ? DEFAULT_FILTER_BETA : filterBeta));
|
|
55
|
+
this.featureManager.addFeature(new TemporalFilterFeature(warmupTolerance === null ? DEFAULT_WARMUP_TOLERANCE : warmupTolerance, missTolerance === null ? DEFAULT_MISS_TOLERANCE : missTolerance));
|
|
56
|
+
this.featureManager.addFeature(new AutoRotationFeature());
|
|
57
|
+
this.featureManager.addFeature(new CropDetectionFeature());
|
|
59
58
|
this.inputLoader = new InputLoader(this.inputWidth, this.inputHeight);
|
|
60
59
|
this.onUpdate = onUpdate;
|
|
61
60
|
this.debugMode = debugMode;
|
|
62
61
|
this.worker = worker;
|
|
63
62
|
if (this.worker)
|
|
64
63
|
this._setupWorkerListener();
|
|
64
|
+
this.featureManager.init({
|
|
65
|
+
inputWidth: this.inputWidth,
|
|
66
|
+
inputHeight: this.inputHeight,
|
|
67
|
+
projectionTransform: [], // Will be set below
|
|
68
|
+
debugMode: this.debugMode
|
|
69
|
+
});
|
|
65
70
|
const near = 10;
|
|
66
71
|
const far = 100000;
|
|
67
72
|
const fovy = (45.0 * Math.PI) / 180;
|
|
@@ -71,6 +76,12 @@ class Controller {
|
|
|
71
76
|
[0, f, this.inputHeight / 2],
|
|
72
77
|
[0, 0, 1],
|
|
73
78
|
];
|
|
79
|
+
this.featureManager.init({
|
|
80
|
+
inputWidth: this.inputWidth,
|
|
81
|
+
inputHeight: this.inputHeight,
|
|
82
|
+
projectionTransform: this.projectionTransform,
|
|
83
|
+
debugMode: this.debugMode
|
|
84
|
+
});
|
|
74
85
|
this.projectionMatrix = this._glProjectionMatrix({
|
|
75
86
|
projectionTransform: this.projectionTransform,
|
|
76
87
|
width: this.inputWidth,
|
|
@@ -149,7 +160,8 @@ class Controller {
|
|
|
149
160
|
}
|
|
150
161
|
dummyRun(input) {
|
|
151
162
|
const inputData = this.inputLoader.loadInput(input);
|
|
152
|
-
this.
|
|
163
|
+
const cropFeature = this.featureManager.getFeature("crop-detection");
|
|
164
|
+
cropFeature?.detect(inputData, false);
|
|
153
165
|
this.tracker.dummyRun(inputData);
|
|
154
166
|
}
|
|
155
167
|
getProjectionMatrix() {
|
|
@@ -167,7 +179,8 @@ class Controller {
|
|
|
167
179
|
return this._glModelViewMatrix(modelViewTransform, targetIndex);
|
|
168
180
|
}
|
|
169
181
|
async _detectAndMatch(inputData, targetIndexes) {
|
|
170
|
-
const
|
|
182
|
+
const cropFeature = this.featureManager.getFeature("crop-detection");
|
|
183
|
+
const { featurePoints } = cropFeature.detect(inputData, true);
|
|
171
184
|
const { targetIndex: matchedTargetIndex, modelViewTransform } = await this._workerMatch(featurePoints, targetIndexes);
|
|
172
185
|
return { targetIndex: matchedTargetIndex, modelViewTransform };
|
|
173
186
|
}
|
|
@@ -193,7 +206,6 @@ class Controller {
|
|
|
193
206
|
currentModelViewTransform: null,
|
|
194
207
|
trackCount: 0,
|
|
195
208
|
trackMiss: 0,
|
|
196
|
-
filter: new OneEuroFilter({ minCutOff: this.filterMinCF, beta: this.filterBeta }),
|
|
197
209
|
});
|
|
198
210
|
}
|
|
199
211
|
const startProcessing = async () => {
|
|
@@ -229,43 +241,29 @@ class Controller {
|
|
|
229
241
|
trackingState.currentModelViewTransform = modelViewTransform;
|
|
230
242
|
}
|
|
231
243
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
trackingState.trackingMatrix = null;
|
|
239
|
-
trackingState.filter.reset();
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
if (trackingState.showing) {
|
|
244
|
-
if (!trackingState.isTracking) {
|
|
245
|
-
trackingState.trackCount = 0;
|
|
246
|
-
trackingState.trackMiss += 1;
|
|
247
|
-
if (trackingState.trackMiss > this.missTolerance) {
|
|
248
|
-
trackingState.showing = false;
|
|
249
|
-
trackingState.trackingMatrix = null;
|
|
250
|
-
this.onUpdate && this.onUpdate({ type: "updateMatrix", targetIndex: i, worldMatrix: null });
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
else {
|
|
254
|
-
trackingState.trackMiss = 0;
|
|
255
|
-
}
|
|
244
|
+
const wasShowing = trackingState.showing;
|
|
245
|
+
trackingState.showing = this.featureManager.shouldShow(i, trackingState.isTracking);
|
|
246
|
+
if (wasShowing && !trackingState.showing) {
|
|
247
|
+
trackingState.trackingMatrix = null;
|
|
248
|
+
this.onUpdate && this.onUpdate({ type: "updateMatrix", targetIndex: i, worldMatrix: null });
|
|
249
|
+
this.featureManager.notifyUpdate({ type: "reset", targetIndex: i });
|
|
256
250
|
}
|
|
257
251
|
if (trackingState.showing) {
|
|
258
252
|
const worldMatrix = this._glModelViewMatrix(trackingState.currentModelViewTransform, i);
|
|
259
|
-
|
|
260
|
-
|
|
253
|
+
const filteredMatrix = this.featureManager.applyWorldMatrixFilters(i, worldMatrix);
|
|
254
|
+
trackingState.trackingMatrix = filteredMatrix;
|
|
255
|
+
let finalMatrix = [...filteredMatrix];
|
|
261
256
|
const isInputRotated = input.width === this.inputHeight && input.height === this.inputWidth;
|
|
262
257
|
if (isInputRotated) {
|
|
263
|
-
|
|
258
|
+
const rotationFeature = this.featureManager.getFeature("auto-rotation");
|
|
259
|
+
if (rotationFeature) {
|
|
260
|
+
finalMatrix = rotationFeature.rotate(finalMatrix);
|
|
261
|
+
}
|
|
264
262
|
}
|
|
265
263
|
this.onUpdate && this.onUpdate({
|
|
266
264
|
type: "updateMatrix",
|
|
267
265
|
targetIndex: i,
|
|
268
|
-
worldMatrix:
|
|
266
|
+
worldMatrix: finalMatrix,
|
|
269
267
|
modelViewTransform: trackingState.currentModelViewTransform
|
|
270
268
|
});
|
|
271
269
|
}
|
|
@@ -286,7 +284,8 @@ class Controller {
|
|
|
286
284
|
}
|
|
287
285
|
async detect(input) {
|
|
288
286
|
const inputData = this.inputLoader.loadInput(input);
|
|
289
|
-
const
|
|
287
|
+
const cropFeature = this.featureManager.getFeature("crop-detection");
|
|
288
|
+
const { featurePoints, debugExtra } = cropFeature.detect(inputData, false);
|
|
290
289
|
return { featurePoints, debugExtra };
|
|
291
290
|
}
|
|
292
291
|
async match(featurePoints, targetIndex) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ControllerFeature } from "./feature-base.js";
|
|
2
|
+
export declare class AutoRotationFeature implements ControllerFeature {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
private inputWidth;
|
|
8
|
+
private inputHeight;
|
|
9
|
+
init(context: {
|
|
10
|
+
inputWidth: number;
|
|
11
|
+
inputHeight: number;
|
|
12
|
+
}): void;
|
|
13
|
+
filterWorldMatrix(targetIndex: number, worldMatrix: number[]): number[];
|
|
14
|
+
rotate(m: number[]): number[];
|
|
15
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export class AutoRotationFeature {
|
|
2
|
+
id = "auto-rotation";
|
|
3
|
+
name = "Auto Rotation Matrix";
|
|
4
|
+
description = "Automatically adjusts the world matrix if the input video is rotated (e.g. portrait mode).";
|
|
5
|
+
enabled = true;
|
|
6
|
+
inputWidth = 0;
|
|
7
|
+
inputHeight = 0;
|
|
8
|
+
init(context) {
|
|
9
|
+
this.inputWidth = context.inputWidth;
|
|
10
|
+
this.inputHeight = context.inputHeight;
|
|
11
|
+
}
|
|
12
|
+
filterWorldMatrix(targetIndex, worldMatrix) {
|
|
13
|
+
if (!this.enabled)
|
|
14
|
+
return worldMatrix;
|
|
15
|
+
// Check if input is rotated (this logic might need the actual current input dimensions)
|
|
16
|
+
// For now, we'll assume the controller passes the 'isRotated' info or we detect it
|
|
17
|
+
// But since this is a matrix post-process, we can just apply it if needed.
|
|
18
|
+
return worldMatrix;
|
|
19
|
+
}
|
|
20
|
+
// We might need a way to pass the 'currentInput' to the feature.
|
|
21
|
+
// Actually, the controller can just call this if it detects rotation.
|
|
22
|
+
rotate(m) {
|
|
23
|
+
return [
|
|
24
|
+
-m[1], m[0], m[2], m[3],
|
|
25
|
+
-m[5], m[4], m[6], m[7],
|
|
26
|
+
-m[9], m[8], m[10], m[11],
|
|
27
|
+
-m[13], m[12], m[14], m[15],
|
|
28
|
+
];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ControllerFeature, FeatureContext } from "./feature-base.js";
|
|
2
|
+
export declare class CropDetectionFeature implements ControllerFeature {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
private cropDetector;
|
|
8
|
+
private debugMode;
|
|
9
|
+
init(context: FeatureContext): void;
|
|
10
|
+
detect(inputData: any, isMoving?: boolean): {
|
|
11
|
+
featurePoints: any[];
|
|
12
|
+
debugExtra: {
|
|
13
|
+
projectedImage: number[];
|
|
14
|
+
} | {
|
|
15
|
+
projectedImage?: undefined;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { CropDetector } from "../detector/crop-detector.js";
|
|
2
|
+
export class CropDetectionFeature {
|
|
3
|
+
id = "crop-detection";
|
|
4
|
+
name = "Crop Detection";
|
|
5
|
+
description = "Optimizes detection by focusing on areas with motion, reducing CPU usage.";
|
|
6
|
+
enabled = true;
|
|
7
|
+
cropDetector = null;
|
|
8
|
+
debugMode = false;
|
|
9
|
+
init(context) {
|
|
10
|
+
this.debugMode = context.debugMode;
|
|
11
|
+
this.cropDetector = new CropDetector(context.inputWidth, context.inputHeight, this.debugMode);
|
|
12
|
+
}
|
|
13
|
+
detect(inputData, isMoving = true) {
|
|
14
|
+
if (!this.enabled || !this.cropDetector) {
|
|
15
|
+
// Fallback to full detection if disabled?
|
|
16
|
+
// Actually CropDetector.detect is just full detection.
|
|
17
|
+
// We'll expose the methods here.
|
|
18
|
+
}
|
|
19
|
+
if (isMoving && this.enabled) {
|
|
20
|
+
return this.cropDetector.detectMoving(inputData);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
return this.cropDetector.detect(inputData);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface FeatureContext {
|
|
2
|
+
inputWidth: number;
|
|
3
|
+
inputHeight: number;
|
|
4
|
+
projectionTransform: number[][];
|
|
5
|
+
debugMode: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface Feature {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
init?(context: FeatureContext): void;
|
|
13
|
+
onUpdate?(data: any): void;
|
|
14
|
+
dispose?(): void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Controller feature interface for processing hooks
|
|
18
|
+
*/
|
|
19
|
+
export interface ControllerFeature extends Feature {
|
|
20
|
+
/**
|
|
21
|
+
* Called before processing a frame
|
|
22
|
+
*/
|
|
23
|
+
beforeProcess?(inputData: any): void;
|
|
24
|
+
/**
|
|
25
|
+
* Called after detection/matching
|
|
26
|
+
*/
|
|
27
|
+
afterMatch?(result: {
|
|
28
|
+
targetIndex: number;
|
|
29
|
+
modelViewTransform: number[][] | null;
|
|
30
|
+
}): void;
|
|
31
|
+
/**
|
|
32
|
+
* Called after tracking update
|
|
33
|
+
*/
|
|
34
|
+
afterTrack?(result: {
|
|
35
|
+
targetIndex: number;
|
|
36
|
+
modelViewTransform: number[][] | null;
|
|
37
|
+
}): void;
|
|
38
|
+
/**
|
|
39
|
+
* Hook to filter or modify the final world matrix
|
|
40
|
+
*/
|
|
41
|
+
filterWorldMatrix?(targetIndex: number, worldMatrix: number[]): number[];
|
|
42
|
+
/**
|
|
43
|
+
* Hook to decide if a target should be shown
|
|
44
|
+
*/
|
|
45
|
+
shouldShow?(targetIndex: number, isTracking: boolean): boolean;
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ControllerFeature, FeatureContext } from "./feature-base.js";
|
|
2
|
+
export declare class FeatureManager {
|
|
3
|
+
private features;
|
|
4
|
+
addFeature(feature: ControllerFeature): void;
|
|
5
|
+
getFeature<T extends ControllerFeature>(id: string): T | undefined;
|
|
6
|
+
init(context: FeatureContext): void;
|
|
7
|
+
beforeProcess(inputData: any): void;
|
|
8
|
+
applyWorldMatrixFilters(targetIndex: number, worldMatrix: number[]): number[];
|
|
9
|
+
shouldShow(targetIndex: number, isTracking: boolean): boolean;
|
|
10
|
+
notifyUpdate(data: any): void;
|
|
11
|
+
dispose(): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export class FeatureManager {
|
|
2
|
+
features = [];
|
|
3
|
+
addFeature(feature) {
|
|
4
|
+
this.features.push(feature);
|
|
5
|
+
}
|
|
6
|
+
getFeature(id) {
|
|
7
|
+
return this.features.find(f => f.id === id);
|
|
8
|
+
}
|
|
9
|
+
init(context) {
|
|
10
|
+
for (const feature of this.features) {
|
|
11
|
+
if (feature.enabled && feature.init) {
|
|
12
|
+
feature.init(context);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
beforeProcess(inputData) {
|
|
17
|
+
for (const feature of this.features) {
|
|
18
|
+
if (feature.enabled && feature.beforeProcess) {
|
|
19
|
+
feature.beforeProcess(inputData);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
applyWorldMatrixFilters(targetIndex, worldMatrix) {
|
|
24
|
+
let result = worldMatrix;
|
|
25
|
+
for (const feature of this.features) {
|
|
26
|
+
if (feature.enabled && feature.filterWorldMatrix) {
|
|
27
|
+
result = feature.filterWorldMatrix(targetIndex, result);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
shouldShow(targetIndex, isTracking) {
|
|
33
|
+
let show = isTracking;
|
|
34
|
+
for (const feature of this.features) {
|
|
35
|
+
if (feature.enabled && feature.shouldShow) {
|
|
36
|
+
show = feature.shouldShow(targetIndex, isTracking);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return show;
|
|
40
|
+
}
|
|
41
|
+
notifyUpdate(data) {
|
|
42
|
+
for (const feature of this.features) {
|
|
43
|
+
if (feature.enabled && feature.onUpdate) {
|
|
44
|
+
feature.onUpdate(data);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
dispose() {
|
|
49
|
+
for (const feature of this.features) {
|
|
50
|
+
if (feature.dispose) {
|
|
51
|
+
feature.dispose();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ControllerFeature, FeatureContext } from "./feature-base.js";
|
|
2
|
+
export declare class OneEuroFilterFeature implements ControllerFeature {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
private filters;
|
|
8
|
+
private minCutOff;
|
|
9
|
+
private beta;
|
|
10
|
+
constructor(minCutOff?: number, beta?: number);
|
|
11
|
+
init(context: FeatureContext): void;
|
|
12
|
+
private getFilter;
|
|
13
|
+
filterWorldMatrix(targetIndex: number, worldMatrix: number[]): number[];
|
|
14
|
+
onUpdate(data: any): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { OneEuroFilter } from "../../libs/one-euro-filter.js";
|
|
2
|
+
export class OneEuroFilterFeature {
|
|
3
|
+
id = "one-euro-filter";
|
|
4
|
+
name = "One Euro Filter";
|
|
5
|
+
description = "Smooths the tracking matrix to reduce jitter using a One Euro Filter.";
|
|
6
|
+
enabled = true;
|
|
7
|
+
filters = [];
|
|
8
|
+
minCutOff;
|
|
9
|
+
beta;
|
|
10
|
+
constructor(minCutOff = 0.5, beta = 0.1) {
|
|
11
|
+
this.minCutOff = minCutOff;
|
|
12
|
+
this.beta = beta;
|
|
13
|
+
}
|
|
14
|
+
init(context) {
|
|
15
|
+
// We'll initialize filters lazily or based on target count if known
|
|
16
|
+
}
|
|
17
|
+
getFilter(targetIndex) {
|
|
18
|
+
if (!this.filters[targetIndex]) {
|
|
19
|
+
this.filters[targetIndex] = new OneEuroFilter({
|
|
20
|
+
minCutOff: this.minCutOff,
|
|
21
|
+
beta: this.beta
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return this.filters[targetIndex];
|
|
25
|
+
}
|
|
26
|
+
filterWorldMatrix(targetIndex, worldMatrix) {
|
|
27
|
+
if (!this.enabled)
|
|
28
|
+
return worldMatrix;
|
|
29
|
+
const filter = this.getFilter(targetIndex);
|
|
30
|
+
return filter.filter(Date.now(), worldMatrix);
|
|
31
|
+
}
|
|
32
|
+
onUpdate(data) {
|
|
33
|
+
if (data.type === "reset" && data.targetIndex !== undefined) {
|
|
34
|
+
this.filters[data.targetIndex]?.reset();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ControllerFeature } from "./feature-base.js";
|
|
2
|
+
export interface TemporalState {
|
|
3
|
+
showing: boolean;
|
|
4
|
+
trackCount: number;
|
|
5
|
+
trackMiss: number;
|
|
6
|
+
}
|
|
7
|
+
export declare class TemporalFilterFeature implements ControllerFeature {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
private states;
|
|
13
|
+
private warmupTolerance;
|
|
14
|
+
private missTolerance;
|
|
15
|
+
private onToggleShowing?;
|
|
16
|
+
constructor(warmup?: number, miss?: number, onToggleShowing?: (targetIndex: number, showing: boolean) => void);
|
|
17
|
+
private getState;
|
|
18
|
+
shouldShow(targetIndex: number, isTracking: boolean): boolean;
|
|
19
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export class TemporalFilterFeature {
|
|
2
|
+
id = "temporal-filter";
|
|
3
|
+
name = "Temporal Filter";
|
|
4
|
+
description = "Provides warmup tolerance (to avoid false positives) and miss tolerance (to maintain tracking during brief occlusions).";
|
|
5
|
+
enabled = true;
|
|
6
|
+
states = [];
|
|
7
|
+
warmupTolerance;
|
|
8
|
+
missTolerance;
|
|
9
|
+
onToggleShowing;
|
|
10
|
+
constructor(warmup = 2, miss = 5, onToggleShowing) {
|
|
11
|
+
this.warmupTolerance = warmup;
|
|
12
|
+
this.missTolerance = miss;
|
|
13
|
+
this.onToggleShowing = onToggleShowing;
|
|
14
|
+
}
|
|
15
|
+
getState(targetIndex) {
|
|
16
|
+
if (!this.states[targetIndex]) {
|
|
17
|
+
this.states[targetIndex] = {
|
|
18
|
+
showing: false,
|
|
19
|
+
trackCount: 0,
|
|
20
|
+
trackMiss: 0,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return this.states[targetIndex];
|
|
24
|
+
}
|
|
25
|
+
shouldShow(targetIndex, isTracking) {
|
|
26
|
+
if (!this.enabled)
|
|
27
|
+
return isTracking;
|
|
28
|
+
const state = this.getState(targetIndex);
|
|
29
|
+
if (!state.showing) {
|
|
30
|
+
if (isTracking) {
|
|
31
|
+
state.trackMiss = 0;
|
|
32
|
+
state.trackCount += 1;
|
|
33
|
+
if (state.trackCount > this.warmupTolerance) {
|
|
34
|
+
state.showing = true;
|
|
35
|
+
this.onToggleShowing?.(targetIndex, true);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
state.trackCount = 0;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
if (!isTracking) {
|
|
44
|
+
state.trackCount = 0;
|
|
45
|
+
state.trackMiss += 1;
|
|
46
|
+
if (state.trackMiss > this.missTolerance) {
|
|
47
|
+
state.showing = false;
|
|
48
|
+
this.onToggleShowing?.(targetIndex, false);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
state.trackMiss = 0;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return state.showing;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -5,26 +5,110 @@
|
|
|
5
5
|
* que NO depende de TensorFlow, eliminando todos los problemas de
|
|
6
6
|
* inicialización, bloqueos y compatibilidad.
|
|
7
7
|
*/
|
|
8
|
-
import { WorkerPool } from "./utils/worker-pool.js";
|
|
9
8
|
export declare class OfflineCompiler {
|
|
10
9
|
data: any;
|
|
11
|
-
workerPool: WorkerPool | null;
|
|
12
10
|
constructor();
|
|
13
|
-
_initNodeWorkers(): Promise<void>;
|
|
14
11
|
compileImageTargets(images: any[], progressCallback: (p: number) => void): Promise<any>;
|
|
15
|
-
_compileTarget(targetImages: any[], progressCallback: (p: number) => void): Promise<
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
_compileTarget(targetImages: any[], progressCallback: (p: number) => void): Promise<{
|
|
13
|
+
matchingData: {
|
|
14
|
+
maximaPoints: any[];
|
|
15
|
+
minimaPoints: any[];
|
|
16
|
+
maximaPointsCluster: {
|
|
17
|
+
rootNode: {
|
|
18
|
+
leaf: boolean;
|
|
19
|
+
pointIndexes: never[];
|
|
20
|
+
centerPointIndex: null;
|
|
21
|
+
};
|
|
22
|
+
} | {
|
|
23
|
+
rootNode: {
|
|
24
|
+
centerPointIndex: any;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
minimaPointsCluster: {
|
|
28
|
+
rootNode: {
|
|
29
|
+
leaf: boolean;
|
|
30
|
+
pointIndexes: never[];
|
|
31
|
+
centerPointIndex: null;
|
|
32
|
+
};
|
|
33
|
+
} | {
|
|
34
|
+
rootNode: {
|
|
35
|
+
centerPointIndex: any;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
width: any;
|
|
39
|
+
height: any;
|
|
40
|
+
scale: any;
|
|
41
|
+
}[];
|
|
42
|
+
trackingData: Object[];
|
|
43
|
+
}[]>;
|
|
44
|
+
_compileMatch(targetImages: any[], progressCallback: (p: number) => void): Promise<{
|
|
45
|
+
maximaPoints: any[];
|
|
46
|
+
minimaPoints: any[];
|
|
47
|
+
maximaPointsCluster: {
|
|
48
|
+
rootNode: {
|
|
49
|
+
leaf: boolean;
|
|
50
|
+
pointIndexes: never[];
|
|
51
|
+
centerPointIndex: null;
|
|
52
|
+
};
|
|
53
|
+
} | {
|
|
54
|
+
rootNode: {
|
|
55
|
+
centerPointIndex: any;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
minimaPointsCluster: {
|
|
59
|
+
rootNode: {
|
|
60
|
+
leaf: boolean;
|
|
61
|
+
pointIndexes: never[];
|
|
62
|
+
centerPointIndex: null;
|
|
63
|
+
};
|
|
64
|
+
} | {
|
|
65
|
+
rootNode: {
|
|
66
|
+
centerPointIndex: any;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
width: any;
|
|
70
|
+
height: any;
|
|
71
|
+
scale: any;
|
|
72
|
+
}[][]>;
|
|
73
|
+
_compileTrack(targetImages: any[], progressCallback: (p: number) => void): Promise<Object[][]>;
|
|
18
74
|
compileTrack({ progressCallback, targetImages, basePercent }: {
|
|
19
75
|
progressCallback: (p: number) => void;
|
|
20
76
|
targetImages: any[];
|
|
21
77
|
basePercent?: number;
|
|
22
|
-
}): Promise<
|
|
78
|
+
}): Promise<Object[][]>;
|
|
23
79
|
compileMatch({ progressCallback, targetImages, basePercent }: {
|
|
24
80
|
progressCallback: (p: number) => void;
|
|
25
81
|
targetImages: any[];
|
|
26
82
|
basePercent?: number;
|
|
27
|
-
}): Promise<
|
|
83
|
+
}): Promise<{
|
|
84
|
+
maximaPoints: any[];
|
|
85
|
+
minimaPoints: any[];
|
|
86
|
+
maximaPointsCluster: {
|
|
87
|
+
rootNode: {
|
|
88
|
+
leaf: boolean;
|
|
89
|
+
pointIndexes: never[];
|
|
90
|
+
centerPointIndex: null;
|
|
91
|
+
};
|
|
92
|
+
} | {
|
|
93
|
+
rootNode: {
|
|
94
|
+
centerPointIndex: any;
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
minimaPointsCluster: {
|
|
98
|
+
rootNode: {
|
|
99
|
+
leaf: boolean;
|
|
100
|
+
pointIndexes: never[];
|
|
101
|
+
centerPointIndex: null;
|
|
102
|
+
};
|
|
103
|
+
} | {
|
|
104
|
+
rootNode: {
|
|
105
|
+
centerPointIndex: any;
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
width: any;
|
|
109
|
+
height: any;
|
|
110
|
+
scale: any;
|
|
111
|
+
}[][]>;
|
|
28
112
|
exportData(): Uint8Array<ArrayBuffer>;
|
|
29
113
|
_getMorton(x: number, y: number): number;
|
|
30
114
|
_columnarize(points: any[], tree: any, width: number, height: number): {
|