@srsergio/taptapp-ar 1.0.43 → 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 +1,9 @@
1
+ /**
2
+ * Generic compute for backward compatibility
3
+ */
1
4
  export function compute(options: any): number;
5
+ /**
6
+ * Super-optimized Hamming distance for 64-bit LSH (2x Uint32)
7
+ * NO OBJECTS, NO OPTIONS, JUST PURE SPEED.
8
+ */
9
+ export function compute64(v1: any, v1Idx: any, v2: any, v2Idx: any): number;
@@ -9,39 +9,58 @@ for (let i = 0; i < 256; i++) {
9
9
  BIT_COUNT_8[i] = c;
10
10
  }
11
11
  /**
12
- * Optimized popcount for 32-bit integers
12
+ * 🚀 Moonshot Optimized Popcount
13
+ * Uses a slightly faster bitwise sequence for 32-bit integers
13
14
  */
14
15
  function popcount32(n) {
15
- n = n - ((n >> 1) & 0x55555555);
16
- n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
17
- return (((n + (n >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
16
+ n = n >>> 0; // Force unsigned
17
+ n -= (n >>> 1) & 0x55555555;
18
+ n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
19
+ return (((n + (n >>> 4)) & 0x0F0F0F0F) * 0x01010101) >>> 24;
18
20
  }
21
+ /**
22
+ * Super-optimized Hamming distance for 64-bit LSH (2x Uint32)
23
+ * NO OBJECTS, NO OPTIONS, JUST PURE SPEED.
24
+ */
25
+ const compute64 = (v1, v1Idx, v2, v2Idx) => {
26
+ // Inline XOR and popcount for maximum speed
27
+ let x1 = (v1[v1Idx] ^ v2[v2Idx]) >>> 0;
28
+ let x2 = (v1[v1Idx + 1] ^ v2[v2Idx + 1]) >>> 0;
29
+ // Popcount 1
30
+ x1 -= (x1 >>> 1) & 0x55555555;
31
+ x1 = (x1 & 0x33333333) + ((x1 >>> 2) & 0x33333333);
32
+ const count1 = (((x1 + (x1 >>> 4)) & 0x0F0F0F0F) * 0x01010101) >>> 24;
33
+ // Popcount 2
34
+ x2 -= (x2 >>> 1) & 0x55555555;
35
+ x2 = (x2 & 0x33333333) + ((x2 >>> 2) & 0x33333333);
36
+ const count2 = (((x2 + (x2 >>> 4)) & 0x0F0F0F0F) * 0x01010101) >>> 24;
37
+ return count1 + count2;
38
+ };
39
+ /**
40
+ * Generic compute for backward compatibility
41
+ */
19
42
  const compute = (options) => {
20
43
  const { v1, v2, v1Offset = 0, v2Offset = 0 } = options;
21
- // Protocol V5 Path: 64-bit LSH (two Uint32)
22
- if (v1.length === v2.length && (v1.length / (v1.buffer.byteLength / v1.length)) === 2) {
23
- // This is a bit hacky check, better if we know the version.
24
- // Assuming if it's not 84 bytes, it's the new 8-byte format.
44
+ const v2Len = v2.length - v2Offset;
45
+ if (v2Len === 2) {
46
+ return compute64(v1, v1Offset, v2, v2Offset);
25
47
  }
26
- // If descriptors are 84 bytes (Protocol V4)
27
- if (v1.length >= v1Offset + 84 && v2.length >= v2Offset + 84 && v1[v1Offset + 83] !== undefined) {
48
+ // Protocol V4: 84-byte descriptors (Uint8Array)
49
+ if (v2Len === 84) {
28
50
  let d = 0;
29
51
  for (let i = 0; i < 84; i++) {
30
52
  d += BIT_COUNT_8[v1[v1Offset + i] ^ v2[v2Offset + i]];
31
53
  }
32
54
  return d;
33
55
  }
34
- // Protocol V5.1 Path: LSH 128-bit (4 x 32-bit)
35
- // We expect v1 and v2 to be slices or offsets of Uint32Array
36
- if (v1.length >= v1Offset + 4 && v2.length >= v2Offset + 4 && v1[v1Offset + 3] !== undefined) {
56
+ // Protocol V5.1: 128-bit LSH (4 x Uint32)
57
+ if (v2Len === 4) {
37
58
  return popcount32(v1[v1Offset] ^ v2[v2Offset]) +
38
59
  popcount32(v1[v1Offset + 1] ^ v2[v2Offset + 1]) +
39
60
  popcount32(v1[v1Offset + 2] ^ v2[v2Offset + 2]) +
40
61
  popcount32(v1[v1Offset + 3] ^ v2[v2Offset + 3]);
41
62
  }
42
- // Protocol V5 Path: LSH 64-bit (2 x 32-bit)
43
- // We expect v1 and v2 to be slices or offsets of Uint32Array
44
63
  return popcount32(v1[v1Offset] ^ v2[v2Offset]) +
45
64
  popcount32(v1[v1Offset + 1] ^ v2[v2Offset + 1]);
46
65
  };
47
- export { compute };
66
+ export { compute, compute64 };
@@ -1,6 +1,15 @@
1
+ /**
2
+ * Build hierarchical clusters
3
+ */
1
4
  export function build({ points }: {
2
5
  points: any;
3
6
  }): {
7
+ rootNode: {
8
+ leaf: boolean;
9
+ pointIndexes: never[];
10
+ centerPointIndex: null;
11
+ };
12
+ } | {
4
13
  rootNode: {
5
14
  centerPointIndex: any;
6
15
  };
@@ -1,109 +1,129 @@
1
- import { compute as hammingCompute } from "./hamming-distance.js";
1
+ import { compute64 as hammingCompute64 } from "./hamming-distance.js";
2
2
  import { createRandomizer } from "../utils/randomizer.js";
3
- const MIN_FEATURE_PER_NODE = 16;
4
- const NUM_ASSIGNMENT_HYPOTHESES = 64;
3
+ const MIN_FEATURE_PER_NODE = 32; // Increased from 16 for speed
4
+ const NUM_ASSIGNMENT_HYPOTHESES = 12; // Reduced from 16 for speed
5
5
  const NUM_CENTERS = 8;
6
+ /**
7
+ * 🚀 Moonshot Optimized K-Medoids
8
+ *
9
+ * Major Optimizations:
10
+ * 1. Flattened Memory: Operates on a single Uint32Array block instead of objects.
11
+ * 2. Zero Property Access: Avoids .descriptors lookup in the tightest loop.
12
+ * 3. Cache-Friendly: Accesses contiguous descriptor data.
13
+ */
6
14
  const _computeKMedoids = (options) => {
7
- const { points, pointIndexes, randomizer } = options;
8
- const randomPointIndexes = [];
9
- for (let i = 0; i < pointIndexes.length; i++) {
10
- randomPointIndexes.push(i);
15
+ const { descriptors, pointIndexes, randomizer } = options;
16
+ const numPointIndexes = pointIndexes.length;
17
+ const randomPointIndexes = new Int32Array(numPointIndexes);
18
+ for (let i = 0; i < numPointIndexes; i++) {
19
+ randomPointIndexes[i] = i;
11
20
  }
12
21
  let bestSumD = Number.MAX_SAFE_INTEGER;
13
- let bestAssignmentIndex = -1;
14
- const assignments = [];
22
+ let bestAssignment = null;
23
+ // Pre-fetch centers indices to avoid nested index lookups
24
+ const centerPointIndices = new Int32Array(NUM_CENTERS);
15
25
  for (let i = 0; i < NUM_ASSIGNMENT_HYPOTHESES; i++) {
16
26
  randomizer.arrayShuffle({ arr: randomPointIndexes, sampleSize: NUM_CENTERS });
27
+ // Set centers for this hypothesis
28
+ for (let k = 0; k < NUM_CENTERS; k++) {
29
+ centerPointIndices[k] = pointIndexes[randomPointIndexes[k]];
30
+ }
17
31
  let sumD = 0;
18
- const assignment = [];
19
- for (let j = 0; j < pointIndexes.length; j++) {
20
- let bestD = Number.MAX_SAFE_INTEGER;
32
+ const currentAssignment = new Int32Array(numPointIndexes);
33
+ for (let j = 0; j < numPointIndexes; j++) {
34
+ const pIdx = pointIndexes[j];
35
+ const pOffset = pIdx * 2;
36
+ let bestD = 255; // Max possible Hamming for 64-bit is 64, but let's be safe
37
+ let bestCenterIdx = -1;
21
38
  for (let k = 0; k < NUM_CENTERS; k++) {
22
- const centerIndex = pointIndexes[randomPointIndexes[k]];
23
- const d = hammingCompute({
24
- v1: points[pointIndexes[j]].descriptors,
25
- v2: points[centerIndex].descriptors,
26
- });
39
+ const cIdx = centerPointIndices[k];
40
+ const cOffset = cIdx * 2;
41
+ // DIRECT CALL TO INLINED HAMMING
42
+ const d = hammingCompute64(descriptors, pOffset, descriptors, cOffset);
27
43
  if (d < bestD) {
28
- assignment[j] = randomPointIndexes[k];
44
+ bestCenterIdx = randomPointIndexes[k];
29
45
  bestD = d;
30
46
  }
31
47
  }
48
+ currentAssignment[j] = bestCenterIdx;
32
49
  sumD += bestD;
33
50
  }
34
- assignments.push(assignment);
35
51
  if (sumD < bestSumD) {
36
52
  bestSumD = sumD;
37
- bestAssignmentIndex = i;
53
+ bestAssignment = currentAssignment;
38
54
  }
39
55
  }
40
- return assignments[bestAssignmentIndex];
56
+ return bestAssignment;
41
57
  };
42
- // kmedoids clustering of points, with hamming distance of FREAK descriptor
43
- //
44
- // node = {
45
- // isLeaf: bool,
46
- // children: [], list of children node
47
- // pointIndexes: [], list of int, point indexes
48
- // centerPointIndex: int
49
- // }
58
+ /**
59
+ * Build hierarchical clusters
60
+ */
50
61
  const build = ({ points }) => {
51
- const pointIndexes = [];
52
- for (let i = 0; i < points.length; i++) {
53
- pointIndexes.push(i);
62
+ const numPoints = points.length;
63
+ if (numPoints === 0)
64
+ return { rootNode: { leaf: true, pointIndexes: [], centerPointIndex: null } };
65
+ // 🚀 MOONSHOT: Flatten all descriptors into a single Uint32Array
66
+ // This is the key to sub-second performance.
67
+ const descriptors = new Uint32Array(numPoints * 2);
68
+ for (let i = 0; i < numPoints; i++) {
69
+ const d = points[i].descriptors;
70
+ descriptors[i * 2] = d[0];
71
+ descriptors[i * 2 + 1] = d[1];
72
+ }
73
+ const pointIndexes = new Int32Array(numPoints);
74
+ for (let i = 0; i < numPoints; i++) {
75
+ pointIndexes[i] = i;
54
76
  }
55
77
  const randomizer = createRandomizer();
56
78
  const rootNode = _build({
57
- points: points,
58
- pointIndexes: pointIndexes,
79
+ descriptors,
80
+ pointIndexes,
59
81
  centerPointIndex: null,
60
82
  randomizer,
61
83
  });
62
84
  return { rootNode };
63
85
  };
64
- // recursive build hierarchy clusters
65
86
  const _build = (options) => {
66
- const { points, pointIndexes, centerPointIndex, randomizer } = options;
87
+ const { descriptors, pointIndexes, centerPointIndex, randomizer } = options;
88
+ const numPoints = pointIndexes.length;
67
89
  let isLeaf = false;
68
- if (pointIndexes.length <= NUM_CENTERS || pointIndexes.length <= MIN_FEATURE_PER_NODE) {
90
+ if (numPoints <= NUM_CENTERS || numPoints <= MIN_FEATURE_PER_NODE) {
69
91
  isLeaf = true;
70
92
  }
71
- const clusters = {};
93
+ const clusters = new Map();
72
94
  if (!isLeaf) {
73
- // compute clusters
74
- const assignment = _computeKMedoids({ points, pointIndexes, randomizer });
95
+ const assignment = _computeKMedoids({ descriptors, pointIndexes, randomizer });
75
96
  for (let i = 0; i < assignment.length; i++) {
76
- if (clusters[pointIndexes[assignment[i]]] === undefined) {
77
- clusters[pointIndexes[assignment[i]]] = [];
97
+ const centerIdx = pointIndexes[assignment[i]];
98
+ let cluster = clusters.get(centerIdx);
99
+ if (cluster === undefined) {
100
+ cluster = [];
101
+ clusters.set(centerIdx, cluster);
78
102
  }
79
- clusters[pointIndexes[assignment[i]]].push(pointIndexes[i]);
103
+ cluster.push(pointIndexes[i]);
104
+ }
105
+ if (clusters.size === 1) {
106
+ isLeaf = true;
80
107
  }
81
- }
82
- if (Object.keys(clusters).length === 1) {
83
- isLeaf = true;
84
108
  }
85
109
  const node = {
86
110
  centerPointIndex: centerPointIndex,
87
111
  };
88
112
  if (isLeaf) {
89
113
  node.leaf = true;
90
- node.pointIndexes = [];
91
- for (let i = 0; i < pointIndexes.length; i++) {
92
- node.pointIndexes.push(pointIndexes[i]);
93
- }
114
+ node.pointIndexes = new Int32Array(pointIndexes);
94
115
  return node;
95
116
  }
96
- // recursive build children
97
117
  node.leaf = false;
98
118
  node.children = [];
99
- Object.keys(clusters).forEach((centerIndex) => {
119
+ for (const [cIdx, clusterPoints] of clusters) {
100
120
  node.children.push(_build({
101
- points: points,
102
- pointIndexes: clusters[centerIndex],
103
- centerPointIndex: centerIndex,
121
+ descriptors,
122
+ pointIndexes: new Int32Array(clusterPoints),
123
+ centerPointIndex: cIdx,
104
124
  randomizer,
105
125
  }));
106
- });
126
+ }
107
127
  return node;
108
128
  };
109
129
  export { build };
@@ -3,11 +3,11 @@ import { compute as hammingCompute } from "./hamming-distance.js";
3
3
  import { computeHoughMatches } from "./hough.js";
4
4
  import { computeHomography } from "./ransacHomography.js";
5
5
  import { multiplyPointHomographyInhomogenous, matrixInverse33 } from "../utils/geometry.js";
6
- const INLIER_THRESHOLD = 3;
6
+ const INLIER_THRESHOLD = 10; // Relaxed from 3 to 10 for better robustness with LSH
7
7
  //const MIN_NUM_INLIERS = 8; //default
8
8
  const MIN_NUM_INLIERS = 6;
9
- const CLUSTER_MAX_POP = 8;
10
- const HAMMING_THRESHOLD = 0.7;
9
+ const CLUSTER_MAX_POP = 20; // Increased to explore more candidate clusters
10
+ const HAMMING_THRESHOLD = 0.85; // Relaxed ratio test for binary descriptors
11
11
  // match list of querpoints against pre-built list of keyframes
12
12
  const match = ({ keyframe, querypoints, querywidth, queryheight, debugMode }) => {
13
13
  let debugExtra = {};
@@ -6,12 +6,35 @@
6
6
  */
7
7
  import { parentPort } from 'node:worker_threads';
8
8
  import { extractTrackingFeatures } from './tracker/extract-utils.js';
9
- import { buildTrackingImageList, buildImageList } from './image-list.js';
9
+ import { buildTrackingImageList } from './image-list.js';
10
10
  import { DetectorLite } from './detector/detector-lite.js';
11
11
  import { build as hierarchicalClusteringBuild } from './matching/hierarchical-clustering.js';
12
12
  if (!parentPort) {
13
13
  throw new Error('This file must be run as a worker thread.');
14
14
  }
15
+ // Helper for Morton Order sorting inside worker
16
+ function getMorton(x, y) {
17
+ let x_int = x | 0;
18
+ let y_int = y | 0;
19
+ x_int = (x_int | (x_int << 8)) & 0x00FF00FF;
20
+ x_int = (x_int | (x_int << 4)) & 0x0F0F0F0F;
21
+ x_int = (x_int | (x_int << 2)) & 0x33333333;
22
+ x_int = (x_int | (x_int << 1)) & 0x55555555;
23
+ y_int = (y_int | (y_int << 8)) & 0x00FF00FF;
24
+ y_int = (y_int | (y_int << 4)) & 0x0F0F0F0F;
25
+ y_int = (y_int | (y_int << 2)) & 0x33333333;
26
+ y_int = (y_int | (y_int << 1)) & 0x55555555;
27
+ return x_int | (y_int << 1);
28
+ }
29
+ const mortonCache = new Int32Array(2048); // Cache for sorting stability
30
+ function sortPoints(points) {
31
+ if (points.length <= 1)
32
+ return points;
33
+ // Sort in-place to avoid allocations
34
+ return points.sort((a, b) => {
35
+ return getMorton(a.x, a.y) - getMorton(b.x, b.y);
36
+ });
37
+ }
15
38
  parentPort.on('message', async (msg) => {
16
39
  if (msg.type === 'compile') {
17
40
  const { targetImage, percentPerImage, basePercent } = msg;
@@ -34,24 +57,50 @@ parentPort.on('message', async (msg) => {
34
57
  catch (error) {
35
58
  parentPort.postMessage({
36
59
  type: 'error',
37
- error: error.message
60
+ error: error.message + '\n' + error.stack
38
61
  });
39
62
  }
40
63
  }
41
64
  else if (msg.type === 'match') {
42
65
  const { targetImage, percentPerImage, basePercent } = msg;
43
66
  try {
44
- const imageList = buildImageList(targetImage);
45
- const percentPerAction = percentPerImage / imageList.length;
46
- let localPercent = 0;
67
+ // 🚀 MOONSHOT: Only run detector ONCE on full-res image.
68
+ // DetectorLite internally builds a pyramid (octaves 1.0, 0.5, 0.25, etc.)
69
+ const detector = new DetectorLite(targetImage.width, targetImage.height, {
70
+ useLSH: true
71
+ });
72
+ parentPort.postMessage({ type: 'progress', percent: basePercent + percentPerImage * 0.1 });
73
+ const { featurePoints: allPoints } = detector.detect(targetImage.data);
74
+ parentPort.postMessage({ type: 'progress', percent: basePercent + percentPerImage * 0.5 });
75
+ // Group points by their scale (octave)
76
+ const scalesMap = new Map();
77
+ for (const p of allPoints) {
78
+ const octaveScale = p.scale;
79
+ let list = scalesMap.get(octaveScale);
80
+ if (!list) {
81
+ list = [];
82
+ scalesMap.set(octaveScale, list);
83
+ }
84
+ // Coordinates in p are already full-res.
85
+ // We need them relative to the scaled image for the keyframe.
86
+ list.push({
87
+ ...p,
88
+ x: p.x / octaveScale,
89
+ y: p.y / octaveScale,
90
+ scale: 1.0 // Keypoint scale is always 1.0 relative to its own keyframe image
91
+ });
92
+ }
93
+ // Optional: Run another detector pass at an intermediate scale to improve coverage
94
+ // (e.g. at 1/1.41 ratio) if tracking robustness suffers.
95
+ // For now, let's stick to octaves for MAXIMUM speed.
47
96
  const keyframes = [];
48
- for (let i = 0; i < imageList.length; i++) {
49
- const image = imageList[i];
50
- const detector = new DetectorLite(image.width, image.height, { useLSH: true });
51
- // Detectar features usando JS puro (sin TensorFlow)
52
- const { featurePoints: ps } = detector.detect(image.data);
53
- const maximaPoints = ps.filter((p) => p.maxima);
54
- const minimaPoints = ps.filter((p) => !p.maxima);
97
+ const sortedScales = Array.from(scalesMap.keys()).sort((a, b) => a - b);
98
+ const percentPerScale = (percentPerImage * 0.4) / sortedScales.length;
99
+ for (const s of sortedScales) {
100
+ const ps = scalesMap.get(s);
101
+ const sortedPs = sortPoints(ps);
102
+ const maximaPoints = sortedPs.filter((p) => p.maxima);
103
+ const minimaPoints = sortedPs.filter((p) => !p.maxima);
55
104
  const maximaPointsCluster = hierarchicalClusteringBuild({ points: maximaPoints });
56
105
  const minimaPointsCluster = hierarchicalClusteringBuild({ points: minimaPoints });
57
106
  keyframes.push({
@@ -59,14 +108,13 @@ parentPort.on('message', async (msg) => {
59
108
  minimaPoints,
60
109
  maximaPointsCluster,
61
110
  minimaPointsCluster,
62
- width: image.width,
63
- height: image.height,
64
- scale: image.scale,
111
+ width: Math.round(targetImage.width / s),
112
+ height: Math.round(targetImage.height / s),
113
+ scale: 1.0 / s, // keyframe.scale is relative to full target image
65
114
  });
66
- localPercent += percentPerAction;
67
115
  parentPort.postMessage({
68
116
  type: 'progress',
69
- percent: basePercent + localPercent
117
+ percent: basePercent + percentPerImage * 0.6 + keyframes.length * percentPerScale
70
118
  });
71
119
  }
72
120
  parentPort.postMessage({
@@ -77,7 +125,85 @@ parentPort.on('message', async (msg) => {
77
125
  catch (error) {
78
126
  parentPort.postMessage({
79
127
  type: 'error',
80
- error: error.message
128
+ error: error.message + '\n' + error.stack
129
+ });
130
+ }
131
+ }
132
+ else if (msg.type === 'compile-all') {
133
+ const { targetImage } = msg;
134
+ try {
135
+ // 1. Single Pass Detection + Pyramid Generation
136
+ const detector = new DetectorLite(targetImage.width, targetImage.height, { useLSH: true });
137
+ parentPort.postMessage({ type: 'progress', percent: 10 });
138
+ const { featurePoints, pyramid } = detector.detect(targetImage.data);
139
+ parentPort.postMessage({ type: 'progress', percent: 40 });
140
+ // 2. Extract Tracking Data using the ALREADY BLURRED pyramid
141
+ // We need 2 levels closest to 256 and 128
142
+ const trackingImageList = [];
143
+ // Octave 0 is Original blured. Octave 1 is 0.5x. Octave 2 is 0.25x.
144
+ // We'll pick the best ones.
145
+ const targetSizes = [256, 128];
146
+ for (const targetSize of targetSizes) {
147
+ let bestLevel = 0;
148
+ let minDiff = Math.abs(Math.min(targetImage.width, targetImage.height) - targetSize);
149
+ for (let l = 1; l < pyramid.length; l++) {
150
+ const img = pyramid[l][0];
151
+ const diff = Math.abs(Math.min(img.width, img.height) - targetSize);
152
+ if (diff < minDiff) {
153
+ minDiff = diff;
154
+ bestLevel = l;
155
+ }
156
+ }
157
+ const levelImg = pyramid[bestLevel][0];
158
+ trackingImageList.push({
159
+ data: levelImg.data,
160
+ width: levelImg.width,
161
+ height: levelImg.height,
162
+ scale: levelImg.width / targetImage.width
163
+ });
164
+ }
165
+ const trackingData = extractTrackingFeatures(trackingImageList, () => { });
166
+ parentPort.postMessage({ type: 'progress', percent: 60 });
167
+ // 3. Build Keyframes for Matching
168
+ const scalesMap = new Map();
169
+ for (const p of featurePoints) {
170
+ const s = p.scale;
171
+ let list = scalesMap.get(s);
172
+ if (!list) {
173
+ list = [];
174
+ scalesMap.set(s, list);
175
+ }
176
+ list.push({ ...p, x: p.x / s, y: p.y / s, scale: 1.0 });
177
+ }
178
+ const keyframes = [];
179
+ const sortedScales = Array.from(scalesMap.keys()).sort((a, b) => a - b);
180
+ for (const s of sortedScales) {
181
+ const ps = scalesMap.get(s);
182
+ const sortedPs = sortPoints(ps);
183
+ const maximaPoints = sortedPs.filter((p) => p.maxima);
184
+ const minimaPoints = sortedPs.filter((p) => !p.maxima);
185
+ const maximaPointsCluster = hierarchicalClusteringBuild({ points: maximaPoints });
186
+ const minimaPointsCluster = hierarchicalClusteringBuild({ points: minimaPoints });
187
+ keyframes.push({
188
+ maximaPoints,
189
+ minimaPoints,
190
+ maximaPointsCluster,
191
+ minimaPointsCluster,
192
+ width: Math.round(targetImage.width / s),
193
+ height: Math.round(targetImage.height / s),
194
+ scale: 1.0 / s,
195
+ });
196
+ }
197
+ parentPort.postMessage({
198
+ type: 'compileDone', // Reusing message type for compatibility with WorkerPool
199
+ matchingData: keyframes,
200
+ trackingData: trackingData
201
+ });
202
+ }
203
+ catch (error) {
204
+ parentPort.postMessage({
205
+ type: 'error',
206
+ error: error.message + '\n' + error.stack
81
207
  });
82
208
  }
83
209
  }
@@ -1,62 +1,42 @@
1
1
  /**
2
- * Compilador offline optimizado sin TensorFlow
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.
3
7
  */
4
- export class OfflineCompiler {
8
+ import { WorkerPool } from "./utils/worker-pool.js";
9
+ export declare class OfflineCompiler {
5
10
  data: any;
6
11
  workerPool: WorkerPool | null;
12
+ constructor();
7
13
  _initNodeWorkers(): Promise<void>;
8
- /**
9
- * Compila una lista de imágenes objetivo
10
- * @param {Array} images - Lista de imágenes {width, height, data}
11
- * @param {Function} progressCallback - Callback de progreso (0-100)
12
- * @returns {Promise<Array>} Datos compilados
13
- */
14
- compileImageTargets(images: any[], progressCallback: Function): Promise<any[]>;
15
- _compileMatch(targetImages: any, progressCallback: any): Promise<any[]>;
16
- _compileTrack(targetImages: any, progressCallback: any): Promise<any[]>;
14
+ compileImageTargets(images: any[], progressCallback: (p: number) => void): Promise<any>;
15
+ _compileTarget(targetImages: any[], progressCallback: (p: number) => void): Promise<any[]>;
16
+ _compileMatch(targetImages: any[], progressCallback: (p: number) => void): Promise<any[]>;
17
+ _compileTrack(targetImages: any[], progressCallback: (p: number) => void): Promise<any[]>;
17
18
  compileTrack({ progressCallback, targetImages, basePercent }: {
18
- progressCallback: any;
19
- targetImages: any;
20
- basePercent?: number | undefined;
19
+ progressCallback: (p: number) => void;
20
+ targetImages: any[];
21
+ basePercent?: number;
21
22
  }): Promise<any[]>;
22
23
  compileMatch({ progressCallback, targetImages, basePercent }: {
23
- progressCallback: any;
24
- targetImages: any;
25
- basePercent?: number | undefined;
24
+ progressCallback: (p: number) => void;
25
+ targetImages: any[];
26
+ basePercent?: number;
26
27
  }): Promise<any[]>;
27
- exportData(): any;
28
- _getMorton(x: any, y: any): number;
29
- _packKeyframe(kf: any): {
30
- w: any;
31
- h: any;
32
- s: any;
33
- max: {
34
- x: Uint16Array<any>;
35
- y: Uint16Array<any>;
36
- a: Int16Array<any>;
37
- s: Uint8Array<any>;
38
- d: Uint32Array<ArrayBuffer>;
39
- t: any[];
40
- };
41
- min: {
42
- x: Uint16Array<any>;
43
- y: Uint16Array<any>;
44
- a: Int16Array<any>;
45
- s: Uint8Array<any>;
46
- d: Uint32Array<ArrayBuffer>;
47
- t: any[];
48
- };
49
- };
50
- _columnarize(points: any, tree: any, width: any, height: any): {
51
- x: Uint16Array<any>;
52
- y: Uint16Array<any>;
53
- a: Int16Array<any>;
54
- s: Uint8Array<any>;
28
+ exportData(): Uint8Array<ArrayBuffer>;
29
+ _getMorton(x: number, y: number): number;
30
+ _columnarize(points: any[], tree: any, width: number, height: number): {
31
+ x: Uint16Array<ArrayBuffer>;
32
+ y: Uint16Array<ArrayBuffer>;
33
+ a: Int16Array<ArrayBuffer>;
34
+ s: Uint8Array<ArrayBuffer>;
55
35
  d: Uint32Array<ArrayBuffer>;
56
- t: any[];
36
+ t: any;
57
37
  };
58
- _compactTree(node: any): any[];
59
- importData(buffer: any): never[] | {
38
+ _compactTree(node: any): any;
39
+ importData(buffer: ArrayBuffer | Uint8Array): {
60
40
  version: any;
61
41
  dataList: any;
62
42
  };
@@ -79,50 +59,21 @@ export class OfflineCompiler {
79
59
  descriptors: any;
80
60
  }[];
81
61
  maximaPointsCluster: {
82
- rootNode: {
83
- leaf: boolean;
84
- centerPointIndex: any;
85
- pointIndexes: any;
86
- children?: undefined;
87
- } | {
88
- leaf: boolean;
89
- centerPointIndex: any;
90
- children: any;
91
- pointIndexes?: undefined;
92
- };
62
+ rootNode: any;
93
63
  };
94
64
  minimaPointsCluster: {
95
- rootNode: {
96
- leaf: boolean;
97
- centerPointIndex: any;
98
- pointIndexes: any;
99
- children?: undefined;
100
- } | {
101
- leaf: boolean;
102
- centerPointIndex: any;
103
- children: any;
104
- pointIndexes?: undefined;
105
- };
65
+ rootNode: any;
106
66
  };
107
67
  };
108
- _decolumnarize(col: any, width: any, height: any): {
68
+ _decolumnarize(col: any, width: number, height: number): {
109
69
  x: number;
110
70
  y: number;
111
71
  angle: any;
112
72
  scale: any;
113
73
  descriptors: any;
114
74
  }[];
115
- _expandTree(node: any): {
116
- leaf: boolean;
117
- centerPointIndex: any;
118
- pointIndexes: any;
119
- children?: undefined;
120
- } | {
121
- leaf: boolean;
122
- centerPointIndex: any;
123
- children: any;
124
- pointIndexes?: undefined;
125
- };
75
+ _expandTree(node: any): any;
126
76
  destroy(): Promise<void>;
77
+ _pack4Bit(data: Uint8Array): Uint8Array<ArrayBufferLike>;
78
+ _unpack4Bit(packed: Uint8Array, width: number, height: number): Uint8Array<ArrayBuffer>;
127
79
  }
128
- import { WorkerPool } from "./utils/worker-pool.js";