Skip to content

Commit 716b501

Browse files
committed
feat: add camera parameters URL support and enhance marker filtering in ARToolKit integration
1 parent 7f5051b commit 716b501

File tree

3 files changed

+67
-41
lines changed

3 files changed

+67
-41
lines changed
176 Bytes
Binary file not shown.

examples/simple-marker/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ <h3>Event Log:</h3>
7373
plugin = new ArtoolkitPlugin({ worker: true,
7474
// ESM bundle path served by your dev server
7575
artoolkitModuleUrl: '../../node_modules/@ar-js-org/artoolkit5-js/dist/ARToolkit.js',
76+
cameraParametersUrl: "../../examples/simple-marker/data/camera_para.dat"
7677
});
7778
await plugin.init(core);
7879
await plugin.enable();
@@ -106,8 +107,7 @@ <h3>Event Log:</h3>
106107
try {
107108
startCamBtn.disabled = true;
108109
log('Starting camera...');
109-
const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } });
110-
video.srcObject = stream;
110+
video.srcObject = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } });
111111
// emit engine:update frames using createImageBitmap
112112
const canvas = document.createElement('canvas');
113113
const ctx = canvas.getContext('2d');

src/worker/worker.js

Lines changed: 65 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,24 @@ let offscreenCtx = null;
1313
let canvasW = 0;
1414
let canvasH = 0;
1515

16-
// Init backoff state
17-
let initInProgress = null; // Promise | null
18-
let initFailCount = 0; // increases on each failure
19-
let initFailedUntil = 0; // timestamp when next retry is allowed
16+
// Marker and filtering state
17+
const loadedMarkers = new Map(); // patternUrl -> markerId
18+
const loadingMarkers = new Map(); // patternUrl -> Promise<markerId>
19+
const trackedPatternIds = new Set(); // Set<number>
20+
let PATTERN_MARKER_TYPE = 0; // will be read from ARToolkit if available
21+
let MIN_CONFIDENCE = 0.6; // configurable via init payload
2022

21-
// Marker cache/dedupe
22-
const loadedMarkers = new Map(); // patternUrl -> markerId
23-
const loadingMarkers = new Map(); // patternUrl -> Promise<markerId>
23+
// Init backoff state
24+
let initInProgress = null;
25+
let initFailCount = 0;
26+
let initFailedUntil = 0;
2427

25-
// Init-time options (can be overridden via init payload if you already set this up)
28+
// Init-time options (overridable from main thread)
2629
let INIT_OPTS = {
2730
moduleUrl: null,
2831
cameraParametersUrl: null,
29-
wasmBaseUrl: null
32+
wasmBaseUrl: null,
33+
minConfidence: null
3034
};
3135

3236
if (typeof self === 'undefined') {
@@ -79,30 +83,52 @@ function serializeGetMarkerEvent(ev) {
7983
}
8084
}
8185

86+
function shouldForwardGetMarker(event) {
87+
const data = event?.data || {};
88+
const type = data.type;
89+
const marker = data.marker || {};
90+
const id = marker.idPatt ?? marker.patternId ?? marker.pattern_id ?? null;
91+
const conf = marker.cfPatt ?? marker.confidence ?? 0;
92+
const matrix = data.matrix;
93+
94+
// Type must be PATTERN_MARKER (fallback numeric 0 if constants not available)
95+
if (type !== PATTERN_MARKER_TYPE) return false;
96+
97+
// Confidence gate
98+
if (!(Number.isFinite(conf) && conf >= MIN_CONFIDENCE)) return false;
99+
100+
// Matrix must exist with at least 16 values
101+
const m = Array.isArray(matrix) ? matrix : (matrix && Array.from(matrix)) || null;
102+
if (!m || m.length < 16) return false;
103+
104+
// If we have tracked IDs, only forward those IDs
105+
if (trackedPatternIds.size && id != null && !trackedPatternIds.has(id)) return false;
106+
107+
return true;
108+
}
109+
82110
function attachGetMarkerForwarder() {
83111
if (!arController || typeof arController.addEventListener !== 'function' || getMarkerForwarderAttached) return;
84112
arController.addEventListener('getMarker', (event) => {
113+
if (!shouldForwardGetMarker(event)) return;
85114
const payload = serializeGetMarkerEvent(event);
86-
try { console.log('[Worker] getMarker', payload); } catch {}
115+
try { console.log('[Worker] getMarker (filtered)', payload); } catch {}
87116
sendMessage({ type: 'getMarker', payload });
88117
});
89118
getMarkerForwarderAttached = true;
90119
}
91120

92-
// IMPORTANT: this function should be the only place that initializes ARToolKit.
93-
// It is guarded by initInProgress and a failure backoff.
121+
// Guarded init with backoff
94122
async function initArtoolkit(width = 640, height = 480) {
95123
if (arControllerInitialized) return true;
96124

97-
// Respect backoff window
98125
const now = Date.now();
99126
if (now < initFailedUntil) {
100127
const waitMs = initFailedUntil - now;
101128
console.warn('[Worker] initArtoolkit skipped due to backoff (ms):', waitMs);
102129
return false;
103130
}
104131

105-
// If an init is already in-flight, await it
106132
if (initInProgress) {
107133
try {
108134
await initInProgress;
@@ -112,30 +138,38 @@ async function initArtoolkit(width = 640, height = 480) {
112138
}
113139
}
114140

115-
// Start a new init attempt
116141
initInProgress = (async () => {
117142
try {
118143
const jsartoolkit = await (async () => {
119144
if (INIT_OPTS.moduleUrl) {
120145
console.log('[Worker] Loading artoolkit from moduleUrl:', INIT_OPTS.moduleUrl);
121146
return await import(INIT_OPTS.moduleUrl);
122147
}
123-
// Fallback to bare import if your environment supports it (import map/bundler)
148+
// If your environment supports bare import (import map/bundler), this will work:
124149
return await import('@ar-js-org/artoolkit5-js');
125150
})();
126151

127-
const { ARController } = jsartoolkit;
152+
//const { ARController, ARToolkit } = jsartoolkit;
153+
154+
// Read the constant if available; else keep default 0
155+
if (ARToolkit && typeof ARToolkit.PATTERN_MARKER === 'number') {
156+
PATTERN_MARKER_TYPE = ARToolkit.PATTERN_MARKER;
157+
}
128158

129159
if (INIT_OPTS.wasmBaseUrl && ARController) {
130160
try {
131161
ARController.baseURL = INIT_OPTS.wasmBaseUrl.endsWith('/') ? INIT_OPTS.wasmBaseUrl : INIT_OPTS.wasmBaseUrl + '/';
132162
} catch {}
133163
}
134164

165+
if (typeof INIT_OPTS.minConfidence === 'number') {
166+
MIN_CONFIDENCE = INIT_OPTS.minConfidence;
167+
}
168+
135169
const camUrl = INIT_OPTS.cameraParametersUrl
136170
|| 'https://raw.githack.com/AR-js-org/AR.js/master/data/data/camera_para.dat';
137171

138-
console.log('[Worker] ARToolKit init', { width, height, camUrl });
172+
console.log('[Worker] ARToolKit init', { width, height, camUrl, minConfidence: MIN_CONFIDENCE, patternType: PATTERN_MARKER_TYPE });
139173
arController = await ARToolkit.ARController.initWithDimensions(width, height, camUrl, {});
140174
arControllerInitialized = !!arController;
141175
console.log('[Worker] ARToolKit initialized:', arControllerInitialized);
@@ -144,46 +178,40 @@ async function initArtoolkit(width = 640, height = 480) {
144178

145179
attachGetMarkerForwarder();
146180

147-
// Reset failure state
148181
initFailCount = 0;
149182
initFailedUntil = 0;
150183
} catch (err) {
151184
console.error('[Worker] ARToolKit init failed:', err);
152185
arController = null;
153186
arControllerInitialized = false;
154187

155-
// Exponential backoff up to 30s
156-
initFailCount = Math.min(initFailCount + 1, 6); // caps at ~64x
157-
const delay = Math.min(30000, 1000 * Math.pow(2, initFailCount)); // 1s,2s,4s,8s,16s,30s
188+
initFailCount = Math.min(initFailCount + 1, 6);
189+
const delay = Math.min(30000, 1000 * Math.pow(2, initFailCount));
158190
initFailedUntil = Date.now() + delay;
159191

160-
// Surface a single error to main thread (optional)
161192
sendMessage({ type: 'error', payload: { message: `ARToolKit init failed (${err?.message || err}). Retrying in ${delay}ms.` } });
162193
throw err;
163194
} finally {
164-
// Mark as done (success or failure)
165-
const ok = arControllerInitialized;
166195
initInProgress = null;
167-
return ok;
196+
return arControllerInitialized;
168197
}
169198
})();
170199

171200
try {
172201
await initInProgress;
173-
} catch {
174-
// already handled
175-
}
202+
} catch {}
176203
return arControllerInitialized;
177204
}
178205

179-
// Dedupe marker loading by URL
206+
// Dedupe marker loading by URL and record tracked IDs
180207
async function loadPatternOnce(patternUrl) {
181208
if (loadedMarkers.has(patternUrl)) return loadedMarkers.get(patternUrl);
182209
if (loadingMarkers.has(patternUrl)) return loadingMarkers.get(patternUrl);
183210

184211
const p = (async () => {
185212
const id = await arController.loadMarker(patternUrl);
186213
loadedMarkers.set(patternUrl, id);
214+
trackedPatternIds.add(id);
187215
loadingMarkers.delete(patternUrl);
188216
return id;
189217
})().catch((e) => {
@@ -199,11 +227,14 @@ onMessage(async (ev) => {
199227
const { type, payload } = ev || {};
200228
try {
201229
if (type === 'init') {
202-
// Accept init overrides
203230
if (payload && typeof payload === 'object') {
204-
INIT_OPTS.moduleUrl = payload.moduleUrl || INIT_OPTS.moduleUrl;
205-
INIT_OPTS.cameraParametersUrl = payload.cameraParametersUrl || INIT_OPTS.cameraParametersUrl;
206-
INIT_OPTS.wasmBaseUrl = payload.wasmBaseUrl || INIT_OPTS.wasmBaseUrl;
231+
INIT_OPTS.moduleUrl = payload.moduleUrl ?? INIT_OPTS.moduleUrl;
232+
INIT_OPTS.cameraParametersUrl = payload.cameraParametersUrl ?? INIT_OPTS.cameraParametersUrl;
233+
INIT_OPTS.wasmBaseUrl = payload.wasmBaseUrl ?? INIT_OPTS.wasmBaseUrl;
234+
if (typeof payload.minConfidence === 'number') {
235+
INIT_OPTS.minConfidence = payload.minConfidence;
236+
MIN_CONFIDENCE = payload.minConfidence;
237+
}
207238
}
208239
sendMessage({ type: 'ready' });
209240
return;
@@ -235,14 +266,11 @@ onMessage(async (ev) => {
235266

236267
if (type === 'processFrame') {
237268
const { imageBitmap, width, height } = payload || {};
238-
239-
// Browser path: only attempt init at controlled cadence (guard handles backoff)
240269
if (!isNodeWorker && imageBitmap) {
241270
try {
242271
const w = width || imageBitmap.width || 640;
243272
const h = height || imageBitmap.height || 480;
244273

245-
// Attempt init once; if it fails, guard prevents hammering it per-frame
246274
await initArtoolkit(w, h);
247275

248276
if (!offscreenCanvas || canvasW !== w || canvasH !== h) {
@@ -269,12 +297,10 @@ onMessage(async (ev) => {
269297
}
270298
} catch (err) {
271299
console.error('[Worker] processFrame error:', err);
272-
// No spam: let initArtoolkit handle error posting and backoff logging
273300
}
274301
return;
275302
}
276303

277-
// Node fallback: do nothing (no real AR in Node)
278304
await new Promise((r) => setTimeout(r, 5));
279305
return;
280306
}

0 commit comments

Comments
 (0)