mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
Save motion as motion box count (#10484)
This commit is contained in:
parent
380b15b286
commit
657fab2787
@ -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")
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user