# @liveposter/ar-compiler

AR target compiler for Liveposter - generates `.mind` files from images for use with MindAR.

**Note:** This is an internal package within the Liveposter monorepo. When you install `liveposter`, this package is included automatically.

## Features

- 🎯 **Automatic target generation** from poster specs and lists
- 📦 **Batch processing** of multiple posters
- 🔄 **Smart caching** - skips existing `.mind` files
- 🛠️ **Developer-friendly API** with modular utilities
- 🖥️ **CLI tool** for command-line usage

## Installation

```bash
# Install the main Liveposter package (includes ar-compiler)
npm install liveposter

# AR compilation requires additional dependencies (install only when needed)
npm install @tensorflow/tfjs @msgpack/msgpack canvas mathjs ml-matrix svd-js tinyqueue @mediapipe/tasks-vision
```

**Note:** The AR dependencies are optional and only required if you use `ar-compile-targets`. Regular Liveposter animations work without them. This keeps the base package lightweight (~2 MB) while allowing AR users to opt-in to the heavier dependencies (~200+ MB) when needed.

## CLI Usage

### Basic Commands

```bash
# Compile targets from a poster spec
npx liveposter ar-compile-targets poster.json

# Compile from a poster list
npx liveposter ar-compile-targets poster-list.json

# Force regenerate existing targets (overwrites all)
npx liveposter ar-compile-targets --force poster.json

# Preview what would be compiled (dry run)
npx liveposter ar-compile-targets --dry-run poster-list.json

# Verbose output with detailed progress
npx liveposter ar-compile-targets -v poster.json
```

### Options

- `-f, --force` - Overwrite existing .mind files (useful when source images change)
- `-d, --dry-run` - Preview what would be compiled without actually compiling
- `-v, --verbose` - Show detailed progress including total image count and per-poster breakdown
- `-h, --help` - Display help message

### How It Works

The tool automatically:
1. Scans your poster spec(s) for JPG, JPEG, PNG, GIF, BMP, and WEBP images
2. Generates `.mind` files alongside each image (`image.jpg` → `image.mind`)
3. **Skips images that already have `.mind` files** (unless `--force` is used)
4. Shows real-time progress: `[22/40] ⏳ Processing: image.jpg... 60%`
5. Displays a summary with compiled, skipped, and failed counts

### Example Output

```
🎯 Starting AR target compilation...

  [1/40] ⊘ Skipped: ./images/image-1.jpg (already exists)
  [2/40] ⏳ Processing: ./images/image-2.jpg...
  [2/40] ✓ Compiled: ./images/image-2.jpg → image-2.mind (849.4 KB)
  ...

======================================================================
SUMMARY
======================================================================
  ✓ Compiled: 15
  ⊘ Skipped:  25 (already exist)
  ⏱️  Time:     42.3s
======================================================================

DETAILED RESULTS:
──────────────────────────────────────────────────────────────────────
✓ Compiled (15):
  ./images/image-2.jpg → image-2.mind (849.4 KB)
  ./images/poster.jpg → poster.mind (756.2 KB)
  ...

⊘ Skipped (25):
  ./images/image-1.jpg
  ./images/image-3.jpg
  ...
──────────────────────────────────────────────────────────────────────
```

**Status Indicators:**
- ✓ **Compiled** - Successfully generated .mind file
- ⊘ **Skipped** - Already has .mind file (use `--force` to regenerate)
- ⏳ **Processing** - Currently compiling (shows progress %)
- ❌ **Failed** - Compilation error (shows error message)

## Programmatic Usage

When using the `liveposter` npm package, import from the internal path:

### Compile from Poster Spec

```javascript
import { compileFromSpec } from 'liveposter/packages/ar-compiler/src/index.js';

// Compile all images from a poster spec
const result = await compileFromSpec('poster.json', {
  verbose: true,
  force: false
});

console.log(result);
// { processed: 3, skipped: 0, errors: 0 }
```

### Compile from Poster List

```javascript
import { compileFromList } from 'liveposter/packages/ar-compiler/src/index.js';

// Compile all images from multiple posters
const result = await compileFromList('poster-list.json', {
  verbose: true
});

console.log(result);
// { processed: 15, skipped: 2, errors: 0 }
```

### Compile Single Image

```javascript
import { compileTarget } from 'liveposter/packages/ar-compiler/src/index.js';

// Compile a single image
const result = await compileTarget('image.jpg', {
  output: 'custom-name.mind',  // Optional
  verbose: true,
  force: false
});

console.log(result);
// { outputPath: '/path/to/custom-name.mind', fileSize: 136084, skipped: false }
```

### Auto-detect and Compile

```javascript
import { compileAuto } from 'liveposter/packages/ar-compiler/src/index.js';

// Automatically detects if input is a spec or list
const result = await compileAuto('input.json', {
  verbose: true,
  dryRun: false
});
```

## Utility Functions

### Extract Images from Spec

```javascript
import { processSpec, extractImagePaths } from 'liveposter/packages/ar-compiler/src/index.js';

// Get all images from a spec
const { basePath, config, imagePaths } = processSpec('poster.json');

console.log(imagePaths);
// ['image1.jpg', 'image2.png', 'image3.webp']
```

### Process Poster List

```javascript
import { processPosterList, getEnabledPosters } from 'liveposter/packages/ar-compiler/src/index.js';

// Parse a poster list
const { basePath, posters, metadata } = processPosterList('list.json');

// Filter enabled posters
const enabled = getEnabledPosters(posters);
```

### Validate Images

```javascript
import { validateImagePaths } from 'liveposter/packages/ar-compiler/src/index.js';

const { valid, missing } = validateImagePaths('/base/path', [
  'image1.jpg',
  'image2.png',
  'missing.jpg'
]);

console.log(valid);   // ['image1.jpg', 'image2.png']
console.log(missing); // ['missing.jpg']
```

## API Reference

### Core Functions

- `compileTarget(imagePath, options)` - Compile single image
- `compileTargets(imagePaths, options)` - Compile multiple images
- `compileFromSpec(specPath, options)` - Compile from poster spec
- `compileFromList(listPath, options)` - Compile from poster list
- `compileAuto(inputPath, options)` - Auto-detect and compile

### Spec Processing

- `loadJsonFile(filePath)` - Load and parse JSON/JSONC
- `extractImagePaths(config)` - Extract image paths from config
- `isImageFile(path)` - Check if path is an image
- `isVideoFile(path)` - Check if path is a video
- `getMindFilePath(imagePath)` - Get `.mind` path for image
- `processSpec(specPath)` - Process a poster spec
- `validateImagePaths(basePath, imagePaths)` - Validate image existence

### List Processing

- `processPosterList(listPath)` - Parse poster list
- `getEnabledPosters(posters)` - Filter enabled posters
- `getConfigPath(posterItem)` - Extract config path
- `getPosterIdentifier(posterItem, configPath, index)` - Generate ID
- `validatePosterList(basePath, posters)` - Validate poster list

## Options

All compilation functions accept these options:

```typescript
{
  verbose?: boolean;     // Enable verbose logging (default: false)
  force?: boolean;       // Overwrite existing .mind files (default: false)
  dryRun?: boolean;      // Preview without compiling (default: false)
  onProgress?: (percent: number) => void;  // Progress callback
}
```

## How It Works

### High-Level Process

1. **Extracts** all image paths from your poster specifications
2. **Filters** for supported formats (JPG, PNG, GIF, BMP, WEBP)
3. **Skips** remote URLs and video files automatically
4. **Compiles** each image using MindAR's offline compiler
5. **Saves** `.mind` files alongside original images (`image.jpg` → `image.mind`)
6. **Caches** results - won't recompile unless `--force` is used

### Technical Details: What Happens During Compilation

The `.mind` file generation is a computer vision process that extracts visual features from your images:

**Phase 1: Feature Detection** (0-50% progress)
- Converts image to greyscale: `(R + G + B) / 3`
- Creates multi-scale image pyramids with scale factor `2^(1/3)`
  - Multiple scales from minimum (100px) to original size for robust matching
  - Fixed 2-scale pyramid (256px, 128px) for fast real-time tracking
- Detects interest points (maxima/minima) at each scale using TensorFlow.js
- Applies hierarchical clustering to organize feature points

**Phase 2: Tracking Data Extraction** (50-100% progress)
- Processes smaller resolution images (256px and 128px)
- Extracts optimized features for real-time AR tracking
- Runs synchronously on CPU (no GPU required in Node.js)

**Phase 3: Binary Encoding**
- Serializes data using MessagePack format
- Typical output: 50-500 KB per image
- Contains:
  - Image dimensions
  - Feature points for image matching
  - Optimized features for AR tracking
  - **NOT** pixel data (original image still needed)

**Result:** A `.mind` file is a "feature map" that helps MindAR recognize your physical poster during AR sessions. It contains abstract mathematical representations of visual features, not the actual image pixels.

**Performance:**
- Time: ~5-30 seconds per image (CPU-based)
- Memory: Peaks during feature detection
- Output: Much smaller than original images

## Requirements

- Node.js >= 14.0.0
- `mind-ar` package (peer dependency)

## Related Internal Packages

- `packages/ar-viewer` - Lightweight AR scene builder (browser)
- `packages/ar-minimal-app` - Deployment tool for AR experiences
- `packages/mind-ar-js-felix` - MindAR image tracking library (forked)

## License

MIT
