fix: validator
This commit is contained in:
parent
791f611f7d
commit
61ac39b4f3
3 changed files with 106 additions and 95 deletions
|
@ -297,18 +297,18 @@ class WebSocketHandler:
|
|||
async def _reconcile_subscriptions_with_tracking(self, target_subscriptions) -> dict:
|
||||
"""Reconcile subscriptions with tracking integration."""
|
||||
try:
|
||||
# First, we need to create tracking integrations for each unique model
|
||||
# Create separate tracking integrations for each subscription (camera isolation)
|
||||
tracking_integrations = {}
|
||||
|
||||
for subscription_payload in target_subscriptions:
|
||||
subscription_id = subscription_payload['subscriptionIdentifier']
|
||||
model_id = subscription_payload['modelId']
|
||||
|
||||
# Create tracking integration if not already created
|
||||
if model_id not in tracking_integrations:
|
||||
# Create separate tracking integration per subscription for camera isolation
|
||||
# Get pipeline configuration for this model
|
||||
pipeline_parser = model_manager.get_pipeline_config(model_id)
|
||||
if pipeline_parser:
|
||||
# Create tracking integration with message sender
|
||||
# Create tracking integration with message sender (separate instance per camera)
|
||||
tracking_integration = TrackingPipelineIntegration(
|
||||
pipeline_parser, model_manager, model_id, self._send_message
|
||||
)
|
||||
|
@ -316,12 +316,12 @@ class WebSocketHandler:
|
|||
# Initialize tracking model
|
||||
success = await tracking_integration.initialize_tracking_model()
|
||||
if success:
|
||||
tracking_integrations[model_id] = tracking_integration
|
||||
logger.info(f"[Tracking] Created tracking integration for model {model_id}")
|
||||
tracking_integrations[subscription_id] = tracking_integration
|
||||
logger.info(f"[Tracking] Created isolated tracking integration for subscription {subscription_id} (model {model_id})")
|
||||
else:
|
||||
logger.warning(f"[Tracking] Failed to initialize tracking for model {model_id}")
|
||||
logger.warning(f"[Tracking] Failed to initialize tracking for subscription {subscription_id} (model {model_id})")
|
||||
else:
|
||||
logger.warning(f"[Tracking] No pipeline config found for model {model_id}")
|
||||
logger.warning(f"[Tracking] No pipeline config found for model {model_id} in subscription {subscription_id}")
|
||||
|
||||
# Now reconcile with StreamManager, adding tracking integrations
|
||||
current_subscription_ids = set()
|
||||
|
@ -379,8 +379,8 @@ class WebSocketHandler:
|
|||
|
||||
logger.info(f"[SUBSCRIPTION_MAPPING] subscription_id='{subscription_id}' → camera_id='{camera_id}'")
|
||||
|
||||
# Get tracking integration for this model
|
||||
tracking_integration = tracking_integrations.get(model_id)
|
||||
# Get tracking integration for this subscription (camera-isolated)
|
||||
tracking_integration = tracking_integrations.get(subscription_id)
|
||||
|
||||
# Extract crop coordinates if present
|
||||
crop_coords = None
|
||||
|
@ -412,7 +412,7 @@ class WebSocketHandler:
|
|||
)
|
||||
|
||||
if success and tracking_integration:
|
||||
logger.info(f"[Tracking] Subscription {subscription_id} configured with tracking for model {model_id}")
|
||||
logger.info(f"[Tracking] Subscription {subscription_id} configured with isolated tracking for model {model_id}")
|
||||
|
||||
return success
|
||||
|
||||
|
|
|
@ -389,20 +389,51 @@ class StreamManager:
|
|||
logger.debug(f"Set session {session_id} for display {display_id}")
|
||||
|
||||
def clear_session_id(self, session_id: str):
|
||||
"""Clear session ID from tracking integrations."""
|
||||
"""Clear session ID from the specific tracking integration handling this session."""
|
||||
with self._lock:
|
||||
# Find the subscription that's handling this session
|
||||
session_subscription = None
|
||||
for subscription_info in self._subscriptions.values():
|
||||
if subscription_info.tracking_integration:
|
||||
# Check if this integration is handling the given session_id
|
||||
integration = subscription_info.tracking_integration
|
||||
if session_id in integration.session_vehicles:
|
||||
session_subscription = subscription_info
|
||||
break
|
||||
|
||||
if session_subscription and session_subscription.tracking_integration:
|
||||
session_subscription.tracking_integration.clear_session_id(session_id)
|
||||
logger.debug(f"Cleared session {session_id} from subscription {session_subscription.subscription_id}")
|
||||
else:
|
||||
logger.warning(f"No tracking integration found for session {session_id}, broadcasting to all subscriptions")
|
||||
# Fallback: broadcast to all (original behavior)
|
||||
for subscription_info in self._subscriptions.values():
|
||||
if subscription_info.tracking_integration:
|
||||
subscription_info.tracking_integration.clear_session_id(session_id)
|
||||
logger.debug(f"Cleared session {session_id}")
|
||||
|
||||
def set_progression_stage(self, session_id: str, stage: str):
|
||||
"""Set progression stage for tracking integrations."""
|
||||
"""Set progression stage for the specific tracking integration handling this session."""
|
||||
with self._lock:
|
||||
# Find the subscription that's handling this session
|
||||
session_subscription = None
|
||||
for subscription_info in self._subscriptions.values():
|
||||
if subscription_info.tracking_integration:
|
||||
# Check if this integration is handling the given session_id
|
||||
# We need to check the integration's active sessions
|
||||
integration = subscription_info.tracking_integration
|
||||
if session_id in integration.session_vehicles:
|
||||
session_subscription = subscription_info
|
||||
break
|
||||
|
||||
if session_subscription and session_subscription.tracking_integration:
|
||||
session_subscription.tracking_integration.set_progression_stage(session_id, stage)
|
||||
logger.debug(f"Set progression stage for session {session_id}: {stage} on subscription {session_subscription.subscription_id}")
|
||||
else:
|
||||
logger.warning(f"No tracking integration found for session {session_id}, broadcasting to all subscriptions")
|
||||
# Fallback: broadcast to all (original behavior)
|
||||
for subscription_info in self._subscriptions.values():
|
||||
if subscription_info.tracking_integration:
|
||||
subscription_info.tracking_integration.set_progression_stage(session_id, stage)
|
||||
logger.debug(f"Set progression stage for session {session_id}: {stage}")
|
||||
|
||||
def get_tracking_stats(self) -> Dict[str, Any]:
|
||||
"""Get tracking statistics from all subscriptions."""
|
||||
|
|
|
@ -36,8 +36,14 @@ class ValidationResult:
|
|||
|
||||
class StableCarValidator:
|
||||
"""
|
||||
Validates whether a tracked vehicle is stable (fueling) or just passing by.
|
||||
Uses multiple criteria including position stability, duration, and movement patterns.
|
||||
Validates whether a tracked vehicle should be processed through the pipeline.
|
||||
|
||||
Updated for BoT-SORT integration: Trusts the sophisticated BoT-SORT tracking algorithm
|
||||
for stability determination and focuses on business logic validation:
|
||||
- Duration requirements for processing
|
||||
- Confidence thresholds
|
||||
- Session management and cooldowns
|
||||
- Camera isolation with composite keys
|
||||
"""
|
||||
|
||||
def __init__(self, config: Optional[Dict] = None):
|
||||
|
@ -169,7 +175,10 @@ class StableCarValidator:
|
|||
|
||||
def _determine_vehicle_state(self, vehicle: TrackedVehicle) -> VehicleState:
|
||||
"""
|
||||
Determine the current state of the vehicle based on movement patterns.
|
||||
Determine the current state of the vehicle based on BoT-SORT tracking results.
|
||||
|
||||
BoT-SORT provides sophisticated tracking, so we trust its stability determination
|
||||
and focus on business logic validation.
|
||||
|
||||
Args:
|
||||
vehicle: The tracked vehicle
|
||||
|
@ -177,53 +186,44 @@ class StableCarValidator:
|
|||
Returns:
|
||||
Current vehicle state
|
||||
"""
|
||||
# Not enough data
|
||||
if len(vehicle.last_position_history) < 3:
|
||||
return VehicleState.UNKNOWN
|
||||
|
||||
# Calculate velocity
|
||||
velocity = self._calculate_velocity(vehicle)
|
||||
|
||||
# Get position zones
|
||||
x_position = vehicle.center[0] / self.frame_width
|
||||
y_position = vehicle.center[1] / self.frame_height
|
||||
|
||||
# Check if vehicle is stable
|
||||
stability = vehicle.calculate_stability()
|
||||
if stability > 0.7 and velocity < self.velocity_threshold:
|
||||
# Check if it's been stable long enough
|
||||
# Trust BoT-SORT's stability determination
|
||||
if vehicle.is_stable:
|
||||
# Check if it's been stable long enough for processing
|
||||
duration = time.time() - vehicle.first_seen
|
||||
if duration > self.min_stable_duration and vehicle.stable_frames >= self.min_stable_frames:
|
||||
if duration >= self.min_stable_duration:
|
||||
return VehicleState.STABLE
|
||||
else:
|
||||
return VehicleState.ENTERING
|
||||
|
||||
# Check if vehicle is entering or leaving
|
||||
# For non-stable vehicles, use simplified state determination
|
||||
if len(vehicle.last_position_history) < 2:
|
||||
return VehicleState.UNKNOWN
|
||||
|
||||
# Calculate velocity for movement classification
|
||||
velocity = self._calculate_velocity(vehicle)
|
||||
|
||||
# Basic movement classification
|
||||
if velocity > self.velocity_threshold:
|
||||
# Determine direction based on position history
|
||||
positions = np.array(vehicle.last_position_history)
|
||||
if len(positions) >= 2:
|
||||
direction = positions[-1] - positions[0]
|
||||
# Vehicle is moving - classify as passing by or entering/leaving
|
||||
x_position = vehicle.center[0] / self.frame_width
|
||||
|
||||
# Entering: moving towards center
|
||||
if x_position < self.entering_zone_ratio or x_position > (1 - self.entering_zone_ratio):
|
||||
if abs(direction[0]) > abs(direction[1]): # Horizontal movement
|
||||
if (x_position < 0.5 and direction[0] > 0) or (x_position > 0.5 and direction[0] < 0):
|
||||
# Simple heuristic: vehicles near edges are entering/leaving, center vehicles are passing
|
||||
if x_position < 0.2 or x_position > 0.8:
|
||||
return VehicleState.ENTERING
|
||||
|
||||
# Leaving: moving away from center
|
||||
if 0.3 < x_position < 0.7: # In center zone
|
||||
if abs(direction[0]) > abs(direction[1]): # Horizontal movement
|
||||
if abs(direction[0]) > 10: # Significant movement
|
||||
return VehicleState.LEAVING
|
||||
|
||||
else:
|
||||
return VehicleState.PASSING_BY
|
||||
|
||||
return VehicleState.UNKNOWN
|
||||
# Low velocity but not marked stable by tracker - likely entering
|
||||
return VehicleState.ENTERING
|
||||
|
||||
def _validate_stable_vehicle(self, vehicle: TrackedVehicle) -> ValidationResult:
|
||||
"""
|
||||
Perform detailed validation of a stable vehicle.
|
||||
Perform business logic validation of a stable vehicle.
|
||||
|
||||
Since BoT-SORT already determined the vehicle is stable, we focus on:
|
||||
- Duration requirements for processing
|
||||
- Confidence thresholds
|
||||
- Business logic constraints
|
||||
|
||||
Args:
|
||||
vehicle: The stable vehicle to validate
|
||||
|
@ -231,7 +231,7 @@ class StableCarValidator:
|
|||
Returns:
|
||||
Detailed validation result
|
||||
"""
|
||||
# Check duration
|
||||
# Check duration (business requirement)
|
||||
duration = time.time() - vehicle.first_seen
|
||||
if duration < self.min_stable_duration:
|
||||
return ValidationResult(
|
||||
|
@ -243,18 +243,7 @@ class StableCarValidator:
|
|||
track_id=vehicle.track_id
|
||||
)
|
||||
|
||||
# Check frame count
|
||||
if vehicle.stable_frames < self.min_stable_frames:
|
||||
return ValidationResult(
|
||||
is_valid=False,
|
||||
state=VehicleState.STABLE,
|
||||
confidence=0.6,
|
||||
reason=f"Not enough stable frames ({vehicle.stable_frames} < {self.min_stable_frames})",
|
||||
should_process=False,
|
||||
track_id=vehicle.track_id
|
||||
)
|
||||
|
||||
# Check confidence
|
||||
# Check confidence (business requirement)
|
||||
if vehicle.avg_confidence < self.min_confidence:
|
||||
return ValidationResult(
|
||||
is_valid=False,
|
||||
|
@ -265,28 +254,19 @@ class StableCarValidator:
|
|||
track_id=vehicle.track_id
|
||||
)
|
||||
|
||||
# Check position variance
|
||||
variance = self._calculate_position_variance(vehicle)
|
||||
if variance > self.position_variance_threshold:
|
||||
return ValidationResult(
|
||||
is_valid=False,
|
||||
state=VehicleState.STABLE,
|
||||
confidence=0.7,
|
||||
reason=f"Position variance too high ({variance:.1f} > {self.position_variance_threshold})",
|
||||
should_process=False,
|
||||
track_id=vehicle.track_id
|
||||
)
|
||||
# Trust BoT-SORT's stability determination - skip position variance check
|
||||
# BoT-SORT's sophisticated tracking already ensures consistent positioning
|
||||
|
||||
# Check state history consistency
|
||||
# Simplified state history check - just ensure recent stability
|
||||
if vehicle.track_id in self.validation_history:
|
||||
history = self.validation_history[vehicle.track_id][-5:] # Last 5 states
|
||||
history = self.validation_history[vehicle.track_id][-3:] # Last 3 states
|
||||
stable_count = sum(1 for s in history if s == VehicleState.STABLE)
|
||||
if stable_count < 3:
|
||||
if len(history) >= 2 and stable_count == 0: # Only fail if clear instability
|
||||
return ValidationResult(
|
||||
is_valid=False,
|
||||
state=VehicleState.STABLE,
|
||||
confidence=0.7,
|
||||
reason="Inconsistent state history",
|
||||
reason="Recent state history shows instability",
|
||||
should_process=False,
|
||||
track_id=vehicle.track_id
|
||||
)
|
||||
|
@ -298,7 +278,7 @@ class StableCarValidator:
|
|||
is_valid=True,
|
||||
state=VehicleState.STABLE,
|
||||
confidence=vehicle.avg_confidence,
|
||||
reason="Vehicle is stable and ready for processing",
|
||||
reason="Vehicle is stable and ready for processing (BoT-SORT validated)",
|
||||
should_process=True,
|
||||
track_id=vehicle.track_id
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue