fix: validator

This commit is contained in:
ziesorx 2025-09-26 14:50:45 +07:00
parent 791f611f7d
commit 61ac39b4f3
3 changed files with 106 additions and 95 deletions

View file

@ -297,31 +297,31 @@ class WebSocketHandler:
async def _reconcile_subscriptions_with_tracking(self, target_subscriptions) -> dict: async def _reconcile_subscriptions_with_tracking(self, target_subscriptions) -> dict:
"""Reconcile subscriptions with tracking integration.""" """Reconcile subscriptions with tracking integration."""
try: try:
# First, we need to create tracking integrations for each unique model # Create separate tracking integrations for each subscription (camera isolation)
tracking_integrations = {} tracking_integrations = {}
for subscription_payload in target_subscriptions: for subscription_payload in target_subscriptions:
subscription_id = subscription_payload['subscriptionIdentifier']
model_id = subscription_payload['modelId'] model_id = subscription_payload['modelId']
# Create tracking integration if not already created # Create separate tracking integration per subscription for camera isolation
if model_id not in tracking_integrations: # Get pipeline configuration for this model
# Get pipeline configuration for this model pipeline_parser = model_manager.get_pipeline_config(model_id)
pipeline_parser = model_manager.get_pipeline_config(model_id) if pipeline_parser:
if pipeline_parser: # Create tracking integration with message sender (separate instance per camera)
# Create tracking integration with message sender tracking_integration = TrackingPipelineIntegration(
tracking_integration = TrackingPipelineIntegration( pipeline_parser, model_manager, model_id, self._send_message
pipeline_parser, model_manager, model_id, self._send_message )
)
# Initialize tracking model # Initialize tracking model
success = await tracking_integration.initialize_tracking_model() success = await tracking_integration.initialize_tracking_model()
if success: if success:
tracking_integrations[model_id] = tracking_integration tracking_integrations[subscription_id] = tracking_integration
logger.info(f"[Tracking] Created tracking integration for model {model_id}") 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}")
else: else:
logger.warning(f"[Tracking] No pipeline config found 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} in subscription {subscription_id}")
# Now reconcile with StreamManager, adding tracking integrations # Now reconcile with StreamManager, adding tracking integrations
current_subscription_ids = set() current_subscription_ids = set()
@ -379,8 +379,8 @@ class WebSocketHandler:
logger.info(f"[SUBSCRIPTION_MAPPING] subscription_id='{subscription_id}' → camera_id='{camera_id}'") logger.info(f"[SUBSCRIPTION_MAPPING] subscription_id='{subscription_id}' → camera_id='{camera_id}'")
# Get tracking integration for this model # Get tracking integration for this subscription (camera-isolated)
tracking_integration = tracking_integrations.get(model_id) tracking_integration = tracking_integrations.get(subscription_id)
# Extract crop coordinates if present # Extract crop coordinates if present
crop_coords = None crop_coords = None
@ -412,7 +412,7 @@ class WebSocketHandler:
) )
if success and tracking_integration: 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 return success

View file

@ -389,20 +389,51 @@ class StreamManager:
logger.debug(f"Set session {session_id} for display {display_id}") logger.debug(f"Set session {session_id} for display {display_id}")
def clear_session_id(self, session_id: str): 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: with self._lock:
# Find the subscription that's handling this session
session_subscription = None
for subscription_info in self._subscriptions.values(): for subscription_info in self._subscriptions.values():
if subscription_info.tracking_integration: if subscription_info.tracking_integration:
subscription_info.tracking_integration.clear_session_id(session_id) # Check if this integration is handling the given session_id
logger.debug(f"Cleared session {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)
def set_progression_stage(self, session_id: str, stage: str): 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: with self._lock:
# Find the subscription that's handling this session
session_subscription = None
for subscription_info in self._subscriptions.values(): for subscription_info in self._subscriptions.values():
if subscription_info.tracking_integration: if subscription_info.tracking_integration:
subscription_info.tracking_integration.set_progression_stage(session_id, stage) # Check if this integration is handling the given session_id
logger.debug(f"Set progression stage for session {session_id}: {stage}") # 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)
def get_tracking_stats(self) -> Dict[str, Any]: def get_tracking_stats(self) -> Dict[str, Any]:
"""Get tracking statistics from all subscriptions.""" """Get tracking statistics from all subscriptions."""

View file

@ -36,8 +36,14 @@ class ValidationResult:
class StableCarValidator: class StableCarValidator:
""" """
Validates whether a tracked vehicle is stable (fueling) or just passing by. Validates whether a tracked vehicle should be processed through the pipeline.
Uses multiple criteria including position stability, duration, and movement patterns.
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): def __init__(self, config: Optional[Dict] = None):
@ -169,7 +175,10 @@ class StableCarValidator:
def _determine_vehicle_state(self, vehicle: TrackedVehicle) -> VehicleState: 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: Args:
vehicle: The tracked vehicle vehicle: The tracked vehicle
@ -177,53 +186,44 @@ class StableCarValidator:
Returns: Returns:
Current vehicle state Current vehicle state
""" """
# Not enough data # Trust BoT-SORT's stability determination
if len(vehicle.last_position_history) < 3: if vehicle.is_stable:
return VehicleState.UNKNOWN # Check if it's been stable long enough for processing
# 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
duration = time.time() - vehicle.first_seen 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 return VehicleState.STABLE
else: else:
return VehicleState.ENTERING 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: if velocity > self.velocity_threshold:
# Determine direction based on position history # Vehicle is moving - classify as passing by or entering/leaving
positions = np.array(vehicle.last_position_history) x_position = vehicle.center[0] / self.frame_width
if len(positions) >= 2:
direction = positions[-1] - positions[0]
# Entering: moving towards center # Simple heuristic: vehicles near edges are entering/leaving, center vehicles are passing
if x_position < self.entering_zone_ratio or x_position > (1 - self.entering_zone_ratio): if x_position < 0.2 or x_position > 0.8:
if abs(direction[0]) > abs(direction[1]): # Horizontal movement return VehicleState.ENTERING
if (x_position < 0.5 and direction[0] > 0) or (x_position > 0.5 and direction[0] < 0): else:
return VehicleState.ENTERING return VehicleState.PASSING_BY
# Leaving: moving away from center # Low velocity but not marked stable by tracker - likely entering
if 0.3 < x_position < 0.7: # In center zone return VehicleState.ENTERING
if abs(direction[0]) > abs(direction[1]): # Horizontal movement
if abs(direction[0]) > 10: # Significant movement
return VehicleState.LEAVING
return VehicleState.PASSING_BY
return VehicleState.UNKNOWN
def _validate_stable_vehicle(self, vehicle: TrackedVehicle) -> ValidationResult: 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: Args:
vehicle: The stable vehicle to validate vehicle: The stable vehicle to validate
@ -231,7 +231,7 @@ class StableCarValidator:
Returns: Returns:
Detailed validation result Detailed validation result
""" """
# Check duration # Check duration (business requirement)
duration = time.time() - vehicle.first_seen duration = time.time() - vehicle.first_seen
if duration < self.min_stable_duration: if duration < self.min_stable_duration:
return ValidationResult( return ValidationResult(
@ -243,18 +243,7 @@ class StableCarValidator:
track_id=vehicle.track_id track_id=vehicle.track_id
) )
# Check frame count # Check confidence (business requirement)
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
if vehicle.avg_confidence < self.min_confidence: if vehicle.avg_confidence < self.min_confidence:
return ValidationResult( return ValidationResult(
is_valid=False, is_valid=False,
@ -265,28 +254,19 @@ class StableCarValidator:
track_id=vehicle.track_id track_id=vehicle.track_id
) )
# Check position variance # Trust BoT-SORT's stability determination - skip position variance check
variance = self._calculate_position_variance(vehicle) # BoT-SORT's sophisticated tracking already ensures consistent positioning
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
)
# Check state history consistency # Simplified state history check - just ensure recent stability
if vehicle.track_id in self.validation_history: 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) 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( return ValidationResult(
is_valid=False, is_valid=False,
state=VehicleState.STABLE, state=VehicleState.STABLE,
confidence=0.7, confidence=0.7,
reason="Inconsistent state history", reason="Recent state history shows instability",
should_process=False, should_process=False,
track_id=vehicle.track_id track_id=vehicle.track_id
) )
@ -298,7 +278,7 @@ class StableCarValidator:
is_valid=True, is_valid=True,
state=VehicleState.STABLE, state=VehicleState.STABLE,
confidence=vehicle.avg_confidence, 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, should_process=True,
track_id=vehicle.track_id track_id=vehicle.track_id
) )