Skip to content

Commit 04e49e4

Browse files
Merge pull request #1419 from liberu-genealogy/copilot/implement-facial-recognition
Implement facial recognition for photo tagging with provider abstraction
2 parents 8ce395b + 9db0377 commit 04e49e4

24 files changed

+2235
-1
lines changed
Lines changed: 339 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,339 @@
1+
# Facial Recognition Feature Documentation
2+
3+
## Overview
4+
5+
The facial recognition feature helps users organize and tag family members in uploaded photos automatically. The system can detect faces, suggest person tags, and allow users to review and confirm these suggestions.
6+
7+
## User Guide
8+
9+
### Uploading Photos
10+
11+
1. Navigate to a Person's profile in the application
12+
2. Click on the "Photos" tab
13+
3. Click "New Photo" or "Upload Photo"
14+
4. Select an image file from your device
15+
5. Add an optional description
16+
6. Click "Save"
17+
18+
The system will automatically analyze the photo for faces upon upload.
19+
20+
### Reviewing Facial Recognition Tags
21+
22+
1. Navigate to **Family Tree > Review Photo Tags** in the main menu
23+
2. You'll see photos with detected faces that need review
24+
3. For each detected face:
25+
- The system shows the suggested person (if a match was found)
26+
- Displays the confidence score
27+
- Shows the face location with a bounding box overlay
28+
29+
4. Review options:
30+
- **Confirm**: Accept the suggested tag
31+
- **Select Different Person**: Choose the correct person from the dropdown
32+
- **Reject**: Mark the tag as incorrect
33+
- **Skip**: Move to the next tag without making a decision
34+
35+
5. The progress bar shows how many tags remain to review
36+
37+
### Managing Photos
38+
39+
From a Person's profile Photos tab, you can:
40+
41+
- **View all photos** associated with that person
42+
- **See tag counts** for each photo
43+
- **Manually analyze** photos that weren't automatically analyzed
44+
- **Edit photo descriptions**
45+
- **Delete photos** (and their associated tags)
46+
47+
### Understanding Confidence Scores
48+
49+
- **90-100%**: Very high confidence - likely accurate
50+
- **80-89%**: High confidence - usually accurate
51+
- **70-79%**: Medium confidence - review carefully
52+
- **Below 70%**: Low confidence - likely needs correction
53+
54+
## Features
55+
56+
### Automatic Face Detection
57+
58+
- Detects multiple faces in a single photo
59+
- Provides bounding box coordinates for each face
60+
- Calculates confidence scores for matches
61+
62+
### Person Matching
63+
64+
- Compares detected faces with known face encodings
65+
- Suggests person tags based on similarity
66+
- Learns from confirmed tags to improve future matches
67+
68+
### Tag Management
69+
70+
- Three status states: Pending, Confirmed, Rejected
71+
- Track who confirmed each tag and when
72+
- Update person assignments as needed
73+
74+
### Face Encoding
75+
76+
- Stores facial features for confirmed tags
77+
- Uses encodings for future matching
78+
- Supports multiple encodings per person
79+
80+
## Developer Guide
81+
82+
### Architecture
83+
84+
The facial recognition system is built with a provider pattern, allowing different facial recognition services to be used:
85+
86+
```
87+
FacialRecognitionService (Main Service)
88+
89+
FacialRecognitionProviderInterface
90+
91+
├── MockProvider (Development/Testing)
92+
├── AwsRekognitionProvider (Production - Not yet implemented)
93+
└── AzureFaceApiProvider (Future option - Not yet implemented)
94+
```
95+
96+
### Adding a New Provider
97+
98+
To add a new facial recognition provider (e.g., AWS Rekognition):
99+
100+
1. Create a new provider class implementing `FacialRecognitionProviderInterface`:
101+
102+
```php
103+
<?php
104+
105+
namespace App\Services\FacialRecognition\Providers;
106+
107+
use App\Services\FacialRecognition\FacialRecognitionProviderInterface;
108+
109+
class AwsRekognitionProvider implements FacialRecognitionProviderInterface
110+
{
111+
public function detectFaces(string $imagePath): array
112+
{
113+
// Implement AWS Rekognition face detection
114+
}
115+
116+
public function matchFaces(string $imagePath, array $faceEncodings): array
117+
{
118+
// Implement AWS Rekognition face matching
119+
}
120+
121+
public function getFaceEncoding(string $imagePath, array $boundingBox): string
122+
{
123+
// Implement AWS Rekognition face encoding
124+
}
125+
126+
public function isAvailable(): bool
127+
{
128+
// Check if AWS credentials are configured
129+
}
130+
}
131+
```
132+
133+
2. Update `FacialRecognitionService::getProvider()` to include your new provider:
134+
135+
```php
136+
protected function getProvider(): FacialRecognitionProviderInterface
137+
{
138+
$provider = config('services.facial_recognition.provider', 'mock');
139+
140+
return match ($provider) {
141+
'mock' => new MockProvider(),
142+
'aws' => new AwsRekognitionProvider(),
143+
// Add your provider here
144+
default => new MockProvider(),
145+
};
146+
}
147+
```
148+
149+
3. Add configuration in `config/services.php`:
150+
151+
```php
152+
'facial_recognition' => [
153+
'provider' => env('FACIAL_RECOGNITION_PROVIDER', 'mock'),
154+
'aws' => [
155+
'key' => env('AWS_ACCESS_KEY_ID'),
156+
'secret' => env('AWS_SECRET_ACCESS_KEY'),
157+
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
158+
],
159+
],
160+
```
161+
162+
4. Add environment variables to `.env`:
163+
164+
```env
165+
FACIAL_RECOGNITION_PROVIDER=aws
166+
AWS_ACCESS_KEY_ID=your_key_here
167+
AWS_SECRET_ACCESS_KEY=your_secret_here
168+
AWS_DEFAULT_REGION=us-east-1
169+
```
170+
171+
### Database Schema
172+
173+
#### person_photos
174+
Stores photo metadata for person photos.
175+
176+
| Column | Type | Description |
177+
|--------|------|-------------|
178+
| id | bigint | Primary key |
179+
| person_id | bigint | Foreign key to people table |
180+
| team_id | bigint | Foreign key to teams table |
181+
| file_path | string | Storage path to photo file |
182+
| file_name | string | Original filename |
183+
| mime_type | string | File MIME type |
184+
| file_size | integer | File size in bytes |
185+
| width | integer | Image width in pixels |
186+
| height | integer | Image height in pixels |
187+
| description | text | Optional photo description |
188+
| is_analyzed | boolean | Whether facial recognition has been run |
189+
| analyzed_at | timestamp | When facial recognition was run |
190+
191+
#### photo_tags
192+
Stores facial recognition tags linking faces to people.
193+
194+
| Column | Type | Description |
195+
|--------|------|-------------|
196+
| id | bigint | Primary key |
197+
| photo_id | bigint | Foreign key to person_photos |
198+
| person_id | bigint | Foreign key to people (nullable) |
199+
| team_id | bigint | Foreign key to teams |
200+
| confidence | decimal(5,2) | Match confidence score (0-100) |
201+
| bounding_box | json | Face coordinates {left, top, width, height} |
202+
| status | enum | Tag status: pending, confirmed, rejected |
203+
| confirmed_by | bigint | Foreign key to users (who confirmed) |
204+
| confirmed_at | timestamp | When tag was confirmed |
205+
206+
#### face_encodings
207+
Stores face encoding data for matching.
208+
209+
| Column | Type | Description |
210+
|--------|------|-------------|
211+
| id | bigint | Primary key |
212+
| person_id | bigint | Foreign key to people |
213+
| team_id | bigint | Foreign key to teams |
214+
| source_photo_id | bigint | Foreign key to person_photos |
215+
| encoding | text | Encrypted face encoding data |
216+
| provider | string | Provider that created encoding |
217+
218+
### API Methods
219+
220+
#### FacialRecognitionService
221+
222+
**analyzePhoto(PersonPhoto $photo): array**
223+
- Analyzes a photo for faces
224+
- Creates tags for detected faces
225+
- Returns results with face count and success status
226+
227+
**confirmTag(PhotoTag $tag, int $userId, bool $createEncoding = true): bool**
228+
- Confirms a photo tag
229+
- Optionally creates face encoding
230+
- Records who confirmed and when
231+
232+
**rejectTag(PhotoTag $tag): bool**
233+
- Rejects a photo tag
234+
- Updates status to rejected
235+
236+
**updateTagPerson(PhotoTag $tag, int $personId, int $userId): bool**
237+
- Updates tag to different person
238+
- Confirms the corrected tag
239+
- Creates face encoding for new assignment
240+
241+
**getPendingTags(?int $teamId = null, int $limit = 50)**
242+
- Returns pending tags for review
243+
- Filtered by team if specified
244+
- Ordered by creation date
245+
246+
### Testing
247+
248+
Run the test suite:
249+
250+
```bash
251+
php artisan test --filter FacialRecognition
252+
php artisan test --filter PhotoTagging
253+
```
254+
255+
Unit tests cover:
256+
- Face detection
257+
- Tag creation and management
258+
- Person matching
259+
- Error handling
260+
261+
Feature tests cover:
262+
- Complete photo upload workflow
263+
- Tag review and confirmation
264+
- Tag correction and rejection
265+
- Relationship integrity
266+
267+
### Security Considerations
268+
269+
1. **Face Encodings**: Stored encrypted in the database
270+
2. **File Storage**: Photos stored in Laravel's storage with proper permissions
271+
3. **Team Isolation**: All queries filtered by team_id for multi-tenancy
272+
4. **User Authorization**: Only team members can review and confirm tags
273+
5. **Input Validation**: File uploads validated for type and size
274+
275+
## Configuration
276+
277+
### Environment Variables
278+
279+
```env
280+
# Facial Recognition Provider
281+
FACIAL_RECOGNITION_PROVIDER=mock # Options: mock, aws
282+
283+
# AWS Rekognition (if using AWS provider)
284+
AWS_ACCESS_KEY_ID=
285+
AWS_SECRET_ACCESS_KEY=
286+
AWS_DEFAULT_REGION=us-east-1
287+
AWS_REKOGNITION_COLLECTION_ID=genealogy-faces
288+
```
289+
290+
### Storage Configuration
291+
292+
Photos are stored using Laravel's filesystem in the `public` disk under `person-photos/` directory.
293+
294+
To publish storage:
295+
```bash
296+
php artisan storage:link
297+
```
298+
299+
## Troubleshooting
300+
301+
### Photos Not Being Analyzed
302+
303+
1. Check if the storage link is created: `php artisan storage:link`
304+
2. Verify file permissions on storage directory
305+
3. Check logs for errors: `tail -f storage/logs/laravel.log`
306+
4. Ensure the provider is configured correctly
307+
308+
### No Faces Detected
309+
310+
1. Verify image quality (should be at least 400x400 pixels)
311+
2. Check if faces are clearly visible and front-facing
312+
3. Try re-analyzing the photo manually
313+
4. Review provider-specific limitations
314+
315+
### Tags Not Matching Correctly
316+
317+
1. Ensure confirmed tags exist for the person
318+
2. Check if face encodings were created
319+
3. Verify the person has multiple confirmed tags for better matching
320+
4. Review confidence threshold in provider
321+
322+
## Future Enhancements
323+
324+
- [ ] Implement AWS Rekognition provider
325+
- [ ] Add batch photo upload
326+
- [ ] Implement automatic tag confirmation above threshold
327+
- [ ] Add face grouping for unknown faces
328+
- [ ] Support for video analysis
329+
- [ ] Mobile-optimized review interface
330+
- [ ] Export tagged photo collections
331+
- [ ] Integration with photo timeline view
332+
333+
## Support
334+
335+
For issues or questions:
336+
- Check the logs: `storage/logs/laravel.log`
337+
- Review the test suite for usage examples
338+
- Consult the codebase documentation
339+
- Contact the development team

0 commit comments

Comments
 (0)