Compare commits
2 commits
dev
...
feat/track
Author | SHA1 | Date | |
---|---|---|---|
|
9631a71073 | ||
|
34ecff244c |
3 changed files with 94 additions and 4 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -16,4 +16,11 @@ feeder/
|
|||
.vscode/
|
||||
dist/
|
||||
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
30
app.py
|
@ -1049,10 +1049,34 @@ async def detect(websocket: WebSocket):
|
|||
from siwatsystem.pympta import run_lightweight_detection
|
||||
basic_detection = run_lightweight_detection(cropped_frame, model_tree)
|
||||
|
||||
any_car_detected = basic_detection and basic_detection.get("car_detected", False)
|
||||
logger.debug(f"🔍 Camera {camera_id}: LIGHTWEIGHT - simple car presence check: {any_car_detected}")
|
||||
# Enhanced car detection: requires both confidence pass AND bbox >= 50% of frame
|
||||
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
|
||||
pipeline_state["absence_counter"] = 0 # Reset absence since cars are present
|
||||
|
||||
|
|
|
@ -732,12 +732,17 @@ def run_detection_with_tracking(frame, node, context=None):
|
|||
|
||||
# Tracking zone validation removed - process all detections
|
||||
|
||||
# Calculate bbox area
|
||||
x1, y1, x2, y2 = bbox
|
||||
bbox_area = (x2 - x1) * (y2 - y1)
|
||||
|
||||
# Create detection object
|
||||
detection = {
|
||||
"class": class_name,
|
||||
"confidence": conf,
|
||||
"id": track_id,
|
||||
"bbox": bbox,
|
||||
"bbox_area": bbox_area,
|
||||
"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
|
||||
|
||||
# ─── 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 ═══
|
||||
logger.debug(f"📋 Camera {camera_id}: === TRACK VALIDATION ANALYSIS ===")
|
||||
logger.debug(f"📋 Camera {camera_id}: Current mode: {current_mode} (validation_mode={is_validation_mode})")
|
||||
|
@ -1442,6 +1472,35 @@ def run_pipeline(frame, node: dict, return_bbox: bool=False, context=None, valid
|
|||
else:
|
||||
# Normal detection stage - Using structured detection function
|
||||
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)
|
||||
# if regions_dict:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue