Refactor: Phase 5: Granular Refactoring
This commit is contained in:
parent
54f21672aa
commit
6c7c4c5d9c
4 changed files with 1216 additions and 15 deletions
|
@ -493,6 +493,9 @@ class YOLODetector:
|
|||
"""
|
||||
Run YOLO detection with BoT-SORT tracking and stability validation.
|
||||
|
||||
This method has been refactored from 226 lines into smaller, focused helper methods
|
||||
for better maintainability and testability.
|
||||
|
||||
Args:
|
||||
frame: Input frame/image
|
||||
node: Pipeline node configuration with model and settings
|
||||
|
@ -504,6 +507,32 @@ class YOLODetector:
|
|||
- regions_dict: Dict mapping class names to highest confidence detections
|
||||
- track_validation_result: Dict with validation status and stable tracks
|
||||
"""
|
||||
try:
|
||||
# Extract context information
|
||||
camera_id, model_id, current_mode = self._extract_detection_context(node, context)
|
||||
|
||||
# Initialize detection configuration
|
||||
tracking_config = self._extract_tracking_config(node)
|
||||
min_confidence = self._determine_confidence_threshold(node)
|
||||
|
||||
# Prepare tracker state
|
||||
self._prepare_tracker_state(node, camera_id, model_id)
|
||||
|
||||
# Run core detection pipeline
|
||||
detection_result = self._execute_detection_pipeline(
|
||||
frame, node, camera_id, model_id, current_mode,
|
||||
tracking_config, min_confidence
|
||||
)
|
||||
|
||||
# Store validation context
|
||||
if context is not None:
|
||||
context["track_validation_result"] = detection_result[2]
|
||||
|
||||
return detection_result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Detection error for camera {context.get('camera_id', 'unknown') if context else 'unknown'}: {e}")
|
||||
return [], {}, {}
|
||||
try:
|
||||
camera_id = context.get("camera_id", "unknown") if context else "unknown"
|
||||
model_id = node.get("modelId", "unknown")
|
||||
|
@ -566,18 +595,116 @@ class YOLODetector:
|
|||
return [], {}, track_validation_result.to_dict()
|
||||
|
||||
logger.info(f"✅ Camera {camera_id}: DETECTION COMPLETE - tracking single car: track_id={track_id}, conf={best_detection['confidence']:.3f}")
|
||||
logger.debug(f"📊 Camera {camera_id}: Detection summary: {len(res.boxes)} raw → {len(candidate_detections)} candidates → 1 selected")
|
||||
|
||||
# Store validation state in context for pipeline decisions
|
||||
if context is not None:
|
||||
context["track_validation_result"] = track_validation_result.to_dict()
|
||||
|
||||
return all_detections, regions_dict, track_validation_result.to_dict()
|
||||
|
||||
|
||||
except Exception as e:
|
||||
camera_id = context.get("camera_id", "unknown") if context else "unknown"
|
||||
model_id = node.get("modelId", "unknown")
|
||||
raise create_detection_error(camera_id, model_id, "detection_with_tracking", e)
|
||||
logger.error(f"Detection error for camera {camera_id}: {e}")
|
||||
return [], {}, {}
|
||||
|
||||
def _extract_detection_context(self, node: Dict[str, Any], context: Optional[Dict[str, Any]]) -> Tuple[str, str, str]:
|
||||
"""Extract camera ID, model ID, and current mode from context."""
|
||||
camera_id = context.get("camera_id", "unknown") if context else "unknown"
|
||||
model_id = node.get("modelId", "unknown")
|
||||
current_mode = context.get("current_mode", "unknown") if context else "unknown"
|
||||
return camera_id, model_id, current_mode
|
||||
|
||||
def _prepare_tracker_state(self, node: Dict[str, Any], camera_id: str, model_id: str) -> None:
|
||||
"""Prepare tracker state and reset if needed."""
|
||||
# Check if we need to reset tracker after cooldown
|
||||
self._reset_yolo_tracker_if_needed(node, camera_id, model_id)
|
||||
|
||||
def _execute_detection_pipeline(
|
||||
self,
|
||||
frame,
|
||||
node: Dict[str, Any],
|
||||
camera_id: str,
|
||||
model_id: str,
|
||||
current_mode: str,
|
||||
tracking_config: TrackingConfig,
|
||||
min_confidence: float
|
||||
) -> Tuple[List[Dict[str, Any]], Dict[str, Any], Dict[str, Any]]:
|
||||
"""Execute the core detection pipeline."""
|
||||
# Run YOLO inference
|
||||
res = self._run_yolo_inference(frame, node, tracking_config)
|
||||
|
||||
# Process detection results
|
||||
candidate_detections = self._process_detections(res, node, camera_id, min_confidence)
|
||||
|
||||
# Select best detection
|
||||
best_detection = self._select_best_detection(candidate_detections, camera_id)
|
||||
|
||||
# Update track stability validation
|
||||
track_validation_result = self._update_track_stability(
|
||||
best_detection, node, camera_id, model_id, current_mode
|
||||
)
|
||||
|
||||
# Handle no detection case
|
||||
if best_detection is None:
|
||||
return [], {}, track_validation_result.to_dict()
|
||||
|
||||
# Process successful detection
|
||||
return self._process_successful_detection(
|
||||
best_detection, node, camera_id, track_validation_result
|
||||
)
|
||||
|
||||
def _update_track_stability(
|
||||
self,
|
||||
detection: Optional[Dict[str, Any]],
|
||||
node: Dict[str, Any],
|
||||
camera_id: str,
|
||||
model_id: str,
|
||||
current_mode: str
|
||||
) -> Any:
|
||||
"""Update track stability validation."""
|
||||
tracking_config = self._extract_tracking_config(node)
|
||||
is_branch_node = node.get("cropClass") is not None or node.get("parallel") is True
|
||||
|
||||
return self.stability_tracker.update_single_track_stability(
|
||||
detection=detection,
|
||||
camera_id=camera_id,
|
||||
model_id=model_id,
|
||||
stability_threshold=tracking_config.stability_threshold,
|
||||
current_mode=current_mode,
|
||||
is_branch_node=is_branch_node
|
||||
)
|
||||
|
||||
def _process_successful_detection(
|
||||
self,
|
||||
detection: Dict[str, Any],
|
||||
node: Dict[str, Any],
|
||||
camera_id: str,
|
||||
track_validation_result: Any
|
||||
) -> Tuple[List[Dict[str, Any]], Dict[str, Any], Dict[str, Any]]:
|
||||
"""Process a successful detection result."""
|
||||
# Apply class mapping
|
||||
detection = self._apply_class_mapping(detection, node)
|
||||
|
||||
# Create regions dictionary
|
||||
mapped_class = detection["class"]
|
||||
track_id = detection["id"]
|
||||
|
||||
all_detections = [detection]
|
||||
regions_dict = {
|
||||
mapped_class: {
|
||||
"bbox": detection["bbox"],
|
||||
"confidence": detection["confidence"],
|
||||
"detection": detection,
|
||||
"track_id": track_id
|
||||
}
|
||||
}
|
||||
|
||||
# Multi-class validation
|
||||
if not self._validate_multi_class(regions_dict, node):
|
||||
return [], {}, track_validation_result.to_dict()
|
||||
|
||||
logger.info(f"✅ Camera {camera_id}: DETECTION COMPLETE - tracking single car: track_id={track_id}, conf={detection['confidence']:.3f}")
|
||||
|
||||
return all_detections, regions_dict, track_validation_result.to_dict()
|
||||
|
||||
|
||||
# Global instances for backward compatibility
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue