@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.
- package/README.md +47 -45
- package/dist/compiler/aframe.js +0 -3
- package/dist/compiler/compiler-base.d.ts +3 -7
- package/dist/compiler/compiler-base.js +28 -14
- package/dist/compiler/compiler.js +1 -1
- package/dist/compiler/compiler.worker.js +1 -1
- package/dist/compiler/controller.js +4 -5
- package/dist/compiler/controller.worker.js +0 -2
- package/dist/compiler/detector/crop-detector.js +0 -2
- package/dist/compiler/detector/detector-lite.d.ts +73 -0
- package/dist/compiler/detector/detector-lite.js +430 -0
- package/dist/compiler/detector/detector.js +236 -243
- package/dist/compiler/detector/kernels/cpu/binomialFilter.js +0 -1
- package/dist/compiler/detector/kernels/cpu/computeLocalization.js +0 -4
- package/dist/compiler/detector/kernels/cpu/computeOrientationHistograms.js +0 -18
- package/dist/compiler/detector/kernels/cpu/fakeShader.js +1 -1
- package/dist/compiler/detector/kernels/cpu/prune.d.ts +7 -1
- package/dist/compiler/detector/kernels/cpu/prune.js +1 -42
- package/dist/compiler/detector/kernels/webgl/upsampleBilinear.js +2 -2
- package/dist/compiler/estimation/refine-estimate.js +0 -1
- package/dist/compiler/estimation/utils.d.ts +1 -1
- package/dist/compiler/estimation/utils.js +1 -14
- package/dist/compiler/image-list.js +4 -4
- package/dist/compiler/input-loader.js +2 -2
- package/dist/compiler/matching/hamming-distance.js +13 -13
- package/dist/compiler/matching/hierarchical-clustering.js +1 -1
- package/dist/compiler/matching/matching.d.ts +20 -4
- package/dist/compiler/matching/matching.js +67 -41
- package/dist/compiler/matching/ransacHomography.js +1 -2
- package/dist/compiler/node-worker.d.ts +1 -0
- package/dist/compiler/node-worker.js +84 -0
- package/dist/compiler/offline-compiler.d.ts +171 -6
- package/dist/compiler/offline-compiler.js +303 -421
- package/dist/compiler/tensorflow-setup.js +27 -1
- package/dist/compiler/three.js +3 -5
- package/dist/compiler/tracker/extract.d.ts +1 -0
- package/dist/compiler/tracker/extract.js +200 -244
- package/dist/compiler/tracker/tracker.d.ts +1 -1
- package/dist/compiler/tracker/tracker.js +13 -18
- package/dist/compiler/utils/cumsum.d.ts +4 -2
- package/dist/compiler/utils/cumsum.js +17 -19
- package/dist/compiler/utils/gpu-compute.d.ts +57 -0
- package/dist/compiler/utils/gpu-compute.js +262 -0
- package/dist/compiler/utils/images.d.ts +4 -4
- package/dist/compiler/utils/images.js +67 -53
- package/dist/compiler/utils/worker-pool.d.ts +14 -0
- package/dist/compiler/utils/worker-pool.js +84 -0
- package/package.json +11 -13
- package/src/compiler/aframe.js +2 -4
- package/src/compiler/compiler-base.js +29 -14
- package/src/compiler/compiler.js +1 -1
- package/src/compiler/compiler.worker.js +1 -1
- package/src/compiler/controller.js +4 -5
- package/src/compiler/controller.worker.js +0 -2
- package/src/compiler/detector/crop-detector.js +0 -2
- package/src/compiler/detector/detector-lite.js +494 -0
- package/src/compiler/detector/detector.js +1052 -1063
- package/src/compiler/detector/kernels/cpu/binomialFilter.js +0 -1
- package/src/compiler/detector/kernels/cpu/computeLocalization.js +0 -4
- package/src/compiler/detector/kernels/cpu/computeOrientationHistograms.js +0 -17
- package/src/compiler/detector/kernels/cpu/fakeShader.js +1 -1
- package/src/compiler/detector/kernels/cpu/prune.js +1 -37
- package/src/compiler/detector/kernels/webgl/upsampleBilinear.js +2 -2
- package/src/compiler/estimation/refine-estimate.js +0 -1
- package/src/compiler/estimation/utils.js +9 -24
- package/src/compiler/image-list.js +4 -4
- package/src/compiler/input-loader.js +2 -2
- package/src/compiler/matching/hamming-distance.js +11 -15
- package/src/compiler/matching/hierarchical-clustering.js +1 -1
- package/src/compiler/matching/matching.js +72 -42
- package/src/compiler/matching/ransacHomography.js +0 -2
- package/src/compiler/node-worker.js +93 -0
- package/src/compiler/offline-compiler.js +339 -504
- package/src/compiler/tensorflow-setup.js +29 -1
- package/src/compiler/three.js +3 -5
- package/src/compiler/tracker/extract.js +211 -267
- package/src/compiler/tracker/tracker.js +13 -22
- package/src/compiler/utils/cumsum.js +17 -19
- package/src/compiler/utils/gpu-compute.js +303 -0
- package/src/compiler/utils/images.js +84 -53
- package/src/compiler/utils/worker-pool.js +89 -0
- package/src/compiler/estimation/esimate-experiment.js +0 -316
- package/src/compiler/estimation/refine-estimate-experiment.js +0 -512
|
@@ -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, (
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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().
|
|
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().
|
|
85
|
+
return tf.engine().makeTensor(outInfo.dataId, outInfo.shape, outInfo.dtype);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
export { InputLoader };
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
d +=
|
|
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 =
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
18
|
-
if (
|
|
17
|
+
const col = querypoint.maxima ? keyframe.max : keyframe.min;
|
|
18
|
+
if (!col || col.x.length === 0)
|
|
19
19
|
continue;
|
|
20
|
-
const rootNode =
|
|
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
|
|
28
|
-
_query({
|
|
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
|
|
34
|
-
|
|
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 =
|
|
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({
|
|
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.
|
|
56
|
-
keyheight: keyframe.
|
|
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
|
-
//
|
|
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
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
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
|
|
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({
|
|
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.
|
|
119
|
-
keyheight: keyframe.
|
|
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,
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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 <
|
|
153
|
-
const childNode =
|
|
154
|
-
const
|
|
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:
|
|
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 <
|
|
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 <
|
|
191
|
+
for (let i = 0; i < childrenOrIndices.length; i++) {
|
|
166
192
|
if (distances[i] !== minD) {
|
|
167
|
-
queue.push({ node:
|
|
193
|
+
queue.push({ node: childrenOrIndices[i], d: distances[i] });
|
|
168
194
|
}
|
|
169
195
|
}
|
|
170
|
-
for (let i = 0; i <
|
|
196
|
+
for (let i = 0; i < childrenOrIndices.length; i++) {
|
|
171
197
|
if (distances[i] === minD) {
|
|
172
|
-
_query({ node:
|
|
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
|
|
202
|
+
const { node } = queue.pop();
|
|
177
203
|
numPop += 1;
|
|
178
|
-
_query({ node,
|
|
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,
|
|
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
|
+
});
|