diff --git a/inference/rules/algorithms.py b/inference/rules/algorithms.py index 3511498..f3c355f 100644 --- a/inference/rules/algorithms.py +++ b/inference/rules/algorithms.py @@ -16,6 +16,7 @@ class LeavePostAlgorithm: STATE_ON_DUTY = "ON_DUTY" STATE_OFF_DUTY_COUNTDOWN = "OFF_DUTY_COUNTDOWN" STATE_NON_WORK_TIME = "NON_WORK_TIME" + STATE_INIT = "INIT" def __init__( self, @@ -32,11 +33,13 @@ class LeavePostAlgorithm: self.alert_cooldowns: Dict[str, datetime] = {} self.cooldown_seconds = 300 - self.state: str = self.STATE_ON_DUTY + self.state: str = self.STATE_INIT self.state_start_time: Optional[datetime] = None self.on_duty_window = deque() self.alarm_sent: bool = False self.last_person_seen_time: Optional[datetime] = None + self.last_detection_time: Optional[datetime] = None + self.init_start_time: Optional[datetime] = None def is_in_working_hours(self, dt: Optional[datetime] = None) -> bool: if not self.working_hours: @@ -62,64 +65,95 @@ class LeavePostAlgorithm: def process( self, + roi_id: str, camera_id: str, tracks: List[Dict], current_time: Optional[datetime] = None, ) -> List[Dict]: current_time = current_time or datetime.now() + roi_has_person = False + for det in tracks: + if self.check_detection_in_roi(det, roi_id): + roi_has_person = True + break + in_work = self.is_in_working_hours(current_time) alerts = [] if not in_work: self.state = self.STATE_NON_WORK_TIME self.last_person_seen_time = None + self.last_detection_time = None self.on_duty_window.clear() self.alarm_sent = False + self.init_start_time = None else: if self.state == self.STATE_NON_WORK_TIME: - self.state = self.STATE_ON_DUTY + self.state = self.STATE_INIT + self.init_start_time = current_time self.on_duty_window.clear() self.alarm_sent = False - roi_has_person = False - for det in tracks: - if self.check_detection_in_roi(det, camera_id): - roi_has_person = True - break - - if self.state == self.STATE_ON_DUTY: - self.on_duty_window.append((current_time, roi_has_person)) - while self.on_duty_window and (current_time - self.on_duty_window[0][0]).total_seconds() > self.confirm_sec: - self.on_duty_window.popleft() - - hit_ratio = sum(1 for t, detected in self.on_duty_window if detected) / max(len(self.on_duty_window), 1) - - if not roi_has_person and hit_ratio == 0: - self.state = self.STATE_OFF_DUTY_COUNTDOWN + if self.state == self.STATE_INIT: + if roi_has_person: + self.state = self.STATE_ON_DUTY self.state_start_time = current_time - self.alarm_sent = False - elif hit_ratio >= 0.75 and (current_time - self.on_duty_window[0][0]).total_seconds() >= self.confirm_sec: + self.on_duty_window.clear() + self.on_duty_window.append((current_time, True)) self.last_person_seen_time = current_time + self.last_detection_time = current_time + self.init_start_time = None + else: + if self.init_start_time is None: + self.init_start_time = current_time + + elapsed_since_init = (current_time - self.init_start_time).total_seconds() + if elapsed_since_init >= self.threshold_sec: + self.state = self.STATE_OFF_DUTY_COUNTDOWN + self.state_start_time = current_time + self.alarm_sent = False + + elif self.state == self.STATE_ON_DUTY: + if roi_has_person: + self.last_person_seen_time = current_time + self.last_detection_time = current_time + + self.on_duty_window.append((current_time, True)) + while self.on_duty_window and (current_time - self.on_duty_window[0][0]).total_seconds() > self.confirm_sec: + self.on_duty_window.popleft() + else: + self.on_duty_window.append((current_time, False)) + while self.on_duty_window and (current_time - self.on_duty_window[0][0]).total_seconds() > self.confirm_sec: + self.on_duty_window.popleft() + + hit_ratio = sum(1 for t, detected in self.on_duty_window if detected) / max(len(self.on_duty_window), 1) + + if hit_ratio == 0: + self.state = self.STATE_OFF_DUTY_COUNTDOWN + self.state_start_time = current_time + self.alarm_sent = False elif self.state == self.STATE_OFF_DUTY_COUNTDOWN: elapsed = (current_time - self.state_start_time).total_seconds() if roi_has_person: self.state = self.STATE_ON_DUTY + self.state_start_time = current_time self.on_duty_window.clear() self.on_duty_window.append((current_time, True)) + self.last_person_seen_time = current_time self.alarm_sent = False elif elapsed >= self.threshold_sec: if not self.alarm_sent: - cooldown_key = f"{camera_id}" + cooldown_key = f"{roi_id}" if cooldown_key not in self.alert_cooldowns or ( current_time - self.alert_cooldowns[cooldown_key] ).total_seconds() > self.cooldown_seconds: - bbox = self.get_latest_bbox_in_roi(tracks, camera_id) + bbox = self.get_latest_bbox_in_roi(tracks, roi_id) elapsed_minutes = int(elapsed / 60) alerts.append({ - "track_id": camera_id, + "track_id": roi_id, "bbox": bbox, "off_duty_duration": elapsed, "alert_type": "leave_post", @@ -137,11 +171,13 @@ class LeavePostAlgorithm: return [] def reset(self): - self.state = self.STATE_ON_DUTY + self.state = self.STATE_INIT self.state_start_time = None self.on_duty_window.clear() self.alarm_sent = False self.last_person_seen_time = None + self.last_detection_time = None + self.init_start_time = None self.alert_cooldowns.clear() def get_state(self, track_id: str) -> Optional[Dict[str, Any]]: @@ -279,7 +315,7 @@ class AlgorithmManager: algo = self.algorithms.get(roi_id, {}).get(algorithm_type) if algo is None: return [] - return algo.process(camera_id, tracks, current_time) + return algo.process(roi_id, camera_id, tracks, current_time) def update_roi_params( self,