@srsergio/taptapp-ar 1.0.42 → 1.0.50

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 (47) hide show
  1. package/README.md +42 -45
  2. package/dist/compiler/aframe.js +8 -8
  3. package/dist/compiler/controller.d.ts +50 -76
  4. package/dist/compiler/controller.js +72 -116
  5. package/dist/compiler/detector/detector-lite.js +82 -99
  6. package/dist/compiler/index.js +3 -3
  7. package/dist/compiler/matching/hamming-distance.d.ts +8 -0
  8. package/dist/compiler/matching/hamming-distance.js +35 -16
  9. package/dist/compiler/matching/hierarchical-clustering.d.ts +9 -0
  10. package/dist/compiler/matching/hierarchical-clustering.js +76 -56
  11. package/dist/compiler/matching/matching.js +3 -3
  12. package/dist/compiler/node-worker.js +144 -18
  13. package/dist/compiler/offline-compiler.d.ts +34 -83
  14. package/dist/compiler/offline-compiler.js +92 -96
  15. package/dist/compiler/simple-ar.d.ts +31 -57
  16. package/dist/compiler/simple-ar.js +32 -73
  17. package/dist/compiler/three.d.ts +13 -8
  18. package/dist/compiler/three.js +6 -6
  19. package/dist/compiler/tracker/extract.js +17 -14
  20. package/dist/compiler/utils/images.js +11 -16
  21. package/dist/compiler/utils/lsh-direct.d.ts +12 -0
  22. package/dist/compiler/utils/lsh-direct.js +76 -0
  23. package/dist/compiler/utils/worker-pool.js +10 -1
  24. package/dist/index.d.ts +2 -2
  25. package/dist/index.js +2 -2
  26. package/dist/react/types.d.ts +1 -1
  27. package/dist/react/types.js +1 -1
  28. package/package.json +2 -1
  29. package/src/compiler/aframe.js +8 -8
  30. package/src/compiler/controller.ts +512 -0
  31. package/src/compiler/detector/detector-lite.js +87 -107
  32. package/src/compiler/index.js +3 -3
  33. package/src/compiler/matching/hamming-distance.js +39 -16
  34. package/src/compiler/matching/hierarchical-clustering.js +85 -57
  35. package/src/compiler/matching/matching.js +3 -3
  36. package/src/compiler/node-worker.js +163 -18
  37. package/src/compiler/offline-compiler.ts +513 -0
  38. package/src/compiler/{simple-ar.js → simple-ar.ts} +64 -91
  39. package/src/compiler/three.js +6 -6
  40. package/src/compiler/tracker/extract.js +18 -15
  41. package/src/compiler/utils/images.js +11 -21
  42. package/src/compiler/utils/lsh-direct.js +86 -0
  43. package/src/compiler/utils/worker-pool.js +9 -1
  44. package/src/index.ts +2 -2
  45. package/src/react/types.ts +2 -2
  46. package/src/compiler/controller.js +0 -554
  47. package/src/compiler/offline-compiler.js +0 -515
@@ -1,515 +0,0 @@
1
- /**
2
- * @fileoverview Compilador Offline Optimizado - Sin TensorFlow para máxima velocidad
3
- *
4
- * Este módulo implementa un compilador de imágenes AR ultrarrápido
5
- * que NO depende de TensorFlow, eliminando todos los problemas de
6
- * inicialización, bloqueos y compatibilidad.
7
- *
8
- * Usa JavaScript puro para:
9
- * - Extracción de features de tracking (extract.js)
10
- * - Detección de features para matching (DetectorLite)
11
- * - Clustering jerárquico para features
12
- *
13
- * Funciona en:
14
- * - Node.js (con workers opcionales)
15
- * - Browser (sin workers)
16
- */
17
-
18
- import { buildTrackingImageList, buildImageList } from "./image-list.js";
19
- import { extractTrackingFeatures } from "./tracker/extract-utils.js";
20
- import { DetectorLite } from "./detector/detector-lite.js";
21
- import { build as hierarchicalClusteringBuild } from "./matching/hierarchical-clustering.js";
22
- import * as msgpack from "@msgpack/msgpack";
23
- import { WorkerPool } from "./utils/worker-pool.js";
24
-
25
- // Detect environment
26
- const isNode = typeof process !== "undefined" &&
27
- process.versions != null &&
28
- process.versions.node != null;
29
-
30
- const CURRENT_VERSION = 6; // Protocol v6: Moonshot - LSH 64-bit
31
-
32
- /**
33
- * Compilador offline optimizado sin TensorFlow
34
- */
35
- export class OfflineCompiler {
36
- constructor() {
37
- this.data = null;
38
- this.workerPool = null;
39
-
40
- // Workers only in Node.js (no en browser)
41
- if (isNode) {
42
- // Lazy init workers only when needed
43
- } else {
44
- console.log("🌐 OfflineCompiler: Browser mode (no workers)");
45
- }
46
- }
47
-
48
- async _initNodeWorkers() {
49
- try {
50
- // Use variables to prevent bundlers from trying to bundle these
51
- const pathModule = "path";
52
- const urlModule = "url";
53
- const osModule = "os";
54
- const workerThreadsModule = "node:worker_threads";
55
-
56
- const [path, url, os, { Worker }] = await Promise.all([
57
- import(pathModule),
58
- import(urlModule),
59
- import(osModule),
60
- import(workerThreadsModule)
61
- ]);
62
-
63
- const __filename = url.fileURLToPath(import.meta.url);
64
- const __dirname = path.dirname(__filename);
65
- const workerPath = path.join(__dirname, "node-worker.js");
66
-
67
- // Limit workers to avoid freezing system
68
- const numWorkers = Math.min(os.cpus().length, 4);
69
-
70
- this.workerPool = new WorkerPool(workerPath, numWorkers, Worker);
71
- } catch (e) {
72
- console.log("⚡ OfflineCompiler: Running without workers (initialization failed)", e);
73
- }
74
- }
75
-
76
- /**
77
- * Compila una lista de imágenes objetivo
78
- * @param {Array} images - Lista de imágenes {width, height, data}
79
- * @param {Function} progressCallback - Callback de progreso (0-100)
80
- * @returns {Promise<Array>} Datos compilados
81
- */
82
- async compileImageTargets(images, progressCallback) {
83
- console.time("⏱️ Compilación total");
84
-
85
- const targetImages = [];
86
-
87
- // Preparar imágenes
88
- for (let i = 0; i < images.length; i++) {
89
- const img = images[i];
90
-
91
- if (!img || !img.width || !img.height || !img.data) {
92
- throw new Error(
93
- `Imagen inválida en posición ${i}. Debe tener propiedades width, height y data.`
94
- );
95
- }
96
-
97
- // Convertir a escala de grises
98
- const greyImageData = new Uint8Array(img.width * img.height);
99
-
100
- if (img.data.length === img.width * img.height) {
101
- greyImageData.set(img.data);
102
- } else if (img.data.length === img.width * img.height * 4) {
103
- for (let j = 0; j < greyImageData.length; j++) {
104
- const offset = j * 4;
105
- greyImageData[j] = Math.floor(
106
- (img.data[offset] + img.data[offset + 1] + img.data[offset + 2]) / 3
107
- );
108
- }
109
- } else {
110
- throw new Error(`Formato de datos de imagen no soportado en posición ${i}`);
111
- }
112
-
113
- targetImages.push({
114
- data: greyImageData,
115
- width: img.width,
116
- height: img.height,
117
- });
118
- }
119
-
120
- // Compilar Match y Track por separado
121
- const matchingDataList = await this._compileMatch(targetImages, (p) => {
122
- progressCallback(p * 0.7); // 70% Match
123
- });
124
-
125
- const trackingDataList = await this._compileTrack(targetImages, (p) => {
126
- progressCallback(70 + p * 0.3); // 30% Track
127
- });
128
-
129
- this.data = targetImages.map((img, i) => ({
130
- targetImage: img,
131
- matchingData: matchingDataList[i],
132
- trackingData: trackingDataList[i],
133
- }));
134
-
135
- console.timeEnd("⏱️ Compilación total");
136
- return this.data;
137
- }
138
-
139
- async _compileMatch(targetImages, progressCallback) {
140
- const percentPerImage = 100 / targetImages.length;
141
- let currentPercent = 0;
142
-
143
- // Use workers if available
144
- if (isNode) await this._initNodeWorkers();
145
- if (this.workerPool) {
146
- const progressMap = new Float32Array(targetImages.length);
147
-
148
- const wrappedPromises = targetImages.map((targetImage, index) => {
149
- return this.workerPool.runTask({
150
- type: 'match',
151
- targetImage,
152
- percentPerImage,
153
- basePercent: 0,
154
- onProgress: (p) => {
155
- progressMap[index] = p;
156
- const sum = progressMap.reduce((a, b) => a + b, 0);
157
- progressCallback(sum);
158
- }
159
- });
160
- });
161
-
162
- return Promise.all(wrappedPromises);
163
- }
164
-
165
- // Serial Fallback
166
- const results = [];
167
- for (let i = 0; i < targetImages.length; i++) {
168
- const targetImage = targetImages[i];
169
- const imageList = buildImageList(targetImage);
170
- const percentPerScale = percentPerImage / imageList.length;
171
-
172
- const keyframes = [];
173
-
174
- for (const image of imageList) {
175
- const detector = new DetectorLite(image.width, image.height, { useLSH: true });
176
- const { featurePoints: ps } = detector.detect(image.data);
177
-
178
- const maximaPoints = ps.filter((p) => p.maxima);
179
- const minimaPoints = ps.filter((p) => !p.maxima);
180
- const maximaPointsCluster = hierarchicalClusteringBuild({ points: maximaPoints });
181
- const minimaPointsCluster = hierarchicalClusteringBuild({ points: minimaPoints });
182
-
183
- keyframes.push({
184
- maximaPoints,
185
- minimaPoints,
186
- maximaPointsCluster,
187
- minimaPointsCluster,
188
- width: image.width,
189
- height: image.height,
190
- scale: image.scale,
191
- });
192
-
193
- currentPercent += percentPerScale;
194
- progressCallback(currentPercent);
195
- }
196
-
197
- results.push(keyframes);
198
- }
199
-
200
- return results;
201
- }
202
-
203
- async _compileTrack(targetImages, progressCallback) {
204
- const percentPerImage = 100 / targetImages.length;
205
- let currentPercent = 0;
206
-
207
- if (this.workerPool) {
208
- const progressMap = new Float32Array(targetImages.length);
209
- const wrappedPromises = targetImages.map((targetImage, index) => {
210
- return this.workerPool.runTask({
211
- type: 'compile',
212
- targetImage,
213
- percentPerImage,
214
- basePercent: 0,
215
- onProgress: (p) => {
216
- progressMap[index] = p;
217
- const sum = progressMap.reduce((a, b) => a + b, 0);
218
- progressCallback(sum);
219
- }
220
- });
221
- });
222
- return Promise.all(wrappedPromises);
223
- }
224
-
225
- // Serial Fallback
226
- const results = [];
227
- for (let i = 0; i < targetImages.length; i++) {
228
- const targetImage = targetImages[i];
229
- const imageList = buildTrackingImageList(targetImage);
230
- const percentPerScale = percentPerImage / imageList.length;
231
-
232
- const trackingData = extractTrackingFeatures(imageList, () => {
233
- currentPercent += percentPerScale;
234
- progressCallback(currentPercent);
235
- });
236
-
237
- results.push(trackingData);
238
- }
239
-
240
- return results;
241
- }
242
-
243
- async compileTrack({ progressCallback, targetImages, basePercent = 0 }) {
244
- return this._compileTrack(targetImages, (percent) => {
245
- progressCallback(basePercent + percent * (100 - basePercent) / 100);
246
- });
247
- }
248
-
249
- async compileMatch({ progressCallback, targetImages, basePercent = 0 }) {
250
- return this._compileMatch(targetImages, (percent) => {
251
- progressCallback(basePercent + percent * (50 - basePercent) / 100);
252
- });
253
- }
254
-
255
- exportData() {
256
- if (!this.data) {
257
- throw new Error("No hay datos compilados para exportar");
258
- }
259
-
260
- const dataList = this.data.map((item) => {
261
- const matchingData = item.matchingData.map((kf) => this._packKeyframe(kf));
262
-
263
- const trackingData = item.trackingData.map((td) => {
264
- const count = td.points.length;
265
- // Packed Coords - Float32 for now as in current import logic
266
- const px = new Float32Array(count);
267
- const py = new Float32Array(count);
268
- for (let i = 0; i < count; i++) {
269
- px[i] = td.points[i].x;
270
- py[i] = td.points[i].y;
271
- }
272
- return {
273
- w: td.width,
274
- h: td.height,
275
- s: td.scale,
276
- px,
277
- py,
278
- d: td.data, // Grayscale pixel data (Uint8Array)
279
- };
280
- });
281
-
282
- return {
283
- targetImage: {
284
- width: item.targetImage.width,
285
- height: item.targetImage.height,
286
- },
287
- trackingData,
288
- matchingData,
289
- };
290
- });
291
-
292
- return msgpack.encode({
293
- v: CURRENT_VERSION,
294
- dataList,
295
- });
296
- }
297
-
298
- _getMorton(x, y) {
299
- // Interleave bits of x and y
300
- let x_int = x | 0;
301
- let y_int = y | 0;
302
-
303
- x_int = (x_int | (x_int << 8)) & 0x00FF00FF;
304
- x_int = (x_int | (x_int << 4)) & 0x0F0F0F0F;
305
- x_int = (x_int | (x_int << 2)) & 0x33333333;
306
- x_int = (x_int | (x_int << 1)) & 0x55555555;
307
-
308
- y_int = (y_int | (y_int << 8)) & 0x00FF00FF;
309
- y_int = (y_int | (y_int << 4)) & 0x0F0F0F0F;
310
- y_int = (y_int | (y_int << 2)) & 0x33333333;
311
- y_int = (y_int | (y_int << 1)) & 0x55555555;
312
-
313
- return x_int | (y_int << 1);
314
- }
315
-
316
- _packKeyframe(kf) {
317
- // Step 2.1: Morton Sorting - Sort points spatially to improve Delta-Descriptor XOR
318
- const sortPoints = (points) => {
319
- return [...points].sort((a, b) => {
320
- return this._getMorton(a.x, a.y) - this._getMorton(b.x, b.y);
321
- });
322
- };
323
-
324
- const sortedMaxima = sortPoints(kf.maximaPoints);
325
- const sortedMinima = sortPoints(kf.minimaPoints);
326
-
327
- // Rebuild clusters with sorted indices
328
- const sortedMaximaCluster = hierarchicalClusteringBuild({ points: sortedMaxima });
329
- const sortedMinimaCluster = hierarchicalClusteringBuild({ points: sortedMinima });
330
-
331
- return {
332
- w: kf.width,
333
- h: kf.height,
334
- s: kf.scale,
335
- max: this._columnarize(sortedMaxima, sortedMaximaCluster, kf.width, kf.height),
336
- min: this._columnarize(sortedMinima, sortedMinimaCluster, kf.width, kf.height),
337
- };
338
- }
339
-
340
- _columnarize(points, tree, width, height) {
341
- const count = points.length;
342
- // Step 1: Packed Coords - Normalize to 16-bit
343
- const x = new Uint16Array(count);
344
- const y = new Uint16Array(count);
345
- // Step 1.1: Angle Quantization - Int16
346
- const angle = new Int16Array(count);
347
- // Step 1.2: Scale Indexing - Uint8
348
- const scale = new Uint8Array(count);
349
-
350
- // Step 3: LSH 64-bit Descriptors - Uint32Array (2 elements per point)
351
- const descriptors = new Uint32Array(count * 2);
352
-
353
- for (let i = 0; i < count; i++) {
354
- x[i] = Math.round((points[i].x / width) * 65535);
355
- y[i] = Math.round((points[i].y / height) * 65535);
356
- angle[i] = Math.round((points[i].angle / Math.PI) * 32767);
357
- scale[i] = Math.round(Math.log2(points[i].scale || 1));
358
-
359
- if (points[i].descriptors && points[i].descriptors.length >= 2) {
360
- descriptors[i * 2] = points[i].descriptors[0];
361
- descriptors[(i * 2) + 1] = points[i].descriptors[1];
362
- }
363
- }
364
-
365
- return {
366
- x,
367
- y,
368
- a: angle,
369
- s: scale,
370
- d: descriptors,
371
- t: this._compactTree(tree.rootNode),
372
- };
373
- }
374
-
375
- _compactTree(node) {
376
- if (node.leaf) {
377
- return [1, node.centerPointIndex || 0, node.pointIndexes];
378
- }
379
- return [0, node.centerPointIndex || 0, node.children.map((c) => this._compactTree(c))];
380
- }
381
-
382
- importData(buffer) {
383
- const content = msgpack.decode(new Uint8Array(buffer));
384
-
385
- const version = content.v || 0;
386
- if (version !== CURRENT_VERSION && version !== 5) {
387
- console.error(`Incompatible .mind version: ${version}. This engine only supports Protocol V5/V6.`);
388
- return [];
389
- }
390
- const descSize = version >= 6 ? 2 : 4;
391
-
392
- // Restore TypedArrays from Uint8Arrays returned by msgpack
393
- const dataList = content.dataList;
394
- for (let i = 0; i < dataList.length; i++) {
395
- const item = dataList[i];
396
-
397
- // Unpack Tracking Data
398
- for (const td of item.trackingData) {
399
- let px = td.px;
400
- let py = td.py;
401
-
402
- if (px instanceof Uint8Array) {
403
- px = new Float32Array(px.buffer.slice(px.byteOffset, px.byteOffset + px.byteLength));
404
- }
405
- if (py instanceof Uint8Array) {
406
- py = new Float32Array(py.buffer.slice(py.byteOffset, py.byteOffset + py.byteLength));
407
- }
408
- td.px = px;
409
- td.py = py;
410
- }
411
-
412
- // Unpack Matching Data
413
- for (const kf of item.matchingData) {
414
- for (const col of [kf.max, kf.min]) {
415
- let xRaw = col.x;
416
- let yRaw = col.y;
417
-
418
- if (xRaw instanceof Uint8Array) {
419
- xRaw = new Uint16Array(xRaw.buffer.slice(xRaw.byteOffset, xRaw.byteOffset + xRaw.byteLength));
420
- }
421
- if (yRaw instanceof Uint8Array) {
422
- yRaw = new Uint16Array(yRaw.buffer.slice(yRaw.byteOffset, yRaw.byteOffset + yRaw.byteLength));
423
- }
424
-
425
- // Rescale for compatibility with Matcher
426
- const count = xRaw.length;
427
- const x = new Float32Array(count);
428
- const y = new Float32Array(count);
429
- for (let k = 0; k < count; k++) {
430
- x[k] = (xRaw[k] / 65535) * kf.w;
431
- y[k] = (yRaw[k] / 65535) * kf.h;
432
- }
433
- col.x = x;
434
- col.y = y;
435
-
436
- if (col.a instanceof Uint8Array) {
437
- const aRaw = new Int16Array(col.a.buffer.slice(col.a.byteOffset, col.a.byteOffset + col.a.byteLength));
438
- const a = new Float32Array(count);
439
- for (let k = 0; k < count; k++) {
440
- a[k] = (aRaw[k] / 32767) * Math.PI;
441
- }
442
- col.a = a;
443
- }
444
- if (col.s instanceof Uint8Array) {
445
- const sRaw = col.s;
446
- const s = new Float32Array(count);
447
- for (let k = 0; k < count; k++) {
448
- s[k] = Math.pow(2, sRaw[k]);
449
- }
450
- col.s = s;
451
- }
452
-
453
- // Restore LSH descriptors (Uint32Array)
454
- if (col.d instanceof Uint8Array) {
455
- col.d = new Uint32Array(col.d.buffer.slice(col.d.byteOffset, col.d.byteOffset + col.d.byteLength));
456
- }
457
- }
458
- }
459
- }
460
-
461
- this.data = dataList;
462
- return { version, dataList };
463
- }
464
-
465
- _unpackKeyframe(kf) {
466
- return {
467
- width: kf.w,
468
- height: kf.h,
469
- scale: kf.s,
470
- maximaPoints: this._decolumnarize(kf.max, kf.w, kf.h),
471
- minimaPoints: this._decolumnarize(kf.min, kf.w, kf.h),
472
- maximaPointsCluster: { rootNode: this._expandTree(kf.max.t) },
473
- minimaPointsCluster: { rootNode: this._expandTree(kf.min.t) },
474
- };
475
- }
476
-
477
- _decolumnarize(col, width, height) {
478
- const points = [];
479
- const count = col.x.length;
480
- const descSize = col.d.length / count;
481
-
482
- for (let i = 0; i < count; i++) {
483
- points.push({
484
- x: (col.x[i] / 65535) * width,
485
- y: (col.y[i] / 65535) * height,
486
- angle: col.a[i],
487
- scale: col.s ? col.s[i] : 1.0,
488
- descriptors: col.d.slice(i * descSize, (i + 1) * descSize),
489
- });
490
- }
491
- return points;
492
- }
493
-
494
- _expandTree(node) {
495
- const isLeaf = node[0] === 1;
496
- if (isLeaf) {
497
- return {
498
- leaf: true,
499
- centerPointIndex: node[1],
500
- pointIndexes: node[2],
501
- };
502
- }
503
- return {
504
- leaf: false,
505
- centerPointIndex: node[1],
506
- children: node[2].map((c) => this._expandTree(c)),
507
- };
508
- }
509
-
510
- async destroy() {
511
- if (this.workerPool) {
512
- await this.workerPool.destroy();
513
- }
514
- }
515
- }