From 62c1a61ed0053f4c58cab820c54642aaa81aca6b Mon Sep 17 00:00:00 2001 From: Blake Blackshear Date: Wed, 9 Feb 2022 06:23:59 -0600 Subject: [PATCH 01/15] remove invalid warning --- frigate/record.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/frigate/record.py b/frigate/record.py index 5409d12c1..e184895c9 100644 --- a/frigate/record.py +++ b/frigate/record.py @@ -51,7 +51,6 @@ class RecordingMaintainer(threading.Thread): self.config = config self.recordings_info_queue = recordings_info_queue self.stop_event = stop_event - self.first_pass = True self.recordings_info = defaultdict(list) self.end_time_cache = {} @@ -334,12 +333,6 @@ class RecordingMaintainer(threading.Thread): logger.error(e) duration = datetime.datetime.now().timestamp() - run_start wait_time = max(0, 5 - duration) - if wait_time == 0 and not self.first_pass: - logger.warning( - "Cache is taking longer than 5 seconds to clear. Your recordings disk may be too slow." - ) - if self.first_pass: - self.first_pass = False logger.info(f"Exiting recording maintenance...") From 54d1a223a535dc27e4b829484313180050bc1003 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Wed, 9 Feb 2022 16:41:38 -0500 Subject: [PATCH 02/15] Allow download of in progress clips --- frigate/http.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frigate/http.py b/frigate/http.py index 679a76763..7fa174369 100644 --- a/frigate/http.py +++ b/frigate/http.py @@ -249,7 +249,10 @@ def event_clip(id): clip_path = os.path.join(CLIPS_DIR, file_name) if not os.path.isfile(clip_path): - return recording_clip(event.camera, event.start_time, event.end_time) + end_ts = ( + datetime.now().timestamp() if event.end_time is None else event.end_time + ) + return recording_clip(event.camera, event.start_time, end_ts) response = make_response() response.headers["Content-Description"] = "File Transfer" From 117177044781c9fe10a51e90463c09ac29cc9565 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Wed, 9 Feb 2022 15:35:35 -0500 Subject: [PATCH 03/15] Only send significant update once when motionless count reaches the defined threshold. --- frigate/object_processing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frigate/object_processing.py b/frigate/object_processing.py index 1ad6433a3..ec507ac7c 100644 --- a/frigate/object_processing.py +++ b/frigate/object_processing.py @@ -158,10 +158,10 @@ class TrackedObject: if self.obj_data["position_changes"] != obj_data["position_changes"]: significant_change = True - # if the motionless_count crosses the stationary threshold + # if the motionless_count reaches the stationary threshold if ( self.obj_data["motionless_count"] - > self.camera_config.detect.stationary_threshold + == self.camera_config.detect.stationary_threshold ): significant_change = True From 4deb36575801423735653a473963b64884069ae1 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Wed, 9 Feb 2022 11:53:19 -0500 Subject: [PATCH 04/15] Fix duration for long events and playback rate for top of the hour --- web/src/components/RecordingPlaylist.jsx | 25 ++++++++++++++++++------ web/src/routes/Recording.jsx | 6 +++--- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/web/src/components/RecordingPlaylist.jsx b/web/src/components/RecordingPlaylist.jsx index 29f1cac22..3b3ff1568 100644 --- a/web/src/components/RecordingPlaylist.jsx +++ b/web/src/components/RecordingPlaylist.jsx @@ -1,6 +1,14 @@ import { h } from 'preact'; import { useState } from 'preact/hooks'; -import { addSeconds, differenceInSeconds, fromUnixTime, format, parseISO, startOfHour } from 'date-fns'; +import { + differenceInSeconds, + fromUnixTime, + format, + parseISO, + startOfHour, + differenceInMinutes, + differenceInHours, +} from 'date-fns'; import ArrowDropdown from '../icons/ArrowDropdown'; import ArrowDropup from '../icons/ArrowDropup'; import Link from '../components/Link'; @@ -89,9 +97,16 @@ export function ExpandableList({ title, events = 0, children, selected = false } export function EventCard({ camera, event, delay }) { const apiHost = useApiHost(); const start = fromUnixTime(event.start_time); - let duration = 0; + let duration = 'In Progress'; if (event.end_time) { - duration = addSeconds(new Date(0), differenceInSeconds(fromUnixTime(event.end_time), start)); + const end = fromUnixTime(event.end_time); + const hours = differenceInHours(end, start); + const minutes = differenceInMinutes(end, start) - hours * 60; + const seconds = differenceInSeconds(end, start) - hours * 60 - minutes * 60; + duration = ''; + if (hours) duration += `${hours}h `; + if (minutes) duration += `${minutes}m `; + duration += `${seconds}s`; } const position = differenceInSeconds(start, startOfHour(start)); const offset = Object.entries(delay) @@ -110,9 +125,7 @@ export function EventCard({ camera, event, delay }) {
{event.label}
Start: {format(start, 'HH:mm:ss')}
-
- Duration: {duration ? format(duration, 'mm:ss') : 'In Progress'} -
+
Duration: {duration}
{(event.top_score * 100).toFixed(1)}%
diff --git a/web/src/routes/Recording.jsx b/web/src/routes/Recording.jsx index 3dba365b0..888872437 100644 --- a/web/src/routes/Recording.jsx +++ b/web/src/routes/Recording.jsx @@ -64,11 +64,11 @@ export default function Recording({ camera, date, hour, seconds }) { this.player.playlist.currentItem(selectedHour); if (seconds !== undefined) { this.player.currentTime(seconds); - // Force playback rate to be correct - const playbackRate = this.player.playbackRate(); - this.player.defaultPlaybackRate(playbackRate); } } + // Force playback rate to be correct + const playbackRate = this.player.playbackRate(); + this.player.defaultPlaybackRate(playbackRate); } return ( From adbc54bcfe26085f2a18b0d7f01ba464b4e2bc66 Mon Sep 17 00:00:00 2001 From: Blake Blackshear Date: Wed, 9 Feb 2022 21:27:33 -0600 Subject: [PATCH 05/15] selectively increment position changes --- docs/docs/integrations/mqtt.md | 2 +- frigate/objects.py | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/docs/integrations/mqtt.md b/docs/docs/integrations/mqtt.md index c868d7e3c..b048abacb 100644 --- a/docs/docs/integrations/mqtt.md +++ b/docs/docs/integrations/mqtt.md @@ -57,7 +57,7 @@ Message published for each changed event. The first message is published when th "has_snapshot": false, "has_clip": false, "motionless_count": 0, // number of frames the object has been motionless - "position_changes": 2 // number of times the object has changed position + "position_changes": 2 // number of times the object has moved from a stationary position }, "after": { "id": "1607123955.475377-mxklsc", diff --git a/frigate/objects.py b/frigate/objects.py index 640130043..1711ed4f4 100644 --- a/frigate/objects.py +++ b/frigate/objects.py @@ -99,8 +99,16 @@ class ObjectTracker: if self.update_position(id, new_obj["box"]): self.tracked_objects[id]["motionless_count"] += 1 else: + # register the first position change and then only increment if + # the object was previously stationary + if ( + self.tracked_objects[id]["position_changes"] == 0 + or self.tracked_objects[id]["motionless_count"] + >= self.detect_config.stationary_threshold + ): + self.tracked_objects[id]["position_changes"] += 1 self.tracked_objects[id]["motionless_count"] = 0 - self.tracked_objects[id]["position_changes"] += 1 + self.tracked_objects[id].update(new_obj) def update_frame_times(self, frame_time): From 7934f8699f5d227cbe27f57594e864372a113172 Mon Sep 17 00:00:00 2001 From: Blake Blackshear Date: Fri, 11 Feb 2022 06:54:42 -0600 Subject: [PATCH 06/15] fix the bounding box calculation position at 10 --- frigate/objects.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frigate/objects.py b/frigate/objects.py index 1711ed4f4..7adf421d9 100644 --- a/frigate/objects.py +++ b/frigate/objects.py @@ -78,9 +78,9 @@ class ObjectTracker: } return False - # if there are less than stationary_threshold entries for the position, add the bounding box + # if there are less than 10 entries for the position, add the bounding box # and recompute the position box - if len(position["xmins"]) < self.detect_config.stationary_threshold: + if len(position["xmins"]) < 10: position["xmins"].append(xmin) position["ymins"].append(ymin) position["xmaxs"].append(xmax) From 4e52461aa9c33560db31dcec8ad4f547b1776a6d Mon Sep 17 00:00:00 2001 From: Blake Blackshear Date: Fri, 11 Feb 2022 07:12:51 -0600 Subject: [PATCH 07/15] set stationary_threshold default to 5x fps --- docs/docs/configuration/index.md | 4 ++-- frigate/config.py | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/docs/configuration/index.md b/docs/docs/configuration/index.md index 80eb3fef8..25c85741f 100644 --- a/docs/docs/configuration/index.md +++ b/docs/docs/configuration/index.md @@ -162,8 +162,8 @@ detect: # Optional: Frequency for running detection on stationary objects (default: shown below) # When set to 0, object detection will never be run on stationary objects. If set to 10, it will be run on every 10th frame. stationary_interval: 0 - # Optional: Number of frames without a position change for an object to be considered stationary (default: shown below) - stationary_threshold: 10 + # Optional: Number of frames without a position change for an object to be considered stationary (default: 5x the frame rate) + stationary_threshold: 25 # Optional: Object configuration # NOTE: Can be overridden at the camera level diff --git a/frigate/config.py b/frigate/config.py index f7f82e91d..a5f5275a2 100644 --- a/frigate/config.py +++ b/frigate/config.py @@ -178,7 +178,6 @@ class DetectConfig(FrigateBaseModel): ge=0, ) stationary_threshold: Optional[int] = Field( - default=10, title="Number of frames without a position change for an object to be considered stationary", ge=1, ) @@ -771,6 +770,11 @@ class FrigateConfig(FrigateBaseModel): if camera_config.detect.max_disappeared is None: camera_config.detect.max_disappeared = max_disappeared + # Default stationary_threshold configuration + stationary_threshold = camera_config.detect.fps * 5 + if camera_config.detect.stationary_threshold is None: + camera_config.detect.stationary_threshold = stationary_threshold + # FFMPEG input substitution for input in camera_config.ffmpeg.inputs: input.path = input.path.format(**FRIGATE_ENV_VARS) From 95ab22d4111f4d09feab2e1a65566896615a4d46 Mon Sep 17 00:00:00 2001 From: Blake Blackshear Date: Fri, 11 Feb 2022 07:30:47 -0600 Subject: [PATCH 08/15] bump default stationary_threshold to 10s --- docs/docs/configuration/index.md | 4 ++-- frigate/config.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/configuration/index.md b/docs/docs/configuration/index.md index 25c85741f..a929d8363 100644 --- a/docs/docs/configuration/index.md +++ b/docs/docs/configuration/index.md @@ -162,8 +162,8 @@ detect: # Optional: Frequency for running detection on stationary objects (default: shown below) # When set to 0, object detection will never be run on stationary objects. If set to 10, it will be run on every 10th frame. stationary_interval: 0 - # Optional: Number of frames without a position change for an object to be considered stationary (default: 5x the frame rate) - stationary_threshold: 25 + # Optional: Number of frames without a position change for an object to be considered stationary (default: 10x the frame rate or 10s) + stationary_threshold: 50 # Optional: Object configuration # NOTE: Can be overridden at the camera level diff --git a/frigate/config.py b/frigate/config.py index a5f5275a2..3bfda8c55 100644 --- a/frigate/config.py +++ b/frigate/config.py @@ -771,7 +771,7 @@ class FrigateConfig(FrigateBaseModel): camera_config.detect.max_disappeared = max_disappeared # Default stationary_threshold configuration - stationary_threshold = camera_config.detect.fps * 5 + stationary_threshold = camera_config.detect.fps * 10 if camera_config.detect.stationary_threshold is None: camera_config.detect.stationary_threshold = stationary_threshold From 6b2bae040c6e80cbb3e4fbc15b62fc59ea5a7609 Mon Sep 17 00:00:00 2001 From: Blake Blackshear Date: Fri, 11 Feb 2022 07:34:42 -0600 Subject: [PATCH 09/15] stop forcing detection all the way to stationary_threshold --- frigate/video.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frigate/video.py b/frigate/video.py index 2d419cc1d..cb201fd22 100755 --- a/frigate/video.py +++ b/frigate/video.py @@ -507,8 +507,8 @@ def process_frames( stationary_object_ids = [ obj["id"] for obj in object_tracker.tracked_objects.values() - # if there hasn't been motion for N frames - if obj["motionless_count"] >= detect_config.stationary_threshold + # if there hasn't been motion for 10 frames + if obj["motionless_count"] >= 10 # and it isn't due for a periodic check and ( detect_config.stationary_interval == 0 From 334e28fe548ba6c7e9e237859fcbc11018e9f20d Mon Sep 17 00:00:00 2001 From: Blake Blackshear Date: Sat, 12 Feb 2022 06:43:46 -0600 Subject: [PATCH 10/15] use second stream in docs example --- docs/docs/guides/getting_started.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/docs/guides/getting_started.md b/docs/docs/guides/getting_started.md index c4af845ae..5fe51cb00 100644 --- a/docs/docs/guides/getting_started.md +++ b/docs/docs/guides/getting_started.md @@ -167,13 +167,17 @@ cameras: roles: - detect - rtmp - - record # <----- Add role + - path: rtsp://10.0.10.10:554/high_res_stream # <----- Add high res stream + roles: + - record detect: ... record: # <----- Enable recording enabled: True motion: ... ``` +If you don't have separate streams for detect and record, you would just add the record role to the list on the first input. + By default, Frigate will retain video of all events for 10 days. The full set of options for recording can be found [here](/configuration/index#full-configuration-reference). ### Step 8: Enable snapshots (optional) From ee01396b363e2ae6fb88d99e4332a00af264ca2b Mon Sep 17 00:00:00 2001 From: Blake Blackshear Date: Sat, 12 Feb 2022 06:59:10 -0600 Subject: [PATCH 11/15] update birdseye to handle stationary objects --- docs/docs/integrations/mqtt.md | 2 ++ frigate/object_processing.py | 2 ++ frigate/output.py | 7 ++----- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/docs/integrations/mqtt.md b/docs/docs/integrations/mqtt.md index b048abacb..ef6c07a42 100644 --- a/docs/docs/integrations/mqtt.md +++ b/docs/docs/integrations/mqtt.md @@ -56,6 +56,7 @@ Message published for each changed event. The first message is published when th "thumbnail": null, "has_snapshot": false, "has_clip": false, + "stationary": false, // whether or not the object is considered stationary "motionless_count": 0, // number of frames the object has been motionless "position_changes": 2 // number of times the object has moved from a stationary position }, @@ -78,6 +79,7 @@ Message published for each changed event. The first message is published when th "thumbnail": null, "has_snapshot": false, "has_clip": false, + "stationary": false, // whether or not the object is considered stationary "motionless_count": 0, // number of frames the object has been motionless "position_changes": 2 // number of times the object has changed position } diff --git a/frigate/object_processing.py b/frigate/object_processing.py index ec507ac7c..527e080b3 100644 --- a/frigate/object_processing.py +++ b/frigate/object_processing.py @@ -193,6 +193,8 @@ class TrackedObject: "box": self.obj_data["box"], "area": self.obj_data["area"], "region": self.obj_data["region"], + "stationary": self.obj_data["motionless_count"] + > self.camera_config.detect.stationary_threshold, "motionless_count": self.obj_data["motionless_count"], "position_changes": self.obj_data["position_changes"], "current_zones": self.current_zones.copy(), diff --git a/frigate/output.py b/frigate/output.py index a780b3f86..dff8fc47e 100644 --- a/frigate/output.py +++ b/frigate/output.py @@ -184,10 +184,7 @@ class BirdsEyeFrameManager: if self.mode == BirdseyeModeEnum.continuous: return True - if ( - self.mode == BirdseyeModeEnum.motion - and object_box_count + motion_box_count > 0 - ): + if self.mode == BirdseyeModeEnum.motion and motion_box_count > 0: return True if self.mode == BirdseyeModeEnum.objects and object_box_count > 0: @@ -418,7 +415,7 @@ def output_frames(config: FrigateConfig, video_output_queue): ): if birdseye_manager.update( camera, - len(current_tracked_objects), + len([o for o in current_tracked_objects if not o["stationary"]]), len(motion_boxes), frame_time, frame, From 889835a59b0fae0d7ce340ff933550afa0c57645 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Sat, 12 Feb 2022 12:51:28 -0700 Subject: [PATCH 12/15] Always show recording link even if recordings are currently disabled (#2787) * Always show recording link even if recordings are currently disabled * Fix test to consider all cameras to have recording link --- web/src/routes/Cameras.jsx | 8 ++------ web/src/routes/__tests__/Cameras.test.jsx | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/web/src/routes/Cameras.jsx b/web/src/routes/Cameras.jsx index 94b7cb7f1..3bb5647bd 100644 --- a/web/src/routes/Cameras.jsx +++ b/web/src/routes/Cameras.jsx @@ -29,12 +29,8 @@ function Camera({ name, conf }) { const { payload: snapshotValue, send: sendSnapshots } = useSnapshotsState(name); const href = `/cameras/${name}`; const buttons = useMemo(() => { - const result = [{ name: 'Events', href: `/events?camera=${name}` }]; - if (conf.record.enabled) { - result.push({ name: 'Recordings', href: `/recording/${name}` }); - } - return result; - }, [name, conf.record.enabled]); + return [{ name: 'Events', href: `/events?camera=${name}` }, { name: 'Recordings', href: `/recording/${name}` }]; + }, [name]); const icons = useMemo( () => [ { diff --git a/web/src/routes/__tests__/Cameras.test.jsx b/web/src/routes/__tests__/Cameras.test.jsx index 68d0d0a80..cdb0c400d 100644 --- a/web/src/routes/__tests__/Cameras.test.jsx +++ b/web/src/routes/__tests__/Cameras.test.jsx @@ -46,7 +46,7 @@ describe('Cameras Route', () => { expect(screen.queryByLabelText('Loading…')).not.toBeInTheDocument(); - expect(screen.queryAllByText('Recordings')).toHaveLength(1); + expect(screen.queryAllByText('Recordings')).toHaveLength(2); }); test('buttons toggle detect, clips, and snapshots', async () => { From 304ffa86e83ff2b9ccac4386f3db730c230589ab Mon Sep 17 00:00:00 2001 From: Blake Blackshear Date: Sun, 13 Feb 2022 08:58:44 -0600 Subject: [PATCH 13/15] refactor stationary config into section --- docs/docs/configuration/index.md | 21 +++++++++++++----- frigate/config.py | 38 +++++++++++++++++++++++--------- frigate/object_processing.py | 4 ++-- frigate/objects.py | 2 +- frigate/video.py | 4 ++-- 5 files changed, 49 insertions(+), 20 deletions(-) diff --git a/docs/docs/configuration/index.md b/docs/docs/configuration/index.md index a929d8363..beb98175b 100644 --- a/docs/docs/configuration/index.md +++ b/docs/docs/configuration/index.md @@ -159,11 +159,22 @@ detect: enabled: True # Optional: Number of frames without a detection before frigate considers an object to be gone. (default: 5x the frame rate) max_disappeared: 25 - # Optional: Frequency for running detection on stationary objects (default: shown below) - # When set to 0, object detection will never be run on stationary objects. If set to 10, it will be run on every 10th frame. - stationary_interval: 0 - # Optional: Number of frames without a position change for an object to be considered stationary (default: 10x the frame rate or 10s) - stationary_threshold: 50 + stationary: + # Optional: Frequency for running detection on stationary objects (default: shown below) + # When set to 0, object detection will never be run on stationary objects. If set to 10, it will be run on every 10th frame. + interval: 0 + # Optional: Number of frames without a position change for an object to be considered stationary (default: 10x the frame rate or 10s) + threshold: 50 + # Optional: Define a maximum number of frames for tracking a stationary object (default: not set, track forever) + # This can help with false positives for objects that should only be stationary for a limited amount of time. + # It can also be used to disable stationary object tracking. For example, you may want to set a value for person, but leave + # car at the default. + max_frames: + # Optional: Default for all object types (default: not set, track forever) + default: 3000 + # Optional: Object specific values + objects: + person: 1000 # Optional: Object configuration # NOTE: Can be overridden at the camera level diff --git a/frigate/config.py b/frigate/config.py index 3bfda8c55..c210713a2 100644 --- a/frigate/config.py +++ b/frigate/config.py @@ -162,6 +162,29 @@ class RuntimeMotionConfig(MotionConfig): extra = Extra.ignore +class StationaryMaxFramesConfig(FrigateBaseModel): + default: Optional[int] = Field(title="Default max frames.", ge=1) + objects: Dict[str, int] = Field( + default_factory=dict, title="Object specific max frames." + ) + + +class StationaryConfig(FrigateBaseModel): + interval: Optional[int] = Field( + default=0, + title="Frame interval for checking stationary objects.", + ge=0, + ) + threshold: Optional[int] = Field( + title="Number of frames without a position change for an object to be considered stationary", + ge=1, + ) + max_frames: StationaryMaxFramesConfig = Field( + default_factory=StationaryMaxFramesConfig, + title="Max frames for stationary objects.", + ) + + class DetectConfig(FrigateBaseModel): height: int = Field(default=720, title="Height of the stream for the detect role.") width: int = Field(default=1280, title="Width of the stream for the detect role.") @@ -172,14 +195,9 @@ class DetectConfig(FrigateBaseModel): max_disappeared: Optional[int] = Field( title="Maximum number of frames the object can dissapear before detection ends." ) - stationary_interval: Optional[int] = Field( - default=0, - title="Frame interval for checking stationary objects.", - ge=0, - ) - stationary_threshold: Optional[int] = Field( - title="Number of frames without a position change for an object to be considered stationary", - ge=1, + stationary: StationaryConfig = Field( + default_factory=StationaryConfig, + title="Stationary objects config.", ) @@ -772,8 +790,8 @@ class FrigateConfig(FrigateBaseModel): # Default stationary_threshold configuration stationary_threshold = camera_config.detect.fps * 10 - if camera_config.detect.stationary_threshold is None: - camera_config.detect.stationary_threshold = stationary_threshold + if camera_config.detect.stationary.threshold is None: + camera_config.detect.stationary.threshold = stationary_threshold # FFMPEG input substitution for input in camera_config.ffmpeg.inputs: diff --git a/frigate/object_processing.py b/frigate/object_processing.py index 527e080b3..37d45e127 100644 --- a/frigate/object_processing.py +++ b/frigate/object_processing.py @@ -161,7 +161,7 @@ class TrackedObject: # if the motionless_count reaches the stationary threshold if ( self.obj_data["motionless_count"] - == self.camera_config.detect.stationary_threshold + == self.camera_config.detect.stationary.threshold ): significant_change = True @@ -194,7 +194,7 @@ class TrackedObject: "area": self.obj_data["area"], "region": self.obj_data["region"], "stationary": self.obj_data["motionless_count"] - > self.camera_config.detect.stationary_threshold, + > self.camera_config.detect.stationary.threshold, "motionless_count": self.obj_data["motionless_count"], "position_changes": self.obj_data["position_changes"], "current_zones": self.current_zones.copy(), diff --git a/frigate/objects.py b/frigate/objects.py index 7adf421d9..874fc9d75 100644 --- a/frigate/objects.py +++ b/frigate/objects.py @@ -104,7 +104,7 @@ class ObjectTracker: if ( self.tracked_objects[id]["position_changes"] == 0 or self.tracked_objects[id]["motionless_count"] - >= self.detect_config.stationary_threshold + >= self.detect_config.stationary.threshold ): self.tracked_objects[id]["position_changes"] += 1 self.tracked_objects[id]["motionless_count"] = 0 diff --git a/frigate/video.py b/frigate/video.py index cb201fd22..26b99176f 100755 --- a/frigate/video.py +++ b/frigate/video.py @@ -511,8 +511,8 @@ def process_frames( if obj["motionless_count"] >= 10 # and it isn't due for a periodic check and ( - detect_config.stationary_interval == 0 - or obj["motionless_count"] % detect_config.stationary_interval != 0 + detect_config.stationary.interval == 0 + or obj["motionless_count"] % detect_config.stationary.interval != 0 ) # and it hasn't disappeared and object_tracker.disappeared[obj["id"]] == 0 From 3be0b915adce887195141f964ed4796ba652805b Mon Sep 17 00:00:00 2001 From: Blake Blackshear Date: Sun, 13 Feb 2022 10:20:38 -0600 Subject: [PATCH 14/15] deregister based on max_frames setting --- frigate/objects.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/frigate/objects.py b/frigate/objects.py index 874fc9d75..7b66536c2 100644 --- a/frigate/objects.py +++ b/frigate/objects.py @@ -93,11 +93,33 @@ class ObjectTracker: return True + def is_expired(self, id): + obj = self.tracked_objects[id] + # get the max frames for this label type or the default + max_frames = self.detect_config.stationary.max_frames.objects.get( + obj["label"], self.detect_config.stationary.max_frames.default + ) + + # if there is no max_frames for this label type, continue + if max_frames is None: + return False + + # if the object has exceeded the max_frames setting, deregister + if ( + obj["motionless_count"] - self.detect_config.stationary.threshold + > max_frames + ): + print(f"expired: {obj['motionless_count']}") + return True + def update(self, id, new_obj): self.disappeared[id] = 0 # update the motionless count if the object has not moved to a new position if self.update_position(id, new_obj["box"]): self.tracked_objects[id]["motionless_count"] += 1 + if self.is_expired(id): + self.deregister(id) + return else: # register the first position change and then only increment if # the object was previously stationary @@ -112,9 +134,11 @@ class ObjectTracker: self.tracked_objects[id].update(new_obj) def update_frame_times(self, frame_time): - for id in self.tracked_objects.keys(): + for id in list(self.tracked_objects.keys()): self.tracked_objects[id]["frame_time"] = frame_time self.tracked_objects[id]["motionless_count"] += 1 + if self.is_expired(id): + self.deregister(id) def match_and_update(self, frame_time, new_objects): # group by name From e5714f5fbc35e226399247f6ef6a26d79cd53c97 Mon Sep 17 00:00:00 2001 From: Blake Blackshear Date: Sun, 13 Feb 2022 10:27:58 -0600 Subject: [PATCH 15/15] add missing optional comment in docs --- docs/docs/configuration/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docs/configuration/index.md b/docs/docs/configuration/index.md index beb98175b..e334d88ee 100644 --- a/docs/docs/configuration/index.md +++ b/docs/docs/configuration/index.md @@ -159,6 +159,7 @@ detect: enabled: True # Optional: Number of frames without a detection before frigate considers an object to be gone. (default: 5x the frame rate) max_disappeared: 25 + # Optional: Configuration for stationary object tracking stationary: # Optional: Frequency for running detection on stationary objects (default: shown below) # When set to 0, object detection will never be run on stationary objects. If set to 10, it will be run on every 10th frame.