@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.
@@ -18,8 +18,8 @@ const getControllerWorker = async () => {
18
18
  }
19
19
  };
20
20
  ControllerWorker = await getControllerWorker();
21
- const DEFAULT_FILTER_CUTOFF = 0.1;
22
- const DEFAULT_FILTER_BETA = 0.01;
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 < 6)
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 = 10; // Relaxed from 3 to 10 for better robustness with LSH
7
- //const MIN_NUM_INLIERS = 8; //default
8
- const MIN_NUM_INLIERS = 6;
9
- const CLUSTER_MAX_POP = 20; // Increased to explore more candidate clusters
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: this._pack4Bit(td.data),
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
- // 🚀 MOONSHOT: Unpack 4-bit tracking data if detected
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.1, beta: 0.01 });
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 = 10; // Reducido de 16 para permitir puntos más cercanos
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.05 * width * height;
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 += 2) {
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 += 2) {
295
+ for (let i = 0; i < templateWidth; i++) {
296
296
  sxy += imageData[rowOffset1 + i] * templateData[rowOffset2 + i];
297
297
  }
298
298
  }
299
- // Factor to normalize sxy back to full template area
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
@@ -22,6 +22,7 @@ export class Tracker {
22
22
  }[];
23
23
  debugExtra: {};
24
24
  };
25
+ lastOctaveIndex: any[] | undefined;
25
26
  /**
26
27
  * Pure JS implementation of NCC matching
27
28
  */
@@ -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
- let octaveIndex = 0;
59
- let minDiff = Infinity;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@srsergio/taptapp-ar",
3
- "version": "1.0.71",
3
+ "version": "1.0.73",
4
4
  "description": "AR Compiler for Node.js and Browser",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,8 +19,8 @@ const getControllerWorker = async () => {
19
19
  };
20
20
  ControllerWorker = await getControllerWorker();
21
21
 
22
- const DEFAULT_FILTER_CUTOFF = 0.1;
23
- const DEFAULT_FILTER_BETA = 0.01;
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 < 6) return null;
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 = 10; // Relaxed from 3 to 10 for better robustness with LSH
8
- //const MIN_NUM_INLIERS = 8; //default
9
- const MIN_NUM_INLIERS = 6;
10
- const CLUSTER_MAX_POP = 20; // Increased to explore more candidate clusters
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: this._pack4Bit(td.data),
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
- // 🚀 MOONSHOT: Unpack 4-bit tracking data if detected
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.1, beta: 0.01 });
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 = 10; // Reducido de 16 para permitir puntos más cercanos
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.05 * width * height;
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 += 2) {
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 += 2) {
350
+ for (let i = 0; i < templateWidth; i++) {
351
351
  sxy += imageData[rowOffset1 + i] * templateData[rowOffset2 + i];
352
352
  }
353
353
  }
354
354
 
355
- // Factor to normalize sxy back to full template area
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
- let octaveIndex = 0;
80
- let minDiff = Infinity;
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,