Refactor: Phase 5: Granular Refactoring

This commit is contained in:
ziesorx 2025-09-12 15:39:19 +07:00
parent 54f21672aa
commit 6c7c4c5d9c
4 changed files with 1216 additions and 15 deletions

View file

@ -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