wait update RX cam sub
This commit is contained in:
parent
5bf2d49e6b
commit
80d9c925de
3 changed files with 530 additions and 60 deletions
192
app.py
192
app.py
|
@ -308,12 +308,14 @@ def get_or_init_session_pipeline_state(camera_id):
|
|||
"""Get or initialize session pipeline state for a camera"""
|
||||
if camera_id not in session_pipeline_states:
|
||||
session_pipeline_states[camera_id] = {
|
||||
"mode": "validation_detecting", # "validation_detecting", "send_detections", "waiting_for_session_id", "full_pipeline", "lightweight"
|
||||
"mode": "validation_detecting", # "validation_detecting", "send_detections", "waiting_for_session_id", "full_pipeline", "lightweight", "car_gone_waiting"
|
||||
"session_id_received": False,
|
||||
"full_pipeline_completed": False,
|
||||
"absence_counter": 0,
|
||||
"max_absence_frames": 3
|
||||
# Removed validation_counter and validation_threshold - now using only track-based validation
|
||||
"max_absence_frames": 3,
|
||||
"yolo_inference_enabled": True, # Controls whether to run YOLO inference
|
||||
"cached_detection_dict": None, # Cached detection dict for lightweight mode
|
||||
"stable_track_id": None # The stable track ID we're monitoring
|
||||
}
|
||||
return session_pipeline_states[camera_id]
|
||||
|
||||
|
@ -597,56 +599,145 @@ async def detect(websocket: WebSocket):
|
|||
"result": detection_result.copy(),
|
||||
"timestamp": time.time()
|
||||
}
|
||||
|
||||
# Cache the detection dict for lightweight mode reuse
|
||||
branch_results = detection_result.get("branch_results", {})
|
||||
cached_dict = {
|
||||
"carModel": branch_results.get("car_brand_cls_v1", {}).get("model"),
|
||||
"carBrand": branch_results.get("car_brand_cls_v1", {}).get("brand"),
|
||||
"carYear": None,
|
||||
"bodyType": branch_results.get("car_bodytype_cls_v1", {}).get("body_type"),
|
||||
"licensePlateText": None,
|
||||
"licensePlateConfidence": None
|
||||
}
|
||||
pipeline_state["cached_detection_dict"] = cached_dict
|
||||
|
||||
# Log what was cached for debugging
|
||||
logger.info(f"💾 Camera {camera_id}: CACHING DETECTION DICT:")
|
||||
logger.info(f"💾 Camera {camera_id}: - Full branch_results: {branch_results}")
|
||||
logger.info(f"💾 Camera {camera_id}: - Cached dict: {cached_dict}")
|
||||
|
||||
# Store the stable track ID for lightweight monitoring
|
||||
track_id = detection_result.get("track_id") or detection_result.get("id")
|
||||
if track_id is not None:
|
||||
pipeline_state["stable_track_id"] = track_id
|
||||
logger.info(f"💾 Camera {camera_id}: Cached stable track_id={track_id}")
|
||||
else:
|
||||
logger.warning(f"⚠️ Camera {camera_id}: No track_id found in detection_result: {detection_result.keys()}")
|
||||
|
||||
# Switch to lightweight mode
|
||||
update_session_pipeline_mode(camera_id, "lightweight")
|
||||
logger.info(f"✅ Camera {camera_id}: Full pipeline completed - switching to LIGHTWEIGHT mode")
|
||||
|
||||
elif current_mode == "lightweight":
|
||||
# ═══ LIGHTWEIGHT MODE ═══
|
||||
# Use tracking to check for stable car presence
|
||||
from siwatsystem.pympta import run_detection_with_tracking
|
||||
all_detections, regions_dict, track_validation_result = run_detection_with_tracking(cropped_frame, model_tree, pipeline_context)
|
||||
# ═══ ENHANCED LIGHTWEIGHT MODE ═══
|
||||
# Only run YOLO11n.pt to check stable track presence, use cached detection dict
|
||||
|
||||
stable_tracks = track_validation_result.get("stable_tracks", [])
|
||||
current_tracks = track_validation_result.get("current_tracks", [])
|
||||
stable_tracks_present = bool(set(stable_tracks) & set(current_tracks))
|
||||
stable_track_id = pipeline_state.get("stable_track_id")
|
||||
cached_detection_dict = pipeline_state.get("cached_detection_dict")
|
||||
|
||||
if stable_tracks_present:
|
||||
# Stable tracked car still present - use cached result
|
||||
pipeline_state["absence_counter"] = 0
|
||||
if camera_id in cached_full_pipeline_results:
|
||||
detection_result = cached_full_pipeline_results[camera_id]["result"]
|
||||
logger.debug(f"🔄 Camera {camera_id}: Stable tracked car still present - using cached detection")
|
||||
else:
|
||||
logger.warning(f"⚠️ Camera {camera_id}: Stable tracked car detected but no cached result available")
|
||||
detection_result = None
|
||||
logger.debug(f"🪶 Camera {camera_id}: LIGHTWEIGHT MODE - monitoring stable track_id={stable_track_id}")
|
||||
|
||||
if not pipeline_state.get("yolo_inference_enabled", True):
|
||||
# YOLO inference disabled - car considered gone, wait for reset
|
||||
logger.debug(f"🛑 Camera {camera_id}: YOLO inference disabled - waiting for reset")
|
||||
detection_result = None # Don't send anything
|
||||
else:
|
||||
# No stable tracked cars - increment absence counter
|
||||
pipeline_state["absence_counter"] += 1
|
||||
absence_count = pipeline_state["absence_counter"]
|
||||
max_absence = pipeline_state["max_absence_frames"]
|
||||
# Run lightweight YOLO inference to check track presence only (no full pipeline)
|
||||
from siwatsystem.pympta import run_detection_with_tracking
|
||||
all_detections, regions_dict, track_validation_result = run_detection_with_tracking(cropped_frame, model_tree, pipeline_context)
|
||||
|
||||
logger.debug(f"👻 Camera {camera_id}: No stable tracked cars - absence {absence_count}/{max_absence}")
|
||||
# OPTION A: Car presence only (track ID kept for internal use)
|
||||
any_car_detected = len(all_detections) > 0
|
||||
current_tracks = track_validation_result.get("current_tracks", [])
|
||||
|
||||
if absence_count >= max_absence:
|
||||
# Send "none" detection and reset to validation mode
|
||||
detection_result = {
|
||||
"class": "none",
|
||||
"confidence": 1.0,
|
||||
"bbox": [0, 0, 0, 0],
|
||||
"branch_results": {}
|
||||
}
|
||||
cached_full_pipeline_results.pop(camera_id, None) # Clear cache
|
||||
update_session_pipeline_mode(camera_id, "validation_detecting")
|
||||
logger.info(f"📤 Camera {camera_id}: Stable tracked cars absent for {absence_count} frames - sending 'none' and resetting to track validation")
|
||||
else:
|
||||
# Still within absence tolerance - use cached result
|
||||
if camera_id in cached_full_pipeline_results:
|
||||
detection_result = cached_full_pipeline_results[camera_id]["result"]
|
||||
logger.debug(f"⏳ Camera {camera_id}: Stable tracked cars absent {absence_count}/{max_absence} - still using cached detection")
|
||||
logger.debug(f"🪶 Camera {camera_id}: LIGHTWEIGHT - any_cars={any_car_detected} (main decision), current_tracks={current_tracks} (internal only)")
|
||||
|
||||
if not any_car_detected:
|
||||
# NO cars detected at all - increment absence counter
|
||||
pipeline_state["absence_counter"] += 1
|
||||
absence_count = pipeline_state["absence_counter"]
|
||||
max_absence = 2 # Changed from 3 to 2 consecutive frames
|
||||
|
||||
logger.info(f"👻 Camera {camera_id}: NO CARS detected - absence {absence_count}/{max_absence}")
|
||||
|
||||
# Check robust AND condition: backend confirmed AND detection confirmed
|
||||
backend_confirmed_gone = (backend_session_id is None)
|
||||
detection_confirmed_gone = (absence_count >= max_absence)
|
||||
|
||||
logger.debug(f"🔍 Camera {camera_id}: Reset conditions - backend_null={backend_confirmed_gone}, absence_2frames={detection_confirmed_gone}")
|
||||
|
||||
if backend_confirmed_gone and detection_confirmed_gone:
|
||||
# BOTH conditions met - RESET TO VALIDATION PHASE
|
||||
logger.info(f"🔄 Camera {camera_id}: ROBUST RESET - both conditions met (backend=null AND absence>=2)")
|
||||
|
||||
# Clear all state and prepare for next car
|
||||
cached_full_pipeline_results.pop(camera_id, None)
|
||||
pipeline_state["cached_detection_dict"] = None
|
||||
pipeline_state["stable_track_id"] = None
|
||||
pipeline_state["absence_counter"] = 0
|
||||
pipeline_state["yolo_inference_enabled"] = True # Re-enable for next car
|
||||
|
||||
# Clear stability tracking data for this camera
|
||||
from siwatsystem.pympta import reset_camera_stability_tracking
|
||||
reset_camera_stability_tracking(camera_id, model_tree.get("modelId", "unknown"))
|
||||
|
||||
# Switch back to validation phase - ready for next car
|
||||
update_session_pipeline_mode(camera_id, "detection_dict")
|
||||
logger.info(f"🔄 Camera {camera_id}: RESET TO VALIDATION - model ready for next car")
|
||||
|
||||
detection_result = None # Stop sending data during reset
|
||||
else:
|
||||
# One or both conditions not met - keep sending cached detection dict
|
||||
if cached_detection_dict:
|
||||
detection_result = cached_detection_dict # Always send cached data
|
||||
logger.info(f"⏳ Camera {camera_id}: NO CARS absence {absence_count}/2, backend_null={backend_confirmed_gone} - sending cached detection dict")
|
||||
else:
|
||||
logger.warning(f"⚠️ Camera {camera_id}: NO CARS but no cached detection dict available")
|
||||
detection_result = None
|
||||
|
||||
else:
|
||||
# Cars detected - reset absence counter, send cached detection dict
|
||||
pipeline_state["absence_counter"] = 0 # Reset absence since cars are present
|
||||
|
||||
if cached_detection_dict:
|
||||
detection_result = cached_detection_dict # Always send cached data
|
||||
logger.info(f"🪶 Camera {camera_id}: CARS DETECTED - sending cached detection dict:")
|
||||
logger.info(f"🪶 Camera {camera_id}: - Cached dict: {cached_detection_dict}")
|
||||
logger.info(f"🪶 Camera {camera_id}: - Track info (internal): {current_tracks}")
|
||||
else:
|
||||
logger.warning(f"⚠️ Camera {camera_id}: Cars detected but no cached detection dict available")
|
||||
detection_result = None
|
||||
|
||||
elif current_mode == "car_gone_waiting":
|
||||
# ═══ CAR GONE WAITING STATE ═══
|
||||
# Car is gone (both conditions met), YOLO inference disabled, waiting for new session
|
||||
|
||||
logger.debug(f"🛑 Camera {camera_id}: CAR GONE WAITING - YOLO inference stopped")
|
||||
|
||||
# Check if backend has started a new session (indicates new car scenario)
|
||||
if backend_session_id is not None:
|
||||
# Backend started new session - re-enable YOLO and reset to validation
|
||||
pipeline_state["yolo_inference_enabled"] = True
|
||||
pipeline_state["absence_counter"] = 0
|
||||
pipeline_state["stable_track_id"] = None
|
||||
pipeline_state["cached_detection_dict"] = None
|
||||
|
||||
# Clear stability tracking data for this camera
|
||||
from siwatsystem.pympta import reset_camera_stability_tracking
|
||||
reset_camera_stability_tracking(camera_id, model_tree.get("modelId", "unknown"))
|
||||
|
||||
update_session_pipeline_mode(camera_id, "validation_detecting")
|
||||
logger.info(f"🔄 Camera {camera_id}: New session detected (id={backend_session_id}) - re-enabling YOLO inference")
|
||||
logger.info(f"✅ Camera {camera_id}: Reset to validation mode - cleared all tracking, ready for new car detection")
|
||||
|
||||
# Don't run detection this frame - let next frame start fresh
|
||||
detection_result = {"class": "none", "confidence": 1.0, "bbox": [0, 0, 0, 0]}
|
||||
else:
|
||||
# Still waiting - no sessionId, no detection to send
|
||||
logger.debug(f"🛑 Camera {camera_id}: Car gone waiting - no YOLO inference, no data sent")
|
||||
detection_result = None
|
||||
|
||||
process_time = (time.time() - start_time) * 1000
|
||||
logger.debug(f"Detection for camera {camera_id} completed in {process_time:.2f}ms (mode: {current_mode})")
|
||||
|
||||
|
@ -698,8 +789,23 @@ async def detect(websocket: WebSocket):
|
|||
# "None" detection in other modes (lightweight) - car left or absent for 3 frames
|
||||
detection_dict = None
|
||||
logger.info(f"📤 SENDING 'NONE' (detection: null) - Car absent, expecting backend to clear session for camera {camera_id}")
|
||||
elif detection_result.get("cached_mode", False):
|
||||
# Cached mode in lightweight - use cached detection dict directly
|
||||
cached_dict = detection_result.get("branch_results", {})
|
||||
detection_dict = cached_dict if cached_dict else {
|
||||
"carModel": None,
|
||||
"carBrand": None,
|
||||
"carYear": None,
|
||||
"bodyType": None,
|
||||
"licensePlateText": None,
|
||||
"licensePlateConfidence": None
|
||||
}
|
||||
logger.info(f"💾 Camera {camera_id}: SENDING CACHED DETECTION_DICT to backend:")
|
||||
logger.info(f"💾 Camera {camera_id}: - Cached branch_results: {cached_dict}")
|
||||
logger.info(f"💾 Camera {camera_id}: - Final detection_dict: {detection_dict}")
|
||||
logger.info(f"💾 Camera {camera_id}: - Track ID: {detection_result.get('track_id')} (lightweight mode)")
|
||||
else:
|
||||
# Valid detection - convert to backend format
|
||||
# Valid detection - convert to backend format (will be populated by branch processing)
|
||||
detection_dict = {
|
||||
"carModel": None,
|
||||
"carBrand": None,
|
||||
|
@ -709,8 +815,8 @@ async def detect(websocket: WebSocket):
|
|||
"licensePlateConfidence": None
|
||||
}
|
||||
|
||||
# Extract and process branch results from parallel classification (only for valid detections)
|
||||
if detection_result.get("class") != "none" and "branch_results" in detection_result:
|
||||
# Extract and process branch results from parallel classification (only for valid detections, skip cached mode)
|
||||
if detection_result.get("class") != "none" and "branch_results" in detection_result and not detection_result.get("cached_mode", False):
|
||||
def process_branch_results(branch_results, depth=0):
|
||||
"""Recursively process branch results including nested branches."""
|
||||
if not isinstance(branch_results, dict):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue