Skip to content

Commit 15d7f16

Browse files
Copilotkalwalt
andcommitted
Implement ARToolKit WASM integration with worker pipeline
Co-authored-by: kalwalt <[email protected]>
1 parent 14ac499 commit 15d7f16

File tree

7 files changed

+963
-33
lines changed

7 files changed

+963
-33
lines changed

README.md

Lines changed: 245 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,260 @@
1-
# arjs-plugin-artoolkit
2-
```markdown
31
# @ar-js-org/arjs-plugin-artoolkit
42

5-
Minimal ARToolKit detection plugin scaffold for AR.js core.
3+
ARToolKit marker detection plugin for AR.js core with WebAssembly support.
4+
5+
## Features
6+
7+
- **Web Worker-based detection** - Runs marker detection in a separate thread for better performance
8+
- **ImageBitmap support** - Zero-copy frame transfer from main thread to worker (browser)
9+
- **Cross-platform** - Works in browsers (Worker) and Node.js (worker_threads)
10+
- **ARToolKit WASM integration** - Supports simple square marker detection
11+
- **Event-driven API** - Emits marker lifecycle events (found/updated/lost)
12+
13+
## Installation
14+
15+
```bash
16+
npm install @ar-js-org/arjs-plugin-artoolkit
17+
```
18+
19+
## ARToolKit WASM Setup
20+
21+
The plugin requires ARToolKit WASM binaries for real marker detection. Without them, the plugin runs in stub mode.
22+
23+
### Option 1: Using artoolkit5-js (Recommended)
24+
25+
```bash
26+
npm install artoolkit5-js
27+
```
28+
29+
The loader will automatically detect and use the WASM module.
30+
31+
### Option 2: Manual WASM Binary
32+
33+
Place the `artoolkit.wasm` file in `src/worker/artoolkit/`:
34+
35+
```
36+
src/worker/artoolkit/
37+
├── loader.js
38+
├── artoolkit.wasm # <- Place WASM binary here
39+
└── README.md
40+
```
41+
42+
See [src/worker/artoolkit/README.md](src/worker/artoolkit/README.md) for more details.
43+
44+
### Fallback Mode
45+
46+
If WASM is not available:
47+
- The plugin will initialize successfully with console warnings
48+
- Detection will return empty results
49+
- All other functionality works normally
650

751
## Usage
852

53+
### Basic Integration
54+
955
Register with the Engine plugin manager:
1056

1157
```js
1258
import { ArtoolkitPlugin } from '@ar-js-org/arjs-plugin-artoolkit';
1359

14-
engine.pluginManager.register('artoolkit', new ArtoolkitPlugin({ /* options */ }));
60+
const plugin = new ArtoolkitPlugin({
61+
worker: true, // Enable worker-based detection (default: true)
62+
lostThreshold: 5, // Frames before marker considered lost
63+
frameDurationMs: 100 // Expected frame duration in ms
64+
});
65+
66+
engine.pluginManager.register('artoolkit', plugin);
1567
await engine.pluginManager.enable('artoolkit');
1668
```
1769

70+
### Event Handling
71+
1872
The plugin emits events on the engine event bus:
19-
- `ar:markerFound`
20-
- `ar:markerUpdated`
21-
- `ar:markerLost`
2273

23-
This repo contains a skeleton. Detection and worker/WASM integration will be implemented in follow-up work.
24-
```
74+
```js
75+
// Marker first detected
76+
engine.eventBus.on('ar:markerFound', (data) => {
77+
console.log('Marker found:', data.id);
78+
console.log('Pose matrix:', data.poseMatrix); // Float32Array[16]
79+
console.log('Confidence:', data.confidence); // 0.0 - 1.0
80+
console.log('Corners:', data.corners); // [{x, y}, ...]
81+
});
82+
83+
// Marker updated (tracking)
84+
engine.eventBus.on('ar:markerUpdated', (data) => {
85+
// Same data structure as markerFound
86+
});
87+
88+
// Marker lost (not detected for lostThreshold frames)
89+
engine.eventBus.on('ar:markerLost', (data) => {
90+
console.log('Marker lost:', data.id);
91+
});
92+
93+
// Worker ready
94+
engine.eventBus.on('ar:workerReady', () => {
95+
console.log('Detection worker initialized');
96+
});
97+
98+
// Worker errors
99+
engine.eventBus.on('ar:workerError', (error) => {
100+
console.error('Worker error:', error);
101+
});
102+
```
103+
104+
### Sending Frames to Plugin
105+
106+
The plugin subscribes to `engine:update` events and expects frames with ImageBitmap:
107+
108+
```js
109+
// Capture from video element
110+
const video = document.getElementById('video');
111+
const imageBitmap = await createImageBitmap(video);
112+
113+
// Send to plugin via engine update
114+
engine.eventBus.emit('engine:update', {
115+
id: frameId,
116+
timestamp: Date.now(),
117+
imageBitmap: imageBitmap, // Transferred to worker (zero-copy)
118+
width: imageBitmap.width,
119+
height: imageBitmap.height
120+
});
121+
```
122+
123+
**Important:** The ImageBitmap is transferred to the worker and cannot be used after the emit. The worker will automatically close it after processing.
124+
125+
## Examples
126+
127+
### Simple Marker Detection
128+
129+
A complete webcam-based marker detection example is available in `examples/simple-marker/`:
130+
131+
```bash
132+
# Start a local server (requires Node.js)
133+
npx http-server examples/simple-marker -p 8080
134+
135+
# Or use Python
136+
python3 -m http.server 8080 --directory examples/simple-marker
137+
```
138+
139+
Then open http://localhost:8080 in your browser.
140+
141+
The example demonstrates:
142+
- Webcam capture with getUserMedia
143+
- ImageBitmap creation from video frames
144+
- Frame submission to the plugin
145+
- Event handling and console output
146+
- Status monitoring
147+
148+
See [examples/simple-marker/script.js](examples/simple-marker/script.js) for the implementation.
149+
150+
## Development
151+
152+
### Install Dependencies
153+
154+
```bash
155+
npm install
156+
```
157+
158+
### Run Tests
159+
160+
```bash
161+
npm test
162+
```
163+
164+
### Build
165+
166+
```bash
167+
npm run build
168+
```
169+
170+
## Architecture
171+
172+
### Detection Pipeline (Browser)
173+
174+
```
175+
Video/Camera
176+
↓ createImageBitmap()
177+
ImageBitmap
178+
↓ postMessage (transfer)
179+
Worker: OffscreenCanvas
180+
↓ drawImage()
181+
Worker: ImageData
182+
↓ ARToolKit WASM
183+
Worker: Detections
184+
↓ postMessage
185+
Plugin: Event Emission
186+
```
187+
188+
### Worker Implementation
189+
190+
- **Platform-agnostic**: Supports both browser Worker and Node worker_threads
191+
- **OffscreenCanvas**: Used for ImageBitmap → ImageData conversion
192+
- **Resource management**: Automatically closes ImageBitmap after processing
193+
- **Defensive**: Falls back gracefully if WASM is not available
194+
195+
### Plugin Features
196+
197+
- **Marker lifecycle tracking**: Maintains state for found/updated/lost transitions
198+
- **Configurable thresholds**: Adjustable lost detection timing
199+
- **Zero-copy transfer**: Uses ImageBitmap transferables in browsers
200+
- **Cross-platform**: Works in both browser and Node.js environments
201+
202+
## API Reference
203+
204+
### ArtoolkitPlugin
205+
206+
#### Constructor Options
207+
208+
```typescript
209+
{
210+
worker?: boolean; // Enable worker (default: true)
211+
lostThreshold?: number; // Frames before lost (default: 5)
212+
frameDurationMs?: number; // Frame duration (default: 200ms)
213+
sweepIntervalMs?: number; // Lost marker check interval (default: 100ms)
214+
}
215+
```
216+
217+
#### Methods
218+
219+
- `async init(core)` - Initialize plugin with engine core
220+
- `async enable()` - Enable plugin and start worker
221+
- `async disable()` - Disable plugin and stop worker
222+
- `dispose()` - Alias for disable()
223+
- `getMarkerState(markerId)` - Get current state of a marker
224+
225+
#### Events Emitted
226+
227+
- `ar:workerReady` - Worker initialized and ready
228+
- `ar:markerFound` - New marker detected
229+
- `ar:markerUpdated` - Tracked marker updated
230+
- `ar:markerLost` - Marker no longer detected
231+
- `ar:workerError` - Worker error occurred
232+
233+
## Troubleshooting
234+
235+
### No detections
236+
237+
1. Check browser console for WASM loading warnings
238+
2. Verify WASM binary is in correct location (see Setup)
239+
3. Ensure camera feed is active and markers are visible
240+
4. Check marker quality and lighting conditions
241+
242+
### Worker not starting
243+
244+
1. Verify browser supports Web Workers and ES modules
245+
2. Check for CSP (Content Security Policy) restrictions
246+
3. Ensure script is served over HTTPS or localhost
247+
248+
### Performance issues
249+
250+
1. Reduce frame rate (increase timeout in example)
251+
2. Lower camera resolution
252+
3. Check CPU usage - detection is compute-intensive
253+
254+
## License
255+
256+
MIT
257+
258+
## Contributing
259+
260+
Contributions welcome! Please follow the existing code style and add tests for new features.

0 commit comments

Comments
 (0)