@srsergio/taptapp-ar 1.0.9 → 1.0.11

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.
Files changed (123) hide show
  1. package/README.md +74 -49
  2. package/dist/compiler/controller.d.ts +15 -22
  3. package/dist/compiler/controller.js +73 -92
  4. package/dist/compiler/detector/crop-detector.d.ts +20 -51
  5. package/dist/compiler/detector/crop-detector.js +21 -15
  6. package/dist/compiler/input-loader.d.ts +15 -17
  7. package/dist/compiler/input-loader.js +58 -76
  8. package/dist/compiler/matching/hamming-distance.js +4 -4
  9. package/dist/compiler/matching/matcher.js +2 -2
  10. package/dist/compiler/matching/matching.d.ts +2 -16
  11. package/dist/compiler/matching/matching.js +72 -60
  12. package/dist/compiler/offline-compiler.d.ts +9 -29
  13. package/dist/compiler/offline-compiler.js +38 -72
  14. package/dist/compiler/three.js +0 -4
  15. package/dist/compiler/tracker/tracker.d.ts +26 -12
  16. package/dist/compiler/tracker/tracker.js +158 -259
  17. package/package.json +1 -1
  18. package/src/compiler/controller.js +71 -93
  19. package/src/compiler/detector/crop-detector.js +26 -15
  20. package/src/compiler/input-loader.js +62 -88
  21. package/src/compiler/matching/hamming-distance.js +4 -4
  22. package/src/compiler/matching/hough.js +1 -1
  23. package/src/compiler/matching/matcher.js +2 -2
  24. package/src/compiler/matching/matching.js +80 -72
  25. package/src/compiler/offline-compiler.js +38 -75
  26. package/src/compiler/three.js +0 -4
  27. package/src/compiler/tracker/tracker.js +183 -283
  28. package/dist/compiler/compiler-base.d.ts +0 -8
  29. package/dist/compiler/compiler-base.js +0 -179
  30. package/dist/compiler/compiler.d.ts +0 -9
  31. package/dist/compiler/compiler.js +0 -24
  32. package/dist/compiler/compiler.worker.d.ts +0 -1
  33. package/dist/compiler/compiler.worker.js +0 -28
  34. package/dist/compiler/detector/detector.d.ts +0 -97
  35. package/dist/compiler/detector/detector.js +0 -1042
  36. package/dist/compiler/detector/kernels/cpu/binomialFilter.d.ts +0 -6
  37. package/dist/compiler/detector/kernels/cpu/binomialFilter.js +0 -50
  38. package/dist/compiler/detector/kernels/cpu/buildExtremas.d.ts +0 -6
  39. package/dist/compiler/detector/kernels/cpu/buildExtremas.js +0 -89
  40. package/dist/compiler/detector/kernels/cpu/computeExtremaAngles.d.ts +0 -7
  41. package/dist/compiler/detector/kernels/cpu/computeExtremaAngles.js +0 -79
  42. package/dist/compiler/detector/kernels/cpu/computeExtremaFreak.d.ts +0 -6
  43. package/dist/compiler/detector/kernels/cpu/computeExtremaFreak.js +0 -68
  44. package/dist/compiler/detector/kernels/cpu/computeFreakDescriptors.d.ts +0 -6
  45. package/dist/compiler/detector/kernels/cpu/computeFreakDescriptors.js +0 -57
  46. package/dist/compiler/detector/kernels/cpu/computeLocalization.d.ts +0 -6
  47. package/dist/compiler/detector/kernels/cpu/computeLocalization.js +0 -50
  48. package/dist/compiler/detector/kernels/cpu/computeOrientationHistograms.d.ts +0 -6
  49. package/dist/compiler/detector/kernels/cpu/computeOrientationHistograms.js +0 -100
  50. package/dist/compiler/detector/kernels/cpu/downsampleBilinear.d.ts +0 -6
  51. package/dist/compiler/detector/kernels/cpu/downsampleBilinear.js +0 -29
  52. package/dist/compiler/detector/kernels/cpu/extremaReduction.d.ts +0 -6
  53. package/dist/compiler/detector/kernels/cpu/extremaReduction.js +0 -50
  54. package/dist/compiler/detector/kernels/cpu/fakeShader.d.ts +0 -20
  55. package/dist/compiler/detector/kernels/cpu/fakeShader.js +0 -80
  56. package/dist/compiler/detector/kernels/cpu/index.d.ts +0 -1
  57. package/dist/compiler/detector/kernels/cpu/index.js +0 -25
  58. package/dist/compiler/detector/kernels/cpu/prune.d.ts +0 -7
  59. package/dist/compiler/detector/kernels/cpu/prune.js +0 -62
  60. package/dist/compiler/detector/kernels/cpu/smoothHistograms.d.ts +0 -6
  61. package/dist/compiler/detector/kernels/cpu/smoothHistograms.js +0 -47
  62. package/dist/compiler/detector/kernels/cpu/upsampleBilinear.d.ts +0 -6
  63. package/dist/compiler/detector/kernels/cpu/upsampleBilinear.js +0 -43
  64. package/dist/compiler/detector/kernels/index.d.ts +0 -1
  65. package/dist/compiler/detector/kernels/index.js +0 -2
  66. package/dist/compiler/detector/kernels/webgl/binomialFilter.d.ts +0 -6
  67. package/dist/compiler/detector/kernels/webgl/binomialFilter.js +0 -67
  68. package/dist/compiler/detector/kernels/webgl/buildExtremas.d.ts +0 -6
  69. package/dist/compiler/detector/kernels/webgl/buildExtremas.js +0 -101
  70. package/dist/compiler/detector/kernels/webgl/computeExtremaAngles.d.ts +0 -6
  71. package/dist/compiler/detector/kernels/webgl/computeExtremaAngles.js +0 -78
  72. package/dist/compiler/detector/kernels/webgl/computeExtremaFreak.d.ts +0 -6
  73. package/dist/compiler/detector/kernels/webgl/computeExtremaFreak.js +0 -86
  74. package/dist/compiler/detector/kernels/webgl/computeFreakDescriptors.d.ts +0 -6
  75. package/dist/compiler/detector/kernels/webgl/computeFreakDescriptors.js +0 -52
  76. package/dist/compiler/detector/kernels/webgl/computeLocalization.d.ts +0 -6
  77. package/dist/compiler/detector/kernels/webgl/computeLocalization.js +0 -58
  78. package/dist/compiler/detector/kernels/webgl/computeOrientationHistograms.d.ts +0 -6
  79. package/dist/compiler/detector/kernels/webgl/computeOrientationHistograms.js +0 -116
  80. package/dist/compiler/detector/kernels/webgl/downsampleBilinear.d.ts +0 -6
  81. package/dist/compiler/detector/kernels/webgl/downsampleBilinear.js +0 -46
  82. package/dist/compiler/detector/kernels/webgl/extremaReduction.d.ts +0 -6
  83. package/dist/compiler/detector/kernels/webgl/extremaReduction.js +0 -48
  84. package/dist/compiler/detector/kernels/webgl/index.d.ts +0 -1
  85. package/dist/compiler/detector/kernels/webgl/index.js +0 -25
  86. package/dist/compiler/detector/kernels/webgl/smoothHistograms.d.ts +0 -6
  87. package/dist/compiler/detector/kernels/webgl/smoothHistograms.js +0 -49
  88. package/dist/compiler/detector/kernels/webgl/upsampleBilinear.d.ts +0 -6
  89. package/dist/compiler/detector/kernels/webgl/upsampleBilinear.js +0 -56
  90. package/dist/compiler/tensorflow-setup.d.ts +0 -6
  91. package/dist/compiler/tensorflow-setup.js +0 -99
  92. package/src/compiler/compiler-base.js +0 -210
  93. package/src/compiler/compiler.js +0 -25
  94. package/src/compiler/compiler.worker.js +0 -30
  95. package/src/compiler/detector/detector.js +0 -1119
  96. package/src/compiler/detector/kernels/cpu/binomialFilter.js +0 -58
  97. package/src/compiler/detector/kernels/cpu/buildExtremas.js +0 -108
  98. package/src/compiler/detector/kernels/cpu/computeExtremaAngles.js +0 -91
  99. package/src/compiler/detector/kernels/cpu/computeExtremaFreak.js +0 -92
  100. package/src/compiler/detector/kernels/cpu/computeFreakDescriptors.js +0 -68
  101. package/src/compiler/detector/kernels/cpu/computeLocalization.js +0 -67
  102. package/src/compiler/detector/kernels/cpu/computeOrientationHistograms.js +0 -124
  103. package/src/compiler/detector/kernels/cpu/downsampleBilinear.js +0 -33
  104. package/src/compiler/detector/kernels/cpu/extremaReduction.js +0 -53
  105. package/src/compiler/detector/kernels/cpu/fakeShader.js +0 -88
  106. package/src/compiler/detector/kernels/cpu/index.js +0 -26
  107. package/src/compiler/detector/kernels/cpu/prune.js +0 -78
  108. package/src/compiler/detector/kernels/cpu/smoothHistograms.js +0 -57
  109. package/src/compiler/detector/kernels/cpu/upsampleBilinear.js +0 -51
  110. package/src/compiler/detector/kernels/index.js +0 -2
  111. package/src/compiler/detector/kernels/webgl/binomialFilter.js +0 -72
  112. package/src/compiler/detector/kernels/webgl/buildExtremas.js +0 -109
  113. package/src/compiler/detector/kernels/webgl/computeExtremaAngles.js +0 -82
  114. package/src/compiler/detector/kernels/webgl/computeExtremaFreak.js +0 -105
  115. package/src/compiler/detector/kernels/webgl/computeFreakDescriptors.js +0 -56
  116. package/src/compiler/detector/kernels/webgl/computeLocalization.js +0 -70
  117. package/src/compiler/detector/kernels/webgl/computeOrientationHistograms.js +0 -129
  118. package/src/compiler/detector/kernels/webgl/downsampleBilinear.js +0 -50
  119. package/src/compiler/detector/kernels/webgl/extremaReduction.js +0 -50
  120. package/src/compiler/detector/kernels/webgl/index.js +0 -26
  121. package/src/compiler/detector/kernels/webgl/smoothHistograms.js +0 -53
  122. package/src/compiler/detector/kernels/webgl/upsampleBilinear.js +0 -62
  123. package/src/compiler/tensorflow-setup.js +0 -116
package/README.md CHANGED
@@ -1,18 +1,18 @@
1
1
  # @srsergio/taptapp-ar
2
2
 
3
- 🚀 **TapTapp AR** is a high-performance Augmented Reality (AR) compiler toolkit for **Node.js** and **Browser** environments. It provides an ultra-fast offline compiler for image targets.
3
+ 🚀 **TapTapp AR** is a high-performance Augmented Reality (AR) toolkit for **Node.js** and **Browser** environments. It provides an ultra-fast offline compiler and a lightweight runtime for image tracking.
4
4
 
5
- Built with performance in mind, this package features a **pure JavaScript offline compiler** that requires **no TensorFlow** for compilation, generating high-quality `.mind` files in record time.
5
+ **100% Pure JavaScript**: This package is now completely independent of **TensorFlow.js** for both compilation and real-time tracking, resulting in massive performance gains and zero-latency initialization.
6
6
 
7
7
  ---
8
8
 
9
9
  ## 🌟 Key Features
10
10
 
11
- - 🖼️ **Ultra-Fast Offline Compiler**: Pure JavaScript compiler that generates `.mind` target files in **~1.3s per image**.
12
- - ⚡ **Zero TensorFlow for Compilation**: The offline compiler uses optimized pure JS algorithms - no TensorFlow installation required.
13
- - 🧵 **Multi-threaded Engine**: Truly parallel processing using Node.js `worker_threads` for bulk image compilation.
14
- - 🚀 **Serverless Ready**: Lightweight compiler with minimal dependencies, perfect for Vercel, AWS Lambda, and Netlify.
15
- - 📦 **Protocol V3 (Columnar Binary)**: Industry-leading performance with zero-copy loading and 80%+ smaller files.
11
+ - 🖼️ **Hyper-Fast Compiler**: Pure JavaScript compiler that generates `.mind` files in **< 0.9s per image**.
12
+ - ⚡ **No TensorFlow Dependency**: No TFJS at all. Works natively in any JS environment (Node, Browser, Workers).
13
+ - 🚀 **Protocol V3 (Columnar Binary)**: Zero-copy loading with 80%+ smaller files and CPU-cache alignment.
14
+ - 🧵 **Optimized Runtime**: Tracking engine with **Buffer Recycling** and **Zero-Copy** for smooth 60fps AR on low-end devices.
15
+ - 📦 **Framework Agnostic**: Includes wrappers for **A-Frame**, **Three.js**, and a raw **Controller** for custom engines.
16
16
 
17
17
  ---
18
18
 
@@ -22,79 +22,104 @@ Built with performance in mind, this package features a **pure JavaScript offlin
22
22
  npm install @srsergio/taptapp-ar
23
23
  ```
24
24
 
25
- ### 📦 Optional Dependencies
26
-
27
- > **Note:** TensorFlow is **NOT required** for the offline compiler. It only uses pure JavaScript.
28
-
29
25
  ---
30
26
 
31
- ## 🖼️ High-Performance Compiler (Protocol V3)
32
-
33
- TaptApp AR features the industry's most advanced **pure JavaScript** offline compiler. With the introduction of **Protocol V3 (Columnar Binary Format)**, it sets a new standard for AR asset management.
27
+ ## 📊 Industry-Leading Benchmarks (v3)
34
28
 
35
- ### Industry-Leading Benchmarks
36
-
37
- | Metric | Official MindAR | TapTapp AR (v3) | Improvement |
29
+ | Metric | Official MindAR | TapTapp AR | Improvement |
38
30
  | :--- | :--- | :--- | :--- |
39
- | **Compilation Time** | ~23.50s | **~1.71s** | 🚀 **13.7x Faster** |
31
+ | **Compilation Time** | ~23.50s | **~0.89s** | 🚀 **26x Faster** |
40
32
  | **Output Size (.mind)** | ~770 KB | **~127 KB** | 📉 **83.5% Smaller** |
41
- | **Loading Latency** | >100ms | **2.6ms** | ⚡ **Zero-Copy** |
42
- | **Memory Footprint** | Heavy (JSON Objects) | **Minimal (Binary)** | 🧠 **CPU-Aligned** |
43
-
44
- > *Tested on 1024x1024 high-detail image target.*
45
-
46
- ### 🚀 Key Technical Breakthroughs
33
+ | **Tracking Latency** | Variable (TFJS) | **Constant (Pure JS)** | ⚡ **Stable 60fps** |
34
+ | **Dependency Size** | ~20MB (TFJS) | **< 100KB** | 📦 **99% Smaller Bundle** |
47
35
 
48
- - **Protocol V3 (Columnar Binary)**: Uses TypedArrays to store coordinates, angles, and descriptors in a cache-aligned layout. No more thousands of slow JavaScript objects.
49
- - **Zero-Copy Loading**: The runtime reads directly from the binary buffer. Initialization is now virtualy instant.
50
- - **Aggressive Matching Optimization**: Tree-based hierarchical clustering compacted into a flattened binary format.
51
- - **No Dependencies**: Works in Node.js and Browser with zero external requirements for the compilation core.
36
+ ---
52
37
 
53
- ### 🖥️ Usage (Node.js & Serverless)
38
+ ## 🖼️ Compiler Usage (Node.js & Web)
54
39
 
55
- Optimized for server-side compilation with multi-core parallelism:
40
+ The compiler is designed to run in workers (Node.js or Browser) for maximum performance.
56
41
 
57
42
  ```javascript
58
43
  import { OfflineCompiler } from '@srsergio/taptapp-ar';
59
44
 
60
45
  const compiler = new OfflineCompiler();
61
46
 
62
- // Compile target image
63
- const compiledData = await compiler.compileImageTargets(
47
+ // Compile target image (provide grayscale pixel data)
48
+ await compiler.compileImageTargets(
64
49
  [{ width, height, data: grayscaleUint8Array }],
65
50
  (progress) => console.log(`Compiling: ${progress}%`)
66
51
  );
67
52
 
68
- // Export to Protocol V3 binary format
69
- const binaryBuffer = compiler.exportData(); // Yields a much smaller .mind file
53
+ // Export to high-efficiency binary format
54
+ const binaryBuffer = compiler.exportData();
70
55
  ```
71
56
 
72
- ### 🌐 Frontend (Zero-Latency Loading)
57
+ ---
73
58
 
74
- ```javascript
75
- import { OfflineCompiler } from '@srsergio/taptapp-ar';
59
+ ## 🎥 Runtime Usage (AR Tracking)
76
60
 
77
- const compiler = new OfflineCompiler();
78
- // Loading 127KB instead of 800KB saves bandwidth and CPU parsing time
79
- compiler.importData(binaryBuffer);
61
+ ### 1. Simple A-Frame Integration
62
+ The easiest way to use TapTapp AR in a web app:
63
+
64
+ ```html
65
+ <script src="https://aframe.io/releases/1.5.0/aframe.min.js"></script>
66
+ <script src="path/to/@srsergio/taptapp-ar/dist/index.js"></script>
67
+
68
+ <a-scene mindar-image="imageTargetSrc: ./targets.mind;">
69
+ <a-camera position="0 0 0" look-controls="enabled: false"></a-camera>
70
+ <a-entity mindar-image-target="targetIndex: 0">
71
+ <a-plane position="0 0 0" height="0.552" width="1"></a-plane>
72
+ </a-entity>
73
+ </a-scene>
80
74
  ```
81
75
 
82
- ---
76
+ ### 2. High-Performance Three.js Wrapper
77
+ For custom Three.js applications:
83
78
 
84
- ## 🏗 Development
79
+ ```javascript
80
+ import { MindARThree } from '@srsergio/taptapp-ar';
85
81
 
86
- ```bash
87
- # Install dependencies
88
- npm install
82
+ const mindarThree = new MindARThree({
83
+ container: document.querySelector("#container"),
84
+ imageTargetSrc: './targets.mind',
85
+ });
86
+
87
+ const {renderer, scene, camera} = mindarThree;
89
88
 
90
- # Build the package
91
- npm run build
89
+ const anchor = mindarThree.addAnchor(0);
90
+ // Add your 3D models to anchor.group
91
+
92
+ await mindarThree.start();
93
+ renderer.setAnimationLoop(() => {
94
+ renderer.render(scene, camera);
95
+ });
92
96
  ```
93
97
 
94
- The package uses **TypeScript** and exports both ESM and CJS compatible builds located in the `dist` folder.
98
+ ### 3. Raw Controller (Custom Logic)
99
+ Use the `Controller` directly for maximum control:
100
+
101
+ ```javascript
102
+ import { Controller } from '@srsergio/taptapp-ar';
103
+
104
+ const controller = new Controller({
105
+ inputWidth: 640,
106
+ inputHeight: 480,
107
+ onUpdate: (data) => {
108
+ if (data.type === 'updateMatrix') {
109
+ // worldMatrix found! Apply to your 3D engine
110
+ const { targetIndex, worldMatrix } = data;
111
+ }
112
+ }
113
+ });
114
+
115
+ await controller.addImageTargets('./targets.mind');
116
+ controller.processVideo(videoElement);
117
+ ```
95
118
 
96
119
  ---
97
120
 
98
- ## 📄 License
121
+ ## 📄 License & Credits
99
122
 
100
123
  MIT © [srsergiolazaro](https://github.com/srsergiolazaro)
124
+
125
+ Based on the core research of MindAR, but completely re-written for high-performance binary processing and JS-only execution.
@@ -1,5 +1,5 @@
1
1
  export class Controller {
2
- constructor({ inputWidth, inputHeight, onUpdate, debugMode, maxTrack, warmupTolerance, missTolerance, filterMinCF, filterBeta, }: {
2
+ constructor({ inputWidth, inputHeight, onUpdate, debugMode, maxTrack, warmupTolerance, missTolerance, filterMinCF, filterBeta, worker, }: {
3
3
  inputWidth: any;
4
4
  inputHeight: any;
5
5
  onUpdate?: null | undefined;
@@ -9,6 +9,7 @@ export class Controller {
9
9
  missTolerance?: null | undefined;
10
10
  filterMinCF?: null | undefined;
11
11
  filterBeta?: null | undefined;
12
+ worker?: null | undefined;
12
13
  });
13
14
  inputWidth: any;
14
15
  inputHeight: any;
@@ -25,12 +26,11 @@ export class Controller {
25
26
  processingVideo: boolean;
26
27
  interestedTargetIndex: number;
27
28
  trackingStates: any[];
29
+ worker: any;
28
30
  projectionTransform: number[][];
29
31
  projectionMatrix: number[];
30
- worker: any;
31
- workerMatchDone: ((data: any) => void) | null;
32
- workerTrackDone: ((data: any) => void) | null;
33
- showTFStats(): void;
32
+ _setupWorkerListener(): void;
33
+ _ensureWorker(): void;
34
34
  addImageTargets(fileURL: any): Promise<any>;
35
35
  addImageTargetsFromBuffer(buffer: any): {
36
36
  dimensions: any[][];
@@ -43,32 +43,23 @@ export class Controller {
43
43
  getProjectionMatrix(): number[];
44
44
  getRotatedZ90Matrix(m: any): any[];
45
45
  getWorldMatrix(modelViewTransform: any, targetIndex: any): any[];
46
- _detectAndMatch(inputT: any, targetIndexes: any): Promise<{
46
+ _detectAndMatch(inputData: any, targetIndexes: any): Promise<{
47
47
  targetIndex: any;
48
48
  modelViewTransform: any;
49
49
  }>;
50
- _trackAndUpdate(inputT: any, lastModelViewTransform: any, targetIndex: any): Promise<any>;
50
+ _trackAndUpdate(inputData: any, lastModelViewTransform: any, targetIndex: any): Promise<any>;
51
51
  processVideo(input: any): void;
52
52
  stopProcessVideo(): void;
53
53
  detect(input: any): Promise<{
54
- featurePoints: {
55
- maxima: boolean;
56
- x: number;
57
- y: number;
58
- scale: number;
59
- angle: any;
60
- descriptors: any[];
61
- }[];
54
+ featurePoints: any[];
62
55
  debugExtra: {
63
- pyramidImages: any[][];
64
- dogPyramidImages: any[];
65
- extremasResults: any[];
66
- extremaAngles: any;
67
- prunedExtremas: number[][];
68
- localizedExtremas: any;
69
- } | null;
56
+ projectedImage: number[];
57
+ } | {
58
+ projectedImage?: undefined;
59
+ };
70
60
  }>;
71
61
  match(featurePoints: any, targetIndex: any): Promise<{
62
+ targetIndex: any;
72
63
  modelViewTransform: any;
73
64
  debugExtra: any;
74
65
  }>;
@@ -86,7 +77,9 @@ export class Controller {
86
77
  }>;
87
78
  trackUpdate(modelViewTransform: any, trackFeatures: any): Promise<any>;
88
79
  _workerMatch(featurePoints: any, targetIndexes: any): Promise<any>;
80
+ workerMatchDone: ((data: any) => void) | undefined;
89
81
  _workerTrackUpdate(modelViewTransform: any, trackingFeatures: any): Promise<any>;
82
+ workerTrackDone: ((data: any) => void) | undefined;
90
83
  _glModelViewMatrix(modelViewTransform: any, targetIndex: any): any[];
91
84
  _glProjectionMatrix({ projectionTransform, width, height, near, far }: {
92
85
  projectionTransform: any;
@@ -1,17 +1,25 @@
1
- import { memory, nextFrame } from "@tensorflow/tfjs";
2
- const tf = { memory, nextFrame };
3
- import ControllerWorker from "./controller.worker.js?worker&inline";
4
1
  import { Tracker } from "./tracker/tracker.js";
5
2
  import { CropDetector } from "./detector/crop-detector.js";
6
- import { Compiler } from "./compiler.js";
3
+ import { OfflineCompiler as Compiler } from "./offline-compiler.js";
7
4
  import { InputLoader } from "./input-loader.js";
8
5
  import { OneEuroFilter } from "../libs/one-euro-filter.js";
6
+ let ControllerWorker;
7
+ // Conditional import for worker to avoid crash in non-vite environments
8
+ try {
9
+ const workerModule = await import("./controller.worker.js?worker&inline");
10
+ ControllerWorker = workerModule.default;
11
+ }
12
+ catch (e) {
13
+ // Fallback for tests or other environments
14
+ ControllerWorker = null;
15
+ }
9
16
  const DEFAULT_FILTER_CUTOFF = 0.001; // 1Hz. time period in milliseconds
10
17
  const DEFAULT_FILTER_BETA = 1000;
11
18
  const DEFAULT_WARMUP_TOLERANCE = 5;
12
19
  const DEFAULT_MISS_TOLERANCE = 5;
13
20
  class Controller {
14
- constructor({ inputWidth, inputHeight, onUpdate = null, debugMode = false, maxTrack = 1, warmupTolerance = null, missTolerance = null, filterMinCF = null, filterBeta = null, }) {
21
+ constructor({ inputWidth, inputHeight, onUpdate = null, debugMode = false, maxTrack = 1, warmupTolerance = null, missTolerance = null, filterMinCF = null, filterBeta = null, worker = null, // Allow custom worker injection
22
+ }) {
15
23
  this.inputWidth = inputWidth;
16
24
  this.inputHeight = inputHeight;
17
25
  this.maxTrack = maxTrack;
@@ -27,13 +35,13 @@ class Controller {
27
35
  this.processingVideo = false;
28
36
  this.interestedTargetIndex = -1;
29
37
  this.trackingStates = [];
38
+ this.worker = worker;
39
+ if (this.worker)
40
+ this._setupWorkerListener();
30
41
  const near = 10;
31
42
  const far = 100000;
32
- const fovy = (45.0 * Math.PI) / 180; // 45 in radian. field of view vertical
43
+ const fovy = (45.0 * Math.PI) / 180;
33
44
  const f = this.inputHeight / 2 / Math.tan(fovy / 2);
34
- // [fx s cx]
35
- // K = [ 0 fx cy]
36
- // [ 0 0 1]
37
45
  this.projectionTransform = [
38
46
  [f, 0, this.inputWidth / 2],
39
47
  [0, f, this.inputHeight / 2],
@@ -46,9 +54,10 @@ class Controller {
46
54
  near: near,
47
55
  far: far,
48
56
  });
49
- this.worker = new ControllerWorker(); //new Worker(new URL('./controller.worker.js', import.meta.url));
50
- this.workerMatchDone = null;
51
- this.workerTrackDone = null;
57
+ }
58
+ _setupWorkerListener() {
59
+ if (!this.worker)
60
+ return;
52
61
  this.worker.onmessage = (e) => {
53
62
  if (e.data.type === "matchDone" && this.workerMatchDone !== null) {
54
63
  this.workerMatchDone(e.data);
@@ -58,9 +67,13 @@ class Controller {
58
67
  }
59
68
  };
60
69
  }
61
- showTFStats() {
62
- console.log(tf.memory().numTensors);
63
- console.table(tf.memory());
70
+ _ensureWorker() {
71
+ if (this.worker)
72
+ return;
73
+ if (ControllerWorker) {
74
+ this.worker = new ControllerWorker();
75
+ this._setupWorkerListener();
76
+ }
64
77
  }
65
78
  addImageTargets(fileURL) {
66
79
  return new Promise(async (resolve) => {
@@ -77,34 +90,37 @@ class Controller {
77
90
  const matchingDataList = [];
78
91
  const dimensions = [];
79
92
  for (let i = 0; i < dataList.length; i++) {
80
- matchingDataList.push(dataList[i].matchingData);
81
- trackingDataList.push(dataList[i].trackingData);
82
- dimensions.push([dataList[i].targetImage.width, dataList[i].targetImage.height]);
93
+ const item = dataList[i];
94
+ matchingDataList.push(item.matchingData);
95
+ trackingDataList.push(item.trackingData);
96
+ dimensions.push([item.targetImage.width, item.targetImage.height]);
83
97
  }
84
98
  this.tracker = new Tracker(dimensions, trackingDataList, this.projectionTransform, this.inputWidth, this.inputHeight, this.debugMode);
85
- this.worker.postMessage({
86
- type: "setup",
87
- inputWidth: this.inputWidth,
88
- inputHeight: this.inputHeight,
89
- projectionTransform: this.projectionTransform,
90
- debugMode: this.debugMode,
91
- matchingDataList,
92
- });
99
+ this._ensureWorker();
100
+ if (this.worker) {
101
+ this.worker.postMessage({
102
+ type: "setup",
103
+ inputWidth: this.inputWidth,
104
+ inputHeight: this.inputHeight,
105
+ projectionTransform: this.projectionTransform,
106
+ debugMode: this.debugMode,
107
+ matchingDataList,
108
+ });
109
+ }
93
110
  this.markerDimensions = dimensions;
94
- return { dimensions: dimensions, matchingDataList, trackingDataList };
111
+ return { dimensions, matchingDataList, trackingDataList };
95
112
  }
96
113
  dispose() {
97
114
  this.stopProcessVideo();
98
- this.worker.postMessage({
99
- type: "dispose",
100
- });
115
+ if (this.worker) {
116
+ this.worker.postMessage({ type: "dispose" });
117
+ this.worker = null;
118
+ }
101
119
  }
102
- // warm up gpu - build kernels is slow
103
120
  dummyRun(input) {
104
- const inputT = this.inputLoader.loadInput(input);
105
- this.cropDetector.detect(inputT);
106
- this.tracker.dummyRun(inputT);
107
- inputT.dispose();
121
+ const inputData = this.inputLoader.loadInput(input);
122
+ this.cropDetector.detect(inputData);
123
+ this.tracker.dummyRun(inputData);
108
124
  }
109
125
  getProjectionMatrix() {
110
126
  return this.projectionMatrix;
@@ -139,13 +155,13 @@ class Controller {
139
155
  getWorldMatrix(modelViewTransform, targetIndex) {
140
156
  return this._glModelViewMatrix(modelViewTransform, targetIndex);
141
157
  }
142
- async _detectAndMatch(inputT, targetIndexes) {
143
- const { featurePoints } = this.cropDetector.detectMoving(inputT);
158
+ async _detectAndMatch(inputData, targetIndexes) {
159
+ const { featurePoints } = this.cropDetector.detectMoving(inputData);
144
160
  const { targetIndex: matchedTargetIndex, modelViewTransform } = await this._workerMatch(featurePoints, targetIndexes);
145
161
  return { targetIndex: matchedTargetIndex, modelViewTransform };
146
162
  }
147
- async _trackAndUpdate(inputT, lastModelViewTransform, targetIndex) {
148
- const { worldCoords, screenCoords } = this.tracker.track(inputT, lastModelViewTransform, targetIndex);
163
+ async _trackAndUpdate(inputData, lastModelViewTransform, targetIndex) {
164
+ const { worldCoords, screenCoords } = this.tracker.track(inputData, lastModelViewTransform, targetIndex);
149
165
  if (worldCoords.length < 4)
150
166
  return null;
151
167
  const modelViewTransform = await this._workerTrackUpdate(lastModelViewTransform, {
@@ -168,13 +184,12 @@ class Controller {
168
184
  trackMiss: 0,
169
185
  filter: new OneEuroFilter({ minCutOff: this.filterMinCF, beta: this.filterBeta }),
170
186
  });
171
- //console.log("filterMinCF", this.filterMinCF, this.filterBeta);
172
187
  }
173
188
  const startProcessing = async () => {
174
189
  while (true) {
175
190
  if (!this.processingVideo)
176
191
  break;
177
- const inputT = this.inputLoader.loadInput(input);
192
+ const inputData = this.inputLoader.loadInput(input);
178
193
  const nTracking = this.trackingStates.reduce((acc, s) => {
179
194
  return acc + (!!s.isTracking ? 1 : 0);
180
195
  }, 0);
@@ -189,7 +204,7 @@ class Controller {
189
204
  continue;
190
205
  matchingIndexes.push(i);
191
206
  }
192
- const { targetIndex: matchedTargetIndex, modelViewTransform } = await this._detectAndMatch(inputT, matchingIndexes);
207
+ const { targetIndex: matchedTargetIndex, modelViewTransform } = await this._detectAndMatch(inputData, matchingIndexes);
193
208
  if (matchedTargetIndex !== -1) {
194
209
  this.trackingStates[matchedTargetIndex].isTracking = true;
195
210
  this.trackingStates[matchedTargetIndex].currentModelViewTransform = modelViewTransform;
@@ -199,7 +214,7 @@ class Controller {
199
214
  for (let i = 0; i < this.trackingStates.length; i++) {
200
215
  const trackingState = this.trackingStates[i];
201
216
  if (trackingState.isTracking) {
202
- let modelViewTransform = await this._trackAndUpdate(inputT, trackingState.currentModelViewTransform, i);
217
+ let modelViewTransform = await this._trackAndUpdate(inputData, trackingState.currentModelViewTransform, i);
203
218
  if (modelViewTransform === null) {
204
219
  trackingState.isTracking = false;
205
220
  }
@@ -251,9 +266,14 @@ class Controller {
251
266
  this.onUpdate({ type: "updateMatrix", targetIndex: i, worldMatrix: clone });
252
267
  }
253
268
  }
254
- inputT.dispose();
255
269
  this.onUpdate && this.onUpdate({ type: "processDone" });
256
- await tf.nextFrame();
270
+ // Use requestAnimationFrame if available, otherwise just wait briefly
271
+ if (typeof requestAnimationFrame !== "undefined") {
272
+ await new Promise(requestAnimationFrame);
273
+ }
274
+ else {
275
+ await new Promise(resolve => setTimeout(resolve, 16));
276
+ }
257
277
  }
258
278
  };
259
279
  startProcessing();
@@ -262,21 +282,19 @@ class Controller {
262
282
  this.processingVideo = false;
263
283
  }
264
284
  async detect(input) {
265
- const inputT = this.inputLoader.loadInput(input);
266
- const { featurePoints, debugExtra } = this.cropDetector.detect(inputT);
267
- inputT.dispose();
285
+ const inputData = this.inputLoader.loadInput(input);
286
+ const { featurePoints, debugExtra } = this.cropDetector.detect(inputData);
268
287
  return { featurePoints, debugExtra };
269
288
  }
270
289
  async match(featurePoints, targetIndex) {
271
- const { modelViewTransform, debugExtra } = await this._workerMatch(featurePoints, [
290
+ const { targetIndex: matchedTargetIndex, modelViewTransform, debugExtra } = await this._workerMatch(featurePoints, [
272
291
  targetIndex,
273
292
  ]);
274
- return { modelViewTransform, debugExtra };
293
+ return { targetIndex: matchedTargetIndex, modelViewTransform, debugExtra };
275
294
  }
276
295
  async track(input, modelViewTransform, targetIndex) {
277
- const inputT = this.inputLoader.loadInput(input);
278
- const result = this.tracker.track(inputT, modelViewTransform, targetIndex);
279
- inputT.dispose();
296
+ const inputData = this.inputLoader.loadInput(input);
297
+ const result = this.tracker.track(inputData, modelViewTransform, targetIndex);
280
298
  return result;
281
299
  }
282
300
  async trackUpdate(modelViewTransform, trackFeatures) {
@@ -294,7 +312,7 @@ class Controller {
294
312
  debugExtra: data.debugExtra,
295
313
  });
296
314
  };
297
- this.worker.postMessage({ type: "match", featurePoints: featurePoints, targetIndexes });
315
+ this.worker && this.worker.postMessage({ type: "match", featurePoints: featurePoints, targetIndexes });
298
316
  });
299
317
  }
300
318
  _workerTrackUpdate(modelViewTransform, trackingFeatures) {
@@ -303,7 +321,7 @@ class Controller {
303
321
  resolve(data.modelViewTransform);
304
322
  };
305
323
  const { worldCoords, screenCoords } = trackingFeatures;
306
- this.worker.postMessage({
324
+ this.worker && this.worker.postMessage({
307
325
  type: "trackUpdate",
308
326
  modelViewTransform,
309
327
  worldCoords,
@@ -313,41 +331,6 @@ class Controller {
313
331
  }
314
332
  _glModelViewMatrix(modelViewTransform, targetIndex) {
315
333
  const height = this.markerDimensions[targetIndex][1];
316
- // Question: can someone verify this interpreation is correct?
317
- // I'm not very convinced, but more like trial and error and works......
318
- //
319
- // First, opengl has y coordinate system go from bottom to top, while the marker corrdinate goes from top to bottom,
320
- // since the modelViewTransform is estimated in marker coordinate, we need to apply this transform before modelViewTransform
321
- // I can see why y = h - y*, but why z = z* ? should we intepret it as rotate 90 deg along x-axis and then translate y by h?
322
- //
323
- // [1 0 0 0]
324
- // [0 -1 0 h]
325
- // [0 0 -1 0]
326
- // [0 0 0 1]
327
- //
328
- // This is tested that if we reverse marker coordinate from bottom to top and estimate the modelViewTransform,
329
- // then the above matrix is not necessary.
330
- //
331
- // Second, in opengl, positive z is away from camera, so we rotate 90 deg along x-axis after transform to fix the axis mismatch
332
- // [1 1 0 0]
333
- // [0 -1 0 0]
334
- // [0 0 -1 0]
335
- // [0 0 0 1]
336
- //
337
- // all together, the combined matrix is
338
- //
339
- // [1 1 0 0] [m00, m01, m02, m03] [1 0 0 0]
340
- // [0 -1 0 0] [m10, m11, m12, m13] [0 -1 0 h]
341
- // [0 0 -1 0] [m20, m21, m22, m23] [0 0 -1 0]
342
- // [0 0 0 1] [ 0 0 0 1] [0 0 0 1]
343
- //
344
- // [ m00, -m01, -m02, (m01 * h + m03) ]
345
- // [-m10, m11, m12, -(m11 * h + m13) ]
346
- // = [-m20, m21, m22, -(m21 * h + m23) ]
347
- // [ 0, 0, 0, 1 ]
348
- //
349
- //
350
- // Finally, in threejs, matrix is represented in col by row, so we transpose it, and get below:
351
334
  const openGLWorldMatrix = [
352
335
  modelViewTransform[0][0],
353
336
  -modelViewTransform[1][0],
@@ -368,8 +351,6 @@ class Controller {
368
351
  ];
369
352
  return openGLWorldMatrix;
370
353
  }
371
- // build openGL projection matrix
372
- // ref: https://strawlab.org/2011/11/05/augmented-reality-with-OpenGL/
373
354
  _glProjectionMatrix({ projectionTransform, width, height, near, far }) {
374
355
  const proj = [
375
356
  [
@@ -4,62 +4,31 @@ export class CropDetector {
4
4
  width: any;
5
5
  height: any;
6
6
  cropSize: number;
7
- detector: Detector;
8
- kernelCaches: {};
7
+ detector: DetectorLite;
9
8
  lastRandomIndex: number;
10
- detect(inputImageT: any): {
11
- featurePoints: {
12
- maxima: boolean;
13
- x: number;
14
- y: number;
15
- scale: number;
16
- angle: any;
17
- descriptors: any[];
18
- }[];
9
+ detect(input: any): {
10
+ featurePoints: any[];
19
11
  debugExtra: {
20
- pyramidImages: any[][];
21
- dogPyramidImages: any[];
22
- extremasResults: any[];
23
- extremaAngles: any;
24
- prunedExtremas: number[][];
25
- localizedExtremas: any;
26
- } | null;
12
+ projectedImage: number[];
13
+ } | {
14
+ projectedImage?: undefined;
15
+ };
27
16
  };
28
- detectMoving(inputImageT: any): {
29
- featurePoints: {
30
- maxima: boolean;
31
- x: number;
32
- y: number;
33
- scale: number;
34
- angle: any;
35
- descriptors: any[];
36
- }[];
17
+ detectMoving(input: any): {
18
+ featurePoints: any[];
37
19
  debugExtra: {
38
- pyramidImages: any[][];
39
- dogPyramidImages: any[];
40
- extremasResults: any[];
41
- extremaAngles: any;
42
- prunedExtremas: number[][];
43
- localizedExtremas: any;
44
- } | null;
20
+ projectedImage: number[];
21
+ } | {
22
+ projectedImage?: undefined;
23
+ };
45
24
  };
46
- _detect(inputImageT: any, startX: any, startY: any): {
47
- featurePoints: {
48
- maxima: boolean;
49
- x: number;
50
- y: number;
51
- scale: number;
52
- angle: any;
53
- descriptors: any[];
54
- }[];
25
+ _detect(imageData: any, startX: any, startY: any): {
26
+ featurePoints: any[];
55
27
  debugExtra: {
56
- pyramidImages: any[][];
57
- dogPyramidImages: any[];
58
- extremasResults: any[];
59
- extremaAngles: any;
60
- prunedExtremas: number[][];
61
- localizedExtremas: any;
62
- } | null;
28
+ projectedImage: number[];
29
+ } | {
30
+ projectedImage?: undefined;
31
+ };
63
32
  };
64
33
  }
65
- import { Detector } from "./detector.js";
34
+ import { DetectorLite } from "./detector-lite.js";