Skip to main content

Prerequisites

Before starting, ensure you have the following installed on your system:

Rust

Install via rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

OpenCV 4.x

Required for image processing
brew install opencv
For Windows installation, follow the OpenCV-Rust installation guide.

Installation

1

Clone the repository

Clone Iris from GitHub and navigate to the project directory:
git clone https://github.com/crane04/iris.git
cd iris
The repository includes:
  • src/ - Rust source code
  • Cargo.toml - Dependency configuration
  • setup.sh - AI model download script
  • Dockerfile - Container deployment config
2

Download AI models

Iris requires two ONNX models for face detection and recognition. These files are excluded from Git due to size (~5MB each).Option 1: Automated setup (recommended)
chmod +x setup.sh
./setup.sh
Option 2: Manual download
# YuNet - Face Detection Model
curl -L https://github.com/opencv/opencv_zoo/raw/main/models/face_detection_yunet/face_detection_yunet_2023mar.onnx \
  -o face_detection_yunet_2023mar.onnx

# SFace - Face Recognition Model
curl -L https://github.com/opencv/opencv_zoo/raw/main/models/face_recognition_sface/face_recognition_sface_2021dec.onnx \
  -o face_recognition_sface_2021dec.onnx
These models are from OpenCV Zoo, a collection of pre-trained deep learning models optimized for OpenCV.
3

Build and start the server

Compile Iris in release mode for optimal performance:
cargo run --release
Expected output:
Initializing Iris Face AI...
Iris API running on http://localhost:8080
First compilation may take 3-5 minutes as Cargo downloads and compiles all dependencies. Subsequent builds will be much faster.
The API server is now running with:
  • Port: 8080
  • Rate limit: 5 requests/second per IP (burst: 10)
  • CORS: Enabled for all origins

Verify Installation

Test that Iris is running correctly:
curl http://localhost:8080/health
Expected response:
OK

Your First Face Comparison

Let’s compare a target face against a database of people to find matches.
1

Understand the request structure

The /compare endpoint accepts a JSON payload with:
  • target_url - The face you want to identify (URL or Base64 data URI)
  • people[] - Array of known individuals to compare against
    • name - Identifier for this person (e.g., patient ID)
    • image_url - Reference photo (URL or Base64 data URI)
{
  "target_url": "<image-to-identify>",
  "people": [
    {
      "name": "Patient-001",
      "image_url": "<reference-photo>"
    }
  ]
}
2

Make a comparison request

Here’s a real example comparing different people:
curl -X POST http://localhost:8080/compare \
  -H "Content-Type: application/json" \
  -d '{
    "target_url": "https://static.wikia.nocookie.net/amazingspiderman/images/3/33/Tobey_Maguire_Infobox.png",
    "people": [
      {
        "name": "Tobey Maguire",
        "image_url": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQqiVCCW7eH5Q_8q4VULShU7O8QnOgp7Us2RBNhAlnesh2_iho_D1Toosuxj_x66J1w8ks&usqp=CAU"
      },
      {
        "name": "Tom Holland",
        "image_url": "https://static.wikia.nocookie.net/marvelcinematicuniverse/images/2/2f/Tom_Holland.jpg"
      }
    ]
  }'
3

Interpret the response

Iris returns matches sorted by probability (highest first):
{
  "matches": [
    {
      "name": "Tobey Maguire",
      "probability": 87.3
    }
  ]
}
Response fields:
  • matches - Array of matching people (empty if no matches)
  • name - The identifier from your request
  • probability - Match confidence (0-100)
Only matches with similarity > 36.3% are returned. This threshold is based on the SFace model’s recommendation for positive matches.

Using Base64 Data URIs

Instead of image URLs, you can send images directly as Base64-encoded data URIs:
# macOS/Linux
base64 -i image.jpg | awk '{printf "data:image/jpeg;base64,%s", $0}'

# Or use Python
python3 -c "import base64; print('data:image/jpeg;base64,' + base64.b64encode(open('image.jpg', 'rb').read()).decode())"
Then use the data URI in your request:
{
  "target_url": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
  "people": [
    {
      "name": "Patient-001",
      "image_url": "data:image/jpeg;base64,/9j/4AAQSkZJRg..."
    }
  ]
}
Data URIs are useful when you already have images in memory or want to avoid external HTTP requests. However, they increase payload size significantly.

Check API Statistics

Monitor API usage with the /stats endpoint:
curl http://localhost:8080/stats
Response:
{
  "total_requests": 42,
  "requests_last_second": 2,
  "requests_last_minute": 15,
  "requests_last_hour": 42
}
This endpoint is useful for:
  • Monitoring traffic patterns
  • Debugging rate limit issues
  • Capacity planning
  • Health checks

Common Issues

Error: Could not find OpenCV librarySolution:
# Reinstall OpenCV
brew reinstall opencv

# Set environment variable
export DYLD_LIBRARY_PATH="$(brew --prefix opencv)/lib:$DYLD_LIBRARY_PATH"
Error: Address already in use (os error 48)Solution: Change the port in src/main.rs:151:
let port = 3000;  // Change from 8080 to another port
let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", port)).await?;
Then rebuild: cargo run --release
Error: Failed to load model: face_detection_yunet_2023mar.onnxSolution: Ensure models are in the project root:
ls -la *.onnx
Should show:
face_detection_yunet_2023mar.onnx
face_recognition_sface_2021dec.onnx
If missing, re-run ./setup.sh or download manually.
Issue: /compare returns {"matches": []}Possible causes:
  1. No face detected: Image quality too low, face too small, or heavily obscured
  2. Similarity too low: Faces don’t match (different people)
  3. Invalid image URL: Download failed or returned non-image data
Debugging:
# Test with known matching images
curl -X POST http://localhost:8080/compare \
  -H "Content-Type: application/json" \
  -d '{...}'
Check that:
  • Images contain clearly visible faces
  • URLs are publicly accessible
  • Images are in supported formats (JPEG, PNG, WebP)
Error: 429 Too Many RequestsCause: Exceeded 5 requests/second per IP (burst: 10)Solution: Implement exponential backoff:
async function compareWithRetry(payload, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch('http://localhost:8080/compare', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });
    
    if (response.status === 429) {
      const delay = Math.pow(2, i) * 1000;  // 1s, 2s, 4s
      await new Promise(resolve => setTimeout(resolve, delay));
      continue;
    }
    
    return await response.json();
  }
  throw new Error('Max retries exceeded');
}
Or adjust rate limits in src/main.rs:128-129:
let quota = Quota::per_second(NonZeroU32::new(10).unwrap())  // Increase to 10/sec
    .allow_burst(NonZeroU32::new(20).unwrap());  // Burst: 20

Next Steps

Now that Iris is running, explore advanced topics:

API Reference

Complete documentation for all endpoints and request/response models

Architecture Deep Dive

Learn how Iris processes faces and maintains stateless design

Docker Deployment

Deploy Iris as a containerized service for production

Security Model

Understand privacy guarantees and rate limiting
Need help? Check the GitHub repository for issues and discussions, or review the complete API reference.

Build docs developers (and LLMs) love