# Camera Navigation Implementation

## Overview
Client-side camera-based exhibit recognition using Alpine.js, Livewire, and perceptual hashing.

## Features
- ✅ Pure client-side image matching (no server processing)
- ✅ Real-time video feed with camera access via `getUserMedia()`
- ✅ Automatic frame capture every 2 seconds
- ✅ Perceptual hash (average hash algorithm) with Hamming distance
- ✅ Match threshold: 15 bits (adjustable)
- ✅ Graceful camera permission handling
- ✅ Auto-redirect to matched exhibit

## Files Created

### 1. Livewire Component
**File**: `app/Livewire/CameraNavigation.php`
- Loads all active exhibits for the venue
- Provides exhibit data with image paths to the view
- Handles `loadExhibit($exhibitId)` method to redirect to matched exhibit

### 2. Blade View
**File**: `resources/views/livewire/camera-navigation.blade.php`
- Single root element wrapper for Livewire compatibility
- Alpine.js component with camera logic
- Live video feed display
- Frame capture and hash computation
- Visual feedback (scanning animation, match confirmation)

### 3. Route
**Route**: `/app/{city}/{district}/{venue}/camera`
**Name**: `app.venues.camera`

### 4. Layout Updates
**File**: `resources/views/layouts/app.blade.php`
- Added `@livewireStyles` in `<head>`
- Added `@livewireScripts` before `</body>`

## How It Works

### 1. Initialization
```javascript
// Alpine.js component initializes
init() → loadReferenceImages() → requestCamera() → startScanning()
```

### 2. Image Hashing Algorithm
- **Average Hash** (8×8 = 64 bits):
  1. Resize image to 8×8 pixels
  2. Convert to grayscale
  3. Calculate average pixel value
  4. Create binary string: '1' if pixel > average, '0' otherwise
  5. Result: 64-character binary string (e.g., '1010110...')

### 3. Frame Matching
Every 2 seconds:
1. Capture video frame to canvas
2. Compute frame hash
3. Compare with all preloaded reference hashes
4. If Hamming distance ≤ 15 bits → **Match found!**
5. Stop camera and redirect to exhibit

### 4. Hamming Distance
Counts differing bits between two hashes:
```javascript
hammingDistance('10101010', '10101110') // Returns 1 (1 bit different)
```

## Usage

### Access Camera Navigation
```blade
<a href="{{ route('app.venues.camera', [
    'city' => $city, 
    'district' => $district, 
    'venue' => $venue
]) }}">
    Camera Navigation
</a>
```

### Example URL
```
/app/istanbul/besiktas/dolmabahce-sarayi/camera
```

## Configuration

### Adjust Match Sensitivity
In `camera-navigation.blade.php`, modify:
```javascript
const THRESHOLD = 15; // Lower = stricter matching (0-64)
```

**Recommended values**:
- **5-10**: Very strict (nearly identical images)
- **10-15**: Balanced (recommended)
- **15-20**: Lenient (allows more variation)
- **20+**: Very lenient (may cause false positives)

### Adjust Scan Frequency
```javascript
// Scan every 2 seconds (default)
this.scanInterval = setInterval(() => {
    this.scanFrame();
}, 2000); // Change to 1000 for 1 second, 3000 for 3 seconds
```

### Camera Settings
```javascript
video: {
    facingMode: 'environment', // 'user' for front camera
    width: { ideal: 1280 },
    height: { ideal: 720 }
}
```

## Exhibit Image Requirements

### Image Storage
- **Location**: `public/uploads/img/{city_id}/{venue_id}/m/{order}.jpg`
- **Database**: Stored in `exhibits.image_paths` as JSON array
- **Access**: First image via `$exhibit->image_paths[0]`

### Best Practices
1. **High contrast images** work best
2. **Consistent lighting** in reference photos
3. **Avoid highly similar exhibits** (use unique visual features)
4. **JPEG quality**: 80%+ recommended
5. **Size**: 800×600px minimum

## Troubleshooting

### Camera Not Working
1. **HTTPS Required**: Camera API only works on HTTPS or localhost
2. **Check permissions**: Browser settings → Camera access
3. **Mobile**: May need to allow camera in app settings

### No Matches Found
1. **Lower threshold** (increase from 15 to 20)
2. **Check image paths** in Exhibit model
3. **Verify images are accessible** (check CORS if different domain)
4. **Test with high-contrast images** first

### False Matches
1. **Increase threshold** (decrease from 15 to 10)
2. **Use higher resolution reference images**
3. **Ensure exhibits have distinct visual features**

## Browser Support
- ✅ Chrome/Edge 53+
- ✅ Firefox 36+
- ✅ Safari 11+
- ✅ Mobile browsers (iOS Safari 11+, Chrome Android)

## Privacy & Performance
- ✅ **100% client-side** - no images sent to server
- ✅ **No external APIs** - all processing in-browser
- ✅ **Lightweight** - average hash is very fast (~5ms per frame)
- ✅ **No dependencies** - pure JavaScript implementation

## Future Enhancements
- [ ] Add confidence percentage display
- [ ] Support for OpenCV.js template matching (heavier but more accurate)
- [ ] Multi-exhibit display when multiple matches found
- [ ] Save scan history to local storage
- [ ] Offline PWA support

## Testing Checklist
- [ ] Test on desktop Chrome
- [ ] Test on mobile (iOS Safari, Android Chrome)
- [ ] Test camera permission denial
- [ ] Test with multiple exhibits
- [ ] Test match accuracy with real images
- [ ] Test lighting conditions (bright, dim)
- [ ] Test different camera angles

## License
Part of the Digital Museum Guide project.
