Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: CI

on:
push:
branches: [dev, master]
pull_request:
branches: [dev, master]

# Cancel superseded runs on the same branch / PR.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
unit:
name: Unit tests (Vitest)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
- run: npm ci
- run: npm test

build-and-e2e:
name: Build wasm + JS, run Playwright
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm

- uses: mymindstorm/setup-emsdk@v14
with:
version: 3.1.61
actions-cache-folder: emsdk-cache

- run: npm ci

- name: Build wasm
run: npm run build

- name: Build JS bundles
run: npm run build-es6

- name: Install Playwright browsers
run: npx playwright install --with-deps chromium

- name: Run Playwright e2e
run: npm run test:e2e

- name: Upload Playwright report on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 7
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@ node_modules
build/libar.bc
build/libar.o
doc/*
# Test outputs
test-results/
playwright-report/
playwright/.cache/
coverage/
4 changes: 4 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ emscripten/**/*
example/*
tools/
tools/*
tests/
test-result/
build/*.bc
build/*.o
*.zip
docs/**/*
docs/
.github/
231 changes: 214 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,225 @@
# FeatureSET-Display

A little javascript lib for display .iset and .fset .fset3 files
> Display the contents of NFT marker files (`.iset`, `.fset`, `.fset3`) — the
> image preview plus the feature points used for detection and tracking — in
> a browser, via WebAssembly.

## Introduction of the project
Useful for inspecting markers generated with
[NFT-Marker-Creator](https://github.com/Carnaux/NFT-Marker-Creator) or [Nft-Marker-Creator-App](https://github.com/webarkit/Nft-Marker-Creator-App) :
see at a glance whether a marker has enough trackable features, whether
they're clustered in a corner, or whether the source image lost
contrast during the dataset build.

With this little library it is possible to display NFT markers (.fset .iset and .fset3) files, generated with [NFT-Marker-Creator](https://github.com/.Carnaux/NFT-Marker-Creator). For now it may show you the image set from .iset file and the feature points from .fset and .fset3 files. Very useful utility to understand the stength and key points of the NFT marker.
---

### List of working features
- Display of the first the ImageSet.
- Display the features points used in identifying and features points used in continuous tracking.
## What you get

Display of various imageSet and FeatureSet infos in the console:
For any marker triplet (`name.iset` + `name.fset` + `name.fset3`),
the library:

- NFT number of ImageSet
- NFT dpi
- NFT marker width
- NFT marker height
- NFT number of Feature sets
- NFT number of feature points
- Reads the imageSet preview into an HTML5 canvas.
- Overlays the detection feature points as **light green** circles.
- Overlays the tracking feature points as **small red** circles.
- Logs the marker dimensions, DPI, and feature-point counts to the
browser console.

### List of features to add
## Install

- display the ImageSet (the entire array of images as a choice)
```bash
npm install @webarkit/featureset-display
```

### Example
Or load the UMD bundle directly:

An example is included in the **example** folder. Run a http server (python or node) and open the **example.html** file.
```html
<script src="https://unpkg.com/@webarkit/featureset-display"></script>
```

## Quick start (ES module)

```html
<script type="module">
import ARFsetModule from '@webarkit/featureset-display';

const ar = new ARFsetModule.ARFset();
await ar.initialize();
await ar.loadNFTMarker('path/to/marker'); // no extension
ar.display();
</script>
```

The library will create a `<canvas id="iSet">` element in the document
body and render the marker preview into it. Use `attachCanvas(id)`
before `initialize()` to mount it inside a specific container instead:

```js
const ar = new ARFsetModule.ARFset();
ar.attachCanvas('my-container');
await ar.initialize();
await ar.loadNFTMarker('path/to/marker');
ar.display();
```

## Quick start (`<script>` tag)

```html
<script src="dist/ARFset.umd.js"></script>
<script>
const ar = new ARFset.ARFset();
ar.initialize().then(() => {
ar.loadNFTMarker('path/to/marker');
ar.display();
});
</script>
```

`window.ARFset` resolves to the same default export as the ESM build.

## API

### `new ARFset(options?)`

| Option | Type | Default | Description |
| -------- | ------ | ------- | --------------------------- |
| `width` | number | `893` | Initial wasm canvas width. |
| `height` | number | `1117` | Initial wasm canvas height. |

These set the wasm-side initial memory layout. The on-screen canvas is
resized at marker-load time to the actual reported marker dimensions,
so the defaults don't need to match your marker.

### `await ar.initialize()`

Loads the WebAssembly runtime and prepares the canvas. Must be awaited
before anything else.

### `ar.attachCanvas(id)`

Mount the canvas inside an existing DOM element instead of `body`.
Call before `initialize()`.

### `await ar.loadNFTMarker(urlPrefix)`

Fetches `urlPrefix.iset`, `urlPrefix.fset`, and `urlPrefix.fset3`,
loads them into the wasm filesystem, and dispatches an `'nftMarker'`
`CustomEvent` on `document` when ready.

### `await ar.loadNFTMarkerBlob([isetUrl, fset3Url, fsetUrl])`

Same as above but for user-uploaded data — pass an array of three
URLs (or data URLs from `FileReader.readAsDataURL`) in the order
`[iset, fset3, fset]`.

### `ar.display()`

Subscribe to the `'nftMarker'` event and render the marker preview
plus feature-point circles whenever a marker loads.

### Events dispatched on `document`

| Event | Detail |
| ----------- | ------------------------------------------------------------------- |
| `nftMarker` | `{ numIset, widthNFT, heightNFT, dpi, numFpoints, nftPoints, ... }` |
| `imageEv` | (no detail) — fired after the canvas has been painted |

## How it works

```mermaid
flowchart LR
user[User code] -->|loadNFTMarker URL| js[ARFset class JS]
js -->|fetch .iset .fset .fset3| net[network]
net -->|Uint8Array| fs[wasm FS]
js -->|_readNFTMarker arId path| wasm[ARimageFsetDisplay wasm]
wasm -->|reads| fs
wasm -->|nftMarker struct + nftPoints + nftFsetPoints| js
js -->|nftMarker event| display[display handler]
display -->|putImageData + arc| canvas[canvas iSet]
```

The C++ side ([`emscripten/ARimageFsetDisplay.cpp`](emscripten/ARimageFsetDisplay.cpp))
links against [WebARKitLib](https://github.com/webarkit/WebARKitLib)
(a maintained fork of jsartoolkit5 / ARToolKit5) to parse the
`.iset` / `.fset` / `.fset3` files and extract feature points. The
result is returned through an embind `value_object`, so JS reads the
data directly from the returned struct — no `EM_ASM` side-channel.

## Build from source

Prerequisites:

- Node.js 22+
- [emsdk](https://emscripten.org/docs/getting_started/downloads.html)
with `EMSDK` set (run `emsdk_env.bat` / `source emsdk_env.sh` once
per shell).
- Python 3 on `PATH` (needed by `emcc.py` on Windows).
- Git submodules initialised:

```bash
git submodule update --init --recursive
```

Then:

```bash
npm install # devDependencies (vite, vitest, playwright)
npm run build # wasm bundles -> build/
npm run build-es6 # JS bundle -> dist/
npm test # unit tests (Vitest)
npm run test:e2e # browser smoke test (Playwright)
```

To try the example locally:

```bash
npm run serve
# open http://localhost:8080/example/example_es6.html
```

## Migration from 0.3.x

`0.4.0` removed the legacy global `window.ARfset` API and the asm.js
build targets.

**Before (0.3.x):**

```html
<script src="build/arfset.min.js"></script>
<script>
const ar = new ARfset();
ar.loadNFTMarker('marker', (nft) => { ... });
</script>
```

**After (0.4.x), ESM:**

```html
<script type="module">
import ARFsetModule from '@webarkit/featureset-display';
const ar = new ARFsetModule.ARFset();
await ar.initialize();
await ar.loadNFTMarker('marker');
ar.display();
</script>
```

**After (0.4.x), classic script:**

```html
<script src="dist/ARFset.umd.js"></script>
<script>
const ar = new ARFset.ARFset();
ar.initialize().then(() => {
ar.loadNFTMarker('marker');
ar.display();
});
</script>
```

Note the new namespace: `window.ARFset` exposes `{ ARFset: class }`,
so the class is reached as `ARFset.ARFset`. The class also requires
an `initialize()` await before `loadNFTMarker`, where the legacy
global used to dispatch a `FeatureSETDisplay-loaded` event instead.

## License

LGPL-3.0 — see [LICENSE.txt](LICENSE.txt).
2,641 changes: 2,639 additions & 2 deletions dist/ARFset.js

Large diffs are not rendered by default.

14 changes: 12 additions & 2 deletions dist/ARFset.umd.js

Large diffs are not rendered by default.

Loading
Loading