@srsergio/taptapp-ar 1.1.1 → 1.1.3

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 (63) hide show
  1. package/dist/compiler/node-worker.js +1 -197
  2. package/dist/compiler/offline-compiler.js +1 -207
  3. package/dist/core/constants.js +1 -38
  4. package/dist/core/detector/crop-detector.js +1 -88
  5. package/dist/core/detector/detector-lite.js +1 -455
  6. package/dist/core/detector/freak.js +1 -89
  7. package/dist/core/estimation/estimate.js +1 -16
  8. package/dist/core/estimation/estimator.js +1 -30
  9. package/dist/core/estimation/morph-refinement.js +1 -116
  10. package/dist/core/estimation/non-rigid-refine.js +1 -70
  11. package/dist/core/estimation/pnp-solver.js +1 -109
  12. package/dist/core/estimation/refine-estimate.js +1 -311
  13. package/dist/core/estimation/utils.js +1 -67
  14. package/dist/core/features/auto-rotation-feature.js +1 -30
  15. package/dist/core/features/crop-detection-feature.js +1 -26
  16. package/dist/core/features/feature-base.js +1 -1
  17. package/dist/core/features/feature-manager.js +1 -55
  18. package/dist/core/features/one-euro-filter-feature.js +1 -44
  19. package/dist/core/features/temporal-filter-feature.js +1 -57
  20. package/dist/core/image-list.js +1 -54
  21. package/dist/core/input-loader.js +1 -87
  22. package/dist/core/matching/hamming-distance.js +1 -66
  23. package/dist/core/matching/hdc.js +1 -102
  24. package/dist/core/matching/hierarchical-clustering.js +1 -130
  25. package/dist/core/matching/hough.js +1 -170
  26. package/dist/core/matching/matcher.js +1 -66
  27. package/dist/core/matching/matching.js +1 -401
  28. package/dist/core/matching/ransacHomography.js +1 -132
  29. package/dist/core/perception/bio-inspired-engine.js +1 -232
  30. package/dist/core/perception/foveal-attention.js +1 -280
  31. package/dist/core/perception/index.js +1 -17
  32. package/dist/core/perception/predictive-coding.js +1 -278
  33. package/dist/core/perception/saccadic-controller.js +1 -269
  34. package/dist/core/perception/saliency-map.js +1 -254
  35. package/dist/core/perception/scale-orchestrator.js +1 -68
  36. package/dist/core/protocol.js +1 -254
  37. package/dist/core/tracker/extract-utils.js +1 -29
  38. package/dist/core/tracker/extract.js +1 -306
  39. package/dist/core/tracker/tracker.js +1 -352
  40. package/dist/core/utils/cumsum.js +1 -37
  41. package/dist/core/utils/delaunay.js +1 -125
  42. package/dist/core/utils/geometry.js +1 -101
  43. package/dist/core/utils/gpu-compute.js +1 -231
  44. package/dist/core/utils/homography.js +1 -138
  45. package/dist/core/utils/images.js +1 -108
  46. package/dist/core/utils/lsh-binarizer.js +1 -37
  47. package/dist/core/utils/lsh-direct.js +1 -76
  48. package/dist/core/utils/projection.js +1 -51
  49. package/dist/core/utils/randomizer.js +1 -25
  50. package/dist/core/utils/worker-pool.js +1 -89
  51. package/dist/index.js +1 -7
  52. package/dist/libs/one-euro-filter.js +1 -70
  53. package/dist/react/TaptappAR.js +1 -151
  54. package/dist/react/types.js +1 -16
  55. package/dist/react/use-ar.js +1 -118
  56. package/dist/runtime/aframe.js +1 -272
  57. package/dist/runtime/bio-inspired-controller.js +1 -358
  58. package/dist/runtime/controller.js +1 -592
  59. package/dist/runtime/controller.worker.js +1 -93
  60. package/dist/runtime/index.js +1 -5
  61. package/dist/runtime/three.js +1 -304
  62. package/dist/runtime/track.js +1 -381
  63. package/package.json +10 -4
@@ -1,254 +1 @@
1
- /**
2
- * Saliency Map Computation
3
- *
4
- * Computes visual saliency - regions that "pop out" and attract attention.
5
- * Used to guide saccadic attention to visually important areas.
6
- *
7
- * Implements a simplified Itti-Koch saliency model:
8
- * - Intensity contrast
9
- * - Edge density
10
- * - Local complexity
11
- *
12
- * For AR tracking, high-saliency regions often contain:
13
- * - Corners and edges (good for feature detection)
14
- * - High-contrast areas (robust to lighting changes)
15
- * - Texture-rich regions (distinctive for matching)
16
- */
17
- class SaliencyMap {
18
- /**
19
- * @param {number} width - Image width
20
- * @param {number} height - Image height
21
- */
22
- constructor(width, height) {
23
- this.width = width;
24
- this.height = height;
25
- // Downsampled dimensions for efficiency
26
- this.scale = 8; // Process at 1/8 resolution
27
- this.scaledW = Math.ceil(width / this.scale);
28
- this.scaledH = Math.ceil(height / this.scale);
29
- // Pre-allocate buffers
30
- this.intensityMap = new Float32Array(this.scaledW * this.scaledH);
31
- this.contrastMap = new Float32Array(this.scaledW * this.scaledH);
32
- this.edgeMap = new Float32Array(this.scaledW * this.scaledH);
33
- this.saliencyBuffer = new Float32Array(this.scaledW * this.scaledH);
34
- // Peak detection parameters
35
- this.maxPeaks = 5;
36
- this.suppressionRadius = Math.max(this.scaledW, this.scaledH) * 0.15;
37
- }
38
- /**
39
- * Compute saliency map for input image
40
- *
41
- * @param {Uint8Array} inputData - Grayscale input image
42
- * @returns {Object} Saliency result with peaks
43
- */
44
- compute(inputData) {
45
- // Step 1: Downsample and compute intensity
46
- this._downsample(inputData);
47
- // Step 2: Compute features
48
- this._computeContrast();
49
- this._computeEdges();
50
- // Step 3: Combine into saliency map
51
- this._combineSaliency();
52
- // Step 4: Find peaks
53
- const peaks = this._findPeaks();
54
- return {
55
- map: this.saliencyBuffer,
56
- width: this.scaledW,
57
- height: this.scaledH,
58
- peaks,
59
- maxSaliency: peaks.length > 0 ? peaks[0].value : 0,
60
- };
61
- }
62
- /**
63
- * Downsample input to working resolution
64
- * @private
65
- */
66
- _downsample(inputData) {
67
- const s = this.scale;
68
- const w = this.width;
69
- for (let sy = 0; sy < this.scaledH; sy++) {
70
- const yStart = sy * s;
71
- const yEnd = Math.min(yStart + s, this.height);
72
- for (let sx = 0; sx < this.scaledW; sx++) {
73
- const xStart = sx * s;
74
- const xEnd = Math.min(xStart + s, this.width);
75
- let sum = 0;
76
- let count = 0;
77
- for (let y = yStart; y < yEnd; y++) {
78
- const rowOffset = y * w;
79
- for (let x = xStart; x < xEnd; x++) {
80
- sum += inputData[rowOffset + x];
81
- count++;
82
- }
83
- }
84
- this.intensityMap[sy * this.scaledW + sx] = sum / count / 255;
85
- }
86
- }
87
- }
88
- /**
89
- * Compute local contrast map
90
- * @private
91
- */
92
- _computeContrast() {
93
- const w = this.scaledW;
94
- const h = this.scaledH;
95
- const intensity = this.intensityMap;
96
- const contrast = this.contrastMap;
97
- // 3x3 local contrast using center-surround
98
- for (let y = 1; y < h - 1; y++) {
99
- for (let x = 1; x < w - 1; x++) {
100
- const idx = y * w + x;
101
- const center = intensity[idx];
102
- // Compute average of 8 neighbors
103
- let surround = 0;
104
- surround += intensity[(y - 1) * w + (x - 1)];
105
- surround += intensity[(y - 1) * w + x];
106
- surround += intensity[(y - 1) * w + (x + 1)];
107
- surround += intensity[y * w + (x - 1)];
108
- surround += intensity[y * w + (x + 1)];
109
- surround += intensity[(y + 1) * w + (x - 1)];
110
- surround += intensity[(y + 1) * w + x];
111
- surround += intensity[(y + 1) * w + (x + 1)];
112
- surround /= 8;
113
- // Contrast is absolute difference
114
- contrast[idx] = Math.abs(center - surround);
115
- }
116
- }
117
- // Handle borders
118
- for (let y = 0; y < h; y++) {
119
- contrast[y * w] = 0;
120
- contrast[y * w + w - 1] = 0;
121
- }
122
- for (let x = 0; x < w; x++) {
123
- contrast[x] = 0;
124
- contrast[(h - 1) * w + x] = 0;
125
- }
126
- }
127
- /**
128
- * Compute edge density map using Sobel-like operator
129
- * @private
130
- */
131
- _computeEdges() {
132
- const w = this.scaledW;
133
- const h = this.scaledH;
134
- const intensity = this.intensityMap;
135
- const edges = this.edgeMap;
136
- for (let y = 1; y < h - 1; y++) {
137
- for (let x = 1; x < w - 1; x++) {
138
- // Simplified Sobel
139
- const gx = -intensity[(y - 1) * w + (x - 1)] + intensity[(y - 1) * w + (x + 1)] +
140
- -2 * intensity[y * w + (x - 1)] + 2 * intensity[y * w + (x + 1)] +
141
- -intensity[(y + 1) * w + (x - 1)] + intensity[(y + 1) * w + (x + 1)];
142
- const gy = -intensity[(y - 1) * w + (x - 1)] - 2 * intensity[(y - 1) * w + x] - intensity[(y - 1) * w + (x + 1)] +
143
- intensity[(y + 1) * w + (x - 1)] + 2 * intensity[(y + 1) * w + x] + intensity[(y + 1) * w + (x + 1)];
144
- edges[y * w + x] = Math.sqrt(gx * gx + gy * gy) / 4; // Normalize
145
- }
146
- }
147
- // Handle borders
148
- for (let y = 0; y < h; y++) {
149
- edges[y * w] = 0;
150
- edges[y * w + w - 1] = 0;
151
- }
152
- for (let x = 0; x < w; x++) {
153
- edges[x] = 0;
154
- edges[(h - 1) * w + x] = 0;
155
- }
156
- }
157
- /**
158
- * Combine features into final saliency map
159
- * @private
160
- */
161
- _combineSaliency() {
162
- const n = this.saliencyBuffer.length;
163
- const contrast = this.contrastMap;
164
- const edges = this.edgeMap;
165
- const saliency = this.saliencyBuffer;
166
- // Weight: 60% contrast, 40% edges
167
- for (let i = 0; i < n; i++) {
168
- saliency[i] = contrast[i] * 0.6 + edges[i] * 0.4;
169
- }
170
- // Normalize to [0, 1]
171
- let max = 0;
172
- for (let i = 0; i < n; i++) {
173
- max = Math.max(max, saliency[i]);
174
- }
175
- if (max > 0) {
176
- for (let i = 0; i < n; i++) {
177
- saliency[i] /= max;
178
- }
179
- }
180
- }
181
- /**
182
- * Find peaks in saliency map using non-maximum suppression
183
- * @private
184
- */
185
- _findPeaks() {
186
- const w = this.scaledW;
187
- const h = this.scaledH;
188
- const saliency = this.saliencyBuffer;
189
- const peaks = [];
190
- const r = this.suppressionRadius;
191
- const r2 = r * r;
192
- // Find all local maxima
193
- const candidates = [];
194
- for (let y = 1; y < h - 1; y++) {
195
- for (let x = 1; x < w - 1; x++) {
196
- const idx = y * w + x;
197
- const val = saliency[idx];
198
- // Check if local maximum (8-connected)
199
- if (val > saliency[(y - 1) * w + (x - 1)] &&
200
- val > saliency[(y - 1) * w + x] &&
201
- val > saliency[(y - 1) * w + (x + 1)] &&
202
- val > saliency[y * w + (x - 1)] &&
203
- val > saliency[y * w + (x + 1)] &&
204
- val > saliency[(y + 1) * w + (x - 1)] &&
205
- val > saliency[(y + 1) * w + x] &&
206
- val > saliency[(y + 1) * w + (x + 1)]) {
207
- candidates.push({ x, y, value: val });
208
- }
209
- }
210
- }
211
- // Sort by value descending
212
- candidates.sort((a, b) => b.value - a.value);
213
- // Non-maximum suppression
214
- for (const cand of candidates) {
215
- if (peaks.length >= this.maxPeaks)
216
- break;
217
- // Check if too close to existing peaks
218
- let suppress = false;
219
- for (const peak of peaks) {
220
- const dx = cand.x - peak.x;
221
- const dy = cand.y - peak.y;
222
- if (dx * dx + dy * dy < r2) {
223
- suppress = true;
224
- break;
225
- }
226
- }
227
- if (!suppress) {
228
- // Convert to original image coordinates
229
- peaks.push({
230
- x: (cand.x + 0.5) * this.scale,
231
- y: (cand.y + 0.5) * this.scale,
232
- value: cand.value,
233
- });
234
- }
235
- }
236
- return peaks;
237
- }
238
- /**
239
- * Get saliency value at a specific location
240
- *
241
- * @param {number} x - X coordinate in original image
242
- * @param {number} y - Y coordinate in original image
243
- * @returns {number} Saliency value (0-1)
244
- */
245
- getSaliencyAt(x, y) {
246
- const sx = Math.floor(x / this.scale);
247
- const sy = Math.floor(y / this.scale);
248
- if (sx < 0 || sx >= this.scaledW || sy < 0 || sy >= this.scaledH) {
249
- return 0;
250
- }
251
- return this.saliencyBuffer[sy * this.scaledW + sx];
252
- }
253
- }
254
- export { SaliencyMap };
1
+ class t{constructor(t,s){this.width=t,this.height=s,this.scale=8,this.scaledW=Math.ceil(t/this.scale),this.scaledH=Math.ceil(s/this.scale),this.intensityMap=new Float32Array(this.scaledW*this.scaledH),this.contrastMap=new Float32Array(this.scaledW*this.scaledH),this.edgeMap=new Float32Array(this.scaledW*this.scaledH),this.saliencyBuffer=new Float32Array(this.scaledW*this.scaledH),this.maxPeaks=5,this.suppressionRadius=.15*Math.max(this.scaledW,this.scaledH)}compute(t){this._downsample(t),this._computeContrast(),this._computeEdges(),this._combineSaliency();const s=this._findPeaks();return{map:this.saliencyBuffer,width:this.scaledW,height:this.scaledH,peaks:s,maxSaliency:s.length>0?s[0].value:0}}_downsample(t){const s=this.scale,e=this.width;for(let i=0;i<this.scaledH;i++){const a=i*s,h=Math.min(a+s,this.height);for(let l=0;l<this.scaledW;l++){const c=l*s,o=Math.min(c+s,this.width);let n=0,r=0;for(let s=a;s<h;s++){const i=s*e;for(let s=c;s<o;s++)n+=t[i+s],r++}this.intensityMap[i*this.scaledW+l]=n/r/255}}}_computeContrast(){const t=this.scaledW,s=this.scaledH,e=this.intensityMap,i=this.contrastMap;for(let a=1;a<s-1;a++)for(let s=1;s<t-1;s++){const h=a*t+s,l=e[h];let c=0;c+=e[(a-1)*t+(s-1)],c+=e[(a-1)*t+s],c+=e[(a-1)*t+(s+1)],c+=e[a*t+(s-1)],c+=e[a*t+(s+1)],c+=e[(a+1)*t+(s-1)],c+=e[(a+1)*t+s],c+=e[(a+1)*t+(s+1)],c/=8,i[h]=Math.abs(l-c)}for(let e=0;e<s;e++)i[e*t]=0,i[e*t+t-1]=0;for(let e=0;e<t;e++)i[e]=0,i[(s-1)*t+e]=0}_computeEdges(){const t=this.scaledW,s=this.scaledH,e=this.intensityMap,i=this.edgeMap;for(let a=1;a<s-1;a++)for(let s=1;s<t-1;s++){const h=-e[(a-1)*t+(s-1)]+e[(a-1)*t+(s+1)]+-2*e[a*t+(s-1)]+2*e[a*t+(s+1)]+-e[(a+1)*t+(s-1)]+e[(a+1)*t+(s+1)],l=-e[(a-1)*t+(s-1)]-2*e[(a-1)*t+s]-e[(a-1)*t+(s+1)]+e[(a+1)*t+(s-1)]+2*e[(a+1)*t+s]+e[(a+1)*t+(s+1)];i[a*t+s]=Math.sqrt(h*h+l*l)/4}for(let e=0;e<s;e++)i[e*t]=0,i[e*t+t-1]=0;for(let e=0;e<t;e++)i[e]=0,i[(s-1)*t+e]=0}_combineSaliency(){const t=this.saliencyBuffer.length,s=this.contrastMap,e=this.edgeMap,i=this.saliencyBuffer;for(let a=0;a<t;a++)i[a]=.6*s[a]+.4*e[a];let a=0;for(let s=0;s<t;s++)a=Math.max(a,i[s]);if(a>0)for(let s=0;s<t;s++)i[s]/=a}_findPeaks(){const t=this.scaledW,s=this.scaledH,e=this.saliencyBuffer,i=[],a=this.suppressionRadius,h=a*a,l=[];for(let i=1;i<s-1;i++)for(let s=1;s<t-1;s++){const a=e[i*t+s];a>e[(i-1)*t+(s-1)]&&a>e[(i-1)*t+s]&&a>e[(i-1)*t+(s+1)]&&a>e[i*t+(s-1)]&&a>e[i*t+(s+1)]&&a>e[(i+1)*t+(s-1)]&&a>e[(i+1)*t+s]&&a>e[(i+1)*t+(s+1)]&&l.push({x:s,y:i,value:a})}l.sort((t,s)=>s.value-t.value);for(const t of l){if(i.length>=this.maxPeaks)break;let s=!1;for(const e of i){const i=t.x-e.x,a=t.y-e.y;if(i*i+a*a<h){s=!0;break}}s||i.push({x:(t.x+.5)*this.scale,y:(t.y+.5)*this.scale,value:t.value})}return i}getSaliencyAt(t,s){const e=Math.floor(t/this.scale),i=Math.floor(s/this.scale);return e<0||e>=this.scaledW||i<0||i>=this.scaledH?0:this.saliencyBuffer[i*this.scaledW+e]}}export{t as SaliencyMap};
@@ -1,68 +1 @@
1
- /**
2
- * Scale Orchestrator
3
- *
4
- * Manages which octaves should be processed based on the current tracking state.
5
- * Implements temporal consistency and interleave strategies to optimize performance.
6
- */
7
- export class ScaleOrchestrator {
8
- constructor(numOctaves, options = {}) {
9
- this.numOctaves = numOctaves;
10
- this.options = {
11
- interleaveInterval: 10,
12
- hysteresis: 1, // Number of adjacent octaves to keep
13
- ...options
14
- };
15
- this.frameCount = 0;
16
- this.lastActiveOctave = -1;
17
- this.interleaveOctave = 0;
18
- }
19
- /**
20
- * Determine which octaves should be processed in the current frame
21
- *
22
- * @param {Object} trackingState - Current state of tracking
23
- * @returns {number[]} Array of octave indices to process
24
- */
25
- getOctavesToProcess(trackingState = null) {
26
- this.frameCount++;
27
- // Case 1: No tracking or lost tracking -> Process all octaves
28
- if (!trackingState || !trackingState.isTracking || trackingState.activeOctave === undefined) {
29
- this.lastActiveOctave = -1;
30
- return Array.from({ length: this.numOctaves }, (_, i) => i);
31
- }
32
- const activeScale = trackingState.activeOctave;
33
- this.lastActiveOctave = activeScale;
34
- // Case 2: Active tracking -> Focus on current scale and neighbors
35
- const octaves = new Set();
36
- // Add current and adjacent scales (Hysteresis)
37
- for (let i = -this.options.hysteresis; i <= this.options.hysteresis; i++) {
38
- const octave = activeScale + i;
39
- if (octave >= 0 && octave < this.numOctaves) {
40
- octaves.add(octave);
41
- }
42
- }
43
- // Case 3: Interleave - Periodically check a distant octave to ensure we don't "drift"
44
- if (this.frameCount % this.options.interleaveInterval === 0) {
45
- this.interleaveOctave = (this.interleaveOctave + 1) % this.numOctaves;
46
- // If the interleave octave is already being processed, pick the next one
47
- if (octaves.has(this.interleaveOctave)) {
48
- this.interleaveOctave = (this.interleaveOctave + 1) % this.numOctaves;
49
- }
50
- octaves.add(this.interleaveOctave);
51
- if (this.options.debug) {
52
- console.log(`[ScaleOrchestrator] Interleave check on octave ${this.interleaveOctave}`);
53
- }
54
- }
55
- const result = Array.from(octaves).sort((a, b) => a - b);
56
- if (this.options.debug) {
57
- console.log(`[ScaleOrchestrator] Active: ${activeScale}, Processing: [${result.join(', ')}]`);
58
- }
59
- return result;
60
- }
61
- /**
62
- * Reset orchestrator state
63
- */
64
- reset() {
65
- this.frameCount = 0;
66
- this.lastActiveOctave = -1;
67
- }
68
- }
1
+ export class ScaleOrchestrator{constructor(t,e={}){this.numOctaves=t,this.options={interleaveInterval:10,hysteresis:1,...e},this.frameCount=0,this.lastActiveOctave=-1,this.interleaveOctave=0}getOctavesToProcess(t=null){if(this.frameCount++,!t||!t.isTracking||void 0===t.activeOctave)return this.lastActiveOctave=-1,Array.from({length:this.numOctaves},(t,e)=>e);const e=t.activeOctave;this.lastActiveOctave=e;const s=new Set;for(let t=-this.options.hysteresis;t<=this.options.hysteresis;t++){const i=e+t;i>=0&&i<this.numOctaves&&s.add(i)}this.frameCount%this.options.interleaveInterval===0&&(this.interleaveOctave=(this.interleaveOctave+1)%this.numOctaves,s.has(this.interleaveOctave)&&(this.interleaveOctave=(this.interleaveOctave+1)%this.numOctaves),s.add(this.interleaveOctave),this.options.debug&&console.log(`[ScaleOrchestrator] Interleave check on octave ${this.interleaveOctave}`));const i=Array.from(s).sort((t,e)=>t-e);return this.options.debug&&console.log(`[ScaleOrchestrator] Active: ${e}, Processing: [${i.join(", ")}]`),i}reset(){this.frameCount=0,this.lastActiveOctave=-1}}
@@ -1,254 +1 @@
1
- import * as msgpack from "@msgpack/msgpack";
2
- export const CURRENT_VERSION = 11; // Bumped for Nanite virtualized features support
3
- export const HDC_SEED = 0x1337BEEF; // Default system seed
4
- /**
5
- * Morton Order calculation for spatial sorting
6
- */
7
- export function getMorton(x, y) {
8
- let x_int = x | 0;
9
- let y_int = y | 0;
10
- x_int = (x_int | (x_int << 8)) & 0x00FF00FF;
11
- x_int = (x_int | (x_int << 4)) & 0x0F0F0F0F;
12
- x_int = (x_int | (x_int << 2)) & 0x33333333;
13
- x_int = (x_int | (x_int << 1)) & 0x55555555;
14
- y_int = (y_int | (y_int << 8)) & 0x00FF00FF;
15
- y_int = (y_int | (y_int << 4)) & 0x0F0F0F0F;
16
- y_int = (y_int | (y_int << 2)) & 0x33333333;
17
- y_int = (y_int | (y_int << 1)) & 0x55555555;
18
- return x_int | (y_int << 1);
19
- }
20
- /**
21
- * Packs 8-bit image data into 4-bit packed data
22
- */
23
- export function pack4Bit(data) {
24
- const length = data.length;
25
- if (length % 2 !== 0)
26
- return data;
27
- const packed = new Uint8Array(length / 2);
28
- for (let i = 0; i < length; i += 2) {
29
- const p1 = (data[i] & 0xF0) >> 4;
30
- const p2 = (data[i + 1] & 0xF0) >> 4;
31
- packed[i / 2] = (p1 << 4) | p2;
32
- }
33
- return packed;
34
- }
35
- /**
36
- * Unpacks 4-bit data back to 8-bit image data
37
- */
38
- export function unpack4Bit(packed, width, height) {
39
- const length = width * height;
40
- const data = new Uint8Array(length);
41
- for (let i = 0; i < packed.length; i++) {
42
- const byte = packed[i];
43
- const p1 = (byte & 0xF0);
44
- const p2 = (byte & 0x0F) << 4;
45
- data[i * 2] = p1;
46
- data[i * 2 + 1] = p2;
47
- }
48
- return data;
49
- }
50
- /**
51
- * Columnarizes point data for efficient storage and transfer
52
- */
53
- export function columnarize(points, tree, width, height, useHDC = false) {
54
- const count = points.length;
55
- const x = new Uint16Array(count);
56
- const y = new Uint16Array(count);
57
- const angle = new Int16Array(count);
58
- const scale = new Uint8Array(count);
59
- let descriptors;
60
- if (useHDC) {
61
- descriptors = new Uint32Array(count); // HDC Signatures (32-bit)
62
- }
63
- else {
64
- descriptors = new Uint32Array(count * 2); // Raw Descriptors (64-bit)
65
- }
66
- for (let i = 0; i < count; i++) {
67
- x[i] = Math.round((points[i].x / width) * 65535);
68
- y[i] = Math.round((points[i].y / height) * 65535);
69
- angle[i] = Math.round((points[i].angle / Math.PI) * 32767);
70
- scale[i] = Math.round(Math.log2(points[i].scale || 1));
71
- if (points[i].descriptors && points[i].descriptors.length >= 2) {
72
- if (useHDC) {
73
- // For HDC, we'd normally call project + compress here
74
- // But protocol.ts should be agnostic of the generator.
75
- // We'll assume points[i].hdcSignature exists if pre-calculated
76
- descriptors[i] = points[i].hdcSignature || 0;
77
- }
78
- else {
79
- descriptors[i * 2] = points[i].descriptors[0];
80
- descriptors[(i * 2) + 1] = points[i].descriptors[1];
81
- }
82
- }
83
- }
84
- return {
85
- x,
86
- y,
87
- a: angle,
88
- s: scale,
89
- d: descriptors,
90
- hdc: useHDC ? 1 : 0, // HDC Flag (renamed from h to avoid collision with height)
91
- t: compactTree(tree.rootNode),
92
- };
93
- }
94
- /**
95
- * Columnarizes point data with COMPACT 32-bit descriptors (XOR folding)
96
- * Reduces descriptor storage by 50% with minimal accuracy loss
97
- */
98
- export function columnarizeCompact(points, tree, width, height) {
99
- const count = points.length;
100
- const x = new Uint16Array(count);
101
- const y = new Uint16Array(count);
102
- const angle = new Int16Array(count);
103
- const scale = new Uint8Array(count);
104
- const descriptors = new Uint32Array(count); // 32-bit compact descriptors
105
- for (let i = 0; i < count; i++) {
106
- x[i] = Math.round((points[i].x / width) * 65535);
107
- y[i] = Math.round((points[i].y / height) * 65535);
108
- angle[i] = Math.round((points[i].angle / Math.PI) * 32767);
109
- scale[i] = Math.round(Math.log2(points[i].scale || 1));
110
- if (points[i].descriptors && points[i].descriptors.length >= 2) {
111
- // XOR folding: Combine two 32-bit values into one 32-bit value
112
- // This preserves discriminative power while halving storage
113
- descriptors[i] = (points[i].descriptors[0] ^ points[i].descriptors[1]) >>> 0;
114
- }
115
- }
116
- return {
117
- x,
118
- y,
119
- a: angle,
120
- s: scale,
121
- d: descriptors,
122
- compact: 1, // Flag to indicate compact 32-bit descriptors
123
- t: compactTree(tree.rootNode),
124
- };
125
- }
126
- /**
127
- * Compacts hierarchical clustering tree into a minimal array structure
128
- */
129
- export function compactTree(node) {
130
- if (node.leaf) {
131
- return [1, node.centerPointIndex || 0, node.pointIndexes];
132
- }
133
- return [0, node.centerPointIndex || 0, node.children.map((c) => compactTree(c))];
134
- }
135
- /**
136
- * Expands a compacted tree back into an object structure
137
- */
138
- export function expandTree(node) {
139
- const isLeaf = node[0] === 1;
140
- if (isLeaf) {
141
- return {
142
- leaf: true,
143
- centerPointIndex: node[1],
144
- pointIndexes: node[2],
145
- };
146
- }
147
- return {
148
- leaf: false,
149
- centerPointIndex: node[1],
150
- children: node[2].map((c) => expandTree(c)),
151
- };
152
- }
153
- /**
154
- * Deserializes and normalizes .taar data from a buffer
155
- */
156
- export function decodeTaar(buffer) {
157
- const content = msgpack.decode(new Uint8Array(buffer));
158
- const version = content.v || 0;
159
- // Support Protocol V5/V6/V7
160
- if (version < 5 || version > CURRENT_VERSION) {
161
- console.warn(`Potential incompatible .taar version: ${version}. Standard is ${CURRENT_VERSION}.`);
162
- }
163
- const dataList = content.dataList;
164
- for (let i = 0; i < dataList.length; i++) {
165
- const item = dataList[i];
166
- // 1. Process Tracking Data
167
- for (const td of item.trackingData) {
168
- // Helper to ensure we have the right TypedArray if it was decoded as Uint8Array by msgpack
169
- const normalizeBuffer = (arr, Type) => {
170
- if (arr instanceof Uint8Array && Type !== Uint8Array) {
171
- return new Type(arr.buffer.slice(arr.byteOffset, arr.byteOffset + arr.byteLength));
172
- }
173
- return arr;
174
- };
175
- td.px = normalizeBuffer(td.px, Float32Array);
176
- td.py = normalizeBuffer(td.py, Float32Array);
177
- // Backwards compatibility for fields named 'd' vs 'data'
178
- const rawData = td.data || td.d;
179
- const w = td.width || td.w;
180
- const h = td.height || td.h;
181
- if (rawData && rawData.length === (w * h) / 2) {
182
- const unpacked = unpack4Bit(rawData, w, h);
183
- if (td.data)
184
- td.data = unpacked;
185
- if (td.d)
186
- td.d = unpacked;
187
- }
188
- if (td.mesh) {
189
- td.mesh.t = normalizeBuffer(td.mesh.t, Uint16Array);
190
- td.mesh.e = normalizeBuffer(td.mesh.e, Uint16Array);
191
- td.mesh.rl = normalizeBuffer(td.mesh.rl, Float32Array);
192
- }
193
- }
194
- // 2. Process Matching Data
195
- for (const kf of item.matchingData) {
196
- for (const col of [kf.max, kf.min]) {
197
- if (!col)
198
- continue;
199
- let xRaw = col.x;
200
- let yRaw = col.y;
201
- if (xRaw instanceof Uint8Array) {
202
- xRaw = new Uint16Array(xRaw.buffer.slice(xRaw.byteOffset, xRaw.byteOffset + xRaw.byteLength));
203
- }
204
- if (yRaw instanceof Uint8Array) {
205
- yRaw = new Uint16Array(yRaw.buffer.slice(yRaw.byteOffset, yRaw.byteOffset + yRaw.byteLength));
206
- }
207
- const count = xRaw.length;
208
- const x = new Float32Array(count);
209
- const y = new Float32Array(count);
210
- for (let k = 0; k < count; k++) {
211
- x[k] = (xRaw[k] / 65535) * kf.w;
212
- y[k] = (yRaw[k] / 65535) * kf.h;
213
- }
214
- col.x = x;
215
- col.y = y;
216
- if (col.a instanceof Uint8Array) {
217
- const aRaw = new Int16Array(col.a.buffer.slice(col.a.byteOffset, col.a.byteOffset + col.a.byteLength));
218
- const a = new Float32Array(count);
219
- for (let k = 0; k < count; k++) {
220
- a[k] = (aRaw[k] / 32767) * Math.PI;
221
- }
222
- col.a = a;
223
- }
224
- if (col.s instanceof Uint8Array) {
225
- const sRaw = col.s;
226
- const s = new Float32Array(count);
227
- for (let k = 0; k < count; k++) {
228
- s[k] = Math.pow(2, sRaw[k]);
229
- }
230
- col.s = s;
231
- }
232
- if (col.d instanceof Uint8Array) {
233
- // Check if it's HDC (Uint32) or Raw (Uint32 x 2)
234
- if (col.hdc === 1) {
235
- col.d = new Uint32Array(col.d.buffer.slice(col.d.byteOffset, col.d.byteOffset + col.d.byteLength));
236
- }
237
- else {
238
- col.d = new Uint32Array(col.d.buffer.slice(col.d.byteOffset, col.d.byteOffset + col.d.byteLength));
239
- }
240
- }
241
- }
242
- }
243
- }
244
- return { version, dataList };
245
- }
246
- /**
247
- * Serializes target data into a .taar binary buffer
248
- */
249
- export function encodeTaar(dataList) {
250
- return msgpack.encode({
251
- v: CURRENT_VERSION,
252
- dataList,
253
- });
254
- }
1
+ import*as t from"@msgpack/msgpack";export const CURRENT_VERSION=11;export const HDC_SEED=322420463;export function getMorton(t,e){let n=0|t,r=0|e;return n=16711935&(n|n<<8),n=252645135&(n|n<<4),n=858993459&(n|n<<2),n=1431655765&(n|n<<1),r=16711935&(r|r<<8),r=252645135&(r|r<<4),r=858993459&(r|r<<2),r=1431655765&(r|r<<1),n|r<<1}export function pack4Bit(t){const e=t.length;if(e%2!=0)return t;const n=new Uint8Array(e/2);for(let r=0;r<e;r+=2){const e=(240&t[r])>>4,a=(240&t[r+1])>>4;n[r/2]=e<<4|a}return n}export function unpack4Bit(t,e,n){const r=new Uint8Array(e*n);for(let e=0;e<t.length;e++){const n=t[e],a=240&n,o=(15&n)<<4;r[2*e]=a,r[2*e+1]=o}return r}export function columnarize(t,e,n,r,a=!1){const o=t.length,c=new Uint16Array(o),i=new Uint16Array(o),s=new Int16Array(o),f=new Uint8Array(o);let d;d=a?new Uint32Array(o):new Uint32Array(2*o);for(let e=0;e<o;e++)c[e]=Math.round(t[e].x/n*65535),i[e]=Math.round(t[e].y/r*65535),s[e]=Math.round(t[e].angle/Math.PI*32767),f[e]=Math.round(Math.log2(t[e].scale||1)),t[e].descriptors&&t[e].descriptors.length>=2&&(a?d[e]=t[e].hdcSignature||0:(d[2*e]=t[e].descriptors[0],d[2*e+1]=t[e].descriptors[1]));return{x:c,y:i,a:s,s:f,d:d,hdc:a?1:0,t:compactTree(e.rootNode)}}export function columnarizeCompact(t,e,n,r){const a=t.length,o=new Uint16Array(a),c=new Uint16Array(a),i=new Int16Array(a),s=new Uint8Array(a),f=new Uint32Array(a);for(let e=0;e<a;e++)o[e]=Math.round(t[e].x/n*65535),c[e]=Math.round(t[e].y/r*65535),i[e]=Math.round(t[e].angle/Math.PI*32767),s[e]=Math.round(Math.log2(t[e].scale||1)),t[e].descriptors&&t[e].descriptors.length>=2&&(f[e]=(t[e].descriptors[0]^t[e].descriptors[1])>>>0);return{x:o,y:c,a:i,s:s,d:f,compact:1,t:compactTree(e.rootNode)}}export function compactTree(t){return t.leaf?[1,t.centerPointIndex||0,t.pointIndexes]:[0,t.centerPointIndex||0,t.children.map(t=>compactTree(t))]}export function expandTree(t){return 1===t[0]?{leaf:!0,centerPointIndex:t[1],pointIndexes:t[2]}:{leaf:!1,centerPointIndex:t[1],children:t[2].map(t=>expandTree(t))}}export function decodeTaar(e){const n=t.decode(new Uint8Array(e)),r=n.v||0;(r<5||r>11)&&console.warn(`Potential incompatible .taar version: ${r}. Standard is 11.`);const a=n.dataList;for(let t=0;t<a.length;t++){const e=a[t];for(const t of e.trackingData){const e=(t,e)=>t instanceof Uint8Array&&e!==Uint8Array?new e(t.buffer.slice(t.byteOffset,t.byteOffset+t.byteLength)):t;t.px=e(t.px,Float32Array),t.py=e(t.py,Float32Array);const n=t.data||t.d,r=t.width||t.w,a=t.height||t.h;if(n&&n.length===r*a/2){const e=unpack4Bit(n,r,a);t.data&&(t.data=e),t.d&&(t.d=e)}t.mesh&&(t.mesh.t=e(t.mesh.t,Uint16Array),t.mesh.e=e(t.mesh.e,Uint16Array),t.mesh.rl=e(t.mesh.rl,Float32Array))}for(const t of e.matchingData)for(const e of[t.max,t.min]){if(!e)continue;let n=e.x,r=e.y;n instanceof Uint8Array&&(n=new Uint16Array(n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength))),r instanceof Uint8Array&&(r=new Uint16Array(r.buffer.slice(r.byteOffset,r.byteOffset+r.byteLength)));const a=n.length,o=new Float32Array(a),c=new Float32Array(a);for(let e=0;e<a;e++)o[e]=n[e]/65535*t.w,c[e]=r[e]/65535*t.h;if(e.x=o,e.y=c,e.a instanceof Uint8Array){const t=new Int16Array(e.a.buffer.slice(e.a.byteOffset,e.a.byteOffset+e.a.byteLength)),n=new Float32Array(a);for(let e=0;e<a;e++)n[e]=t[e]/32767*Math.PI;e.a=n}if(e.s instanceof Uint8Array){const t=e.s,n=new Float32Array(a);for(let e=0;e<a;e++)n[e]=Math.pow(2,t[e]);e.s=n}e.d instanceof Uint8Array&&(e.hdc,e.d=new Uint32Array(e.d.buffer.slice(e.d.byteOffset,e.d.byteOffset+e.d.byteLength)))}}return{version:r,dataList:a}}export function encodeTaar(e){return t.encode({v:11,dataList:e})}
@@ -1,29 +1 @@
1
- import { extract } from "./extract.js";
2
- /**
3
- * Extrae características de tracking de una lista de imágenes
4
- * Procesa cada imagen para obtener puntos característicos y datos de escala
5
- * @param {Array<Object>} imageList - Lista de imágenes a procesar
6
- * @param {Function} doneCallback - Función de callback para reportar progreso
7
- * @returns {Array<Object>} Conjunto de características extraídas
8
- */
9
- export const extractTrackingFeatures = (imageList, doneCallback) => {
10
- const featureSets = [];
11
- // Procesar cada imagen en la lista
12
- for (let i = 0; i < imageList.length; i++) {
13
- const image = imageList[i];
14
- // Extraer puntos característicos
15
- const points = extract(image);
16
- // Construir conjunto de características
17
- const featureSet = {
18
- data: image.data,
19
- scale: image.scale,
20
- width: image.width,
21
- height: image.height,
22
- points,
23
- };
24
- featureSets.push(featureSet);
25
- // Reportar progreso
26
- doneCallback(i);
27
- }
28
- return featureSets;
29
- };
1
+ import{extract as t}from"./extract.js";export const extractTrackingFeatures=(e,a)=>{const r=[];for(let s=0;s<e.length;s++){const c=e[s],h=t(c),o={data:c.data,scale:c.scale,width:c.width,height:c.height,points:h};r.push(o),a(s)}return r};