@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.
- package/dist/compiler/detector/crop-detector.js +1 -1
- package/dist/compiler/node-worker.js +37 -12
- package/dist/compiler/offline-compiler.d.ts +1 -4
- package/dist/compiler/offline-compiler.js +17 -4
- package/package.json +1 -1
- package/src/compiler/detector/crop-detector.js +1 -1
- package/src/compiler/node-worker.js +44 -12
- package/src/compiler/offline-compiler.ts +18 -4
|
@@ -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
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
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:
|
|
87
|
-
height:
|
|
88
|
-
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 +
|
|
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
|
-
|
|
92
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
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
|
-
|
|
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:
|
|
98
|
-
height:
|
|
99
|
-
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 +
|
|
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
|
-
|
|
114
|
-
|
|
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
|
-
|
|
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);
|