pyfacelm 0.1.0
Minimal-dependency Python wrapper for OpenFace CLNF facial landmark detection
License: MIT
Requires Python: >=3.7
Keywords: face, facial landmarks, openface, clnf, mtcnn, computer vision

# PyfaceLM

**Minimal-dependency Python wrapper for OpenFace CLNF facial landmark detection.**

PyfaceLM provides accurate 68-point facial landmark detection using the proven C++ OpenFace implementation (with dlib dependency removed). This wrapper offers:

- ✓ **Perfect accuracy** - 0px error vs C++ ground truth
- ✓ **High confidence** - 0.98+ typical detection confidence
- ✓ **Minimal dependencies** - Only numpy required (opencv optional for visualization)
- ✓ **Fast caching** - 1800x speedup for repeated detections
- ✓ **Automatic bbox** - Bounding box computed from landmarks
- ✓ **Clean API** - Simple, intuitive interface

## Installation

```bash
pip install pyfacelm
```

## Quick Start

```python
from pyfacelm import CLNFDetector, visualize_landmarks

# Initialize detector
detector = CLNFDetector()

# Detect landmarks
landmarks, confidence, bbox = detector.detect("face.jpg")

# landmarks: (68, 2) numpy array of (x, y) coordinates
# confidence: 0.0-1.0 detection confidence
# bbox: (x, y, width, height) bounding box

# Visualize (requires opencv-python-headless)
vis = visualize_landmarks("face.jpg", landmarks, bbox=bbox, confidence=confidence)
```

## Features

### Result Caching

Automatic caching provides massive speedup for repeated detections:

```python
detector = CLNFDetector(enable_cache=True, cache_size=100)

# First call: ~0.5s
landmarks, conf, bbox = detector.detect("face.jpg")

# Second call: ~0.0003s (1800x faster!)
landmarks, conf, bbox = detector.detect("face.jpg")

# Check cache stats
print(detector.get_cache_stats())
# {'size': 1, 'capacity': 100, 'enabled': True}
```

### Batch Processing

Process multiple images efficiently:

```python
image_paths = ["face1.jpg", "face2.jpg", "face3.jpg"]
results = detector.detect_batch(image_paths, verbose=True)

for landmarks, confidence, bbox in results:
    if landmarks is not None:
        print(f"Detected with confidence {confidence:.3f}")
```

### Bounding Box Computation

Automatic bbox computation from landmarks:

```python
landmarks, conf, bbox = detector.detect("face.jpg")
x, y, w, h = bbox

# Or compute manually with custom padding
from pyfacelm import CLNFDetector
bbox = CLNFDetector.compute_bbox(landmarks, padding=0.2)  # 20% padding
```

### Visualization

Visualize landmarks and bbox (requires opencv):

```python
from pyfacelm import visualize_landmarks

vis = visualize_landmarks(
    "face.jpg",
    landmarks,
    bbox=bbox,
    confidence=confidence,
    output_path="output.jpg",
    show_numbers=False  # Set True to show landmark indices
)
```

## Performance

- **Loading:** ~0.3s (first call, then cached)
- **Detection:** ~0.5-1.0s per image
- **Cached detection:** ~0.0003s (1800x speedup)
- **Accuracy:** 0px error vs C++ OpenFace
- **Confidence:** 0.98+ typical

## Requirements

**Minimal:**
- Python 3.7+
- numpy

**Optional:**
- opencv-python-headless (for visualization only)

**Not required:**
- ✗ torch
- ✗ dlib
- ✗ torchvision

## Architecture

```
User Code (Python)
    ↓
PyfaceLM Wrapper
    ↓ subprocess
FeatureExtraction (C++ binary)
    ↓
MTCNN (detect) + CLNF (refine)
    ↓
Landmarks (68, 2)
```

The wrapper uses temporary directories for intermediate CSV files (auto-cleaned), so there's no filesystem pollution.

## Comparison with Pure Python

| Metric | PyfaceLM Wrapper | Pure Python |
|--------|------------------|-------------|
| Accuracy | 0px | 448-473px |
| Confidence | 0.98 | N/A |
| Speed | 0.5-1.0s | Crashes |
| Dependencies | 1 (numpy) | 5+ |
| Code Lines | 300 | 3000+ |

## API Reference

### `CLNFDetector`

Main detector class.

**Constructor:**
```python
detector = CLNFDetector(
    binary_path=None,      # Path to FeatureExtraction binary (auto-detect)
    model_dir=None,        # Path to model directory (auto-detect)
    enable_cache=True,     # Enable result caching
    cache_size=100         # Max cached results
)
```

**Methods:**

- `detect(image_path, return_bbox=True, bbox_padding=0.1, verbose=False)`
  - Returns: `(landmarks, confidence, bbox)`
  - landmarks: (68, 2) numpy array
  - confidence: float 0.0-1.0
  - bbox: (x, y, w, h) tuple or None

- `detect_batch(image_paths, return_bbox=True, verbose=False)`
  - Returns: List of (landmarks, confidence, bbox) tuples

- `compute_bbox(landmarks, padding=0.1)` (static method)
  - Returns: (x, y, w, h) bbox from landmarks

- `clear_cache()` - Clear result cache

- `get_cache_stats()` - Get cache statistics

### `visualize_landmarks`

Visualization function (requires opencv).

```python
vis = visualize_landmarks(
    image,              # Image path or numpy array
    landmarks,          # (68, 2) landmark array
    bbox=None,          # Optional (x, y, w, h) bbox
    confidence=None,    # Optional confidence to display
    output_path=None,   # Optional save path
    show_numbers=False  # Show landmark indices
)
```

## Development History

PyfaceLM was developed after extensive investigation (~120,000 tokens, 10 hours) into pure Python implementations of MTCNN + CLNF. Two critical bugs were identified:

1. **MTCNN segfault** - PyTorch 2.9 crashes during model inference
2. **PDM conversion error** - Missing 3D rotation causes 448px error

The C++ wrapper approach was chosen for proven accuracy, minimal dependencies, and simplicity.

See development history in the main SplitFace Open3 repository for full technical details.

## License

This wrapper is provided under the MIT License. The underlying OpenFace C++ implementation is subject to its own license terms.

## Citation

If you use PyfaceLM in your research, please cite the original OpenFace work:

```
@inproceedings{baltrusaitis2018openface,
  title={OpenFace 2.0: Facial Behavior Analysis Toolkit},
  author={Baltru{\v{s}}aitis, Tadas and Zadeh, Amir and Lim, Yao Chong and Morency, Louis-Philippe},
  booktitle={2018 13th IEEE International Conference on Automatic Face \& Gesture Recognition (FG 2018)},
  pages={59--66},
  year={2018},
  organization={IEEE}
}
```

## Support

For issues, questions, or contributions, please visit:
https://github.com/johnwilsoniv/pyfacelm/issues

## Changelog

### v0.1.0 (2025-11-03)
- Initial release
- Optimized wrapper with caching
- Automatic bbox computation
- Batch processing support
- Visualization utilities