PIV-compliant facial processing CLI tool for .NET. Converts photos to FIPS 201-3 compliant ID card format with JPEG 2000 ROI encoding.
$ dotnet add package FaceOFFx.Cli
"I want to take his face... off." — Castor Troy, Face/Off (1997)
Quick Start • Installation • Samples • API • CLI • Configuration
FaceOFFx is a specialized, high-performance facial processing library for .NET, focused on PIV (Personal Identity Verification) compatibility for issuing credentials that follow government standards (FIPS 201). Derived from the excellent * FaceONNX* library, FaceOFFx extends its capabilities with PIV-specific transformations, FIPS 201-3 compatibility features, and advanced JPEG 2000 ROI encoding.
The new v2.0 API provides automatic service management with standard .NET error handling:
using FaceOFFx.Infrastructure.Services;
// Simplest: Default PIV processing (20KB target)
byte[] imageData = File.ReadAllBytes("photo.jpg");
var result = await FacialImageEncoder.ProcessAsync(imageData);
File.WriteAllBytes("output.png", result.ImageData);
Console.WriteLine($"Size: {result.Metadata.FileSize:N0} bytes");
// TWIC processing (14KB maximum for card compatibility)
var twicResult = await FacialImageEncoder.ProcessForTwicAsync(imageData);
// Custom target size
var customResult = await FacialImageEncoder.ProcessToSizeAsync(imageData, 25000);
// Fixed compression rate
var rateResult = await FacialImageEncoder.ProcessWithRateAsync(imageData, 1.5f);
// Try pattern for error handling
var (success, result, error) = await FacialImageEncoder.TryProcessAsync(imageData);
if (success)
{
Console.WriteLine($"Processed to {result!.Metadata.FileSize} bytes");
}
else
{
Console.WriteLine($"Processing failed: {error}");
}
| Preset | Target Size | Use Case |
|---|---|---|
ProcessingOptions.TwicMax | 14KB | TWIC cards maximum size |
ProcessingOptions.PivMin | 12KB | PIV minimum size |
ProcessingOptions.PivBalanced | 22KB | Standard PIV compatibility |
ProcessingOptions.PivHigh | 30KB | Enhanced PIV quality |
ProcessingOptions.PivVeryHigh | 50KB | Premium quality |
ProcessingOptions.Archival | 4.0 bpp | Long-term preservation |
ProcessingOptions.Fast | 0.5 bpp | Minimal file size |
For 420×560 images:
| Rate (bpp) | Approx. Size | Quality Level |
|---|---|---|
| 0.40 | 12KB | PIV minimum |
| 0.48 | 14KB | TWIC maximum |
| 0.70 | 20KB | PIV standard |
| 1.00 | 29KB | Enhanced |
| 1.50 | 45KB | High quality |
| 2.00 | 60KB | Premium |
| 4.00 | 118KB | Archival |
# Install from NuGet
dotnet tool install --global FaceOFFx.Cli
# Update to latest version
dotnet tool update --global FaceOFFx.Cli
# Package Manager
dotnet add package FaceOFFx
# Package Manager Console
Install-Package FaceOFFx
See the power of FaceOFFx with these real-world examples demonstrating our four quality presets. Additional samples for all images and presets are available in the docs/samples/ directory.
| Quality Preset | Original | PIV Processed | ROI Visualization |
|---|---|---|---|
| PIV High (28.8KB) | ![]() | ![]() | ![]() |
| PIV Balanced (20.6KB) | ![]() | ![]() | ![]() |
| PIV Minimum (11.8KB) | ![]() | ![]() | ![]() |
| Minimum (8.8KB) | ![]() | ![]() | ![]() |
See how JPEG 2000 compression quality affects the final image, from lowest to highest quality:
| Minimum (8.8KB) | PIV Minimum (11.8KB) | PIV Balanced (20.7KB) |
|---|---|---|
![]() | ![]() | ![]() |
| Size: 8,845 bytes | Size: 11,789 bytes | Size: 17,723 bytes |
| Rate: 0.35 bpp | Rate: 0.36 bpp | Rate: 0.55 bpp |
| Bare minimum quality | PIV/TWIC compliant | Standard PIV quality |
| PIV High (28.8KB) | PIV Very High (48.6KB) | PIV Archival (80.2KB) |
|---|---|---|
![]() | ![]() | ![]() |
| Size: 29,485 bytes | Size: 49,732 bytes | Size: 82,127 bytes |
| Rate: 0.96 bpp | Rate: 1.70 bpp | Rate: 4.00 bpp |
| Enhanced PIV quality | High quality | Long-term preservation |
The head width measurement is crucial for PIV compatibility but presents challenges with 68-point facial landmarks:
What we measure: The widest points of the face contour (landmarks 0-16), which represent the jawline from ear to ear. We then create a level line at the average Y-position of these widest points.
Why this approach:
Limitations:
PIV Compatibility: The key requirement is that Line CC width ≥ 240 pixels. The exact vertical position is less critical than ensuring the face is large enough in the frame.
For advanced scenarios where you need direct control over the services:
// Initialize services (typically done via DI)
var faceDetector = new RetinaFaceDetector(modelPath);
var landmarkExtractor = new OnnxLandmarkExtractor(modelPath);
var jpeg2000Encoder = new Jpeg2000EncoderService();
// Load source image
using var sourceImage = await Image.LoadAsync<Rgba32>("photo.jpg");
// Process with default settings
var result = await PivProcessor.ProcessAsync(
sourceImage,
faceDetector,
landmarkExtractor,
jpeg2000Encoder);
if (result.IsSuccess)
{
// Save the processed image
await File.WriteAllBytesAsync("output.png", result.Value.ImageData);
Console.WriteLine($"Processing succeeded: {result.Value.ProcessingSummary}");
}
else
{
Console.WriteLine($"Processing failed: {result.Error}");
}
// Configure processing options
var options = new PivProcessingOptions
{
BaseRate = 0.8f, // 24KB target
RoiStartLevel = 2, // Conservative ROI
MinFaceConfidence = 0.9f
};
// Process with custom settings
var result = await PivProcessor.ProcessAsync(
sourceImage,
faceDetector,
landmarkExtractor,
jpeg2000Encoder,
options,
logger); // ROI enabled by default, no alignment by default
// Handle result
if (result.IsSuccess)
{
var pivResult = result.Value;
// Transformation details
Console.WriteLine($"Rotation: {pivResult.AppliedTransform.RotationDegrees}°");
Console.WriteLine($"Scale: {pivResult.AppliedTransform.ScaleFactor}x");
// Compliance validation
var validation = pivResult.Metadata["ComplianceValidation"] as PivComplianceValidation;
Console.WriteLine($"Head width: {validation?.HeadWidthPixels}px");
Console.WriteLine($"Eye position: {validation?.BBFromBottom:P0} from bottom");
}
else
{
Console.WriteLine($"Processing failed: {result.Error}");
}
| Option | Type | Default | Description |
|---|---|---|---|
BaseRate | float | 0.7 | Compression rate in bits/pixel (0.6-1.0) |
RoiStartLevel | int | 3 | ROI quality level (0=aggressive, 3=smoothest) |
MinFaceConfidence | float | 0.8 | Minimum face detection confidence (0-1) |
RequireSingleFace | bool | true | Fail if multiple faces detected |
PreserveExifMetadata | bool | false | Keep EXIF data in output |
// Optimized for ~20KB files with smooth quality transitions
var defaultOptions = PivProcessingOptions.Default;
// Maximum quality for archival (larger files)
var highQualityOptions = PivProcessingOptions.HighQuality;
// Fast processing with smaller files
var fastOptions = PivProcessingOptions.Fast;
| Preset | Target Size | Actual Size | Compression Rate | Use Case |
|---|---|---|---|---|
| PIV Archival | - | ~82KB | 4.00 bpp | Long-term preservation and archival storage |
| PIV Very High | 50KB | ~49.7KB | 1.70 bpp | Premium quality with excellent detail preservation |
| PIV High | 30KB | ~29.4KB | 0.96 bpp | Enhanced quality for applications requiring superior detail |
| PIV Balanced | 22KB | ~20.6KB | 0.68 bpp | Default - Optimal quality/size balance for ID cards |
| PIV Minimum | 12KB | ~11.8KB | 0.36 bpp | Minimum acceptable quality, works for both PIV and TWIC (14KB max) |
| Minimum | 10KB | ~8.8KB | 0.35 bpp | Smallest possible file size |
# Process image with default settings (20KB, ROI enabled)
faceoffx process photo.jpg
# Specify output file
faceoffx process photo.jpg --output id_photo.png
# Generate ROI visualization
faceoffx roi photo.jpg --show-piv-lines
# Custom file size target (24KB)
faceoffx process photo.jpg --rate 0.8
# Disable ROI for uniform quality
faceoffx process photo.jpg --no-roi
# Different ROI quality levels
faceoffx process photo.jpg --roi-level 0 # Aggressive
faceoffx process photo.jpg --roi-level 2 # Conservative
# Enable ROI alignment (may create harsh boundaries)
faceoffx process photo.jpg --align
# Verbose output with debugging
faceoffx process photo.jpg --verbose --debug
| Option | Description | Default |
|---|---|---|
--output <PATH> | Output file path | input.png |
--rate <RATE> | Compression rate (0.6-1.0) | 0.7 |
--roi-level <LEVEL> | ROI priority (0-3) | 3 |
--no-roi | Disable ROI encoding | ROI enabled |
--align | Enable ROI block alignment | Disabled |
--verbose | Show detailed information | Off |
--debug | Enable debug logging | Off |
// Standard try-catch pattern
try
{
var result = await FacialImageEncoder.ProcessAsync(imageData);
Console.WriteLine($"Processed size: {result.Metadata.FileSize} bytes");
// Check optional values
if (result.Metadata.TargetSize.HasValue)
{
Console.WriteLine($"Target size was: {result.Metadata.TargetSize.Value}");
}
}
catch (ArgumentNullException ex)
{
Console.WriteLine($"Invalid input: {ex.Message}");
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"Processing failed: {ex.Message}");
}
// Or use the Try pattern
var (success, result, error) = await FacialImageEncoder.TryProcessAsync(imageData);
if (!success)
{
Console.WriteLine($"Failed: {error}");
return;
}
// Additional processing based on file size
if (result!.Metadata.FileSize > 25000)
{
// Try with higher compression
result = await FacialImageEncoder.ProcessWithRateAsync(imageData, 0.5f);
}
# Clone the repository
git clone https://github.com/mistial-dev/FaceOFFx.git
cd FaceOFFx
# Build the solution
dotnet build
# Run tests
dotnet test
# Create NuGet package
dotnet pack --configuration Release
FaceOFFx/
├── src/
│ ├── FaceOFFx/ # Domain models and interfaces
│ ├── FaceOFFx.Infrastructure/ # ONNX implementations
│ ├── FaceOFFx.Models/ # Embedded ONNX models
│ └── FaceOFFx.Cli/ # Command-line interface
├── tests/ # Unit and integration tests
└── docs/ # Documentation and samples
FaceOFFx ensures compatibility with government standards:
The library uses advanced ROI (Region of Interest) encoding to optimize quality:
FaceOFFx uses two specialized ONNX models for facial processing, each optimized for specific tasks in the PIV compatibility pipeline.
File: FaceDetector.onnx (104MB, stored with Git LFS)
Architecture: RetinaFace single-stage face detector
Input: 640×640×3 RGB image, normalized to [0,1]
Output: Face bounding boxes with confidence scores and 5 key facial points
The RetinaFace model performs initial face detection and provides coarse facial landmarks:
Pre-processing: Images are resized to 640×640 with letterboxing to maintain aspect ratio, then normalized to floating-point values between 0 and 1.
Post-processing: Non-maximum suppression filters overlapping detections, retaining only the highest confidence frontal face for PIV processing.
File: landmarks_68_pfld.onnx (2.8MB)
Architecture: PFLD (Practical Facial Landmark Detector)
Input: 112×112×3 RGB face crop, normalized to [0,1]
Output: 136 floats (68 landmarks × 2 coordinates)
The PFLD model extracts precise 68-point facial landmarks using the standard iBUG annotation scheme:
Coordinate System: All landmarks are normalized to [0,1] relative to the 112×112 input crop and must be transformed back to full image coordinates for PIV processing.
Precision: The PFLD model achieves sub-pixel accuracy for facial feature localization, essential for precise PIV alignment and ROI calculation.
| Model | Inference Time* | Memory Usage | Accuracy |
|---|---|---|---|
| RetinaFace | ~50ms | ~200MB | >95% face detection |
| PFLD | ~15ms | ~50MB | <2px landmark error |
*CPU inference on modern Intel/AMD processors
| Model | Purpose | Input Size | Framework |
|---|---|---|---|
FaceDetector.onnx | Face detection | 640×640 | RetinaFace |
landmarks_68_pfld.onnx | Landmark detection | 112×112 | PFLD |
FaceOFFx follows a carefully orchestrated pipeline to transform input images into PIV-compatible JPEG 2000 files:
Input Image (any format) → ImageSharp Image<Rgba32>
Image<Rgba32> → RetinaFace Model → DetectedFace[]
DetectedFace → Face Region Crop (Variable Size)
Face Crop → Resize to 112×112 → PFLD Model → 68 Landmarks
68 Landmarks → Geometric Analysis → PivTransform
Original Image → Rotate → Crop → Resize → PIV Image (420×560)
Critical Order: Rotation is applied to the full original image first to avoid black borders, then cropping and resizing follow.
Original Landmarks → Transform Matrix → PIV Space Landmarks
PIV Landmarks → Facial Region Analysis → ROI Bounds
PIV Image + ROI → CoreJ2K → PIV-Compatible JP2 File
Input Image
↓
Face Detection (RetinaFace 640×640)
↓
Face Crop Extraction
↓
Landmark Detection (PFLD 112×112)
↓
Geometric Analysis (Eye angle, face bounds)
↓
Image Transformation (Rotate → Crop → Resize)
↓
Landmark Transformation (Match image transforms)
↓
ROI Calculation (Facial region bounds)
↓
JPEG 2000 Encoding (Single tile + ROI)
↓
PIV-Compatible JP2 Output (420×560, exact target size)
The pipeline involves multiple coordinate space transformations:
Each transformation maintains mathematical precision to ensure accurate facial feature alignment throughout the process.
Contributions are welcome! Please read our Contributing Guide for details on our code of conduct and the process for submitting pull requests.
A complete Software Bill of Materials is available in sbom/faceoffx-sbom.json in CycloneDX format. This includes:
For security vulnerabilities, please see our Security Policy.
This project is licensed under the MIT License - see the LICENSE file for details.
| Component | Description | License | Source/Credit |
|---|---|---|---|
| FaceONNX | Base facial processing library this project is derived from | MIT | FaceONNX/FaceONNX |
| RetinaFace | Face detection model (FaceDetector.onnx) | MIT | discipleofhamilton/RetinaFace |
| PFLD | 68-point facial landmark detection (landmarks_68_pfld.onnx) | MIT | FaceONNX/FaceONNX.Models |
| ONNX Runtime | High-performance inference engine | MIT | Microsoft/onnxruntime |
| ImageSharp | Cross-platform 2D graphics library | Apache-2.0 | SixLabors/ImageSharp |
| CoreJ2K | JPEG 2000 encoding with ROI support | BSD-2-Clause | cinderblocks/CoreJ2K |
| CSharpFunctionalExtensions | Functional programming extensions | MIT | vkhorikov/CSharpFunctionalExtensions |
| Spectre.Console | Beautiful console applications | MIT | spectreconsole/spectre.console |
| Standard | Description | Organization |
|---|---|---|
| FIPS 201-3 | Personal Identity Verification (PIV) Requirements | NIST |
| INCITS 385-2004 | Face Recognition Format for Data Interchange | ANSI/INCITS |
| SP 800-76-2 | Biometric Specifications for Personal Identity Verification | NIST |
"Face... off... No more drugs for that man!" - Watch Scene