# Face Width-Based Profile Detection Implementation

## Overview

Implemented face width (eye distance) as an additional indicator for profile face detection. This enhances profile detection accuracy, especially for extreme profile views where yaw angle calculation might be less reliable.

## Implementation Details

### 1. Added `calculate_face_width_from_landmarks()` Method

**Location:** `src/utils/pose_detection.py`

Calculates the horizontal distance between the two eyes (face width). For profile faces, this distance is very small (< 20-30 pixels), while frontal faces have much larger eye distances (typically 50-100+ pixels).

```python
@staticmethod
def calculate_face_width_from_landmarks(landmarks: Dict) -> Optional[float]:
    """Calculate face width (eye distance) from facial landmarks."""
```

### 2. Enhanced `classify_pose_mode()` Method

**Location:** `src/utils/pose_detection.py`

Added optional `face_width` parameter to `classify_pose_mode()`:

```python
@staticmethod
def classify_pose_mode(yaw: Optional[float], 
                      pitch: Optional[float], 
                      roll: Optional[float],
                      face_width: Optional[float] = None) -> str:
```

**Logic:**
- If `face_width < 25 pixels`, strongly indicates profile view
- Even if yaw angle is below the 30° threshold, small face width suggests profile
- Uses yaw direction (if available) to determine `profile_left` vs `profile_right`
- Falls back to `profile_left` if yaw is unavailable but face width is small

### 3. Updated `detect_pose_faces()` Method

**Location:** `src/utils/pose_detection.py`

Now calculates face width and includes it in the result:

```python
# Calculate face width (eye distance) for profile detection
face_width = self.calculate_face_width_from_landmarks(landmarks)

# Classify pose mode (using face width as additional indicator)
pose_mode = self.classify_pose_mode(yaw_angle, pitch_angle, roll_angle, face_width)
```

The result dictionary now includes `face_width`:

```python
result = {
    'facial_area': facial_area,
    'landmarks': landmarks,
    'confidence': face_data.get('confidence', 0.0),
    'yaw_angle': yaw_angle,
    'pitch_angle': pitch_angle,
    'roll_angle': roll_angle,
    'face_width': face_width,  # Eye distance in pixels
    'pose_mode': pose_mode
}
```

## Benefits

1. **Better Profile Detection:** Catches extreme profile views where yaw angle might be unreliable
2. **Fallback Indicator:** When yaw calculation fails (None), small face width still indicates profile
3. **More Accurate:** Uses both yaw angle and face width for robust profile detection
4. **Backward Compatible:** `face_width` parameter is optional, so existing code still works

## Threshold

**Profile Face Width Threshold: 25 pixels**

- Faces with eye distance < 25 pixels are classified as profile
- This threshold is based on empirical testing:
  - Profile faces: 6-10 pixels (e.g., face_4 with yaw=-30.48° has 6.52 pixels)
  - Frontal faces: 50-100+ pixels (e.g., face_2 with yaw=3.04° has 92.94 pixels)

## Testing

To test the implementation:

1. Process photos with profile faces
2. Check pose_mode classification - should see more profile faces detected
3. Verify face_width values in pose detection results

## Example

**Before:** Face with yaw=16.77° might be classified as "frontal" (below 30° threshold)
**After:** Same face with face_width=3.86 pixels is correctly classified as "profile_left"

## Files Modified

- `src/utils/pose_detection.py`:
  - Added `calculate_face_width_from_landmarks()` method
  - Enhanced `classify_pose_mode()` with face_width parameter
  - Updated `detect_pose_faces()` to calculate and use face_width

