@srsergio/taptapp-ar 1.0.71 → 1.0.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compiler/controller.js +4 -4
- package/dist/compiler/matching/matching.js +4 -5
- package/dist/compiler/offline-compiler.js +6 -7
- package/dist/compiler/simple-ar.js +1 -1
- package/dist/compiler/tracker/extract.js +5 -7
- package/dist/compiler/tracker/tracker.d.ts +1 -0
- package/dist/compiler/tracker/tracker.js +29 -3
- package/package.json +1 -1
- package/src/compiler/controller.ts +3 -3
- package/src/compiler/matching/matching.js +4 -5
- package/src/compiler/offline-compiler.ts +6 -7
- package/src/compiler/simple-ar.ts +1 -1
- package/src/compiler/tracker/extract.js +5 -7
- package/src/compiler/tracker/tracker.js +26 -3
|
@@ -18,8 +18,8 @@ const getControllerWorker = async () => {
|
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
20
|
ControllerWorker = await getControllerWorker();
|
|
21
|
-
const DEFAULT_FILTER_CUTOFF = 0
|
|
22
|
-
const DEFAULT_FILTER_BETA = 0.
|
|
21
|
+
const DEFAULT_FILTER_CUTOFF = 1.0;
|
|
22
|
+
const DEFAULT_FILTER_BETA = 0.1;
|
|
23
23
|
const DEFAULT_WARMUP_TOLERANCE = 8;
|
|
24
24
|
const DEFAULT_MISS_TOLERANCE = 2;
|
|
25
25
|
class Controller {
|
|
@@ -173,8 +173,8 @@ class Controller {
|
|
|
173
173
|
}
|
|
174
174
|
async _trackAndUpdate(inputData, lastModelViewTransform, targetIndex) {
|
|
175
175
|
const { worldCoords, screenCoords } = this.tracker.track(inputData, lastModelViewTransform, targetIndex);
|
|
176
|
-
if (worldCoords.length <
|
|
177
|
-
return null;
|
|
176
|
+
if (worldCoords.length < 10)
|
|
177
|
+
return null; // Increased from 6 to 10 for better noise rejection
|
|
178
178
|
const modelViewTransform = await this._workerTrackUpdate(lastModelViewTransform, {
|
|
179
179
|
worldCoords,
|
|
180
180
|
screenCoords,
|
|
@@ -3,11 +3,10 @@ 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 =
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const HAMMING_THRESHOLD = 0.85; // Relaxed ratio test for binary descriptors
|
|
6
|
+
const INLIER_THRESHOLD = 5.0; // Tightened from 10 to 5 for better precision
|
|
7
|
+
const MIN_NUM_INLIERS = 8; // Restored to 8
|
|
8
|
+
const CLUSTER_MAX_POP = 20;
|
|
9
|
+
const HAMMING_THRESHOLD = 0.8; // Tightened from 0.85 to 0.8 for cleaner matches
|
|
11
10
|
// match list of querpoints against pre-built list of keyframes
|
|
12
11
|
const match = ({ keyframe, querypoints, querywidth, queryheight, debugMode }) => {
|
|
13
12
|
let debugExtra = {};
|
|
@@ -32,10 +32,10 @@ export class OfflineCompiler {
|
|
|
32
32
|
const osModule = "os";
|
|
33
33
|
const workerThreadsModule = "node:worker_threads";
|
|
34
34
|
const [path, url, os, { Worker }] = await Promise.all([
|
|
35
|
-
import(pathModule),
|
|
36
|
-
import(urlModule),
|
|
37
|
-
import(osModule),
|
|
38
|
-
import(workerThreadsModule)
|
|
35
|
+
import(/* @vite-ignore */ pathModule),
|
|
36
|
+
import(/* @vite-ignore */ urlModule),
|
|
37
|
+
import(/* @vite-ignore */ osModule),
|
|
38
|
+
import(/* @vite-ignore */ workerThreadsModule)
|
|
39
39
|
]);
|
|
40
40
|
const __filename = url.fileURLToPath(import.meta.url);
|
|
41
41
|
const __dirname = path.dirname(__filename);
|
|
@@ -229,7 +229,7 @@ export class OfflineCompiler {
|
|
|
229
229
|
s: td.scale,
|
|
230
230
|
px,
|
|
231
231
|
py,
|
|
232
|
-
d:
|
|
232
|
+
d: td.data,
|
|
233
233
|
};
|
|
234
234
|
}),
|
|
235
235
|
matchingData: item.matchingData.map((kf) => ({
|
|
@@ -313,11 +313,10 @@ export class OfflineCompiler {
|
|
|
313
313
|
}
|
|
314
314
|
td.px = px;
|
|
315
315
|
td.py = py;
|
|
316
|
-
//
|
|
316
|
+
// No longer unpacking 4-bit, keeping original data
|
|
317
317
|
if (td.data && td.data.length === (td.width * td.height) / 2) {
|
|
318
318
|
td.data = this._unpack4Bit(td.data, td.width, td.height);
|
|
319
319
|
}
|
|
320
|
-
// Also handle 'd' property if it exists (msgpack mapping)
|
|
321
320
|
if (td.d && td.d.length === (td.w * td.h) / 2) {
|
|
322
321
|
td.d = this._unpack4Bit(td.d, td.w, td.h);
|
|
323
322
|
}
|
|
@@ -123,7 +123,7 @@ class SimpleAR {
|
|
|
123
123
|
}
|
|
124
124
|
this.lastMatrix = worldMatrix;
|
|
125
125
|
if (!this.filters[targetIndex]) {
|
|
126
|
-
this.filters[targetIndex] = new OneEuroFilter({ minCutOff: 0
|
|
126
|
+
this.filters[targetIndex] = new OneEuroFilter({ minCutOff: 1.0, beta: 0.1 });
|
|
127
127
|
}
|
|
128
128
|
const flatMVT = [
|
|
129
129
|
modelViewTransform[0][0], modelViewTransform[0][1], modelViewTransform[0][2], modelViewTransform[0][3],
|
|
@@ -9,7 +9,7 @@ const MAX_SIM_THRESH = 0.95;
|
|
|
9
9
|
const MAX_THRESH = 0.9;
|
|
10
10
|
const MIN_THRESH = 0.2;
|
|
11
11
|
const SD_THRESH = 8.0;
|
|
12
|
-
const OCCUPANCY_SIZE =
|
|
12
|
+
const OCCUPANCY_SIZE = 8; // Reduced from 10 to allow more density
|
|
13
13
|
// GPU mode flag - set to false to use original JS implementation
|
|
14
14
|
let useGPU = true;
|
|
15
15
|
/**
|
|
@@ -88,7 +88,7 @@ const extract = (image) => {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
// Determine dValue threshold for top 5% (aumentado de 2% para más candidatos)
|
|
91
|
-
const maxPoints = 0.
|
|
91
|
+
const maxPoints = 0.10 * width * height; // Increased to 10% for more candidates
|
|
92
92
|
let kThresh = 999;
|
|
93
93
|
let filteredCount = 0;
|
|
94
94
|
while (kThresh >= 0) {
|
|
@@ -289,16 +289,14 @@ const _getSimilarityOptimized = (options) => {
|
|
|
289
289
|
// Full calculation - Optimized with 2x2 sub-sampling for SPEED
|
|
290
290
|
let sxy = 0;
|
|
291
291
|
const p1_start = (cy - templateSize) * width + (cx - templateSize);
|
|
292
|
-
for (let j = 0; j < templateWidth; j
|
|
292
|
+
for (let j = 0; j < templateWidth; j++) {
|
|
293
293
|
const rowOffset1 = p1_start + j * width;
|
|
294
294
|
const rowOffset2 = j * templateWidth;
|
|
295
|
-
for (let i = 0; i < templateWidth; i
|
|
295
|
+
for (let i = 0; i < templateWidth; i++) {
|
|
296
296
|
sxy += imageData[rowOffset1 + i] * templateData[rowOffset2 + i];
|
|
297
297
|
}
|
|
298
298
|
}
|
|
299
|
-
|
|
300
|
-
// templateWidth is 13, steps of 2 hit 7 points per dim = 49 total points (vs 169)
|
|
301
|
-
const sampledCount = Math.ceil(templateWidth / 2) ** 2;
|
|
299
|
+
const sampledCount = templateWidth * templateWidth;
|
|
302
300
|
const totalCount = templateWidth * templateWidth;
|
|
303
301
|
sxy *= (totalCount / sampledCount);
|
|
304
302
|
// Covariance check
|
|
@@ -55,15 +55,22 @@ class Tracker {
|
|
|
55
55
|
const p1 = computeScreenCoordiate(modelViewProjectionTransform, mW, 0);
|
|
56
56
|
const screenW = Math.sqrt((p1.x - p0.x) ** 2 + (p1.y - p0.y) ** 2);
|
|
57
57
|
// Select octave whose image width is closest to screenW
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
// Select the best octave based on current estimated distance/scale
|
|
59
|
+
// Hysteresis: prevent flip-flopping between octaves
|
|
60
|
+
if (!this.lastOctaveIndex)
|
|
61
|
+
this.lastOctaveIndex = [];
|
|
62
|
+
let octaveIndex = this.lastOctaveIndex[targetIndex] !== undefined ? this.lastOctaveIndex[targetIndex] : 0;
|
|
63
|
+
let minDiff = Math.abs(this.prebuiltData[targetIndex][octaveIndex].width - screenW);
|
|
64
|
+
// Threshold to switch: only switch if another octave is much better (20% improvement)
|
|
65
|
+
const switchThreshold = 0.8;
|
|
60
66
|
for (let i = 0; i < this.prebuiltData[targetIndex].length; i++) {
|
|
61
67
|
const diff = Math.abs(this.prebuiltData[targetIndex][i].width - screenW);
|
|
62
|
-
if (diff < minDiff) {
|
|
68
|
+
if (diff < minDiff * switchThreshold) {
|
|
63
69
|
minDiff = diff;
|
|
64
70
|
octaveIndex = i;
|
|
65
71
|
}
|
|
66
72
|
}
|
|
73
|
+
this.lastOctaveIndex[targetIndex] = octaveIndex;
|
|
67
74
|
const prebuilt = this.prebuiltData[targetIndex][octaveIndex];
|
|
68
75
|
// 1. Compute Projection (Warping)
|
|
69
76
|
this._computeProjection(modelViewProjectionTransform, inputData, prebuilt);
|
|
@@ -87,6 +94,25 @@ class Tracker {
|
|
|
87
94
|
});
|
|
88
95
|
}
|
|
89
96
|
}
|
|
97
|
+
// 2.1 Spatial distribution check: Avoid getting stuck in corners/noise
|
|
98
|
+
if (screenCoords.length >= 10) {
|
|
99
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
100
|
+
for (const p of screenCoords) {
|
|
101
|
+
if (p.x < minX)
|
|
102
|
+
minX = p.x;
|
|
103
|
+
if (p.y < minY)
|
|
104
|
+
minY = p.y;
|
|
105
|
+
if (p.x > maxX)
|
|
106
|
+
maxX = p.x;
|
|
107
|
+
if (p.y > maxY)
|
|
108
|
+
maxY = p.y;
|
|
109
|
+
}
|
|
110
|
+
const detectedDiagonal = Math.sqrt((maxX - minX) ** 2 + (maxY - minY) ** 2);
|
|
111
|
+
// If the points cover too little space compared to the screen size of the marker, it's a glitch
|
|
112
|
+
if (detectedDiagonal < screenW * 0.15) {
|
|
113
|
+
return { worldCoords: [], screenCoords: [], debugExtra };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
90
116
|
if (this.debugMode) {
|
|
91
117
|
debugExtra = {
|
|
92
118
|
octaveIndex,
|
package/package.json
CHANGED
|
@@ -19,8 +19,8 @@ const getControllerWorker = async () => {
|
|
|
19
19
|
};
|
|
20
20
|
ControllerWorker = await getControllerWorker();
|
|
21
21
|
|
|
22
|
-
const DEFAULT_FILTER_CUTOFF = 0
|
|
23
|
-
const DEFAULT_FILTER_BETA = 0.
|
|
22
|
+
const DEFAULT_FILTER_CUTOFF = 1.0;
|
|
23
|
+
const DEFAULT_FILTER_BETA = 0.1;
|
|
24
24
|
const DEFAULT_WARMUP_TOLERANCE = 8;
|
|
25
25
|
const DEFAULT_MISS_TOLERANCE = 2;
|
|
26
26
|
|
|
@@ -233,7 +233,7 @@ class Controller {
|
|
|
233
233
|
lastModelViewTransform,
|
|
234
234
|
targetIndex,
|
|
235
235
|
);
|
|
236
|
-
if (worldCoords.length <
|
|
236
|
+
if (worldCoords.length < 10) return null; // Increased from 6 to 10 for better noise rejection
|
|
237
237
|
const modelViewTransform = await this._workerTrackUpdate(lastModelViewTransform, {
|
|
238
238
|
worldCoords,
|
|
239
239
|
screenCoords,
|
|
@@ -4,11 +4,10 @@ import { computeHoughMatches } from "./hough.js";
|
|
|
4
4
|
import { computeHomography } from "./ransacHomography.js";
|
|
5
5
|
import { multiplyPointHomographyInhomogenous, matrixInverse33 } from "../utils/geometry.js";
|
|
6
6
|
|
|
7
|
-
const INLIER_THRESHOLD =
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const HAMMING_THRESHOLD = 0.85; // Relaxed ratio test for binary descriptors
|
|
7
|
+
const INLIER_THRESHOLD = 5.0; // Tightened from 10 to 5 for better precision
|
|
8
|
+
const MIN_NUM_INLIERS = 8; // Restored to 8
|
|
9
|
+
const CLUSTER_MAX_POP = 20;
|
|
10
|
+
const HAMMING_THRESHOLD = 0.8; // Tightened from 0.85 to 0.8 for cleaner matches
|
|
12
11
|
|
|
13
12
|
// match list of querpoints against pre-built list of keyframes
|
|
14
13
|
const match = ({ keyframe, querypoints, querywidth, queryheight, debugMode }) => {
|
|
@@ -39,10 +39,10 @@ export class OfflineCompiler {
|
|
|
39
39
|
const workerThreadsModule = "node:worker_threads";
|
|
40
40
|
|
|
41
41
|
const [path, url, os, { Worker }] = await Promise.all([
|
|
42
|
-
import(pathModule),
|
|
43
|
-
import(urlModule),
|
|
44
|
-
import(osModule),
|
|
45
|
-
import(workerThreadsModule)
|
|
42
|
+
import(/* @vite-ignore */ pathModule),
|
|
43
|
+
import(/* @vite-ignore */ urlModule),
|
|
44
|
+
import(/* @vite-ignore */ osModule),
|
|
45
|
+
import(/* @vite-ignore */ workerThreadsModule)
|
|
46
46
|
]);
|
|
47
47
|
|
|
48
48
|
const __filename = url.fileURLToPath(import.meta.url);
|
|
@@ -274,7 +274,7 @@ export class OfflineCompiler {
|
|
|
274
274
|
px,
|
|
275
275
|
py,
|
|
276
276
|
|
|
277
|
-
d:
|
|
277
|
+
d: td.data,
|
|
278
278
|
};
|
|
279
279
|
}),
|
|
280
280
|
matchingData: item.matchingData.map((kf: any) => ({
|
|
@@ -375,11 +375,10 @@ export class OfflineCompiler {
|
|
|
375
375
|
td.px = px;
|
|
376
376
|
td.py = py;
|
|
377
377
|
|
|
378
|
-
//
|
|
378
|
+
// No longer unpacking 4-bit, keeping original data
|
|
379
379
|
if (td.data && td.data.length === (td.width * td.height) / 2) {
|
|
380
380
|
td.data = this._unpack4Bit(td.data, td.width, td.height);
|
|
381
381
|
}
|
|
382
|
-
// Also handle 'd' property if it exists (msgpack mapping)
|
|
383
382
|
if (td.d && td.d.length === (td.w * td.h) / 2) {
|
|
384
383
|
td.d = this._unpack4Bit(td.d, td.w, td.h);
|
|
385
384
|
}
|
|
@@ -163,7 +163,7 @@ class SimpleAR {
|
|
|
163
163
|
this.lastMatrix = worldMatrix;
|
|
164
164
|
|
|
165
165
|
if (!this.filters[targetIndex]) {
|
|
166
|
-
this.filters[targetIndex] = new OneEuroFilter({ minCutOff: 0
|
|
166
|
+
this.filters[targetIndex] = new OneEuroFilter({ minCutOff: 1.0, beta: 0.1 });
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
const flatMVT = [
|
|
@@ -12,7 +12,7 @@ const MAX_SIM_THRESH = 0.95;
|
|
|
12
12
|
const MAX_THRESH = 0.9;
|
|
13
13
|
const MIN_THRESH = 0.2;
|
|
14
14
|
const SD_THRESH = 8.0;
|
|
15
|
-
const OCCUPANCY_SIZE =
|
|
15
|
+
const OCCUPANCY_SIZE = 8; // Reduced from 10 to allow more density
|
|
16
16
|
|
|
17
17
|
// GPU mode flag - set to false to use original JS implementation
|
|
18
18
|
let useGPU = true;
|
|
@@ -102,7 +102,7 @@ const extract = (image) => {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
// Determine dValue threshold for top 5% (aumentado de 2% para más candidatos)
|
|
105
|
-
const maxPoints = 0.
|
|
105
|
+
const maxPoints = 0.10 * width * height; // Increased to 10% for more candidates
|
|
106
106
|
let kThresh = 999;
|
|
107
107
|
let filteredCount = 0;
|
|
108
108
|
while (kThresh >= 0) {
|
|
@@ -344,17 +344,15 @@ const _getSimilarityOptimized = (options) => {
|
|
|
344
344
|
let sxy = 0;
|
|
345
345
|
const p1_start = (cy - templateSize) * width + (cx - templateSize);
|
|
346
346
|
|
|
347
|
-
for (let j = 0; j < templateWidth; j
|
|
347
|
+
for (let j = 0; j < templateWidth; j++) {
|
|
348
348
|
const rowOffset1 = p1_start + j * width;
|
|
349
349
|
const rowOffset2 = j * templateWidth;
|
|
350
|
-
for (let i = 0; i < templateWidth; i
|
|
350
|
+
for (let i = 0; i < templateWidth; i++) {
|
|
351
351
|
sxy += imageData[rowOffset1 + i] * templateData[rowOffset2 + i];
|
|
352
352
|
}
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
-
|
|
356
|
-
// templateWidth is 13, steps of 2 hit 7 points per dim = 49 total points (vs 169)
|
|
357
|
-
const sampledCount = Math.ceil(templateWidth / 2) ** 2;
|
|
355
|
+
const sampledCount = templateWidth * templateWidth;
|
|
358
356
|
const totalCount = templateWidth * templateWidth;
|
|
359
357
|
sxy *= (totalCount / sampledCount);
|
|
360
358
|
|
|
@@ -76,15 +76,24 @@ class Tracker {
|
|
|
76
76
|
const screenW = Math.sqrt((p1.x - p0.x) ** 2 + (p1.y - p0.y) ** 2);
|
|
77
77
|
|
|
78
78
|
// Select octave whose image width is closest to screenW
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
// Select the best octave based on current estimated distance/scale
|
|
80
|
+
// Hysteresis: prevent flip-flopping between octaves
|
|
81
|
+
if (!this.lastOctaveIndex) this.lastOctaveIndex = [];
|
|
82
|
+
|
|
83
|
+
let octaveIndex = this.lastOctaveIndex[targetIndex] !== undefined ? this.lastOctaveIndex[targetIndex] : 0;
|
|
84
|
+
let minDiff = Math.abs(this.prebuiltData[targetIndex][octaveIndex].width - screenW);
|
|
85
|
+
|
|
86
|
+
// Threshold to switch: only switch if another octave is much better (20% improvement)
|
|
87
|
+
const switchThreshold = 0.8;
|
|
88
|
+
|
|
81
89
|
for (let i = 0; i < this.prebuiltData[targetIndex].length; i++) {
|
|
82
90
|
const diff = Math.abs(this.prebuiltData[targetIndex][i].width - screenW);
|
|
83
|
-
if (diff < minDiff) {
|
|
91
|
+
if (diff < minDiff * switchThreshold) {
|
|
84
92
|
minDiff = diff;
|
|
85
93
|
octaveIndex = i;
|
|
86
94
|
}
|
|
87
95
|
}
|
|
96
|
+
this.lastOctaveIndex[targetIndex] = octaveIndex;
|
|
88
97
|
|
|
89
98
|
const prebuilt = this.prebuiltData[targetIndex][octaveIndex];
|
|
90
99
|
|
|
@@ -127,6 +136,20 @@ class Tracker {
|
|
|
127
136
|
}
|
|
128
137
|
}
|
|
129
138
|
|
|
139
|
+
// 2.1 Spatial distribution check: Avoid getting stuck in corners/noise
|
|
140
|
+
if (screenCoords.length >= 10) {
|
|
141
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
142
|
+
for (const p of screenCoords) {
|
|
143
|
+
if (p.x < minX) minX = p.x; if (p.y < minY) minY = p.y;
|
|
144
|
+
if (p.x > maxX) maxX = p.x; if (p.y > maxY) maxY = p.y;
|
|
145
|
+
}
|
|
146
|
+
const detectedDiagonal = Math.sqrt((maxX - minX) ** 2 + (maxY - minY) ** 2);
|
|
147
|
+
|
|
148
|
+
// If the points cover too little space compared to the screen size of the marker, it's a glitch
|
|
149
|
+
if (detectedDiagonal < screenW * 0.15) {
|
|
150
|
+
return { worldCoords: [], screenCoords: [], debugExtra };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
130
153
|
if (this.debugMode) {
|
|
131
154
|
debugExtra = {
|
|
132
155
|
octaveIndex,
|