@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.
- package/dist/compiler/node-worker.js +1 -197
- package/dist/compiler/offline-compiler.js +1 -207
- package/dist/core/constants.js +1 -38
- package/dist/core/detector/crop-detector.js +1 -88
- package/dist/core/detector/detector-lite.js +1 -455
- package/dist/core/detector/freak.js +1 -89
- package/dist/core/estimation/estimate.js +1 -16
- package/dist/core/estimation/estimator.js +1 -30
- package/dist/core/estimation/morph-refinement.js +1 -116
- package/dist/core/estimation/non-rigid-refine.js +1 -70
- package/dist/core/estimation/pnp-solver.js +1 -109
- package/dist/core/estimation/refine-estimate.js +1 -311
- package/dist/core/estimation/utils.js +1 -67
- package/dist/core/features/auto-rotation-feature.js +1 -30
- package/dist/core/features/crop-detection-feature.js +1 -26
- package/dist/core/features/feature-base.js +1 -1
- package/dist/core/features/feature-manager.js +1 -55
- package/dist/core/features/one-euro-filter-feature.js +1 -44
- package/dist/core/features/temporal-filter-feature.js +1 -57
- package/dist/core/image-list.js +1 -54
- package/dist/core/input-loader.js +1 -87
- package/dist/core/matching/hamming-distance.js +1 -66
- package/dist/core/matching/hdc.js +1 -102
- package/dist/core/matching/hierarchical-clustering.js +1 -130
- package/dist/core/matching/hough.js +1 -170
- package/dist/core/matching/matcher.js +1 -66
- package/dist/core/matching/matching.js +1 -401
- package/dist/core/matching/ransacHomography.js +1 -132
- package/dist/core/perception/bio-inspired-engine.js +1 -232
- package/dist/core/perception/foveal-attention.js +1 -280
- package/dist/core/perception/index.js +1 -17
- package/dist/core/perception/predictive-coding.js +1 -278
- package/dist/core/perception/saccadic-controller.js +1 -269
- package/dist/core/perception/saliency-map.js +1 -254
- package/dist/core/perception/scale-orchestrator.js +1 -68
- package/dist/core/protocol.js +1 -254
- package/dist/core/tracker/extract-utils.js +1 -29
- package/dist/core/tracker/extract.js +1 -306
- package/dist/core/tracker/tracker.js +1 -352
- package/dist/core/utils/cumsum.js +1 -37
- package/dist/core/utils/delaunay.js +1 -125
- package/dist/core/utils/geometry.js +1 -101
- package/dist/core/utils/gpu-compute.js +1 -231
- package/dist/core/utils/homography.js +1 -138
- package/dist/core/utils/images.js +1 -108
- package/dist/core/utils/lsh-binarizer.js +1 -37
- package/dist/core/utils/lsh-direct.js +1 -76
- package/dist/core/utils/projection.js +1 -51
- package/dist/core/utils/randomizer.js +1 -25
- package/dist/core/utils/worker-pool.js +1 -89
- package/dist/index.js +1 -7
- package/dist/libs/one-euro-filter.js +1 -70
- package/dist/react/TaptappAR.js +1 -151
- package/dist/react/types.js +1 -16
- package/dist/react/use-ar.js +1 -118
- package/dist/runtime/aframe.js +1 -272
- package/dist/runtime/bio-inspired-controller.js +1 -358
- package/dist/runtime/controller.js +1 -592
- package/dist/runtime/controller.worker.js +1 -93
- package/dist/runtime/index.js +1 -5
- package/dist/runtime/three.js +1 -304
- package/dist/runtime/track.js +1 -381
- package/package.json +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}}
|
package/dist/core/protocol.js
CHANGED
|
@@ -1,254 +1 @@
|
|
|
1
|
-
import
|
|
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
|
|
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};
|