update/car-in_no_fueling//next: car-in_fueling
This commit is contained in:
parent
5875b76d74
commit
72eb7d55ea
7 changed files with 4184 additions and 357 deletions
|
@ -681,7 +681,7 @@ def run_detection_with_tracking(frame, node, context=None):
|
|||
# Update stability tracking even when no detection (to reset counters)
|
||||
camera_id = context.get("camera_id", "unknown") if context else "unknown"
|
||||
model_id = node.get("modelId", "unknown")
|
||||
track_validation_result = update_single_track_stability(node, None, camera_id, frame.shape, stability_threshold)
|
||||
track_validation_result = update_single_track_stability(node, None, camera_id, frame.shape, stability_threshold, context)
|
||||
|
||||
# Store validation state in context for pipeline decisions
|
||||
if context is not None:
|
||||
|
@ -745,7 +745,7 @@ def run_detection_with_tracking(frame, node, context=None):
|
|||
# Update stability tracking even when no detection (to reset counters)
|
||||
camera_id = context.get("camera_id", "unknown") if context else "unknown"
|
||||
model_id = node.get("modelId", "unknown")
|
||||
track_validation_result = update_single_track_stability(node, None, camera_id, frame.shape, stability_threshold)
|
||||
track_validation_result = update_single_track_stability(node, None, camera_id, frame.shape, stability_threshold, context)
|
||||
|
||||
# Store validation state in context for pipeline decisions
|
||||
if context is not None:
|
||||
|
@ -813,7 +813,7 @@ def run_detection_with_tracking(frame, node, context=None):
|
|||
model_id = node.get("modelId", "unknown")
|
||||
|
||||
# Update stability tracking for the single best detection
|
||||
track_validation_result = update_single_track_stability(node, best_detection, camera_id, frame.shape, stability_threshold)
|
||||
track_validation_result = update_single_track_stability(node, best_detection, camera_id, frame.shape, stability_threshold, context)
|
||||
|
||||
# Store validation state in context for pipeline decisions
|
||||
if context is not None:
|
||||
|
@ -866,14 +866,8 @@ def get_camera_stability_data(camera_id, model_id):
|
|||
"waiting_for_backend_session": False,
|
||||
"wait_start_time": 0.0,
|
||||
"reset_tracker_on_resume": False
|
||||
},
|
||||
"occupancy_state": {
|
||||
"phase": "validation", # "validation", "waiting_for_session", or "occupancy"
|
||||
"absence_counter": 0, # Count consecutive frames without stable tracks
|
||||
"max_absence_frames": 3, # Trigger "none" after this many absent frames
|
||||
"pipeline_completed": False # Track if full pipeline has run
|
||||
}
|
||||
# Removed detection_counter - using only track-based validation now
|
||||
# Removed obsolete occupancy_state - app.py handles all mode transitions now
|
||||
}
|
||||
|
||||
return _camera_stability_tracking[camera_id][model_id]
|
||||
|
@ -886,6 +880,7 @@ def reset_camera_stability_tracking(camera_id, model_id):
|
|||
# Clear all tracking data
|
||||
track_counters = stability_data["track_stability_counters"]
|
||||
stable_tracks = stability_data["stable_tracks"]
|
||||
session_state = stability_data["session_state"]
|
||||
|
||||
old_counters = dict(track_counters)
|
||||
old_stable = list(stable_tracks)
|
||||
|
@ -893,17 +888,16 @@ def reset_camera_stability_tracking(camera_id, model_id):
|
|||
track_counters.clear()
|
||||
stable_tracks.clear()
|
||||
|
||||
# Reset occupancy state to validation
|
||||
stability_data["occupancy_state"]["phase"] = "validation"
|
||||
stability_data["occupancy_state"]["absence_counter"] = 0
|
||||
stability_data["occupancy_state"]["pipeline_completed"] = False
|
||||
# IMPORTANT: Set flag to reset YOLO tracker on next detection run
|
||||
# This will ensure track IDs start fresh (1, 2, 3...) instead of continuing from old IDs
|
||||
session_state["reset_tracker_on_resume"] = True
|
||||
|
||||
logger.info(f"🧹 Camera {camera_id}: CLEARED stability tracking - old_counters={old_counters}, old_stable={old_stable}")
|
||||
# Occupancy state reset logging removed - not used in enhanced lightweight mode
|
||||
logger.info(f"🔄 Camera {camera_id}: YOLO tracker will be reset on next detection - fresh track IDs will start from 1")
|
||||
else:
|
||||
logger.debug(f"🧹 Camera {camera_id}: No stability tracking data to clear for model {model_id}")
|
||||
|
||||
def update_single_track_stability(node, detection, camera_id, frame_shape=None, stability_threshold=4):
|
||||
def update_single_track_stability(node, detection, camera_id, frame_shape=None, stability_threshold=4, context=None):
|
||||
"""Update track stability validation for a single highest confidence car."""
|
||||
model_id = node.get("modelId", "unknown")
|
||||
|
||||
|
@ -913,113 +907,102 @@ def update_single_track_stability(node, detection, camera_id, frame_shape=None,
|
|||
logger.debug(f"⏭️ Camera {camera_id}: Skipping validation for branch node {model_id} - validation only done at main pipeline level")
|
||||
return {"validation_complete": False, "branch_node": True, "stable_tracks": [], "current_tracks": []}
|
||||
|
||||
# Check current mode - VALIDATION COUNTERS should increment in both validation_detecting and full_pipeline modes
|
||||
current_mode = context.get("current_mode", "unknown") if context else "unknown"
|
||||
is_validation_mode = (current_mode in ["validation_detecting", "full_pipeline"])
|
||||
|
||||
# Get camera-specific stability data
|
||||
stability_data = get_camera_stability_data(camera_id, model_id)
|
||||
track_counters = stability_data["track_stability_counters"]
|
||||
stable_tracks = stability_data["stable_tracks"]
|
||||
occupancy_state = stability_data["occupancy_state"]
|
||||
|
||||
current_phase = occupancy_state["phase"]
|
||||
current_track_id = detection.get("id") if detection else None
|
||||
|
||||
if current_phase == "validation":
|
||||
# ═══ VALIDATION PHASE: Count consecutive frames for single track ═══
|
||||
logger.debug(f"📋 Camera {camera_id}: === TRACK VALIDATION ANALYSIS ===")
|
||||
logger.debug(f"📋 Camera {camera_id}: Current track_id: {current_track_id}")
|
||||
logger.debug(f"📋 Camera {camera_id}: Existing counters: {dict(track_counters)}")
|
||||
logger.debug(f"📋 Camera {camera_id}: Stable tracks: {list(stable_tracks)}")
|
||||
# ═══ MODE-AWARE TRACK VALIDATION ═══
|
||||
logger.debug(f"📋 Camera {camera_id}: === TRACK VALIDATION ANALYSIS ===")
|
||||
logger.debug(f"📋 Camera {camera_id}: Current mode: {current_mode} (validation_mode={is_validation_mode})")
|
||||
logger.debug(f"📋 Camera {camera_id}: Current track_id: {current_track_id} (assigned by YOLO tracking - not sequential)")
|
||||
logger.debug(f"📋 Camera {camera_id}: Existing counters: {dict(track_counters)}")
|
||||
logger.debug(f"📋 Camera {camera_id}: Stable tracks: {list(stable_tracks)}")
|
||||
|
||||
# IMPORTANT: Only modify validation counters during validation_detecting mode
|
||||
if not is_validation_mode:
|
||||
logger.debug(f"🚫 Camera {camera_id}: NOT in validation mode - skipping counter modifications")
|
||||
return {
|
||||
"validation_complete": False,
|
||||
"stable_tracks": list(stable_tracks),
|
||||
"current_tracks": [current_track_id] if current_track_id is not None else []
|
||||
}
|
||||
|
||||
if current_track_id is not None:
|
||||
# Check if this is a different track than we were tracking
|
||||
previous_track_ids = list(track_counters.keys())
|
||||
|
||||
if current_track_id is not None:
|
||||
# Check if this is a different track than we were tracking
|
||||
previous_track_ids = list(track_counters.keys())
|
||||
|
||||
# ALWAYS reset counter if:
|
||||
# 1. This is a different track ID than before
|
||||
# 2. OR if we had no previous tracking (fresh start)
|
||||
should_reset = (
|
||||
len(previous_track_ids) == 0 or # No previous tracking
|
||||
current_track_id not in previous_track_ids # Different track ID
|
||||
)
|
||||
|
||||
logger.debug(f"📋 Camera {camera_id}: Previous track_ids: {previous_track_ids}")
|
||||
logger.debug(f"📋 Camera {camera_id}: Should reset counters: {should_reset} (no_previous={len(previous_track_ids) == 0}, different_id={current_track_id not in previous_track_ids})")
|
||||
|
||||
if should_reset and previous_track_ids:
|
||||
# Clear all previous tracking - different car detected
|
||||
# VALIDATION MODE: Reset counter if different track OR if track was previously stable
|
||||
should_reset = (
|
||||
len(previous_track_ids) == 0 or # No previous tracking
|
||||
current_track_id not in previous_track_ids or # Different track ID
|
||||
current_track_id in stable_tracks # Track was stable - start fresh validation
|
||||
)
|
||||
|
||||
logger.debug(f"📋 Camera {camera_id}: Previous track_ids: {previous_track_ids}")
|
||||
logger.debug(f"📋 Camera {camera_id}: Track {current_track_id} was stable: {current_track_id in stable_tracks}")
|
||||
logger.debug(f"📋 Camera {camera_id}: Should reset counters: {should_reset}")
|
||||
|
||||
if should_reset:
|
||||
# Clear all previous tracking - fresh validation needed
|
||||
if previous_track_ids:
|
||||
for old_track_id in previous_track_ids:
|
||||
old_count = track_counters.pop(old_track_id, 0)
|
||||
stable_tracks.discard(old_track_id)
|
||||
logger.info(f"🔄 Camera {camera_id}: Different car detected (track {current_track_id}) - RESET previous track {old_track_id} counter from {old_count} to 0")
|
||||
logger.debug(f"🔄 Camera {camera_id}: Cleared track {old_track_id} from counters and stable_tracks")
|
||||
logger.info(f"🔄 Camera {camera_id}: VALIDATION RESET - track {old_track_id} counter from {old_count} to 0 (reason: {'stable_track_restart' if current_track_id == old_track_id else 'different_track'})")
|
||||
|
||||
# Set counter to 1 for current track (fresh start each frame)
|
||||
# Start fresh validation for this track
|
||||
old_count = track_counters.get(current_track_id, 0) # Store old count for logging
|
||||
track_counters[current_track_id] = 1
|
||||
current_count = 1
|
||||
logger.info(f"🆕 Camera {camera_id}: FRESH VALIDATION - Track {current_track_id} starting at 1/{stability_threshold}")
|
||||
else:
|
||||
# Continue validation for same track
|
||||
old_count = track_counters.get(current_track_id, 0)
|
||||
track_counters[current_track_id] = track_counters.get(current_track_id, 0) + 1
|
||||
track_counters[current_track_id] = old_count + 1
|
||||
current_count = track_counters[current_track_id]
|
||||
|
||||
logger.debug(f"🔢 Camera {camera_id}: Track {current_track_id} counter: {old_count} → {current_count}")
|
||||
logger.info(f"🔍 Camera {camera_id}: Track ID {current_track_id} validation {current_count}/{stability_threshold}")
|
||||
|
||||
# Check if track has reached stability threshold
|
||||
logger.debug(f"📊 Camera {camera_id}: Checking stability: {current_count} >= {stability_threshold}? {current_count >= stability_threshold}")
|
||||
logger.debug(f"📊 Camera {camera_id}: Already stable: {current_track_id in stable_tracks}")
|
||||
|
||||
if current_count >= stability_threshold and current_track_id not in stable_tracks:
|
||||
stable_tracks.add(current_track_id)
|
||||
occupancy_state["phase"] = "waiting_for_session"
|
||||
occupancy_state["pipeline_completed"] = False
|
||||
logger.info(f"✅ Camera {camera_id}: Track ID {current_track_id} STABLE after {current_count} consecutive frames")
|
||||
logger.info(f"🎯 Camera {camera_id}: TRACK VALIDATION COMPLETE")
|
||||
logger.debug(f"🎯 Camera {camera_id}: Phase changed to: waiting_for_session")
|
||||
logger.debug(f"🎯 Camera {camera_id}: Stable tracks now: {list(stable_tracks)}")
|
||||
return {
|
||||
"validation_complete": True,
|
||||
"send_none_detection": True,
|
||||
"stable_tracks": [current_track_id],
|
||||
"newly_stable_tracks": [current_track_id],
|
||||
"current_tracks": [current_track_id]
|
||||
}
|
||||
elif current_count >= stability_threshold:
|
||||
logger.debug(f"📊 Camera {camera_id}: Track {current_track_id} already stable - not re-adding")
|
||||
else:
|
||||
# No car detected - ALWAYS clear all tracking and reset counters
|
||||
logger.debug(f"🚫 Camera {camera_id}: NO CAR DETECTED - clearing all tracking")
|
||||
if track_counters:
|
||||
logger.debug(f"🚫 Camera {camera_id}: Existing counters before reset: {dict(track_counters)}")
|
||||
for track_id in list(track_counters.keys()):
|
||||
old_count = track_counters.pop(track_id, 0)
|
||||
stable_tracks.discard(track_id)
|
||||
logger.info(f"🔄 Camera {camera_id}: No car detected - RESET track {track_id} counter from {old_count} to 0")
|
||||
logger.debug(f"🚫 Camera {camera_id}: Cleared track {track_id} (was at {old_count}/{stability_threshold})")
|
||||
track_counters.clear() # Ensure complete reset
|
||||
stable_tracks.clear() # Clear all stable tracks
|
||||
logger.debug(f"🚫 Camera {camera_id}: All counters and stable tracks cleared")
|
||||
else:
|
||||
logger.debug(f"🚫 Camera {camera_id}: No existing counters to clear")
|
||||
logger.debug(f"Camera {camera_id}: VALIDATION - no car detected (all counters reset)")
|
||||
|
||||
elif current_phase == "waiting_for_session":
|
||||
# ═══ WAITING PHASE: Maintain track stability ═══
|
||||
logger.debug(f"⏳ Camera {camera_id}: WAITING FOR SESSION - monitoring stable track")
|
||||
logger.debug(f"⏳ Camera {camera_id}: Current track_id: {current_track_id}, Stable tracks: {list(stable_tracks)}")
|
||||
|
||||
if current_track_id is None or current_track_id not in stable_tracks:
|
||||
# Lost the stable track
|
||||
logger.debug(f"⏳ Camera {camera_id}: Stable track lost - clearing all tracking")
|
||||
stable_tracks.clear()
|
||||
track_counters.clear()
|
||||
logger.info(f"🔄 Camera {camera_id}: Lost stable track during waiting phase")
|
||||
else:
|
||||
logger.debug(f"⏳ Camera {camera_id}: Stable track {current_track_id} still present")
|
||||
logger.debug(f"🔢 Camera {camera_id}: Track {current_track_id} counter: {old_count} → {current_count}")
|
||||
logger.info(f"🔍 Camera {camera_id}: Track ID {current_track_id} validation {current_count}/{stability_threshold}")
|
||||
|
||||
elif current_phase == "occupancy":
|
||||
# ═══ OCCUPANCY PHASE: UNUSED in enhanced lightweight mode ═══
|
||||
# This phase is bypassed by the new lightweight mode system
|
||||
# Keeping minimal logic for backward compatibility but no CLI logging
|
||||
if current_track_id is not None and current_track_id in stable_tracks:
|
||||
occupancy_state["absence_counter"] = 0
|
||||
# Check if track has reached stability threshold
|
||||
logger.debug(f"📊 Camera {camera_id}: Checking stability: {current_count} >= {stability_threshold}? {current_count >= stability_threshold}")
|
||||
logger.debug(f"📊 Camera {camera_id}: Already stable: {current_track_id in stable_tracks}")
|
||||
|
||||
if current_count >= stability_threshold and current_track_id not in stable_tracks:
|
||||
stable_tracks.add(current_track_id)
|
||||
logger.info(f"✅ Camera {camera_id}: Track ID {current_track_id} STABLE after {current_count} consecutive frames")
|
||||
logger.info(f"🎯 Camera {camera_id}: TRACK VALIDATION COMPLETE")
|
||||
logger.debug(f"🎯 Camera {camera_id}: Stable tracks now: {list(stable_tracks)}")
|
||||
return {
|
||||
"validation_complete": True,
|
||||
"send_none_detection": True,
|
||||
"stable_tracks": [current_track_id],
|
||||
"newly_stable_tracks": [current_track_id],
|
||||
"current_tracks": [current_track_id]
|
||||
}
|
||||
elif current_count >= stability_threshold:
|
||||
logger.debug(f"📊 Camera {camera_id}: Track {current_track_id} already stable - not re-adding")
|
||||
else:
|
||||
# No car detected - ALWAYS clear all tracking and reset counters
|
||||
logger.debug(f"🚫 Camera {camera_id}: NO CAR DETECTED - clearing all tracking")
|
||||
if track_counters or stable_tracks:
|
||||
logger.debug(f"🚫 Camera {camera_id}: Existing state before reset: counters={dict(track_counters)}, stable={list(stable_tracks)}")
|
||||
for track_id in list(track_counters.keys()):
|
||||
old_count = track_counters.pop(track_id, 0)
|
||||
logger.info(f"🔄 Camera {camera_id}: No car detected - RESET track {track_id} counter from {old_count} to 0")
|
||||
track_counters.clear() # Ensure complete reset
|
||||
stable_tracks.clear() # Clear all stable tracks
|
||||
logger.info(f"✅ Camera {camera_id}: RESET TO VALIDATION PHASE - All counters and stable tracks cleared")
|
||||
else:
|
||||
occupancy_state["absence_counter"] += 1
|
||||
logger.debug(f"🚫 Camera {camera_id}: No existing counters to clear")
|
||||
logger.debug(f"Camera {camera_id}: VALIDATION - no car detected (all counters reset)")
|
||||
|
||||
# Final return - validation not complete
|
||||
result = {
|
||||
|
@ -1040,9 +1023,9 @@ def update_track_stability_validation(node, detections, camera_id, frame_shape=N
|
|||
logger.warning(f"update_track_stability_validation called for camera {camera_id} - this function is deprecated, use update_single_track_stability instead")
|
||||
if detections:
|
||||
best_detection = max(detections, key=lambda x: x.get("confidence", 0))
|
||||
return update_single_track_stability(node, best_detection, camera_id, frame_shape, stability_threshold)
|
||||
return update_single_track_stability(node, best_detection, camera_id, frame_shape, stability_threshold, None)
|
||||
else:
|
||||
return update_single_track_stability(node, None, camera_id, frame_shape, stability_threshold)
|
||||
return update_single_track_stability(node, None, camera_id, frame_shape, stability_threshold, None)
|
||||
|
||||
def update_detection_stability(node, detections, camera_id, frame_shape=None):
|
||||
"""Legacy detection-based stability counter - DEPRECATED."""
|
||||
|
@ -1051,95 +1034,9 @@ def update_detection_stability(node, detections, camera_id, frame_shape=None):
|
|||
return {"validation_complete": False, "valid_detections": 0, "deprecated": True}
|
||||
|
||||
def update_track_stability(node, detections, camera_id, frame_shape=None):
|
||||
"""Update stability counters with two-phase detection system: validation → occupancy."""
|
||||
stability_threshold = node.get("stabilityThreshold", 1)
|
||||
model_id = node.get("modelId", "unknown")
|
||||
min_bbox_area_ratio = node.get("minBboxAreaRatio", 0.0)
|
||||
|
||||
# Note: This function is deprecated - using detection-based stability now
|
||||
|
||||
# Get camera-specific stability data
|
||||
stability_data = get_camera_stability_data(camera_id, model_id)
|
||||
track_counters = stability_data["track_stability_counters"]
|
||||
stable_tracks = stability_data["stable_tracks"]
|
||||
occupancy_state = stability_data["occupancy_state"]
|
||||
|
||||
# Validate detections against confidence + area requirements
|
||||
valid_detections = []
|
||||
if frame_shape is not None:
|
||||
frame_height, frame_width = frame_shape[:2]
|
||||
frame_area = frame_width * frame_height
|
||||
|
||||
for detection in detections:
|
||||
bbox = detection.get("bbox", [])
|
||||
if len(bbox) >= 4:
|
||||
x1, y1, x2, y2 = bbox
|
||||
bbox_width = abs(x2 - x1)
|
||||
bbox_height = abs(y2 - y1)
|
||||
bbox_area = bbox_width * bbox_height
|
||||
area_ratio = bbox_area / frame_area if frame_area > 0 else 0.0
|
||||
|
||||
if area_ratio >= min_bbox_area_ratio:
|
||||
valid_detections.append(detection)
|
||||
pass # Valid detection - no debug spam
|
||||
else:
|
||||
pass # Small detection - no debug spam
|
||||
else:
|
||||
valid_detections = detections
|
||||
|
||||
current_phase = occupancy_state["phase"]
|
||||
|
||||
if current_phase == "validation":
|
||||
# ═══ VALIDATION PHASE: Count detections until stable ═══
|
||||
detection_key = f"camera_{camera_id}_detections"
|
||||
|
||||
if valid_detections:
|
||||
# Valid detection found - increment counter
|
||||
track_counters[detection_key] = track_counters.get(detection_key, 0) + 1
|
||||
current_count = track_counters[detection_key]
|
||||
|
||||
pass # Validation count - shown in main logs
|
||||
|
||||
# Check if we've reached the stability threshold
|
||||
if current_count >= stability_threshold and detection_key not in stable_tracks:
|
||||
stable_tracks.add(detection_key)
|
||||
# Switch to waiting for backend session phase
|
||||
occupancy_state["phase"] = "waiting_for_session"
|
||||
occupancy_state["absence_counter"] = 0
|
||||
occupancy_state["pipeline_completed"] = False
|
||||
logger.info(f"✅ Camera {camera_id}: VALIDATION COMPLETE after {current_count} detections - READY FOR FULL PIPELINE")
|
||||
else:
|
||||
# No valid detections - reset validation counter for consecutive requirement
|
||||
if detection_key in track_counters:
|
||||
old_count = track_counters[detection_key]
|
||||
track_counters.pop(detection_key, None)
|
||||
stable_tracks.discard(detection_key)
|
||||
logger.info(f"🔄 Camera {camera_id}: VALIDATION RESET - no valid detection, counter reset from {old_count} to 0 (requires consecutive detections)")
|
||||
else:
|
||||
logger.debug(f"Camera {camera_id}: VALIDATION - no valid detection, counter remains 0")
|
||||
|
||||
elif current_phase == "waiting_for_session":
|
||||
# ═══ WAITING FOR BACKEND SESSION PHASE ═══
|
||||
# Don't do occupancy monitoring yet, just maintain validation of current detections
|
||||
# The main pipeline will handle sessionId detection and phase transition
|
||||
pass # Waiting phase - no occupancy logic yet
|
||||
|
||||
elif current_phase == "occupancy":
|
||||
# ═══ OCCUPANCY PHASE: Monitor car presence ═══
|
||||
if valid_detections:
|
||||
# Car still present - reset absence counter
|
||||
if occupancy_state["absence_counter"] > 0:
|
||||
pass # Car detected - counter reset (no debug spam)
|
||||
occupancy_state["absence_counter"] = 0
|
||||
else:
|
||||
# No car detected - increment absence counter
|
||||
occupancy_state["absence_counter"] += 1
|
||||
pass # Absence count - will show in timeout log
|
||||
|
||||
pass # Phase summary - excessive debug
|
||||
|
||||
# Return occupancy state for pipeline decisions
|
||||
return occupancy_state
|
||||
"""DEPRECATED: This function is obsolete and should not be used."""
|
||||
logger.warning(f"update_track_stability called for camera {camera_id} - this function is deprecated and obsolete")
|
||||
return {"phase": "validation", "absence_counter": 0, "deprecated": True}
|
||||
|
||||
def check_stable_tracks(camera_id, model_id, regions_dict):
|
||||
"""Check if any stable tracks match the detected classes for a specific camera."""
|
||||
|
@ -1434,7 +1331,7 @@ def run_lightweight_detection(frame, node: dict):
|
|||
logger.error(f"Error in lightweight detection: {str(e)}", exc_info=True)
|
||||
return {"car_detected": False, "best_detection": None}
|
||||
|
||||
def run_pipeline(frame, node: dict, return_bbox: bool=False, context=None):
|
||||
def run_pipeline(frame, node: dict, return_bbox: bool=False, context=None, validated_detection=None):
|
||||
"""
|
||||
Enhanced pipeline that supports:
|
||||
- Multi-class detection (detecting multiple classes simultaneously)
|
||||
|
@ -1501,8 +1398,31 @@ def run_pipeline(frame, node: dict, return_bbox: bool=False, context=None):
|
|||
}
|
||||
return (none_detection, [0, 0, 0, 0]) if return_bbox else none_detection
|
||||
|
||||
# ─── Detection stage - Using structured detection function ──────────────────
|
||||
all_detections, regions_dict, track_validation_result = run_detection_with_tracking(frame, node, context)
|
||||
# ─── Detection stage - Use validated detection if provided (full_pipeline mode) ───
|
||||
if validated_detection:
|
||||
track_id = validated_detection.get('track_id')
|
||||
logger.info(f"🔄 PIPELINE: Using validated detection from validation phase - track_id={track_id}")
|
||||
# Convert validated detection back to all_detections format for branch processing
|
||||
all_detections = [validated_detection]
|
||||
# Create regions_dict based on validated detection class with proper structure
|
||||
class_name = validated_detection.get("class", "car")
|
||||
regions_dict = {
|
||||
class_name: {
|
||||
"confidence": validated_detection.get("confidence"),
|
||||
"bbox": validated_detection.get("bbox", [0, 0, 0, 0]),
|
||||
"detection": validated_detection
|
||||
}
|
||||
}
|
||||
# Bypass track validation completely - force pipeline execution
|
||||
track_validation_result = {
|
||||
"validation_complete": True,
|
||||
"stable_tracks": ["cached"], # Use dummy stable track to force pipeline execution
|
||||
"current_tracks": ["cached"],
|
||||
"bypass_validation": True
|
||||
}
|
||||
else:
|
||||
# Normal detection stage - Using structured detection function
|
||||
all_detections, regions_dict, track_validation_result = run_detection_with_tracking(frame, node, context)
|
||||
|
||||
if not all_detections:
|
||||
logger.debug("No detections from structured detection function - sending 'none' detection")
|
||||
|
@ -1524,14 +1444,14 @@ def run_pipeline(frame, node: dict, return_bbox: bool=False, context=None):
|
|||
camera_id = context.get("camera_id", "unknown") if context else "unknown"
|
||||
|
||||
if stability_threshold > 1 and tracking_config.get("enabled", True):
|
||||
# Extract occupancy state from stability data (updated by track validation function)
|
||||
# Note: Old occupancy state system removed - app.py handles all mode transitions now
|
||||
# Track validation is handled by update_single_track_stability function
|
||||
model_id = node.get("modelId", "unknown")
|
||||
stability_data = get_camera_stability_data(camera_id, model_id)
|
||||
occupancy_state = stability_data["occupancy_state"]
|
||||
|
||||
current_phase = occupancy_state.get("phase", "validation")
|
||||
absence_counter = occupancy_state.get("absence_counter", 0)
|
||||
max_absence_frames = occupancy_state.get("max_absence_frames", 3)
|
||||
# Simplified: just check if we have stable tracks from track validation
|
||||
current_phase = "validation" # Always validation phase in simplified system
|
||||
absence_counter = 0
|
||||
max_absence_frames = 3
|
||||
|
||||
if current_phase == "validation":
|
||||
# ═══ TRACK VALIDATION PHASE ═══
|
||||
|
@ -1562,78 +1482,8 @@ def run_pipeline(frame, node: dict, return_bbox: bool=False, context=None):
|
|||
# We have stable tracks - validation is complete, proceed with pipeline
|
||||
logger.info(f"🎯 Camera {camera_id}: STABLE TRACKS DETECTED - proceeding with full pipeline (tracks: {stable_tracks})")
|
||||
|
||||
elif current_phase == "waiting_for_session":
|
||||
# ═══ WAITING FOR BACKEND SESSION PHASE ═══
|
||||
if backend_session_id:
|
||||
# Backend has responded with sessionId - NOW run the full pipeline for the first time
|
||||
logger.info(f"🎯 Camera {camera_id}: BACKEND SESSION RECEIVED - RUNNING FULL PIPELINE (sessionId: {backend_session_id})")
|
||||
occupancy_state["phase"] = "occupancy"
|
||||
occupancy_state["absence_counter"] = 0
|
||||
# Continue with normal pipeline processing now that we have sessionId
|
||||
else:
|
||||
# Still waiting for backend sessionId - send None detection dict to trigger sessionId generation
|
||||
if not occupancy_state["pipeline_completed"]:
|
||||
# First time in waiting phase - send empty detection to trigger sessionId
|
||||
logger.info(f"⚙️ Camera {camera_id}: WAITING PHASE - sending empty detection {{}} for sessionId generation")
|
||||
occupancy_state["pipeline_completed"] = True
|
||||
|
||||
# Return a special detection that signals app.py to send empty detection: {}
|
||||
none_detection = {
|
||||
"class": "validation_complete",
|
||||
"confidence": 1.0,
|
||||
"bbox": [0, 0, 0, 0],
|
||||
"send_empty_detection": True
|
||||
}
|
||||
return (none_detection, [0, 0, 0, 0]) if return_bbox else none_detection
|
||||
else:
|
||||
# Already sent None detection - continue waiting for sessionId
|
||||
logger.debug(f"⏳ Camera {camera_id}: WAITING FOR BACKEND SESSION - None detection already sent, waiting for sessionId")
|
||||
|
||||
waiting_detection = {
|
||||
"class": "waiting_session_id",
|
||||
"confidence": 1.0,
|
||||
"bbox": [0, 0, 0, 0],
|
||||
"waiting_for_session": True
|
||||
}
|
||||
return (waiting_detection, [0, 0, 0, 0]) if return_bbox else waiting_detection
|
||||
|
||||
elif current_phase == "occupancy":
|
||||
# ═══ OCCUPANCY PHASE ═══
|
||||
stable_tracks = track_validation_result.get("stable_tracks", [])
|
||||
current_tracks = track_validation_result.get("current_tracks", [])
|
||||
|
||||
# Check if any stable tracks are still present
|
||||
stable_tracks_present = bool(set(stable_tracks) & set(current_tracks))
|
||||
|
||||
if absence_counter >= max_absence_frames:
|
||||
# Stable tracks have been absent for too long - trigger "none" detection and reset
|
||||
# Occupancy timeout logging removed - not used in enhanced lightweight mode
|
||||
|
||||
# Reset occupancy state to validation phase
|
||||
stability_data = get_camera_stability_data(camera_id, model_id)
|
||||
stability_data["occupancy_state"]["phase"] = "validation"
|
||||
stability_data["occupancy_state"]["absence_counter"] = 0
|
||||
stability_data["track_stability_counters"].clear()
|
||||
stability_data["stable_tracks"].clear()
|
||||
|
||||
logger.info(f"🔄 Camera {camera_id}: RESET TO VALIDATION PHASE - cleared track stability tracking (sessionId should become null)")
|
||||
|
||||
# Return "none" detection to trigger cache clearing in app.py
|
||||
none_detection = {"class": "none", "confidence": 1.0, "bbox": [0, 0, 0, 0], "occupancy_triggered": True}
|
||||
return (none_detection, [0, 0, 0, 0]) if return_bbox else none_detection
|
||||
else:
|
||||
# Still in occupancy phase - check if stable tracks are present
|
||||
if stable_tracks_present:
|
||||
# Stable tracks detected - continue with cached result or light processing
|
||||
# Occupancy phase logging removed - not used in enhanced lightweight mode
|
||||
pass
|
||||
else:
|
||||
# No stable tracks - absence counter was already incremented in track validation
|
||||
# Occupancy phase logging removed - not used in enhanced lightweight mode
|
||||
pass
|
||||
|
||||
# Continue with normal pipeline processing
|
||||
pass
|
||||
# Note: Old waiting_for_session and occupancy phases removed
|
||||
# app.py lightweight mode handles all state transitions now
|
||||
|
||||
# ─── Pre-validate pipeline execution (only proceed if we have stable tracks for main pipeline) ────────────────────────
|
||||
is_branch_node = node.get("cropClass") is not None or node.get("parallel") is True
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue