Fix models folder issues: bugs and code cleanup
Browse filesFixed Issues:
1. tracker.py - Fixed duplicate tracks bug
- Recovered detections were indexed against wrong list
- Recovery function now receives (original_index, detection) pairs
- Returns set of original indices that were recovered
- Prevents creating duplicate tracks for recovered items
2. Code cleanup across all model files:
- tracker.py: Removed unused imports (Path, Union, InferenceError, LogTimer, get_config)
- audio_analyzer.py: Removed unused imports (Dict, ModelLoadError, batch_list)
- visual_analyzer.py: Removed unused import (batch_list) and parameters (prompt, batch_size)
- face_recognizer.py: Removed unused import (validate_image_file)
- body_recognizer.py: Removed unused import (ModelLoadError)
- motion_detector.py: Removed unused imports (Path, Union, ModelLoadError, InferenceError)
- motion_detector.py: Removed dead assignment (_raft_transforms)
Impact:
- Tracker now correctly handles lost track recovery without duplicates
- Cleaner codebase with no unused imports or parameters
- Better code maintainability
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- models/audio_analyzer.py +2 -2
- models/body_recognizer.py +1 -1
- models/face_recognizer.py +1 -1
- models/motion_detector.py +1 -6
- models/tracker.py +21 -14
- models/visual_analyzer.py +1 -5
|
@@ -14,12 +14,12 @@ Features extracted:
|
|
| 14 |
"""
|
| 15 |
|
| 16 |
from pathlib import Path
|
| 17 |
-
from typing import List, Optional, Tuple
|
| 18 |
from dataclasses import dataclass
|
| 19 |
import numpy as np
|
| 20 |
|
| 21 |
from utils.logger import get_logger, LogTimer
|
| 22 |
-
from utils.helpers import
|
| 23 |
from config import get_config, ModelConfig
|
| 24 |
|
| 25 |
logger = get_logger("models.audio_analyzer")
|
|
|
|
| 14 |
"""
|
| 15 |
|
| 16 |
from pathlib import Path
|
| 17 |
+
from typing import List, Optional, Tuple
|
| 18 |
from dataclasses import dataclass
|
| 19 |
import numpy as np
|
| 20 |
|
| 21 |
from utils.logger import get_logger, LogTimer
|
| 22 |
+
from utils.helpers import InferenceError, normalize_scores
|
| 23 |
from config import get_config, ModelConfig
|
| 24 |
|
| 25 |
logger = get_logger("models.audio_analyzer")
|
|
@@ -15,7 +15,7 @@ from dataclasses import dataclass
|
|
| 15 |
import numpy as np
|
| 16 |
|
| 17 |
from utils.logger import get_logger, LogTimer
|
| 18 |
-
from utils.helpers import
|
| 19 |
from config import get_config, ModelConfig
|
| 20 |
|
| 21 |
logger = get_logger("models.body_recognizer")
|
|
|
|
| 15 |
import numpy as np
|
| 16 |
|
| 17 |
from utils.logger import get_logger, LogTimer
|
| 18 |
+
from utils.helpers import InferenceError
|
| 19 |
from config import get_config, ModelConfig
|
| 20 |
|
| 21 |
logger = get_logger("models.body_recognizer")
|
|
@@ -14,7 +14,7 @@ from dataclasses import dataclass
|
|
| 14 |
import numpy as np
|
| 15 |
|
| 16 |
from utils.logger import get_logger, LogTimer
|
| 17 |
-
from utils.helpers import ModelLoadError, InferenceError
|
| 18 |
from config import get_config, ModelConfig
|
| 19 |
|
| 20 |
logger = get_logger("models.face_recognizer")
|
|
|
|
| 14 |
import numpy as np
|
| 15 |
|
| 16 |
from utils.logger import get_logger, LogTimer
|
| 17 |
+
from utils.helpers import ModelLoadError, InferenceError
|
| 18 |
from config import get_config, ModelConfig
|
| 19 |
|
| 20 |
logger = get_logger("models.face_recognizer")
|
|
@@ -10,13 +10,11 @@ Uses RAFT (Recurrent All-Pairs Field Transforms) for high-quality
|
|
| 10 |
optical flow, with fallback to Farneback for speed.
|
| 11 |
"""
|
| 12 |
|
| 13 |
-
from
|
| 14 |
-
from typing import List, Optional, Tuple, Union
|
| 15 |
from dataclasses import dataclass
|
| 16 |
import numpy as np
|
| 17 |
|
| 18 |
from utils.logger import get_logger, LogTimer
|
| 19 |
-
from utils.helpers import ModelLoadError, InferenceError
|
| 20 |
from config import get_config, ModelConfig
|
| 21 |
|
| 22 |
logger = get_logger("models.motion_detector")
|
|
@@ -90,9 +88,6 @@ class MotionDetector:
|
|
| 90 |
|
| 91 |
self.raft_model.eval()
|
| 92 |
|
| 93 |
-
# Store preprocessing transforms
|
| 94 |
-
self._raft_transforms = weights.transforms()
|
| 95 |
-
|
| 96 |
logger.info("RAFT model loaded successfully")
|
| 97 |
|
| 98 |
except Exception as e:
|
|
|
|
| 10 |
optical flow, with fallback to Farneback for speed.
|
| 11 |
"""
|
| 12 |
|
| 13 |
+
from typing import List, Optional, Tuple
|
|
|
|
| 14 |
from dataclasses import dataclass
|
| 15 |
import numpy as np
|
| 16 |
|
| 17 |
from utils.logger import get_logger, LogTimer
|
|
|
|
| 18 |
from config import get_config, ModelConfig
|
| 19 |
|
| 20 |
logger = get_logger("models.motion_detector")
|
|
|
|
| 88 |
|
| 89 |
self.raft_model.eval()
|
| 90 |
|
|
|
|
|
|
|
|
|
|
| 91 |
logger.info("RAFT model loaded successfully")
|
| 92 |
|
| 93 |
except Exception as e:
|
|
@@ -9,14 +9,11 @@ Multi-object tracking using ByteTrack for:
|
|
| 9 |
ByteTrack uses two-stage association for robust tracking.
|
| 10 |
"""
|
| 11 |
|
| 12 |
-
from
|
| 13 |
-
from typing import List, Optional, Dict, Tuple, Union
|
| 14 |
from dataclasses import dataclass, field
|
| 15 |
import numpy as np
|
| 16 |
|
| 17 |
-
from utils.logger import get_logger
|
| 18 |
-
from utils.helpers import InferenceError
|
| 19 |
-
from config import get_config
|
| 20 |
|
| 21 |
logger = get_logger("models.tracker")
|
| 22 |
|
|
@@ -162,15 +159,17 @@ class ObjectTracker:
|
|
| 162 |
self._lost_tracks[track_id] = self._tracks.pop(track_id)
|
| 163 |
|
| 164 |
# Try to recover lost tracks with unmatched detections
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
|
|
|
|
|
|
| 169 |
|
| 170 |
# Create new tracks for remaining detections
|
| 171 |
new_tracks = []
|
| 172 |
for i in unmatched_dets:
|
| 173 |
-
if i not in
|
| 174 |
det = high_conf[i] if i < len(high_conf) else low_conf[i - len(high_conf)]
|
| 175 |
bbox, conf = det
|
| 176 |
track_id = self._create_track(bbox, conf)
|
|
@@ -308,15 +307,23 @@ class ObjectTracker:
|
|
| 308 |
|
| 309 |
def _recover_lost_tracks(
|
| 310 |
self,
|
| 311 |
-
detections: List[Tuple[Tuple[int, int, int, int], float]],
|
| 312 |
) -> set:
|
| 313 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 314 |
recovered = set()
|
| 315 |
|
| 316 |
if not self._lost_tracks or not detections:
|
| 317 |
return recovered
|
| 318 |
|
| 319 |
-
for
|
| 320 |
best_iou = 0
|
| 321 |
best_track_id = None
|
| 322 |
|
|
@@ -328,7 +335,7 @@ class ObjectTracker:
|
|
| 328 |
|
| 329 |
if best_track_id is not None:
|
| 330 |
self._update_track(best_track_id, bbox, conf)
|
| 331 |
-
recovered.add(
|
| 332 |
logger.debug(f"Recovered track {best_track_id}")
|
| 333 |
|
| 334 |
return recovered
|
|
|
|
| 9 |
ByteTrack uses two-stage association for robust tracking.
|
| 10 |
"""
|
| 11 |
|
| 12 |
+
from typing import List, Optional, Dict, Tuple
|
|
|
|
| 13 |
from dataclasses import dataclass, field
|
| 14 |
import numpy as np
|
| 15 |
|
| 16 |
+
from utils.logger import get_logger
|
|
|
|
|
|
|
| 17 |
|
| 18 |
logger = get_logger("models.tracker")
|
| 19 |
|
|
|
|
| 159 |
self._lost_tracks[track_id] = self._tracks.pop(track_id)
|
| 160 |
|
| 161 |
# Try to recover lost tracks with unmatched detections
|
| 162 |
+
# Build list of (original_index, detection) pairs
|
| 163 |
+
unmatched_detections = [
|
| 164 |
+
(i, high_conf[i] if i < len(high_conf) else low_conf[i - len(high_conf)])
|
| 165 |
+
for i in unmatched_dets
|
| 166 |
+
]
|
| 167 |
+
recovered_indices = self._recover_lost_tracks(unmatched_detections)
|
| 168 |
|
| 169 |
# Create new tracks for remaining detections
|
| 170 |
new_tracks = []
|
| 171 |
for i in unmatched_dets:
|
| 172 |
+
if i not in recovered_indices:
|
| 173 |
det = high_conf[i] if i < len(high_conf) else low_conf[i - len(high_conf)]
|
| 174 |
bbox, conf = det
|
| 175 |
track_id = self._create_track(bbox, conf)
|
|
|
|
| 307 |
|
| 308 |
def _recover_lost_tracks(
|
| 309 |
self,
|
| 310 |
+
detections: List[Tuple[int, Tuple[Tuple[int, int, int, int], float]]],
|
| 311 |
) -> set:
|
| 312 |
+
"""
|
| 313 |
+
Try to recover lost tracks with unmatched detections.
|
| 314 |
+
|
| 315 |
+
Args:
|
| 316 |
+
detections: List of (original_index, (bbox, confidence)) tuples
|
| 317 |
+
|
| 318 |
+
Returns:
|
| 319 |
+
Set of original indices that were successfully recovered
|
| 320 |
+
"""
|
| 321 |
recovered = set()
|
| 322 |
|
| 323 |
if not self._lost_tracks or not detections:
|
| 324 |
return recovered
|
| 325 |
|
| 326 |
+
for orig_idx, (bbox, conf) in detections:
|
| 327 |
best_iou = 0
|
| 328 |
best_track_id = None
|
| 329 |
|
|
|
|
| 335 |
|
| 336 |
if best_track_id is not None:
|
| 337 |
self._update_track(best_track_id, bbox, conf)
|
| 338 |
+
recovered.add(orig_idx) # Add original index, not enumeration index
|
| 339 |
logger.debug(f"Recovered track {best_track_id}")
|
| 340 |
|
| 341 |
return recovered
|
|
@@ -16,7 +16,7 @@ from dataclasses import dataclass
|
|
| 16 |
import numpy as np
|
| 17 |
|
| 18 |
from utils.logger import get_logger, LogTimer
|
| 19 |
-
from utils.helpers import ModelLoadError, InferenceError
|
| 20 |
from config import get_config, ModelConfig
|
| 21 |
|
| 22 |
logger = get_logger("models.visual_analyzer")
|
|
@@ -180,7 +180,6 @@ Respond with just the emotion."""
|
|
| 180 |
def analyze_frame(
|
| 181 |
self,
|
| 182 |
image: Union[str, Path, np.ndarray, "PIL.Image.Image"],
|
| 183 |
-
prompt: Optional[str] = None,
|
| 184 |
timestamp: float = 0.0,
|
| 185 |
) -> VisualFeatures:
|
| 186 |
"""
|
|
@@ -188,7 +187,6 @@ Respond with just the emotion."""
|
|
| 188 |
|
| 189 |
Args:
|
| 190 |
image: Image path, numpy array, or PIL Image
|
| 191 |
-
prompt: Custom prompt (uses default if None)
|
| 192 |
timestamp: Timestamp for this frame
|
| 193 |
|
| 194 |
Returns:
|
|
@@ -350,7 +348,6 @@ Respond with just the emotion."""
|
|
| 350 |
self,
|
| 351 |
images: List[Union[str, Path, np.ndarray]],
|
| 352 |
timestamps: Optional[List[float]] = None,
|
| 353 |
-
batch_size: int = 4,
|
| 354 |
) -> List[VisualFeatures]:
|
| 355 |
"""
|
| 356 |
Analyze multiple frames in batches.
|
|
@@ -358,7 +355,6 @@ Respond with just the emotion."""
|
|
| 358 |
Args:
|
| 359 |
images: List of images (paths or arrays)
|
| 360 |
timestamps: Timestamps for each image
|
| 361 |
-
batch_size: Number of images per batch
|
| 362 |
|
| 363 |
Returns:
|
| 364 |
List of VisualFeatures for each image
|
|
|
|
| 16 |
import numpy as np
|
| 17 |
|
| 18 |
from utils.logger import get_logger, LogTimer
|
| 19 |
+
from utils.helpers import ModelLoadError, InferenceError
|
| 20 |
from config import get_config, ModelConfig
|
| 21 |
|
| 22 |
logger = get_logger("models.visual_analyzer")
|
|
|
|
| 180 |
def analyze_frame(
|
| 181 |
self,
|
| 182 |
image: Union[str, Path, np.ndarray, "PIL.Image.Image"],
|
|
|
|
| 183 |
timestamp: float = 0.0,
|
| 184 |
) -> VisualFeatures:
|
| 185 |
"""
|
|
|
|
| 187 |
|
| 188 |
Args:
|
| 189 |
image: Image path, numpy array, or PIL Image
|
|
|
|
| 190 |
timestamp: Timestamp for this frame
|
| 191 |
|
| 192 |
Returns:
|
|
|
|
| 348 |
self,
|
| 349 |
images: List[Union[str, Path, np.ndarray]],
|
| 350 |
timestamps: Optional[List[float]] = None,
|
|
|
|
| 351 |
) -> List[VisualFeatures]:
|
| 352 |
"""
|
| 353 |
Analyze multiple frames in batches.
|
|
|
|
| 355 |
Args:
|
| 356 |
images: List of images (paths or arrays)
|
| 357 |
timestamps: Timestamps for each image
|
|
|
|
| 358 |
|
| 359 |
Returns:
|
| 360 |
List of VisualFeatures for each image
|