@srsergio/taptapp-ar 1.0.61 → 1.0.64

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.
@@ -8,7 +8,7 @@ class CropDetector {
8
8
  let minDimension = Math.min(width, height) / 2;
9
9
  let cropSize = Math.pow(2, Math.round(Math.log(minDimension) / Math.log(2)));
10
10
  this.cropSize = cropSize;
11
- this.detector = new DetectorLite(cropSize, cropSize, { useLSH: true, maxOctaves: 1 });
11
+ this.detector = new DetectorLite(cropSize, cropSize, { useLSH: true });
12
12
  this.lastRandomIndex = 4;
13
13
  }
14
14
  detect(input) {
@@ -64,15 +64,40 @@ parentPort.on('message', async (msg) => {
64
64
  else if (msg.type === 'match') {
65
65
  const { targetImage, percentPerImage, basePercent } = msg;
66
66
  try {
67
- const { buildImageList } = await import('./image-list.js');
68
- const imageList = buildImageList(targetImage);
69
- const percentPerScale = percentPerImage / imageList.length;
67
+ // 🚀 MOONSHOT: Only run detector ONCE on full-res image.
68
+ // DetectorLite internally builds a pyramid (octaves 1.0, 0.5, 0.25, etc.)
69
+ const detector = new DetectorLite(targetImage.width, targetImage.height, {
70
+ useLSH: true
71
+ });
72
+ parentPort.postMessage({ type: 'progress', percent: basePercent + percentPerImage * 0.1 });
73
+ const { featurePoints: allPoints } = detector.detect(targetImage.data);
74
+ parentPort.postMessage({ type: 'progress', percent: basePercent + percentPerImage * 0.5 });
75
+ // Group points by their scale (octave)
76
+ const scalesMap = new Map();
77
+ for (const p of allPoints) {
78
+ const octaveScale = p.scale;
79
+ let list = scalesMap.get(octaveScale);
80
+ if (!list) {
81
+ list = [];
82
+ scalesMap.set(octaveScale, list);
83
+ }
84
+ // Coordinates in p are already full-res.
85
+ // We need them relative to the scaled image for the keyframe.
86
+ list.push({
87
+ ...p,
88
+ x: p.x / octaveScale,
89
+ y: p.y / octaveScale,
90
+ scale: 1.0 // Keypoint scale is always 1.0 relative to its own keyframe image
91
+ });
92
+ }
93
+ // Optional: Run another detector pass at an intermediate scale to improve coverage
94
+ // (e.g. at 1/1.41 ratio) if tracking robustness suffers.
95
+ // For now, let's stick to octaves for MAXIMUM speed.
70
96
  const keyframes = [];
71
- for (let i = 0; i < imageList.length; i++) {
72
- const image = imageList[i];
73
- // Disable internal pyramid (maxOctaves: 1) as we are already processing a scale list
74
- const detector = new DetectorLite(image.width, image.height, { useLSH: true, maxOctaves: 1 });
75
- const { featurePoints: ps } = detector.detect(image.data);
97
+ const sortedScales = Array.from(scalesMap.keys()).sort((a, b) => a - b);
98
+ const percentPerScale = (percentPerImage * 0.4) / sortedScales.length;
99
+ for (const s of sortedScales) {
100
+ const ps = scalesMap.get(s);
76
101
  const sortedPs = sortPoints(ps);
77
102
  const maximaPoints = sortedPs.filter((p) => p.maxima);
78
103
  const minimaPoints = sortedPs.filter((p) => !p.maxima);
@@ -83,13 +108,13 @@ parentPort.on('message', async (msg) => {
83
108
  minimaPoints,
84
109
  maximaPointsCluster,
85
110
  minimaPointsCluster,
86
- width: image.width,
87
- height: image.height,
88
- scale: image.scale,
111
+ width: Math.round(targetImage.width / s),
112
+ height: Math.round(targetImage.height / s),
113
+ scale: 1.0 / s, // keyframe.scale is relative to full target image
89
114
  });
90
115
  parentPort.postMessage({
91
116
  type: 'progress',
92
- percent: basePercent + (i + 1) * percentPerScale
117
+ percent: basePercent + percentPerImage * 0.6 + keyframes.length * percentPerScale
93
118
  });
94
119
  }
95
120
  parentPort.postMessage({
@@ -12,10 +12,7 @@ export declare class OfflineCompiler {
12
12
  constructor();
13
13
  _initNodeWorkers(): Promise<void>;
14
14
  compileImageTargets(images: any[], progressCallback: (p: number) => void): Promise<any>;
15
- _compileTarget(targetImages: any[], progressCallback: (p: number) => void): Promise<{
16
- matchingData: any;
17
- trackingData: any;
18
- }[]>;
15
+ _compileTarget(targetImages: any[], progressCallback: (p: number) => void): Promise<any[]>;
19
16
  _compileMatch(targetImages: any[], progressCallback: (p: number) => void): Promise<any[]>;
20
17
  _compileTrack(targetImages: any[], progressCallback: (p: number) => void): Promise<any[]>;
21
18
  compileTrack({ progressCallback, targetImages, basePercent }: {
@@ -88,8 +88,22 @@ export class OfflineCompiler {
88
88
  async _compileTarget(targetImages, progressCallback) {
89
89
  if (isNode)
90
90
  await this._initNodeWorkers();
91
- // Reverted: 'compile-all' combined task was causing issues with pyramid processing
92
- // We go back to sequential match and track for reliability
91
+ if (this.workerPool) {
92
+ const progressMap = new Float32Array(targetImages.length);
93
+ const wrappedPromises = targetImages.map((targetImage, index) => {
94
+ return this.workerPool.runTask({
95
+ type: 'compile-all', // 🚀 MOONSHOT: Combined task
96
+ targetImage,
97
+ onProgress: (p) => {
98
+ progressMap[index] = p;
99
+ const sum = progressMap.reduce((a, b) => a + b, 0);
100
+ progressCallback(sum / targetImages.length);
101
+ }
102
+ });
103
+ });
104
+ return Promise.all(wrappedPromises);
105
+ }
106
+ // Fallback or non-worker implementation: run match and track sequentially
93
107
  const matchingResults = await this._compileMatch(targetImages, (p) => progressCallback(p * 0.5));
94
108
  const trackingResults = await this._compileTrack(targetImages, (p) => progressCallback(50 + p * 0.5));
95
109
  return targetImages.map((_, i) => ({
@@ -126,8 +140,7 @@ export class OfflineCompiler {
126
140
  const percentPerImageScale = percentPerImage / imageList.length;
127
141
  const keyframes = [];
128
142
  for (const image of imageList) {
129
- // Disabling internal pyramid (maxOctaves: 1) as we are already processing a scale list
130
- const detector = new DetectorLite(image.width, image.height, { useLSH: true, maxOctaves: 1 });
143
+ const detector = new DetectorLite(image.width, image.height, { useLSH: true });
131
144
  const { featurePoints: ps } = detector.detect(image.data);
132
145
  const maximaPoints = ps.filter((p) => p.maxima);
133
146
  const minimaPoints = ps.filter((p) => !p.maxima);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@srsergio/taptapp-ar",
3
- "version": "1.0.61",
3
+ "version": "1.0.64",
4
4
  "author": "Sergio Lazaro <srsergiolazaro@gmail.com>",
5
5
  "license": "GPL-3.0",
6
6
  "description": "Ultra-fast, lightweight Augmented Reality Image Tracking SDK for the web. Features an optimized offline compiler, React components, and compatibility with Three.js/A-Frame. No heavy ML frameworks required.",
@@ -11,7 +11,7 @@ class CropDetector {
11
11
  let cropSize = Math.pow(2, Math.round(Math.log(minDimension) / Math.log(2)));
12
12
  this.cropSize = cropSize;
13
13
 
14
- this.detector = new DetectorLite(cropSize, cropSize, { useLSH: true, maxOctaves: 1 });
14
+ this.detector = new DetectorLite(cropSize, cropSize, { useLSH: true });
15
15
 
16
16
  this.lastRandomIndex = 4;
17
17
  }
@@ -71,17 +71,49 @@ parentPort.on('message', async (msg) => {
71
71
  const { targetImage, percentPerImage, basePercent } = msg;
72
72
 
73
73
  try {
74
- const { buildImageList } = await import('./image-list.js');
75
- const imageList = buildImageList(targetImage);
76
- const percentPerScale = percentPerImage / imageList.length;
74
+ // 🚀 MOONSHOT: Only run detector ONCE on full-res image.
75
+ // DetectorLite internally builds a pyramid (octaves 1.0, 0.5, 0.25, etc.)
76
+ const detector = new DetectorLite(targetImage.width, targetImage.height, {
77
+ useLSH: true
78
+ });
79
+
80
+ parentPort.postMessage({ type: 'progress', percent: basePercent + percentPerImage * 0.1 });
81
+
82
+ const { featurePoints: allPoints } = detector.detect(targetImage.data);
83
+
84
+ parentPort.postMessage({ type: 'progress', percent: basePercent + percentPerImage * 0.5 });
85
+
86
+ // Group points by their scale (octave)
87
+ const scalesMap = new Map();
88
+ for (const p of allPoints) {
89
+ const octaveScale = p.scale;
90
+ let list = scalesMap.get(octaveScale);
91
+ if (!list) {
92
+ list = [];
93
+ scalesMap.set(octaveScale, list);
94
+ }
95
+
96
+ // Coordinates in p are already full-res.
97
+ // We need them relative to the scaled image for the keyframe.
98
+ list.push({
99
+ ...p,
100
+ x: p.x / octaveScale,
101
+ y: p.y / octaveScale,
102
+ scale: 1.0 // Keypoint scale is always 1.0 relative to its own keyframe image
103
+ });
104
+ }
105
+
106
+ // Optional: Run another detector pass at an intermediate scale to improve coverage
107
+ // (e.g. at 1/1.41 ratio) if tracking robustness suffers.
108
+ // For now, let's stick to octaves for MAXIMUM speed.
109
+
77
110
  const keyframes = [];
111
+ const sortedScales = Array.from(scalesMap.keys()).sort((a, b) => a - b);
78
112
 
79
- for (let i = 0; i < imageList.length; i++) {
80
- const image = imageList[i];
81
- // Disable internal pyramid (maxOctaves: 1) as we are already processing a scale list
82
- const detector = new DetectorLite(image.width, image.height, { useLSH: true, maxOctaves: 1 });
83
- const { featurePoints: ps } = detector.detect(image.data);
113
+ const percentPerScale = (percentPerImage * 0.4) / sortedScales.length;
84
114
 
115
+ for (const s of sortedScales) {
116
+ const ps = scalesMap.get(s);
85
117
  const sortedPs = sortPoints(ps);
86
118
  const maximaPoints = sortedPs.filter((p) => p.maxima);
87
119
  const minimaPoints = sortedPs.filter((p) => !p.maxima);
@@ -94,14 +126,14 @@ parentPort.on('message', async (msg) => {
94
126
  minimaPoints,
95
127
  maximaPointsCluster,
96
128
  minimaPointsCluster,
97
- width: image.width,
98
- height: image.height,
99
- scale: image.scale,
129
+ width: Math.round(targetImage.width / s),
130
+ height: Math.round(targetImage.height / s),
131
+ scale: 1.0 / s, // keyframe.scale is relative to full target image
100
132
  });
101
133
 
102
134
  parentPort.postMessage({
103
135
  type: 'progress',
104
- percent: basePercent + (i + 1) * percentPerScale
136
+ percent: basePercent + percentPerImage * 0.6 + keyframes.length * percentPerScale
105
137
  });
106
138
  }
107
139
 
@@ -110,8 +110,23 @@ export class OfflineCompiler {
110
110
  async _compileTarget(targetImages: any[], progressCallback: (p: number) => void) {
111
111
  if (isNode) await this._initNodeWorkers();
112
112
 
113
- // Reverted: 'compile-all' combined task was causing issues with pyramid processing
114
- // We go back to sequential match and track for reliability
113
+ if (this.workerPool) {
114
+ const progressMap = new Float32Array(targetImages.length);
115
+ const wrappedPromises = targetImages.map((targetImage: any, index: number) => {
116
+ return this.workerPool!.runTask({
117
+ type: 'compile-all', // 🚀 MOONSHOT: Combined task
118
+ targetImage,
119
+ onProgress: (p: number) => {
120
+ progressMap[index] = p;
121
+ const sum = progressMap.reduce((a, b) => a + b, 0);
122
+ progressCallback(sum / targetImages.length);
123
+ }
124
+ });
125
+ });
126
+ return Promise.all(wrappedPromises);
127
+ }
128
+
129
+ // Fallback or non-worker implementation: run match and track sequentially
115
130
  const matchingResults = await this._compileMatch(targetImages, (p) => progressCallback(p * 0.5));
116
131
  const trackingResults = await this._compileTrack(targetImages, (p) => progressCallback(50 + p * 0.5));
117
132
 
@@ -155,8 +170,7 @@ export class OfflineCompiler {
155
170
  const keyframes = [];
156
171
 
157
172
  for (const image of imageList as any[]) {
158
- // Disabling internal pyramid (maxOctaves: 1) as we are already processing a scale list
159
- const detector = new DetectorLite(image.width, image.height, { useLSH: true, maxOctaves: 1 });
173
+ const detector = new DetectorLite(image.width, image.height, { useLSH: true });
160
174
  const { featurePoints: ps } = detector.detect(image.data);
161
175
 
162
176
  const maximaPoints = ps.filter((p: any) => p.maxima);