@srsergio/taptapp-ar 1.0.2 → 1.0.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 (83) hide show
  1. package/README.md +47 -45
  2. package/dist/compiler/aframe.js +0 -3
  3. package/dist/compiler/compiler-base.d.ts +3 -7
  4. package/dist/compiler/compiler-base.js +28 -14
  5. package/dist/compiler/compiler.js +1 -1
  6. package/dist/compiler/compiler.worker.js +1 -1
  7. package/dist/compiler/controller.js +4 -5
  8. package/dist/compiler/controller.worker.js +0 -2
  9. package/dist/compiler/detector/crop-detector.js +0 -2
  10. package/dist/compiler/detector/detector-lite.d.ts +73 -0
  11. package/dist/compiler/detector/detector-lite.js +430 -0
  12. package/dist/compiler/detector/detector.js +236 -243
  13. package/dist/compiler/detector/kernels/cpu/binomialFilter.js +0 -1
  14. package/dist/compiler/detector/kernels/cpu/computeLocalization.js +0 -4
  15. package/dist/compiler/detector/kernels/cpu/computeOrientationHistograms.js +0 -18
  16. package/dist/compiler/detector/kernels/cpu/fakeShader.js +1 -1
  17. package/dist/compiler/detector/kernels/cpu/prune.d.ts +7 -1
  18. package/dist/compiler/detector/kernels/cpu/prune.js +1 -42
  19. package/dist/compiler/detector/kernels/webgl/upsampleBilinear.js +2 -2
  20. package/dist/compiler/estimation/refine-estimate.js +0 -1
  21. package/dist/compiler/estimation/utils.d.ts +1 -1
  22. package/dist/compiler/estimation/utils.js +1 -14
  23. package/dist/compiler/image-list.js +4 -4
  24. package/dist/compiler/input-loader.js +2 -2
  25. package/dist/compiler/matching/hamming-distance.js +13 -13
  26. package/dist/compiler/matching/hierarchical-clustering.js +1 -1
  27. package/dist/compiler/matching/matching.d.ts +20 -4
  28. package/dist/compiler/matching/matching.js +67 -41
  29. package/dist/compiler/matching/ransacHomography.js +1 -2
  30. package/dist/compiler/node-worker.d.ts +1 -0
  31. package/dist/compiler/node-worker.js +84 -0
  32. package/dist/compiler/offline-compiler.d.ts +171 -6
  33. package/dist/compiler/offline-compiler.js +303 -421
  34. package/dist/compiler/tensorflow-setup.js +27 -1
  35. package/dist/compiler/three.js +3 -5
  36. package/dist/compiler/tracker/extract.d.ts +1 -0
  37. package/dist/compiler/tracker/extract.js +200 -244
  38. package/dist/compiler/tracker/tracker.d.ts +1 -1
  39. package/dist/compiler/tracker/tracker.js +13 -18
  40. package/dist/compiler/utils/cumsum.d.ts +4 -2
  41. package/dist/compiler/utils/cumsum.js +17 -19
  42. package/dist/compiler/utils/gpu-compute.d.ts +57 -0
  43. package/dist/compiler/utils/gpu-compute.js +262 -0
  44. package/dist/compiler/utils/images.d.ts +4 -4
  45. package/dist/compiler/utils/images.js +67 -53
  46. package/dist/compiler/utils/worker-pool.d.ts +14 -0
  47. package/dist/compiler/utils/worker-pool.js +84 -0
  48. package/package.json +11 -13
  49. package/src/compiler/aframe.js +2 -4
  50. package/src/compiler/compiler-base.js +29 -14
  51. package/src/compiler/compiler.js +1 -1
  52. package/src/compiler/compiler.worker.js +1 -1
  53. package/src/compiler/controller.js +4 -5
  54. package/src/compiler/controller.worker.js +0 -2
  55. package/src/compiler/detector/crop-detector.js +0 -2
  56. package/src/compiler/detector/detector-lite.js +494 -0
  57. package/src/compiler/detector/detector.js +1052 -1063
  58. package/src/compiler/detector/kernels/cpu/binomialFilter.js +0 -1
  59. package/src/compiler/detector/kernels/cpu/computeLocalization.js +0 -4
  60. package/src/compiler/detector/kernels/cpu/computeOrientationHistograms.js +0 -17
  61. package/src/compiler/detector/kernels/cpu/fakeShader.js +1 -1
  62. package/src/compiler/detector/kernels/cpu/prune.js +1 -37
  63. package/src/compiler/detector/kernels/webgl/upsampleBilinear.js +2 -2
  64. package/src/compiler/estimation/refine-estimate.js +0 -1
  65. package/src/compiler/estimation/utils.js +9 -24
  66. package/src/compiler/image-list.js +4 -4
  67. package/src/compiler/input-loader.js +2 -2
  68. package/src/compiler/matching/hamming-distance.js +11 -15
  69. package/src/compiler/matching/hierarchical-clustering.js +1 -1
  70. package/src/compiler/matching/matching.js +72 -42
  71. package/src/compiler/matching/ransacHomography.js +0 -2
  72. package/src/compiler/node-worker.js +93 -0
  73. package/src/compiler/offline-compiler.js +339 -504
  74. package/src/compiler/tensorflow-setup.js +29 -1
  75. package/src/compiler/three.js +3 -5
  76. package/src/compiler/tracker/extract.js +211 -267
  77. package/src/compiler/tracker/tracker.js +13 -22
  78. package/src/compiler/utils/cumsum.js +17 -19
  79. package/src/compiler/utils/gpu-compute.js +303 -0
  80. package/src/compiler/utils/images.js +84 -53
  81. package/src/compiler/utils/worker-pool.js +89 -0
  82. package/src/compiler/estimation/esimate-experiment.js +0 -316
  83. package/src/compiler/estimation/refine-estimate-experiment.js +0 -512
@@ -1,7 +1,6 @@
1
1
  import * as FakeShader from "./fakeShader.js";
2
2
  function GetKernels(image) {
3
3
  const imageWidth = image.shape[1];
4
- const key = "w" + imageWidth;
5
4
  const imageHeight = image.shape[0];
6
5
  const kernel1 = {
7
6
  variableNames: ["p"],
@@ -33,10 +33,6 @@ function GetProgram(numDogPyramidImages, extremasListLength) {
33
33
  //}
34
34
  return program;
35
35
  }
36
- const int = Math.trunc;
37
- function clamp(n, min, max) {
38
- return Math.min(Math.max(min, n), max - 1);
39
- }
40
36
  export const computeLocalization = (args) => {
41
37
  /** @type {import('@tensorflow/tfjs').TensorInfo} */
42
38
  const { prunedExtremasList, dogPyramidImagesT } = args.inputs;
@@ -1,23 +1,11 @@
1
1
  import * as FakeShader from "./fakeShader.js";
2
2
  const oneOver2PI = 0.159154943091895;
3
3
  const ORIENTATION_NUM_BINS = 36;
4
- const cache = {};
5
4
  function GetPrograms(prunedExtremasT, radialPropertiesT, pyramidImagesLength) {
6
- const key = `${pyramidImagesLength}|${prunedExtremasT.shape[0]}|${radialPropertiesT.shape[0]}`;
7
- //if (!cache.hasOwnProperty(key)) {
8
5
  const imageVariableNames = [];
9
6
  for (let i = 1; i < pyramidImagesLength; i++) {
10
7
  imageVariableNames.push("image" + i);
11
8
  }
12
- /* let kernel1SubCodes = `float getPixel(int octave, int y, int x) {`;
13
- for (let i = 1; i < pyramidImagesLength; i++) {
14
- kernel1SubCodes += `
15
- if (octave == ${i}) {
16
- return getImage${i}(y, x);
17
- }
18
- `;
19
- }
20
- kernel1SubCodes += `}`; */
21
9
  const kernel1 = {
22
10
  variableNames: [...imageVariableNames, "extrema", "radial"],
23
11
  outputShape: [prunedExtremasT.shape[0], radialPropertiesT.shape[0], 2], // last dimension: [fbin, magnitude]
@@ -31,12 +19,6 @@ function GetPrograms(prunedExtremasT, radialPropertiesT, pyramidImagesLength) {
31
19
  }
32
20
  return this[k](y, x);
33
21
  };
34
- /** replicated undefined behavior like you have on OpenGL */
35
- function atan(x, y) {
36
- if (x == 0 && y == 0)
37
- return 1.57;
38
- return Math.atan2(x, y);
39
- }
40
22
  //void main() {
41
23
  const coords = this.getOutputCoords();
42
24
  const featureIndex = coords[0];
@@ -36,7 +36,7 @@ function runCode(backend, kernel, inputs, dtype) {
36
36
  //const temp = new Matrix();
37
37
  //console.log("Creating output shape:",kernel.outputShape);
38
38
  const temp = zeros(kernel.outputShape); //reshape([0,0,0],kernel.outputShape);
39
- const output = map(temp, (value, index, matrix) => {
39
+ const output = map(temp, (_value, index) => {
40
40
  tempData.getOutputCoords = () => {
41
41
  return index;
42
42
  };
@@ -1 +1,7 @@
1
- export {};
1
+ export namespace pruneConfig {
2
+ export let kernelName: string;
3
+ export let backendName: string;
4
+ export { prune as kernelFunc };
5
+ }
6
+ export function prune(args: any): any;
7
+ export function pruneImpl(vals: any, width: any, height: any): Float32Array<ArrayBuffer>;
@@ -1,40 +1,3 @@
1
- import * as FakeShader from "./fakeShader.js";
2
- /*
3
- const kernel = {
4
- variableNames: ['extrema'],
5
- outputShape: [Math.floor(extremaHeight/2), Math.floor(extremaWidth/2)],
6
- userCode: `
7
- void main() {
8
- ivec2 coords = getOutputCoords();
9
- int y = coords[0] * 2;
10
- int x = coords[1] * 2;
11
-
12
- float location = 0.0;
13
- float values = getExtrema(y, x);
14
-
15
- if (getExtrema(y+1, x) != 0.0) {
16
- location = 1.0;
17
- values = getExtrema(y+1, x);
18
- }
19
- else if (getExtrema(y, x+1) != 0.0) {
20
- location = 2.0;
21
- values = getExtrema(y, x+1);
22
- }
23
- else if (getExtrema(y+1, x+1) != 0.0) {
24
- location = 3.0;
25
- values = getExtrema(y+1, x+1);
26
- }
27
-
28
- if (values < 0.0) {
29
- setOutput(location * -1000.0 + values);
30
- } else {
31
- setOutput(location * 1000.0 + values);
32
- }
33
- }
34
- `
35
- }
36
-
37
- */
38
1
  function clamp(n, min, max) {
39
2
  return Math.min(Math.max(min, n), max - 1);
40
3
  }
@@ -96,8 +59,4 @@ const pruneConfig = {
96
59
  backendName: "cpu",
97
60
  kernelFunc: prune, // as {} as KernelFunc,
98
61
  };
99
- module.exports = {
100
- pruneConfig,
101
- prune,
102
- pruneImpl,
103
- };
62
+ export { pruneConfig, prune, pruneImpl, };
@@ -1,6 +1,6 @@
1
1
  import { MathBackendWebGL } from "@tensorflow/tfjs-backend-webgl";
2
2
  const cache = {};
3
- function GetProgram(image, targetImage) {
3
+ function GetProgram(targetImage) {
4
4
  const targetImageWidth = targetImage.shape[1];
5
5
  const targetImageHeight = targetImage.shape[0];
6
6
  const kernelKey = "w" + targetImageWidth + "h" + targetImageHeight;
@@ -45,7 +45,7 @@ export const upsampleBilinear = (args) => {
45
45
  const { image, targetImage } = args.inputs;
46
46
  /** @type {MathBackendWebGL} */
47
47
  const backend = args.backend;
48
- const program = GetProgram(image, targetImage);
48
+ const program = GetProgram(targetImage);
49
49
  return backend.runWebGLProgram(program, [image], image.dtype);
50
50
  };
51
51
  export const upsampleBilinearConfig = {
@@ -5,7 +5,6 @@ const K2_FACTOR = 4.0; // Question: should it be relative to the size of the scr
5
5
  const ICP_MAX_LOOP = 10;
6
6
  const ICP_BREAK_LOOP_ERROR_THRESH = 0.1;
7
7
  const ICP_BREAK_LOOP_ERROR_RATIO_THRESH = 0.99;
8
- const ICP_BREAK_LOOP_ERROR_THRESH2 = 4.0;
9
8
  // some temporary/intermediate variables used later. Declare them beforehand to reduce new object allocations
10
9
  let mat = [[], [], []];
11
10
  let J_U_Xc = [[], []]; // 2x3
@@ -1,5 +1,5 @@
1
1
  export function buildModelViewProjectionTransform(projectionTransform: any, modelViewTransform: any): any[][];
2
- export function applyModelViewProjectionTransform(modelViewProjectionTransform: any, x: any, y: any, z: any): {
2
+ export function applyModelViewProjectionTransform(modelViewProjectionTransform: any, x: any, y: any, _z: any): {
3
3
  x: any;
4
4
  y: any;
5
5
  z: any;
@@ -46,7 +46,7 @@ const buildModelViewProjectionTransform = (projectionTransform, modelViewTransfo
46
46
  return modelViewProjectionTransform;
47
47
  */
48
48
  };
49
- const applyModelViewProjectionTransform = (modelViewProjectionTransform, x, y, z) => {
49
+ const applyModelViewProjectionTransform = (modelViewProjectionTransform, x, y, _z) => {
50
50
  // assume z is zero
51
51
  const ux = modelViewProjectionTransform[0][0] * x +
52
52
  modelViewProjectionTransform[0][1] * y +
@@ -64,17 +64,4 @@ const computeScreenCoordiate = (modelViewProjectionTransform, x, y, z) => {
64
64
  //if( Math.abs(uz) < 0.000001 ) return null;
65
65
  return { x: ux / uz, y: uy / uz };
66
66
  };
67
- const screenToMarkerCoordinate = (modelViewProjectionTransform, sx, sy) => {
68
- const c11 = modelViewProjectionTransform[2][0] * sx - modelViewProjectionTransform[0][0];
69
- const c12 = modelViewProjectionTransform[2][1] * sx - modelViewProjectionTransform[0][1];
70
- const c21 = modelViewProjectionTransform[2][0] * sy - modelViewProjectionTransform[1][0];
71
- const c22 = modelViewProjectionTransform[2][1] * sy - modelViewProjectionTransform[1][1];
72
- const b1 = modelViewProjectionTransform[0][3] - modelViewProjectionTransform[2][3] * sx;
73
- const b2 = modelViewProjectionTransform[1][3] - modelViewProjectionTransform[2][3] * sy;
74
- const m = c11 * c22 - c12 * c21;
75
- return {
76
- x: (c22 * b1 - c12 * b2) / m,
77
- y: (c11 * b2 - c21 * b1) / m,
78
- };
79
- };
80
67
  export { buildModelViewProjectionTransform, applyModelViewProjectionTransform, computeScreenCoordiate, };
@@ -16,7 +16,9 @@ const buildImageList = (inputImage) => {
16
16
  let c = minScale;
17
17
  while (true) {
18
18
  scaleList.push(c);
19
- c *= Math.pow(2.0, 1.0 / 3.0);
19
+ // Optimization: Paso balanceado (aprox 1.5)
20
+ // Mejor cobertura que 2.0, pero mucho más ligero que 1.41 o 1.26
21
+ c *= Math.pow(2.0, 0.6);
20
22
  if (c >= 0.95) {
21
23
  c = 1;
22
24
  break;
@@ -26,8 +28,6 @@ const buildImageList = (inputImage) => {
26
28
  scaleList.reverse();
27
29
  const imageList = [];
28
30
  for (let i = 0; i < scaleList.length; i++) {
29
- const w = inputImage.width * scaleList[i];
30
- const h = inputImage.height * scaleList[i];
31
31
  imageList.push(Object.assign(resize({ image: inputImage, ratio: scaleList[i] }), { scale: scaleList[i] }));
32
32
  }
33
33
  return imageList;
@@ -42,7 +42,7 @@ const buildTrackingImageList = (inputImage) => {
42
42
  const minDimension = Math.min(inputImage.width, inputImage.height);
43
43
  const scaleList = [];
44
44
  const imageList = [];
45
- scaleList.push(256.0 / minDimension);
45
+ // Solo generamos la versión de 128px para ahorrar espacio (antes generaba 256px y 128px)
46
46
  scaleList.push(128.0 / minDimension);
47
47
  for (let i = 0; i < scaleList.length; i++) {
48
48
  imageList.push(Object.assign(resize({ image: inputImage, ratio: scaleList[i] }), { scale: scaleList[i] }));
@@ -78,11 +78,11 @@ class InputLoader {
78
78
  }
79
79
  _compileAndRun(program, inputs) {
80
80
  const outInfo = tf.backend().compileAndRun(program, inputs);
81
- return tf.engine().makeTensorFromDataId(outInfo.dataId, outInfo.shape, outInfo.dtype);
81
+ return tf.engine().makeTensor(outInfo.dataId, outInfo.shape, outInfo.dtype);
82
82
  }
83
83
  _runWebGLProgram(program, inputs, outputType) {
84
84
  const outInfo = tf.backend().runWebGLProgram(program, inputs, outputType);
85
- return tf.engine().makeTensorFromDataId(outInfo.dataId, outInfo.shape, outInfo.dtype);
85
+ return tf.engine().makeTensor(outInfo.dataId, outInfo.shape, outInfo.dtype);
86
86
  }
87
87
  }
88
88
  export { InputLoader };
@@ -1,20 +1,20 @@
1
- // Fast computation on number of bit sets
2
- // Ref: https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
1
+ // Precomputed bit count lookup table for Uint8Array (Much faster than bit manipulation)
2
+ const BIT_COUNT_8 = new Uint8Array(256);
3
+ for (let i = 0; i < 256; i++) {
4
+ let c = 0, n = i;
5
+ while (n > 0) {
6
+ n &= (n - 1);
7
+ c++;
8
+ }
9
+ BIT_COUNT_8[i] = c;
10
+ }
3
11
  const compute = (options) => {
4
12
  const { v1, v2 } = options;
5
13
  let d = 0;
6
- for (let i = 0; i < v1.length; i++) {
7
- let x = (v1[i] ^ v2[i]) >>> 0;
8
- d += bitCount(x);
14
+ const len = v1.length;
15
+ for (let i = 0; i < len; i++) {
16
+ d += BIT_COUNT_8[v1[i] ^ v2[i]];
9
17
  }
10
18
  return d;
11
19
  };
12
- const bitCount = (v) => {
13
- var c = v - ((v >> 1) & 0x55555555);
14
- c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
15
- c = ((c >> 4) + c) & 0x0f0f0f0f;
16
- c = ((c >> 8) + c) & 0x00ff00ff;
17
- c = ((c >> 16) + c) & 0x0000ffff;
18
- return c;
19
- };
20
20
  export { compute };
@@ -1,7 +1,7 @@
1
1
  import { compute as hammingCompute } from "./hamming-distance.js";
2
2
  import { createRandomizer } from "../utils/randomizer.js";
3
3
  const MIN_FEATURE_PER_NODE = 16;
4
- const NUM_ASSIGNMENT_HYPOTHESES = 128;
4
+ const NUM_ASSIGNMENT_HYPOTHESES = 64;
5
5
  const NUM_CENTERS = 8;
6
6
  const _computeKMedoids = (options) => {
7
7
  const { points, pointIndexes, randomizer } = options;
@@ -8,13 +8,21 @@ export function match({ keyframe, querypoints, querywidth, queryheight, debugMod
8
8
  debugExtra: {
9
9
  matches: {
10
10
  querypoint: any;
11
- keypoint: any;
11
+ keypoint: {
12
+ x: any;
13
+ y: any;
14
+ angle: any;
15
+ };
12
16
  }[];
13
17
  houghMatches: any[];
14
18
  inlierMatches: any[];
15
19
  matches2: {
16
20
  querypoint: any;
17
- keypoint: any;
21
+ keypoint: {
22
+ x: any;
23
+ y: any;
24
+ angle: any;
25
+ };
18
26
  }[];
19
27
  houghMatches2: any[];
20
28
  inlierMatches2: any[];
@@ -27,13 +35,21 @@ export function match({ keyframe, querypoints, querywidth, queryheight, debugMod
27
35
  debugExtra: {
28
36
  matches: {
29
37
  querypoint: any;
30
- keypoint: any;
38
+ keypoint: {
39
+ x: any;
40
+ y: any;
41
+ angle: any;
42
+ };
31
43
  }[];
32
44
  houghMatches: any[];
33
45
  inlierMatches: any[];
34
46
  matches2: {
35
47
  querypoint: any;
36
- keypoint: any;
48
+ keypoint: {
49
+ x: any;
50
+ y: any;
51
+ angle: any;
52
+ };
37
53
  }[];
38
54
  houghMatches2: any[];
39
55
  inlierMatches2: any[];
@@ -14,28 +14,35 @@ const match = ({ keyframe, querypoints, querywidth, queryheight, debugMode }) =>
14
14
  const matches = [];
15
15
  for (let j = 0; j < querypoints.length; j++) {
16
16
  const querypoint = querypoints[j];
17
- const keypoints = querypoint.maxima ? keyframe.maximaPoints : keyframe.minimaPoints;
18
- if (keypoints.length === 0)
17
+ const col = querypoint.maxima ? keyframe.max : keyframe.min;
18
+ if (!col || col.x.length === 0)
19
19
  continue;
20
- const rootNode = querypoint.maxima
21
- ? keyframe.maximaPointsCluster.rootNode
22
- : keyframe.minimaPointsCluster.rootNode;
20
+ const rootNode = col.t;
23
21
  const keypointIndexes = [];
24
22
  const queue = new TinyQueue([], (a1, a2) => {
25
23
  return a1.d - a2.d;
26
24
  });
27
- // query all potential keypoints
28
- _query({ node: rootNode, keypoints, querypoint, queue, keypointIndexes, numPop: 0 });
25
+ // query potential candidates from the columnar tree
26
+ _query({
27
+ node: rootNode,
28
+ descriptors: col.d,
29
+ querypoint,
30
+ queue,
31
+ keypointIndexes,
32
+ numPop: 0
33
+ });
29
34
  let bestIndex = -1;
30
35
  let bestD1 = Number.MAX_SAFE_INTEGER;
31
36
  let bestD2 = Number.MAX_SAFE_INTEGER;
32
37
  for (let k = 0; k < keypointIndexes.length; k++) {
33
- const keypoint = keypoints[keypointIndexes[k]];
34
- const d = hammingCompute({ v1: keypoint.descriptors, v2: querypoint.descriptors });
38
+ const idx = keypointIndexes[k];
39
+ // Access descriptor directly from binary buffer (Zero-copy)
40
+ const keypointDescriptor = col.d.subarray(idx * 84, (idx + 1) * 84);
41
+ const d = hammingCompute({ v1: keypointDescriptor, v2: querypoint.descriptors });
35
42
  if (d < bestD1) {
36
43
  bestD2 = bestD1;
37
44
  bestD1 = d;
38
- bestIndex = keypointIndexes[k];
45
+ bestIndex = idx;
39
46
  }
40
47
  else if (d < bestD2) {
41
48
  bestD2 = d;
@@ -43,7 +50,14 @@ const match = ({ keyframe, querypoints, querywidth, queryheight, debugMode }) =>
43
50
  }
44
51
  if (bestIndex !== -1 &&
45
52
  (bestD2 === Number.MAX_SAFE_INTEGER || (1.0 * bestD1) / bestD2 < HAMMING_THRESHOLD)) {
46
- matches.push({ querypoint, keypoint: keypoints[bestIndex] });
53
+ matches.push({
54
+ querypoint,
55
+ keypoint: {
56
+ x: col.x[bestIndex],
57
+ y: col.y[bestIndex],
58
+ angle: col.a[bestIndex]
59
+ }
60
+ });
47
61
  }
48
62
  }
49
63
  if (debugMode) {
@@ -52,8 +66,8 @@ const match = ({ keyframe, querypoints, querywidth, queryheight, debugMode }) =>
52
66
  if (matches.length < MIN_NUM_INLIERS)
53
67
  return { debugExtra };
54
68
  const houghMatches = computeHoughMatches({
55
- keywidth: keyframe.width,
56
- keyheight: keyframe.height,
69
+ keywidth: keyframe.w, // Protocol V3 uses .w, .h
70
+ keyheight: keyframe.h,
57
71
  querywidth,
58
72
  queryheight,
59
73
  matches,
@@ -64,7 +78,7 @@ const match = ({ keyframe, querypoints, querywidth, queryheight, debugMode }) =>
64
78
  const H = computeHomography({
65
79
  srcPoints: houghMatches.map((m) => [m.keypoint.x, m.keypoint.y]),
66
80
  dstPoints: houghMatches.map((m) => [m.querypoint.x, m.querypoint.y]),
67
- keyframe,
81
+ keyframe: { width: keyframe.w, height: keyframe.h },
68
82
  });
69
83
  if (H === null)
70
84
  return { debugExtra };
@@ -78,7 +92,7 @@ const match = ({ keyframe, querypoints, querywidth, queryheight, debugMode }) =>
78
92
  }
79
93
  if (inlierMatches.length < MIN_NUM_INLIERS)
80
94
  return { debugExtra };
81
- // do another loop of match using the homography
95
+ // Second pass with homography guided matching
82
96
  const HInv = matrixInverse33(H, 0.00001);
83
97
  const dThreshold2 = 10 * 10;
84
98
  const matches2 = [];
@@ -88,15 +102,17 @@ const match = ({ keyframe, querypoints, querywidth, queryheight, debugMode }) =>
88
102
  let bestIndex = -1;
89
103
  let bestD1 = Number.MAX_SAFE_INTEGER;
90
104
  let bestD2 = Number.MAX_SAFE_INTEGER;
91
- const keypoints = querypoint.maxima ? keyframe.maximaPoints : keyframe.minimaPoints;
92
- for (let k = 0; k < keypoints.length; k++) {
93
- const keypoint = keypoints[k];
94
- // check distance threshold
95
- const d2 = (keypoint.x - mapquerypoint[0]) * (keypoint.x - mapquerypoint[0]) +
96
- (keypoint.y - mapquerypoint[1]) * (keypoint.y - mapquerypoint[1]);
105
+ const col = querypoint.maxima ? keyframe.max : keyframe.min;
106
+ if (!col)
107
+ continue;
108
+ for (let k = 0; k < col.x.length; k++) {
109
+ const dx = col.x[k] - mapquerypoint[0];
110
+ const dy = col.y[k] - mapquerypoint[1];
111
+ const d2 = dx * dx + dy * dy;
97
112
  if (d2 > dThreshold2)
98
113
  continue;
99
- const d = hammingCompute({ v1: keypoint.descriptors, v2: querypoint.descriptors });
114
+ const keypointDescriptor = col.d.subarray(k * 84, (k + 1) * 84);
115
+ const d = hammingCompute({ v1: keypointDescriptor, v2: querypoint.descriptors });
100
116
  if (d < bestD1) {
101
117
  bestD2 = bestD1;
102
118
  bestD1 = d;
@@ -108,15 +124,22 @@ const match = ({ keyframe, querypoints, querywidth, queryheight, debugMode }) =>
108
124
  }
109
125
  if (bestIndex !== -1 &&
110
126
  (bestD2 === Number.MAX_SAFE_INTEGER || (1.0 * bestD1) / bestD2 < HAMMING_THRESHOLD)) {
111
- matches2.push({ querypoint, keypoint: keypoints[bestIndex] });
127
+ matches2.push({
128
+ querypoint,
129
+ keypoint: {
130
+ x: col.x[bestIndex],
131
+ y: col.y[bestIndex],
132
+ angle: col.a[bestIndex]
133
+ }
134
+ });
112
135
  }
113
136
  }
114
137
  if (debugMode) {
115
138
  debugExtra.matches2 = matches2;
116
139
  }
117
140
  const houghMatches2 = computeHoughMatches({
118
- keywidth: keyframe.width,
119
- keyheight: keyframe.height,
141
+ keywidth: keyframe.w,
142
+ keyheight: keyframe.h,
120
143
  querywidth,
121
144
  queryheight,
122
145
  matches: matches2,
@@ -127,7 +150,7 @@ const match = ({ keyframe, querypoints, querywidth, queryheight, debugMode }) =>
127
150
  const H2 = computeHomography({
128
151
  srcPoints: houghMatches2.map((m) => [m.keypoint.x, m.keypoint.y]),
129
152
  dstPoints: houghMatches2.map((m) => [m.querypoint.x, m.querypoint.y]),
130
- keyframe,
153
+ keyframe: { width: keyframe.w, height: keyframe.h },
131
154
  });
132
155
  if (H2 === null)
133
156
  return { debugExtra };
@@ -141,41 +164,44 @@ const match = ({ keyframe, querypoints, querywidth, queryheight, debugMode }) =>
141
164
  }
142
165
  return { H: H2, matches: inlierMatches2, debugExtra };
143
166
  };
144
- const _query = ({ node, keypoints, querypoint, queue, keypointIndexes, numPop }) => {
145
- if (node.leaf) {
146
- for (let i = 0; i < node.pointIndexes.length; i++) {
147
- keypointIndexes.push(node.pointIndexes[i]);
167
+ const _query = ({ node, descriptors, querypoint, queue, keypointIndexes, numPop }) => {
168
+ const isLeaf = node[0] === 1;
169
+ const centerIdx = node[1];
170
+ const childrenOrIndices = node[2];
171
+ if (isLeaf) {
172
+ for (let i = 0; i < childrenOrIndices.length; i++) {
173
+ keypointIndexes.push(childrenOrIndices[i]);
148
174
  }
149
175
  return;
150
176
  }
151
177
  const distances = [];
152
- for (let i = 0; i < node.children.length; i++) {
153
- const childNode = node.children[i];
154
- const centerPointIndex = childNode.centerPointIndex;
178
+ for (let i = 0; i < childrenOrIndices.length; i++) {
179
+ const childNode = childrenOrIndices[i];
180
+ const cIdx = childNode[1];
155
181
  const d = hammingCompute({
156
- v1: keypoints[centerPointIndex].descriptors,
182
+ v1: descriptors.subarray(cIdx * 84, (cIdx + 1) * 84),
157
183
  v2: querypoint.descriptors,
158
184
  });
159
185
  distances.push(d);
160
186
  }
161
187
  let minD = Number.MAX_SAFE_INTEGER;
162
- for (let i = 0; i < node.children.length; i++) {
188
+ for (let i = 0; i < childrenOrIndices.length; i++) {
163
189
  minD = Math.min(minD, distances[i]);
164
190
  }
165
- for (let i = 0; i < node.children.length; i++) {
191
+ for (let i = 0; i < childrenOrIndices.length; i++) {
166
192
  if (distances[i] !== minD) {
167
- queue.push({ node: node.children[i], d: distances[i] });
193
+ queue.push({ node: childrenOrIndices[i], d: distances[i] });
168
194
  }
169
195
  }
170
- for (let i = 0; i < node.children.length; i++) {
196
+ for (let i = 0; i < childrenOrIndices.length; i++) {
171
197
  if (distances[i] === minD) {
172
- _query({ node: node.children[i], keypoints, querypoint, queue, keypointIndexes, numPop });
198
+ _query({ node: childrenOrIndices[i], descriptors, querypoint, queue, keypointIndexes, numPop });
173
199
  }
174
200
  }
175
201
  if (numPop < CLUSTER_MAX_POP && queue.length > 0) {
176
- const { node, d } = queue.pop();
202
+ const { node } = queue.pop();
177
203
  numPop += 1;
178
- _query({ node, keypoints, querypoint, queue, keypointIndexes, numPop });
204
+ _query({ node, descriptors, querypoint, queue, keypointIndexes, numPop });
179
205
  }
180
206
  };
181
207
  const _findInlierMatches = (options) => {
@@ -1,6 +1,5 @@
1
- import { Matrix, inverse } from "ml-matrix";
2
1
  import { createRandomizer } from "../utils/randomizer.js";
3
- import { quadrilateralConvex, matrixInverse33, smallestTriangleArea, multiplyPointHomographyInhomogenous, checkThreePointsConsistent, checkFourPointsConsistent, determinant, } from "../utils/geometry.js";
2
+ import { quadrilateralConvex, matrixInverse33, smallestTriangleArea, multiplyPointHomographyInhomogenous, checkThreePointsConsistent, checkFourPointsConsistent, } from "../utils/geometry.js";
4
3
  import { solveHomography } from "../utils/homography.js";
5
4
  const CAUCHY_SCALE = 0.01;
6
5
  const CHUNK_SIZE = 10;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Worker Node.js para compilación de imágenes AR
3
+ *
4
+ * OPTIMIZADO: Sin TensorFlow para evitar bloqueos de inicialización.
5
+ * Usa JavaScript puro para máxima velocidad.
6
+ */
7
+ import { parentPort } from 'node:worker_threads';
8
+ import { extractTrackingFeatures } from './tracker/extract-utils.js';
9
+ import { buildTrackingImageList, buildImageList } from './image-list.js';
10
+ import { DetectorLite } from './detector/detector-lite.js';
11
+ import { build as hierarchicalClusteringBuild } from './matching/hierarchical-clustering.js';
12
+ if (!parentPort) {
13
+ throw new Error('This file must be run as a worker thread.');
14
+ }
15
+ parentPort.on('message', async (msg) => {
16
+ if (msg.type === 'compile') {
17
+ const { targetImage, percentPerImage, basePercent } = msg;
18
+ try {
19
+ const imageList = buildTrackingImageList(targetImage);
20
+ const percentPerAction = percentPerImage / imageList.length;
21
+ let localPercent = 0;
22
+ const trackingData = extractTrackingFeatures(imageList, (index) => {
23
+ localPercent += percentPerAction;
24
+ parentPort.postMessage({
25
+ type: 'progress',
26
+ percent: basePercent + localPercent
27
+ });
28
+ });
29
+ parentPort.postMessage({
30
+ type: 'compileDone',
31
+ trackingData
32
+ });
33
+ }
34
+ catch (error) {
35
+ parentPort.postMessage({
36
+ type: 'error',
37
+ error: error.message
38
+ });
39
+ }
40
+ }
41
+ else if (msg.type === 'match') {
42
+ const { targetImage, percentPerImage, basePercent } = msg;
43
+ try {
44
+ const imageList = buildImageList(targetImage);
45
+ const percentPerAction = percentPerImage / imageList.length;
46
+ let localPercent = 0;
47
+ 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);
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);
55
+ const maximaPointsCluster = hierarchicalClusteringBuild({ points: maximaPoints });
56
+ const minimaPointsCluster = hierarchicalClusteringBuild({ points: minimaPoints });
57
+ keyframes.push({
58
+ maximaPoints,
59
+ minimaPoints,
60
+ maximaPointsCluster,
61
+ minimaPointsCluster,
62
+ width: image.width,
63
+ height: image.height,
64
+ scale: image.scale,
65
+ });
66
+ localPercent += percentPerAction;
67
+ parentPort.postMessage({
68
+ type: 'progress',
69
+ percent: basePercent + localPercent
70
+ });
71
+ }
72
+ parentPort.postMessage({
73
+ type: 'matchDone',
74
+ matchingData: keyframes
75
+ });
76
+ }
77
+ catch (error) {
78
+ parentPort.postMessage({
79
+ type: 'error',
80
+ error: error.message
81
+ });
82
+ }
83
+ }
84
+ });