# Migration Plan: Replace face_recognition with DeepFace in PunimTag

**Version:** 1.0  
**Created:** October 15, 2025  
**Status:** Planning Phase

---

## Executive Summary

This plan outlines the complete migration from `face_recognition` library to `DeepFace` library for the PunimTag photo tagging application. Based on testing in `test_deepface_gui.py`, DeepFace provides superior accuracy using the ArcFace model with configurable detector backends.

**Key Changes:**
- Face encoding dimensions: 128 → 512 (ArcFace model)
- Detection method: HOG/CNN → RetinaFace/MTCNN/OpenCV/SSD (configurable)
- Similarity metric: Euclidean distance → Cosine similarity
- Face location format: (top, right, bottom, left) → {x, y, w, h}
- No backward compatibility - fresh start with new database

---

## PHASE 1: Database Schema Updates

### Step 1.1: Update Database Schema
**File:** `src/core/database.py`

**Actions:**
1. **Modify `faces` table** to add DeepFace-specific columns:
```sql
ALTER TABLE faces ADD COLUMN detector_backend TEXT DEFAULT 'retinaface';
ALTER TABLE faces ADD COLUMN model_name TEXT DEFAULT 'ArcFace';
ALTER TABLE faces ADD COLUMN face_confidence REAL DEFAULT 0.0;
```

2. **Update `person_encodings` table** similarly:
```sql
ALTER TABLE person_encodings ADD COLUMN detector_backend TEXT DEFAULT 'retinaface';
ALTER TABLE person_encodings ADD COLUMN model_name TEXT DEFAULT 'ArcFace';
```

3. **Update `init_database()` method** in `DatabaseManager` class:
   - Add new columns to CREATE TABLE statements for `faces` and `person_encodings`
   - Add indices for new columns if needed

**Expected encoding size change:**
- face_recognition: 128 floats × 8 bytes = 1,024 bytes per encoding
- DeepFace ArcFace: 512 floats × 8 bytes = 4,096 bytes per encoding

### Step 1.2: Update Database Methods
**File:** `src/core/database.py`

**Actions:**
1. **Modify `add_face()` method signature:**
```python
def add_face(self, photo_id: int, encoding: bytes, location: str, 
             confidence: float = 0.0, quality_score: float = 0.0, 
             person_id: Optional[int] = None,
             detector_backend: str = 'retinaface',
             model_name: str = 'ArcFace',
             face_confidence: float = 0.0) -> int:
```

2. **Modify `add_person_encoding()` method signature:**
```python
def add_person_encoding(self, person_id: int, face_id: int, encoding: bytes, 
                       quality_score: float,
                       detector_backend: str = 'retinaface',
                       model_name: str = 'ArcFace'):
```

3. **Update all database queries** that insert/update faces to include new fields

---

## PHASE 2: Configuration Updates

### Step 2.1: Update Configuration Constants
**File:** `src/core/config.py`

**Actions:**
1. **Replace face_recognition settings:**
```python
# OLD - Remove these:
# DEFAULT_FACE_DETECTION_MODEL = "hog"
# DEFAULT_FACE_TOLERANCE = 0.6

# NEW - Add these:
DEEPFACE_DETECTOR_BACKEND = "retinaface"  # Options: retinaface, mtcnn, opencv, ssd
DEEPFACE_MODEL_NAME = "ArcFace"  # Best accuracy model
DEEPFACE_DISTANCE_METRIC = "cosine"  # For similarity calculation
DEEPFACE_ENFORCE_DETECTION = False  # Don't fail if no faces found
DEEPFACE_ALIGN_FACES = True  # Face alignment for better accuracy

# Tolerance/threshold adjustments for DeepFace
DEFAULT_FACE_TOLERANCE = 0.4  # Lower for DeepFace (was 0.6 for face_recognition)
DEEPFACE_SIMILARITY_THRESHOLD = 60  # Minimum similarity percentage (0-100)

# Environment settings for TensorFlow (DeepFace uses TensorFlow backend)
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'  # Suppress TensorFlow warnings
```

2. **Add detector backend options:**
```python
DEEPFACE_DETECTOR_OPTIONS = ["retinaface", "mtcnn", "opencv", "ssd"]
DEEPFACE_MODEL_OPTIONS = ["ArcFace", "Facenet", "Facenet512", "VGG-Face"]
```

### Step 2.2: Add TensorFlow Suppression
**File:** Add to all main entry points (dashboard_gui.py, photo_tagger.py, etc.)

**Actions:**
```python
# At the top of file, before other imports
import os
import warnings
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
warnings.filterwarnings('ignore')

# Then import DeepFace
from deepface import DeepFace
```

---

## PHASE 3: Face Processing Core Migration

### Step 3.1: Replace Face Detection and Encoding
**File:** `src/core/face_processing.py` → `FaceProcessor` class

**Method:** `process_faces()`

**Current Implementation (lines 59-144):**
```python
# OLD CODE:
image = face_recognition.load_image_file(photo_path)
face_locations = face_recognition.face_locations(image, model=model)
face_encodings = face_recognition.face_encodings(image, face_locations)
```

**New Implementation:**
```python
# NEW CODE:
try:
    # Use DeepFace.represent() to get face detection and encodings
    results = DeepFace.represent(
        img_path=photo_path,
        model_name=DEEPFACE_MODEL_NAME,  # 'ArcFace'
        detector_backend=DEEPFACE_DETECTOR_BACKEND,  # 'retinaface'
        enforce_detection=DEEPFACE_ENFORCE_DETECTION,  # False
        align=DEEPFACE_ALIGN_FACES  # True
    )
    
    if not results:
        if self.verbose >= 1:
            print(f"   👤 No faces found")
        # Mark as processed even with no faces
        self.db.mark_photo_processed(photo_id)
        continue
    
    if self.verbose >= 1:
        print(f"   👤 Found {len(results)} faces")
    
    # Process each detected face
    for i, result in enumerate(results):
        # Extract face region info from DeepFace result
        facial_area = result.get('facial_area', {})
        face_confidence = result.get('face_confidence', 0.0)
        embedding = np.array(result['embedding'])
        
        # Convert DeepFace facial_area {x, y, w, h} to our location format
        # Store as dict for consistency
        location = {
            'x': facial_area.get('x', 0),
            'y': facial_area.get('y', 0),
            'w': facial_area.get('w', 0),
            'h': facial_area.get('h', 0)
        }
        
        # Calculate face quality score (reuse existing method)
        # Convert facial_area to (top, right, bottom, left) for quality calculation
        face_location_tuple = (
            facial_area.get('y', 0),  # top
            facial_area.get('x', 0) + facial_area.get('w', 0),  # right
            facial_area.get('y', 0) + facial_area.get('h', 0),  # bottom
            facial_area.get('x', 0)  # left
        )
        
        # Load image for quality calculation
        image = Image.open(photo_path)
        image_np = np.array(image)
        quality_score = self._calculate_face_quality_score(image_np, face_location_tuple)
        
        # Store in database with new format
        self.db.add_face(
            photo_id=photo_id,
            encoding=embedding.tobytes(),
            location=str(location),  # Store as string representation of dict
            confidence=0.0,  # Legacy field, keep for compatibility
            quality_score=quality_score,
            person_id=None,
            detector_backend=DEEPFACE_DETECTOR_BACKEND,
            model_name=DEEPFACE_MODEL_NAME,
            face_confidence=face_confidence
        )
        
        if self.verbose >= 3:
            print(f"      Face {i+1}: {location} (quality: {quality_score:.2f}, confidence: {face_confidence:.2f})")
    
    # Mark as processed
    self.db.mark_photo_processed(photo_id)
    processed_count += 1
    
except Exception as e:
    print(f"❌ Error processing {filename}: {e}")
    self.db.mark_photo_processed(photo_id)
```

### Step 3.2: Update Face Location Handling
**File:** `src/core/face_processing.py`

**Method:** `_extract_face_crop()` (appears twice, lines 212-271 and 541-600)

**Current Implementation:**
```python
# OLD: face_recognition format (top, right, bottom, left)
top, right, bottom, left = location
```

**New Implementation:**
```python
# NEW: DeepFace format {x, y, w, h}
# Parse location from string if needed
if isinstance(location, str):
    location = eval(location)  # Convert string to dict

# Handle both formats for compatibility during migration
if isinstance(location, dict):
    # DeepFace format
    left = location.get('x', 0)
    top = location.get('y', 0)
    width = location.get('w', 0)
    height = location.get('h', 0)
    right = left + width
    bottom = top + height
else:
    # Legacy face_recognition format (top, right, bottom, left)
    top, right, bottom, left = location

# Rest of the method remains the same
face_width = right - left
face_height = bottom - top
# ... etc
```

### Step 3.3: Replace Similarity Calculation
**File:** `src/core/face_processing.py`

**Method:** `find_similar_faces()` and helper methods

**Current Implementation (line 457):**
```python
# OLD:
distance = face_recognition.face_distance([target_encoding], other_enc)[0]
```

**New Implementation:**
```python
# NEW: Use cosine similarity (same as test_deepface_gui.py)
def _calculate_cosine_similarity(self, encoding1: np.ndarray, encoding2: np.ndarray) -> float:
    """Calculate cosine similarity between two face encodings"""
    try:
        # Ensure encodings are numpy arrays
        enc1 = np.array(encoding1).flatten()
        enc2 = np.array(encoding2).flatten()
        
        # Check if encodings have the same length
        if len(enc1) != len(enc2):
            print(f"Warning: Encoding length mismatch: {len(enc1)} vs {len(enc2)}")
            return 0.0
        
        # Normalize encodings
        enc1_norm = enc1 / (np.linalg.norm(enc1) + 1e-8)
        enc2_norm = enc2 / (np.linalg.norm(enc2) + 1e-8)
        
        # Calculate cosine similarity
        cosine_sim = np.dot(enc1_norm, enc2_norm)
        
        # Clamp to valid range [-1, 1]
        cosine_sim = np.clip(cosine_sim, -1.0, 1.0)
        
        # Convert to distance (0 = identical, 2 = opposite)
        # For consistency with face_recognition's distance metric
        distance = 1.0 - cosine_sim  # Range [0, 2], where 0 is perfect match
        
        return distance
        
    except Exception as e:
        print(f"Error calculating similarity: {e}")
        return 2.0  # Maximum distance on error

# Replace in find_similar_faces():
distance = self._calculate_cosine_similarity(target_encoding, other_enc)
```

**Update adaptive tolerance calculation (line 333-351):**
```python
def _calculate_adaptive_tolerance(self, base_tolerance: float, face_quality: float, match_confidence: float = None) -> float:
    """Calculate adaptive tolerance based on face quality and match confidence
    
    Note: For DeepFace, tolerance values are generally lower than face_recognition
    """
    # Start with base tolerance (e.g., 0.4 instead of 0.6)
    tolerance = base_tolerance
    
    # Adjust based on face quality
    quality_factor = 0.9 + (face_quality * 0.2)  # Range: 0.9 to 1.1
    tolerance *= quality_factor
    
    # Adjust based on match confidence if provided
    if match_confidence is not None:
        confidence_factor = 0.95 + (match_confidence * 0.1)
        tolerance *= confidence_factor
    
    # Ensure tolerance stays within reasonable bounds for DeepFace
    return max(0.2, min(0.6, tolerance))  # Lower range for DeepFace
```

---

## PHASE 4: GUI Integration Updates

### Step 4.1: Add DeepFace Settings to Dashboard
**File:** `src/gui/dashboard_gui.py`

**Actions:**
1. **Add detector selection to menu or settings:**
```python
# Add to settings menu or control panel
detector_frame = ttk.Frame(settings_panel)
detector_frame.pack(fill=tk.X, padx=5, pady=5)

ttk.Label(detector_frame, text="Face Detector:").pack(side=tk.LEFT, padx=5)
self.detector_var = tk.StringVar(value=DEEPFACE_DETECTOR_BACKEND)
detector_combo = ttk.Combobox(detector_frame, textvariable=self.detector_var,
                             values=DEEPFACE_DETECTOR_OPTIONS, 
                             state="readonly", width=12)
detector_combo.pack(side=tk.LEFT, padx=5)

ttk.Label(detector_frame, text="Model:").pack(side=tk.LEFT, padx=5)
self.model_var = tk.StringVar(value=DEEPFACE_MODEL_NAME)
model_combo = ttk.Combobox(detector_frame, textvariable=self.model_var,
                          values=DEEPFACE_MODEL_OPTIONS,
                          state="readonly", width=12)
model_combo.pack(side=tk.LEFT, padx=5)
```

2. **Update process_faces calls to use selected settings:**
```python
# When calling face processor
detector = self.detector_var.get()
model = self.model_var.get()

# Pass to FaceProcessor (need to update FaceProcessor.__init__ to accept these)
self.face_processor = FaceProcessor(
    db_manager=self.db,
    verbose=self.verbose,
    detector_backend=detector,
    model_name=model
)
```

### Step 4.2: Update Face Processor Initialization
**File:** `src/core/face_processing.py`

**Class:** `FaceProcessor.__init__()`

**Actions:**
```python
def __init__(self, db_manager: DatabaseManager, verbose: int = 0,
             detector_backend: str = None, model_name: str = None):
    """Initialize face processor with DeepFace settings"""
    self.db = db_manager
    self.verbose = verbose
    self.detector_backend = detector_backend or DEEPFACE_DETECTOR_BACKEND
    self.model_name = model_name or DEEPFACE_MODEL_NAME
    self._face_encoding_cache = {}
    self._image_cache = {}
```

### Step 4.3: Update GUI Display Methods
**File:** Multiple panel files (identify_panel.py, auto_match_panel.py, modify_panel.py)

**Actions:**
1. **Update all face thumbnail extraction** to handle new location format
2. **Update confidence display** to use cosine similarity percentages
3. **Update any hardcoded face_recognition references**

**Example for identify_panel.py:**
```python
# In display methods, convert distance to percentage
confidence_pct = (1 - distance) * 100  # Already done correctly
# But ensure distance calculation uses cosine similarity
```

---

## PHASE 5: Dependencies and Installation

### Step 5.1: Update requirements.txt
**File:** `requirements.txt`

**Actions:**
```python
# REMOVE these:
# face-recognition==1.3.0
# face-recognition-models==0.3.0
# dlib>=20.0.0

# ADD these:
deepface>=0.0.79
tensorflow>=2.13.0  # Required by DeepFace
opencv-python>=4.8.0  # Required by DeepFace
retina-face>=0.0.13  # For RetinaFace detector (best accuracy)

# KEEP these:
numpy>=1.21.0
pillow>=8.0.0
click>=8.0.0
setuptools>=40.0.0
```

### Step 5.2: Create Migration Script
**File:** `scripts/migrate_to_deepface.py` (new file)

**Purpose:** Drop all tables and reinitialize database for fresh start

**Actions:**
```python
#!/usr/bin/env python3
"""
Migration script to prepare database for DeepFace
Drops all existing tables and recreates with new schema
"""

import sqlite3
import sys
from pathlib import Path

# Add parent directory to path
sys.path.insert(0, str(Path(__file__).parent.parent))

from src.core.database import DatabaseManager
from src.core.config import DEFAULT_DB_PATH

def migrate_database():
    """Drop all tables and reinitialize with DeepFace schema"""
    print("⚠️  WARNING: This will delete all existing data!")
    response = input("Type 'DELETE ALL DATA' to confirm: ")
    
    if response != "DELETE ALL DATA":
        print("Migration cancelled.")
        return
    
    print("\n🗑️  Dropping all existing tables...")
    
    # Connect directly to database
    conn = sqlite3.connect(DEFAULT_DB_PATH)
    cursor = conn.cursor()
    
    # Drop all tables
    tables = ['phototaglinkage', 'person_encodings', 'faces', 'tags', 'people', 'photos']
    for table in tables:
        cursor.execute(f'DROP TABLE IF EXISTS {table}')
        print(f"   Dropped table: {table}")
    
    conn.commit()
    conn.close()
    
    print("\n✅ All tables dropped successfully")
    print("\n🔄 Reinitializing database with DeepFace schema...")
    
    # Reinitialize with new schema
    db = DatabaseManager(DEFAULT_DB_PATH, verbose=1)
    
    print("\n✅ Database migration complete!")
    print("\nNext steps:")
    print("1. Add photos using the dashboard")
    print("2. Process faces with DeepFace")
    print("3. Identify people")

if __name__ == "__main__":
    migrate_database()
```

---

## PHASE 6: Testing and Validation

### Step 6.1: Create Test Suite
**File:** `tests/test_deepface_integration.py` (new file)

**Actions:**
```python
#!/usr/bin/env python3
"""
Test DeepFace integration in PunimTag
"""

import os
import sys
from pathlib import Path

# Suppress TensorFlow warnings
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
import warnings
warnings.filterwarnings('ignore')

from src.core.database import DatabaseManager
from src.core.face_processing import FaceProcessor
from src.core.config import DEEPFACE_DETECTOR_BACKEND, DEEPFACE_MODEL_NAME

def test_face_detection():
    """Test face detection with DeepFace"""
    print("🧪 Testing DeepFace face detection...")
    
    db = DatabaseManager(":memory:", verbose=0)  # In-memory database for testing
    processor = FaceProcessor(db, verbose=1)
    
    # Test with a sample image
    test_image = "demo_photos/2019-11-22_0011.jpg"
    if not os.path.exists(test_image):
        print(f"❌ Test image not found: {test_image}")
        return False
    
    # Add photo to database
    photo_id = db.add_photo(test_image, Path(test_image).name, None)
    
    # Process faces
    count = processor.process_faces(limit=1)
    
    # Verify results
    stats = db.get_statistics()
    print(f"✅ Processed {count} photos, found {stats['total_faces']} faces")
    
    return stats['total_faces'] > 0

def test_face_matching():
    """Test face matching with DeepFace"""
    print("\n🧪 Testing DeepFace face matching...")
    
    db = DatabaseManager(":memory:", verbose=0)
    processor = FaceProcessor(db, verbose=1)
    
    # Test with multiple images
    test_images = [
        "demo_photos/2019-11-22_0011.jpg",
        "demo_photos/2019-11-22_0012.jpg"
    ]
    
    for img in test_images:
        if os.path.exists(img):
            photo_id = db.add_photo(img, Path(img).name, None)
    
    # Process all faces
    processor.process_faces(limit=10)
    
    # Find similar faces
    faces = db.get_all_face_encodings()
    if len(faces) >= 2:
        matches = processor.find_similar_faces(faces[0][0])
        print(f"✅ Found {len(matches)} similar faces")
        return len(matches) >= 0
    
    return False

def run_all_tests():
    """Run all tests"""
    print("=" * 60)
    print("DeepFace Integration Test Suite")
    print("=" * 60)
    
    tests = [
        test_face_detection,
        test_face_matching
    ]
    
    results = []
    for test in tests:
        try:
            result = test()
            results.append(result)
        except Exception as e:
            print(f"❌ Test failed with error: {e}")
            results.append(False)
    
    print("\n" + "=" * 60)
    print(f"Tests passed: {sum(results)}/{len(results)}")
    print("=" * 60)
    
    return all(results)

if __name__ == "__main__":
    success = run_all_tests()
    sys.exit(0 if success else 1)
```

### Step 6.2: Validation Checklist
1. **Face Detection:**
   - [ ] DeepFace successfully detects faces in test images
   - [ ] Face locations are correctly stored in new format
   - [ ] Face encodings are 512-dimensional (ArcFace)
   - [ ] Multiple detector backends work (retinaface, mtcnn, etc.)

2. **Face Matching:**
   - [ ] Similar faces are correctly identified
   - [ ] Cosine similarity produces reasonable confidence scores
   - [ ] Adaptive tolerance works with new metric
   - [ ] No false positives at default threshold

3. **GUI Integration:**
   - [ ] All panels display faces correctly
   - [ ] Face thumbnails extract properly with new location format
   - [ ] Confidence scores display correctly
   - [ ] Detector/model selection works in settings

4. **Database:**
   - [ ] New columns are created correctly
   - [ ] Encodings are stored as 4096-byte BLOBs
   - [ ] Queries work with new schema
   - [ ] Indices improve performance

---

## PHASE 7: Implementation Order

**Execute in this order to minimize issues:**

1. **Day 1: Database & Configuration**
   - Update `requirements.txt`
   - Install DeepFace: `pip install deepface tensorflow opencv-python retina-face`
   - Update `src/core/config.py` with DeepFace settings
   - Update `src/core/database.py` schema and methods
   - Create and run `scripts/migrate_to_deepface.py`

2. **Day 2: Core Face Processing**
   - Update `src/core/face_processing.py` `process_faces()` method
   - Update `_extract_face_crop()` to handle new location format
   - Implement `_calculate_cosine_similarity()` method
   - Update `find_similar_faces()` to use new similarity
   - Update `_calculate_adaptive_tolerance()` for DeepFace ranges

3. **Day 3: GUI Updates**
   - Add detector/model selection to dashboard
   - Update `FaceProcessor.__init__()` to accept settings
   - Test face processing with GUI

4. **Day 4: Panel Updates**
   - Update `src/gui/identify_panel.py` for new format
   - Update `src/gui/auto_match_panel.py` for new format
   - Update `src/gui/modify_panel.py` for new format
   - Verify all face displays work correctly

5. **Day 5: Testing & Refinement**
   - Create `tests/test_deepface_integration.py`
   - Run all tests and fix issues
   - Process test photos from `demo_photos/testdeepface/`
   - Validate matching accuracy
   - Adjust thresholds if needed

6. **Day 6: Documentation & Cleanup**
   - Update README.md with DeepFace information
   - Document detector backend options
   - Document model options and trade-offs
   - Remove old face_recognition references
   - Final testing

---

## PHASE 8: Key Differences and Gotchas

### Encoding Size Change
- **face_recognition:** 128 floats = 1,024 bytes
- **DeepFace ArcFace:** 512 floats = 4,096 bytes
- **Impact:** Database size will be ~4x larger for encodings
- **Action:** Ensure sufficient disk space

### Location Format Change
- **face_recognition:** tuple `(top, right, bottom, left)`
- **DeepFace:** dict `{'x': x, 'y': y, 'w': w, 'h': h}`
- **Impact:** All location parsing code must be updated
- **Action:** Create helper function to convert between formats

### Tolerance/Threshold Adjustments
- **face_recognition:** Default 0.6 works well
- **DeepFace:** Lower tolerance needed (0.4 recommended)
- **Impact:** Matching sensitivity changes
- **Action:** Test and adjust `DEFAULT_FACE_TOLERANCE` in config

### Performance Considerations
- **DeepFace:** Slower than face_recognition (uses deep learning)
- **Mitigation:** Use GPU if available, cache results, process in batches
- **Action:** Add progress indicators, allow cancellation

### Dependencies
- **DeepFace requires:** TensorFlow, OpenCV, specific detectors
- **Size:** ~500MB+ of additional packages and models
- **Action:** Warn users about download size during installation

---

## PHASE 9: Rollback Plan (If Needed)

Since we're starting fresh (no backward compatibility), rollback is simple:

1. **Restore database:**
   ```bash
   rm data/photos.db
   cp data/photos.db.backup data/photos.db  # If backup exists
   ```

2. **Restore code:**
   ```bash
   git checkout HEAD -- src/core/face_processing.py src/core/database.py src/core/config.py requirements.txt
   ```

3. **Reinstall dependencies:**
   ```bash
   pip uninstall deepface tensorflow
   pip install face-recognition
   ```

---

## PHASE 10: Success Criteria

Migration is complete when:

- [ ] All face_recognition imports removed
- [ ] DeepFace successfully detects faces in test images
- [ ] Face matching produces accurate results
- [ ] All GUI panels work with new format
- [ ] Database stores DeepFace encodings correctly
- [ ] Test suite passes all tests
- [ ] Documentation updated
- [ ] No regression in core functionality
- [ ] Performance is acceptable (may be slower but accurate)

---

## Additional Notes for Agent

1. **Import Order Matters:**
   ```python
   # ALWAYS import in this order:
   import os
   import warnings
   os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
   warnings.filterwarnings('ignore')
   # THEN import DeepFace
   from deepface import DeepFace
   ```

2. **Error Handling:**
   - DeepFace can throw various TensorFlow errors
   - Always wrap DeepFace calls in try-except
   - Use `enforce_detection=False` to avoid crashes on no-face images

3. **Model Downloads:**
   - First run will download models (~100MB+)
   - Store in `~/.deepface/weights/`
   - Plan for initial download time

4. **Testing Strategy:**
   - Use `demo_photos/testdeepface/` for initial testing
   - These were already tested in `test_deepface_gui.py`
   - Known good results for comparison

5. **Code Quality:**
   - Maintain existing code style
   - Keep verbose levels consistent
   - Preserve all existing functionality
   - Add comments explaining DeepFace-specific code

---

## Files to Modify (Summary)

1. **`requirements.txt`** - Update dependencies
2. **`src/core/config.py`** - Add DeepFace configuration
3. **`src/core/database.py`** - Update schema and methods
4. **`src/core/face_processing.py`** - Replace all face_recognition code
5. **`src/gui/dashboard_gui.py`** - Add detector/model selection
6. **`src/gui/identify_panel.py`** - Update for new formats
7. **`src/gui/auto_match_panel.py`** - Update for new formats
8. **`src/gui/modify_panel.py`** - Update for new formats
9. **`scripts/migrate_to_deepface.py`** - New migration script
10. **`tests/test_deepface_integration.py`** - New test suite

---

**END OF MIGRATION PLAN**

This plan provides a complete, step-by-step guide for migrating from face_recognition to DeepFace. Execute phases in order, test thoroughly, and refer to `tests/test_deepface_gui.py` for working DeepFace implementation examples.

