feat: Implement quality filtering in Identify Panel for enhanced face identification

This commit adds a quality filtering feature to the Identify Panel, allowing users to filter faces based on a quality score (0-100%). The `_get_unidentified_faces()` method has been updated to accept a `min_quality_score` parameter, and the SQL query now includes a WHERE clause for quality filtering. All relevant call sites have been modified to utilize this new feature, improving the user experience during face identification. The unique checkbox default state has also been confirmed to be unchecked, ensuring consistency in the UI behavior.
This commit is contained in:
tanyar09 2025-10-16 15:02:43 -04:00
parent 986fc81005
commit ddb156520b
2 changed files with 198 additions and 7 deletions

166
IDENTIFY_PANEL_FIXES.md Normal file
View File

@ -0,0 +1,166 @@
# Identify Panel Fixes
**Date:** October 16, 2025
**Status:** ✅ Complete
## Issues Fixed
### 1. ✅ Unique Checkbox Default State
**Issue:** User requested that the "Unique faces only" checkbox be unchecked by default.
**Status:** Already correct! The checkbox was already unchecked by default.
**Code Location:** `src/gui/identify_panel.py`, line 76
```python
self.components['unique_var'] = tk.BooleanVar() # Defaults to False (unchecked)
```
### 2. ✅ Quality Filter Not Working
**Issue:** The "Min quality" filter slider wasn't actually filtering faces when loading them from the database.
**Root Cause:**
- The quality filter value was being captured in the GUI (slider with 0-100% range)
- However, the `_get_unidentified_faces()` method wasn't using this filter when querying the database
- Quality filtering was only happening during navigation (Back/Next buttons), not during initial load
**Solution:**
1. Modified `_get_unidentified_faces()` to accept a `min_quality_score` parameter
2. Added SQL WHERE clause to filter by quality score: `AND f.quality_score >= ?`
3. Updated all 4 calls to `_get_unidentified_faces()` to pass the quality filter value:
- `_start_identification()` - Initial load
- `on_unique_change()` - When toggling unique faces filter
- `_load_more_faces()` - Loading additional batches
- `_apply_date_filters()` - When applying date filters
**Code Changes:**
**File:** `src/gui/identify_panel.py`
**Modified Method Signature (line 519-521):**
```python
def _get_unidentified_faces(self, batch_size: int, date_from: str = None, date_to: str = None,
date_processed_from: str = None, date_processed_to: str = None,
min_quality_score: float = 0.0) -> List[Tuple]:
```
**Added SQL Filter (lines 537-540):**
```python
# Add quality filtering if specified
if min_quality_score > 0.0:
query += ' AND f.quality_score >= ?'
params.append(min_quality_score)
```
**Updated Call Sites:**
1. **`_start_identification()` (lines 494-501):**
```python
# Get quality filter
min_quality = self.components['quality_filter_var'].get()
min_quality_score = min_quality / 100.0
# Get unidentified faces with quality filter
self.current_faces = self._get_unidentified_faces(batch_size, date_from, date_to,
date_processed_from, date_processed_to,
min_quality_score)
```
2. **`on_unique_change()` (lines 267-274):**
```python
# Get quality filter
min_quality = self.components['quality_filter_var'].get()
min_quality_score = min_quality / 100.0
# Reload faces with current filters
self.current_faces = self._get_unidentified_faces(batch_size, date_from, date_to,
date_processed_from, date_processed_to,
min_quality_score)
```
3. **`_load_more_faces()` (lines 1378-1385):**
```python
# Get quality filter
min_quality = self.components['quality_filter_var'].get()
min_quality_score = min_quality / 100.0
# Get more faces
more_faces = self._get_unidentified_faces(DEFAULT_BATCH_SIZE, date_from, date_to,
date_processed_from, date_processed_to,
min_quality_score)
```
4. **`_apply_date_filters()` (lines 1575-1581):**
```python
# Quality filter is already extracted above in min_quality
min_quality_score = min_quality / 100.0
# Reload faces with new filters
self.current_faces = self._get_unidentified_faces(batch_size, date_from, date_to,
date_processed_from, date_processed_to,
min_quality_score)
```
## Testing
**Syntax Check:** ✅ Passed
```bash
python3 -m py_compile src/gui/identify_panel.py
```
**Linter Check:** ✅ No errors found
## How Quality Filter Now Works
1. **User adjusts slider:** Sets quality from 0% to 100% (in 5% increments)
2. **User clicks "Start Identification":**
- Gets quality value (e.g., 75%)
- Converts to 0.0-1.0 scale (e.g., 0.75)
- Passes to `_get_unidentified_faces()`
- SQL query filters: `WHERE f.quality_score >= 0.75`
- Only faces with quality ≥ 75% are loaded
3. **Quality filter persists:**
- When loading more batches
- When toggling unique faces
- When applying date filters
- When navigating (Back/Next already had quality filtering)
## Expected Behavior
### Quality Filter = 0% (default)
- Shows all faces regardless of quality
- SQL: No quality filter applied
### Quality Filter = 50%
- Shows only faces with quality ≥ 50%
- SQL: `WHERE f.quality_score >= 0.5`
### Quality Filter = 75%
- Shows only faces with quality ≥ 75%
- SQL: `WHERE f.quality_score >= 0.75`
### Quality Filter = 100%
- Shows only perfect quality faces
- SQL: `WHERE f.quality_score >= 1.0`
## Notes
- The quality score is stored in the database as a float between 0.0 and 1.0
- The GUI displays it as a percentage (0-100%) for user-friendliness
- The conversion happens at every call site: `min_quality_score = min_quality / 100.0`
- The Back/Next navigation already had quality filtering logic via `_find_next_qualifying_face()` - this continues to work as before
## Files Modified
- `src/gui/identify_panel.py` (1 file, ~15 lines changed)
## Validation Checklist
- [x] Quality filter parameter added to method signature
- [x] SQL WHERE clause added for quality filtering
- [x] All 4 call sites updated with quality filter
- [x] Syntax validation passed
- [x] No linter errors
- [x] Unique checkbox already defaults to unchecked
- [x] Code follows PEP 8 style guidelines
- [x] Changes are backward compatible (min_quality_score defaults to 0.0)

View File

@ -264,9 +264,14 @@ class IdentifyPanel:
except Exception:
batch_size = DEFAULT_BATCH_SIZE
# Get quality filter
min_quality = self.components['quality_filter_var'].get()
min_quality_score = min_quality / 100.0
# Reload faces with current filters
self.current_faces = self._get_unidentified_faces(batch_size, date_from, date_to,
date_processed_from, date_processed_to)
date_processed_from, date_processed_to,
min_quality_score)
print(f"✅ Reloaded: {len(self.current_faces)} faces")
@ -491,9 +496,14 @@ class IdentifyPanel:
date_processed_from = self.components['date_processed_from_var'].get().strip() or None
date_processed_to = self.components['date_processed_to_var'].get().strip() or None
# Get unidentified faces (without quality filter - we'll filter in display)
# Get quality filter
min_quality = self.components['quality_filter_var'].get()
min_quality_score = min_quality / 100.0
# Get unidentified faces with quality filter
self.current_faces = self._get_unidentified_faces(batch_size, date_from, date_to,
date_processed_from, date_processed_to)
date_processed_from, date_processed_to,
min_quality_score)
if not self.current_faces:
messagebox.showinfo("No Faces", "🎉 All faces have been identified!")
@ -517,8 +527,9 @@ class IdentifyPanel:
self.is_active = True
def _get_unidentified_faces(self, batch_size: int, date_from: str = None, date_to: str = None,
date_processed_from: str = None, date_processed_to: str = None) -> List[Tuple]:
"""Get unidentified faces from database with optional date filtering"""
date_processed_from: str = None, date_processed_to: str = None,
min_quality_score: float = 0.0) -> List[Tuple]:
"""Get unidentified faces from database with optional date and quality filtering"""
with self.db.get_db_connection() as conn:
cursor = conn.cursor()
@ -533,6 +544,11 @@ class IdentifyPanel:
'''
params = []
# Add quality filtering if specified
if min_quality_score > 0.0:
query += ' AND f.quality_score >= ?'
params.append(min_quality_score)
# Add date taken filtering if specified
if date_from:
query += ' AND p.date_taken >= ?'
@ -1359,9 +1375,14 @@ class IdentifyPanel:
date_processed_from = self.components['date_processed_from_var'].get().strip() or None
date_processed_to = self.components['date_processed_to_var'].get().strip() or None
# Get quality filter
min_quality = self.components['quality_filter_var'].get()
min_quality_score = min_quality / 100.0
# Get more faces
more_faces = self._get_unidentified_faces(DEFAULT_BATCH_SIZE, date_from, date_to,
date_processed_from, date_processed_to)
date_processed_from, date_processed_to,
min_quality_score)
if more_faces:
# Add to current faces
@ -1551,9 +1572,13 @@ class IdentifyPanel:
except Exception:
batch_size = DEFAULT_BATCH_SIZE
# Quality filter is already extracted above in min_quality
min_quality_score = min_quality / 100.0
# Reload faces with new filters
self.current_faces = self._get_unidentified_faces(batch_size, date_from, date_to,
date_processed_from, date_processed_to)
date_processed_from, date_processed_to,
min_quality_score)
if not self.current_faces:
messagebox.showinfo("No Faces Found", "No unidentified faces found with the current filters.")