@srsergio/taptapp-ar 1.0.72 → 1.0.74

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.
@@ -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,
@@ -22,6 +22,19 @@ export class CropDetector {
22
22
  projectedImage?: undefined;
23
23
  };
24
24
  };
25
+ /**
26
+ * Scans the ENTIRE frame by downsampling it to cropSize
27
+ */
28
+ _detectGlobal(imageData: any): {
29
+ featurePoints: any[];
30
+ debugExtra: {
31
+ projectedImage: number[];
32
+ isGlobal: boolean;
33
+ } | {
34
+ projectedImage?: undefined;
35
+ isGlobal?: undefined;
36
+ };
37
+ };
25
38
  _detect(imageData: any, startX: any, startY: any): {
26
39
  featurePoints: any[];
27
40
  debugExtra: {
@@ -24,27 +24,51 @@ class CropDetector {
24
24
  }
25
25
  detectMoving(input) {
26
26
  const imageData = input;
27
- // Expanded to 5x5 grid (25 positions) for better coverage
27
+ // 🚀 MOONSHOT: Alternate between local crops and GLOBAL scan
28
+ // This solves the "not reading the whole screen" issue.
29
+ // Every 3 frames, we do a full screen downsampled scan.
30
+ if (this.lastRandomIndex % 3 === 0) {
31
+ this.lastRandomIndex = (this.lastRandomIndex + 1) % 25;
32
+ return this._detectGlobal(imageData);
33
+ }
34
+ // Original moving crop logic for high-detail local detection
28
35
  const gridSize = 5;
29
- const dx = this.lastRandomIndex % gridSize;
30
- const dy = Math.floor(this.lastRandomIndex / gridSize);
31
- // Calculate offset from center, with overlap for better detection
36
+ const idx = (this.lastRandomIndex - 1) % (gridSize * gridSize);
37
+ const dx = idx % gridSize;
38
+ const dy = Math.floor(idx / gridSize);
32
39
  const stepX = this.cropSize / 3;
33
40
  const stepY = this.cropSize / 3;
34
41
  let startY = Math.floor(this.height / 2 - this.cropSize / 2 + (dy - 2) * stepY);
35
42
  let startX = Math.floor(this.width / 2 - this.cropSize / 2 + (dx - 2) * stepX);
36
- // Clamp to valid bounds
37
- if (startX < 0)
38
- startX = 0;
39
- if (startY < 0)
40
- startY = 0;
41
- if (startX >= this.width - this.cropSize)
42
- startX = this.width - this.cropSize - 1;
43
- if (startY >= this.height - this.cropSize)
44
- startY = this.height - this.cropSize - 1;
45
- this.lastRandomIndex = (this.lastRandomIndex + 1) % (gridSize * gridSize);
46
- const result = this._detect(imageData, startX, startY);
47
- return result;
43
+ startX = Math.max(0, Math.min(this.width - this.cropSize - 1, startX));
44
+ startY = Math.max(0, Math.min(this.height - this.cropSize - 1, startY));
45
+ this.lastRandomIndex = (this.lastRandomIndex + 1) % 25;
46
+ return this._detect(imageData, startX, startY);
47
+ }
48
+ /**
49
+ * Scans the ENTIRE frame by downsampling it to cropSize
50
+ */
51
+ _detectGlobal(imageData) {
52
+ const croppedData = new Float32Array(this.cropSize * this.cropSize);
53
+ const scaleX = this.width / this.cropSize;
54
+ const scaleY = this.height / this.cropSize;
55
+ // Fast downsample (nearest neighbor is enough for initial feature detection)
56
+ for (let y = 0; y < this.cropSize; y++) {
57
+ const srcY = Math.floor(y * scaleY) * this.width;
58
+ const dstY = y * this.cropSize;
59
+ for (let x = 0; x < this.cropSize; x++) {
60
+ croppedData[dstY + x] = imageData[srcY + Math.floor(x * scaleX)];
61
+ }
62
+ }
63
+ const { featurePoints } = this.detector.detect(croppedData);
64
+ featurePoints.forEach((p) => {
65
+ p.x *= scaleX;
66
+ p.y *= scaleY;
67
+ });
68
+ return {
69
+ featurePoints,
70
+ debugExtra: this.debugMode ? { projectedImage: Array.from(croppedData), isGlobal: true } : {}
71
+ };
48
72
  }
49
73
  _detect(imageData, startX, startY) {
50
74
  // Crop manually since imageData is now a flat array (width * height)
@@ -1,7 +1,7 @@
1
1
  import { buildModelViewProjectionTransform, computeScreenCoordiate } from "../estimation/utils.js";
2
2
  const AR2_DEFAULT_TS = 6;
3
3
  const AR2_DEFAULT_TS_GAP = 1;
4
- const AR2_SEARCH_SIZE = 18;
4
+ const AR2_SEARCH_SIZE = 34; // Increased from 18 to 34 for much better fast-motion tracking
5
5
  const AR2_SEARCH_GAP = 1;
6
6
  const AR2_SIM_THRESH = 0.6;
7
7
  const TRACKING_KEYFRAME = 0; // 0: 128px (optimized)
@@ -94,6 +94,25 @@ class Tracker {
94
94
  });
95
95
  }
96
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
+ }
97
116
  if (this.debugMode) {
98
117
  debugExtra = {
99
118
  octaveIndex,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@srsergio/taptapp-ar",
3
- "version": "1.0.72",
3
+ "version": "1.0.74",
4
4
  "description": "AR Compiler for Node.js and Browser",
5
5
  "repository": {
6
6
  "type": "git",
@@ -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,
@@ -33,28 +33,62 @@ class CropDetector {
33
33
  detectMoving(input) {
34
34
  const imageData = input;
35
35
 
36
- // Expanded to 5x5 grid (25 positions) for better coverage
36
+ // 🚀 MOONSHOT: Alternate between local crops and GLOBAL scan
37
+ // This solves the "not reading the whole screen" issue.
38
+ // Every 3 frames, we do a full screen downsampled scan.
39
+ if (this.lastRandomIndex % 3 === 0) {
40
+ this.lastRandomIndex = (this.lastRandomIndex + 1) % 25;
41
+ return this._detectGlobal(imageData);
42
+ }
43
+
44
+ // Original moving crop logic for high-detail local detection
37
45
  const gridSize = 5;
38
- const dx = this.lastRandomIndex % gridSize;
39
- const dy = Math.floor(this.lastRandomIndex / gridSize);
46
+ const idx = (this.lastRandomIndex - 1) % (gridSize * gridSize);
47
+ const dx = idx % gridSize;
48
+ const dy = Math.floor(idx / gridSize);
40
49
 
41
- // Calculate offset from center, with overlap for better detection
42
50
  const stepX = this.cropSize / 3;
43
51
  const stepY = this.cropSize / 3;
44
52
 
45
53
  let startY = Math.floor(this.height / 2 - this.cropSize / 2 + (dy - 2) * stepY);
46
54
  let startX = Math.floor(this.width / 2 - this.cropSize / 2 + (dx - 2) * stepX);
47
55
 
48
- // Clamp to valid bounds
49
- if (startX < 0) startX = 0;
50
- if (startY < 0) startY = 0;
51
- if (startX >= this.width - this.cropSize) startX = this.width - this.cropSize - 1;
52
- if (startY >= this.height - this.cropSize) startY = this.height - this.cropSize - 1;
56
+ startX = Math.max(0, Math.min(this.width - this.cropSize - 1, startX));
57
+ startY = Math.max(0, Math.min(this.height - this.cropSize - 1, startY));
53
58
 
54
- this.lastRandomIndex = (this.lastRandomIndex + 1) % (gridSize * gridSize);
59
+ this.lastRandomIndex = (this.lastRandomIndex + 1) % 25;
55
60
 
56
- const result = this._detect(imageData, startX, startY);
57
- return result;
61
+ return this._detect(imageData, startX, startY);
62
+ }
63
+
64
+ /**
65
+ * Scans the ENTIRE frame by downsampling it to cropSize
66
+ */
67
+ _detectGlobal(imageData) {
68
+ const croppedData = new Float32Array(this.cropSize * this.cropSize);
69
+ const scaleX = this.width / this.cropSize;
70
+ const scaleY = this.height / this.cropSize;
71
+
72
+ // Fast downsample (nearest neighbor is enough for initial feature detection)
73
+ for (let y = 0; y < this.cropSize; y++) {
74
+ const srcY = Math.floor(y * scaleY) * this.width;
75
+ const dstY = y * this.cropSize;
76
+ for (let x = 0; x < this.cropSize; x++) {
77
+ croppedData[dstY + x] = imageData[srcY + Math.floor(x * scaleX)];
78
+ }
79
+ }
80
+
81
+ const { featurePoints } = this.detector.detect(croppedData);
82
+
83
+ featurePoints.forEach((p) => {
84
+ p.x *= scaleX;
85
+ p.y *= scaleY;
86
+ });
87
+
88
+ return {
89
+ featurePoints,
90
+ debugExtra: this.debugMode ? { projectedImage: Array.from(croppedData), isGlobal: true } : {}
91
+ };
58
92
  }
59
93
 
60
94
  _detect(imageData, startX, startY) {
@@ -2,7 +2,7 @@ import { buildModelViewProjectionTransform, computeScreenCoordiate } from "../es
2
2
 
3
3
  const AR2_DEFAULT_TS = 6;
4
4
  const AR2_DEFAULT_TS_GAP = 1;
5
- const AR2_SEARCH_SIZE = 18;
5
+ const AR2_SEARCH_SIZE = 34; // Increased from 18 to 34 for much better fast-motion tracking
6
6
  const AR2_SEARCH_GAP = 1;
7
7
  const AR2_SIM_THRESH = 0.6;
8
8
 
@@ -136,6 +136,20 @@ class Tracker {
136
136
  }
137
137
  }
138
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
+ }
139
153
  if (this.debugMode) {
140
154
  debugExtra = {
141
155
  octaveIndex,