Save motion as motion box count (#10484)

This commit is contained in:
Nicolas Mowen 2024-03-15 13:13:40 -06:00 committed by GitHub
parent 380b15b286
commit 657fab2787
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 13 additions and 36 deletions

View File

@ -366,7 +366,7 @@ def motion_activity():
data: list[Recordings] = ( data: list[Recordings] = (
Recordings.select( Recordings.select(
Recordings.start_time, Recordings.start_time,
Recordings.regions, Recordings.motion,
) )
.where(reduce(operator.and_, clauses)) .where(reduce(operator.and_, clauses))
.order_by(Recordings.start_time.asc()) .order_by(Recordings.start_time.asc())
@ -378,8 +378,7 @@ def motion_activity():
scale = request.args.get("scale", type=int, default=30) scale = request.args.get("scale", type=int, default=30)
# resample data using pandas to get activity on scaled basis # resample data using pandas to get activity on scaled basis
df = pd.DataFrame(data, columns=["start_time", "regions"]) df = pd.DataFrame(data, columns=["start_time", "motion"])
df = df.rename(columns={"regions": "motion"})
# set date as datetime index # set date as datetime index
df["start_time"] = pd.to_datetime(df["start_time"], unit="s") df["start_time"] = pd.to_datetime(df["start_time"], unit="s")

View File

@ -28,7 +28,6 @@ from frigate.const import (
RECORD_DIR, RECORD_DIR,
) )
from frigate.models import Event, Recordings from frigate.models import Event, Recordings
from frigate.util.image import area
from frigate.util.services import get_video_properties from frigate.util.services import get_video_properties
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -39,12 +38,12 @@ QUEUE_READ_TIMEOUT = 0.00001 # seconds
class SegmentInfo: class SegmentInfo:
def __init__( def __init__(
self, self,
motion_area: int, motion_count: int,
active_object_count: int, active_object_count: int,
region_count: int, region_count: int,
average_dBFS: int, average_dBFS: int,
) -> None: ) -> None:
self.motion_area = motion_area self.motion_count = motion_count
self.active_object_count = active_object_count self.active_object_count = active_object_count
self.region_count = region_count self.region_count = region_count
self.average_dBFS = average_dBFS self.average_dBFS = average_dBFS
@ -52,7 +51,7 @@ class SegmentInfo:
def should_discard_segment(self, retain_mode: RetainModeEnum) -> bool: def should_discard_segment(self, retain_mode: RetainModeEnum) -> bool:
return ( return (
retain_mode == RetainModeEnum.motion retain_mode == RetainModeEnum.motion
and self.motion_area == 0 and self.motion_count == 0
and self.average_dBFS == 0 and self.average_dBFS == 0
) or ( ) or (
retain_mode == RetainModeEnum.active_objects retain_mode == RetainModeEnum.active_objects
@ -76,13 +75,6 @@ class RecordingMaintainer(threading.Thread):
self.audio_recordings_info: dict[str, list] = defaultdict(list) self.audio_recordings_info: dict[str, list] = defaultdict(list)
self.end_time_cache: dict[str, Tuple[datetime.datetime, float]] = {} self.end_time_cache: dict[str, Tuple[datetime.datetime, float]] = {}
self.camera_frame_area: dict[str, int] = {}
for camera in self.config.cameras.values():
self.camera_frame_area[camera.name] = (
camera.detect.width * camera.detect.height * 0.1
)
async def move_files(self) -> None: async def move_files(self) -> None:
cache_files = [ cache_files = [
d d
@ -304,7 +296,7 @@ class RecordingMaintainer(threading.Thread):
video_frame_count = 0 video_frame_count = 0
active_count = 0 active_count = 0
region_count = 0 region_count = 0
total_motion_area = 0 motion_count = 0
for frame in self.object_recordings_info[camera]: for frame in self.object_recordings_info[camera]:
# frame is after end time of segment # frame is after end time of segment
if frame[0] > end_time.timestamp(): if frame[0] > end_time.timestamp():
@ -321,23 +313,9 @@ class RecordingMaintainer(threading.Thread):
if not o["false_positive"] and o["motionless_count"] == 0 if not o["false_positive"] and o["motionless_count"] == 0
] ]
) )
total_motion_area += sum([area(box) for box in frame[2]]) motion_count += len(frame[2])
region_count += len(frame[3]) region_count += len(frame[3])
if video_frame_count > 0:
normalized_motion_area = min(
int(
(
total_motion_area
/ (self.camera_frame_area[camera] * video_frame_count)
)
* 100
),
100,
)
else:
normalized_motion_area = 0
audio_values = [] audio_values = []
for frame in self.audio_recordings_info[camera]: for frame in self.audio_recordings_info[camera]:
# frame is after end time of segment # frame is after end time of segment
@ -357,7 +335,7 @@ class RecordingMaintainer(threading.Thread):
average_dBFS = 0 if not audio_values else np.average(audio_values) average_dBFS = 0 if not audio_values else np.average(audio_values)
return SegmentInfo( return SegmentInfo(
normalized_motion_area, active_count, region_count, round(average_dBFS) motion_count, active_count, region_count, round(average_dBFS)
) )
async def move_segment( async def move_segment(
@ -443,7 +421,7 @@ class RecordingMaintainer(threading.Thread):
Recordings.start_time: start_time.timestamp(), Recordings.start_time: start_time.timestamp(),
Recordings.end_time: end_time.timestamp(), Recordings.end_time: end_time.timestamp(),
Recordings.duration: duration, Recordings.duration: duration,
Recordings.motion: segment_info.motion_area, Recordings.motion: segment_info.motion_count,
# TODO: update this to store list of active objects at some point # TODO: update this to store list of active objects at some point
Recordings.objects: segment_info.active_object_count, Recordings.objects: segment_info.active_object_count,
Recordings.regions: segment_info.region_count, Recordings.regions: segment_info.region_count,

View File

@ -7,27 +7,27 @@ from frigate.record.maintainer import SegmentInfo
class TestRecordRetention(unittest.TestCase): class TestRecordRetention(unittest.TestCase):
def test_motion_should_keep_motion_not_object(self): def test_motion_should_keep_motion_not_object(self):
segment_info = SegmentInfo( segment_info = SegmentInfo(
motion_area=1, active_object_count=0, region_count=0, average_dBFS=0 motion_count=1, active_object_count=0, region_count=0, average_dBFS=0
) )
assert not segment_info.should_discard_segment(RetainModeEnum.motion) assert not segment_info.should_discard_segment(RetainModeEnum.motion)
assert segment_info.should_discard_segment(RetainModeEnum.active_objects) assert segment_info.should_discard_segment(RetainModeEnum.active_objects)
def test_object_should_keep_object_not_motion(self): def test_object_should_keep_object_not_motion(self):
segment_info = SegmentInfo( segment_info = SegmentInfo(
motion_area=0, active_object_count=1, region_count=0, average_dBFS=0 motion_count=0, active_object_count=1, region_count=0, average_dBFS=0
) )
assert segment_info.should_discard_segment(RetainModeEnum.motion) assert segment_info.should_discard_segment(RetainModeEnum.motion)
assert not segment_info.should_discard_segment(RetainModeEnum.active_objects) assert not segment_info.should_discard_segment(RetainModeEnum.active_objects)
def test_all_should_keep_all(self): def test_all_should_keep_all(self):
segment_info = SegmentInfo( segment_info = SegmentInfo(
motion_area=0, active_object_count=0, region_count=0, average_dBFS=0 motion_count=0, active_object_count=0, region_count=0, average_dBFS=0
) )
assert not segment_info.should_discard_segment(RetainModeEnum.all) assert not segment_info.should_discard_segment(RetainModeEnum.all)
def test_should_keep_audio_in_motion_mode(self): def test_should_keep_audio_in_motion_mode(self):
segment_info = SegmentInfo( segment_info = SegmentInfo(
motion_area=0, active_object_count=0, region_count=0, average_dBFS=1 motion_count=0, active_object_count=0, region_count=0, average_dBFS=1
) )
assert not segment_info.should_discard_segment(RetainModeEnum.motion) assert not segment_info.should_discard_segment(RetainModeEnum.motion)
assert segment_info.should_discard_segment(RetainModeEnum.active_objects) assert segment_info.should_discard_segment(RetainModeEnum.active_objects)