Compare commits

...
Sign in to create a new pull request.

2 commits

3 changed files with 94 additions and 4 deletions

7
.gitignore vendored
View file

@ -17,3 +17,10 @@ feeder/
dist/ dist/
websocket_comm.log websocket_comm.log
temp_debug/ temp_debug/
.claude
# Video Test
video_rtsp/
multi_stream_viewer.py
multi_camera_simulator.py
start_4_cameras.bat

30
app.py
View file

@ -1049,10 +1049,34 @@ async def detect(websocket: WebSocket):
from siwatsystem.pympta import run_lightweight_detection from siwatsystem.pympta import run_lightweight_detection
basic_detection = run_lightweight_detection(cropped_frame, model_tree) basic_detection = run_lightweight_detection(cropped_frame, model_tree)
any_car_detected = basic_detection and basic_detection.get("car_detected", False) # Enhanced car detection: requires both confidence pass AND bbox >= 50% of frame
logger.debug(f"🔍 Camera {camera_id}: LIGHTWEIGHT - simple car presence check: {any_car_detected}") car_detected_confidence = basic_detection and basic_detection.get("car_detected", False)
car_detected_with_bbox_validation = False
if any_car_detected: if car_detected_confidence:
# Car passed confidence - now check bbox area
best_detection = basic_detection.get("best_detection")
if best_detection and best_detection.get("bbox"):
bbox = best_detection["bbox"]
x1, y1, x2, y2 = bbox
bbox_area = (x2 - x1) * (y2 - y1)
frame_height, frame_width = cropped_frame.shape[:2]
frame_area = frame_height * frame_width
bbox_area_ratio = bbox_area / frame_area if frame_area > 0 else 0
min_area_ratio = 0.2 # 20% of frame
car_detected_with_bbox_validation = bbox_area_ratio >= min_area_ratio
if not car_detected_with_bbox_validation:
logger.info(f"🚫 Camera {camera_id}: LIGHTWEIGHT - car detected but bbox {bbox_area_ratio:.1%} < {min_area_ratio:.0%} (too distant) - counting as absent")
else:
logger.debug(f"✅ Camera {camera_id}: LIGHTWEIGHT - car detected with valid bbox {bbox_area_ratio:.1%} >= {min_area_ratio:.0%}")
else:
logger.debug(f"⚠️ Camera {camera_id}: LIGHTWEIGHT - car detected but no bbox info available")
logger.debug(f"🔍 Camera {camera_id}: LIGHTWEIGHT - enhanced car presence check: confidence={car_detected_confidence}, bbox_valid={car_detected_with_bbox_validation}")
if car_detected_with_bbox_validation:
# Car detected - reset absence counter, continue sending cached detection dict # Car detected - reset absence counter, continue sending cached detection dict
pipeline_state["absence_counter"] = 0 # Reset absence since cars are present pipeline_state["absence_counter"] = 0 # Reset absence since cars are present

View file

@ -732,12 +732,17 @@ def run_detection_with_tracking(frame, node, context=None):
# Tracking zone validation removed - process all detections # Tracking zone validation removed - process all detections
# Calculate bbox area
x1, y1, x2, y2 = bbox
bbox_area = (x2 - x1) * (y2 - y1)
# Create detection object # Create detection object
detection = { detection = {
"class": class_name, "class": class_name,
"confidence": conf, "confidence": conf,
"id": track_id, "id": track_id,
"bbox": bbox, "bbox": bbox,
"bbox_area": bbox_area,
"class_id": cls_id "class_id": cls_id
} }
@ -926,6 +931,31 @@ def update_single_track_stability(node, detection, camera_id, frame_shape=None,
current_track_id = detection.get("id") if detection else None current_track_id = detection.get("id") if detection else None
# ─── Bbox Area Validation for Stability ───
# Only count detections where bbox area is >=20% of frame (close cars only)
if detection and frame_shape is not None:
bbox = detection.get("bbox", [0, 0, 0, 0])
if bbox and len(bbox) >= 4:
x1, y1, x2, y2 = bbox
bbox_area = (x2 - x1) * (y2 - y1)
frame_height, frame_width = frame_shape[:2]
frame_area = frame_height * frame_width
bbox_area_ratio = bbox_area / frame_area if frame_area > 0 else 0
# Require bbox to be at least 20% of frame area
min_area_ratio = 0.2
if bbox_area_ratio < min_area_ratio:
logger.info(f"🚫 Camera {camera_id}: Track {current_track_id} REJECTED for stability - bbox area {bbox_area_ratio:.1%} < {min_area_ratio:.0%} (too small/distant)")
# Completely reset - remove track entirely (same as trackId change)
if current_track_id and current_track_id in track_counters:
old_count = track_counters.pop(current_track_id, 0) # Remove completely
stable_tracks.discard(current_track_id) # Remove from stable
logger.info(f"🔄 Camera {camera_id}: COMPLETELY RESET track {current_track_id} counter from {old_count} to 0 (reason: bbox too small)")
return {"validation_complete": False, "stable_tracks": list(stable_tracks), "current_tracks": [], "bbox_too_small": True}
else:
logger.debug(f"✅ Camera {camera_id}: Track {current_track_id} bbox area {bbox_area_ratio:.1%} >= {min_area_ratio:.0%} - acceptable for stability")
# ═══ MODE-AWARE TRACK VALIDATION ═══ # ═══ MODE-AWARE TRACK VALIDATION ═══
logger.debug(f"📋 Camera {camera_id}: === TRACK VALIDATION ANALYSIS ===") 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 mode: {current_mode} (validation_mode={is_validation_mode})")
@ -1443,6 +1473,35 @@ def run_pipeline(frame, node: dict, return_bbox: bool=False, context=None, valid
# Normal detection stage - Using structured detection function # Normal detection stage - Using structured detection function
all_detections, regions_dict, track_validation_result = run_detection_with_tracking(frame, node, context) all_detections, regions_dict, track_validation_result = run_detection_with_tracking(frame, node, context)
# ─── Apply largest bbox area selection for full pipeline mode ───
# When we have stable tracks and multiple detections, select the largest bbox area one
stable_tracks = track_validation_result.get("stable_tracks", [])
if stable_tracks and len(all_detections) > 1:
logger.info(f"🔍 PIPELINE: Full pipeline mode - selecting largest bbox area from {len(all_detections)} detections")
# Select detection with largest bbox area
largest_detection = max(all_detections, key=lambda x: x.get("bbox_area", 0))
logger.info(f"🎯 PIPELINE: Selected largest bbox area detection: conf={largest_detection.get('confidence', 0):.3f}, area={largest_detection.get('bbox_area', 0):.0f}")
# Update all_detections to only contain the largest bbox area detection
all_detections = [largest_detection]
# Update regions_dict to reflect the selected detection
class_name = largest_detection.get("class", "car")
regions_dict = {
class_name: {
"confidence": largest_detection.get("confidence"),
"bbox": largest_detection.get("bbox", [0, 0, 0, 0]),
"detection": largest_detection
}
}
logger.debug(f"🔄 PIPELINE: Updated regions_dict for largest bbox area selection: {list(regions_dict.keys())}")
elif stable_tracks:
logger.debug(f"🔄 PIPELINE: Full pipeline mode - single detection, no area selection needed")
else:
logger.debug(f"🔄 PIPELINE: No stable tracks yet, proceeding with confidence-based detection")
# Debug: Save crops for debugging (disabled for production) # Debug: Save crops for debugging (disabled for production)
# if regions_dict: # if regions_dict:
# try: # try: