mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-12-23 19:11:14 +01:00
remove clip_ready event type
this doesnt really mean anything more than "end" anymore. new has_clip property added
This commit is contained in:
parent
86a5b46c68
commit
1b2134c49e
@ -36,7 +36,7 @@ Message published for each changed event. The first message is published when th
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "update", // new, update, end or clip_ready
|
"type": "update", // new, update, end
|
||||||
"before": {
|
"before": {
|
||||||
"id": "1607123955.475377-mxklsc",
|
"id": "1607123955.475377-mxklsc",
|
||||||
"camera": "front_door",
|
"camera": "front_door",
|
||||||
@ -53,7 +53,9 @@ Message published for each changed event. The first message is published when th
|
|||||||
"region": [264, 450, 667, 853],
|
"region": [264, 450, 667, 853],
|
||||||
"current_zones": ["driveway"],
|
"current_zones": ["driveway"],
|
||||||
"entered_zones": ["yard", "driveway"],
|
"entered_zones": ["yard", "driveway"],
|
||||||
"thumbnail": null
|
"thumbnail": null,
|
||||||
|
"has_snapshot": false,
|
||||||
|
"has_clip": false
|
||||||
},
|
},
|
||||||
"after": {
|
"after": {
|
||||||
"id": "1607123955.475377-mxklsc",
|
"id": "1607123955.475377-mxklsc",
|
||||||
@ -71,7 +73,9 @@ Message published for each changed event. The first message is published when th
|
|||||||
"region": [218, 440, 693, 915],
|
"region": [218, 440, 693, 915],
|
||||||
"current_zones": ["yard", "driveway"],
|
"current_zones": ["yard", "driveway"],
|
||||||
"entered_zones": ["yard", "driveway"],
|
"entered_zones": ["yard", "driveway"],
|
||||||
"thumbnail": null
|
"thumbnail": null,
|
||||||
|
"has_snapshot": false,
|
||||||
|
"has_clip": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -29,38 +29,6 @@ class EventProcessor(threading.Thread):
|
|||||||
self.events_in_process = {}
|
self.events_in_process = {}
|
||||||
self.stop_event = stop_event
|
self.stop_event = stop_event
|
||||||
|
|
||||||
def should_create_clip(self, camera, event_data):
|
|
||||||
if event_data["false_positive"]:
|
|
||||||
return False
|
|
||||||
|
|
||||||
record_config: RecordConfig = self.config.cameras[camera].record
|
|
||||||
|
|
||||||
# Recording is disabled
|
|
||||||
if not record_config.enabled:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# If there are required zones and there is no overlap
|
|
||||||
required_zones = record_config.events.required_zones
|
|
||||||
if len(required_zones) > 0 and not set(event_data["entered_zones"]) & set(
|
|
||||||
required_zones
|
|
||||||
):
|
|
||||||
logger.debug(
|
|
||||||
f"Not creating clip for {event_data['id']} because it did not enter required zones"
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
|
|
||||||
# If the required objects are not present
|
|
||||||
if (
|
|
||||||
record_config.events.objects is not None
|
|
||||||
and event_data["label"] not in record_config.events.objects
|
|
||||||
):
|
|
||||||
logger.debug(
|
|
||||||
f"Not creating clip for {event_data['id']} because it did not contain required objects"
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
while not self.stop_event.is_set():
|
while not self.stop_event.is_set():
|
||||||
try:
|
try:
|
||||||
@ -74,11 +42,9 @@ class EventProcessor(threading.Thread):
|
|||||||
self.events_in_process[event_data["id"]] = event_data
|
self.events_in_process[event_data["id"]] = event_data
|
||||||
|
|
||||||
if event_type == "end":
|
if event_type == "end":
|
||||||
has_clip = self.should_create_clip(camera, event_data)
|
|
||||||
|
|
||||||
event_config: EventsConfig = self.config.cameras[camera].record.events
|
event_config: EventsConfig = self.config.cameras[camera].record.events
|
||||||
|
|
||||||
if has_clip or event_data["has_snapshot"]:
|
if event_data["has_clip"] or event_data["has_snapshot"]:
|
||||||
Event.create(
|
Event.create(
|
||||||
id=event_data["id"],
|
id=event_data["id"],
|
||||||
label=event_data["label"],
|
label=event_data["label"],
|
||||||
@ -89,12 +55,12 @@ class EventProcessor(threading.Thread):
|
|||||||
false_positive=event_data["false_positive"],
|
false_positive=event_data["false_positive"],
|
||||||
zones=list(event_data["entered_zones"]),
|
zones=list(event_data["entered_zones"]),
|
||||||
thumbnail=event_data["thumbnail"],
|
thumbnail=event_data["thumbnail"],
|
||||||
has_clip=has_clip,
|
has_clip=event_data["has_clip"],
|
||||||
has_snapshot=event_data["has_snapshot"],
|
has_snapshot=event_data["has_snapshot"],
|
||||||
)
|
)
|
||||||
|
|
||||||
del self.events_in_process[event_data["id"]]
|
del self.events_in_process[event_data["id"]]
|
||||||
self.event_processed_queue.put((event_data["id"], camera, has_clip))
|
self.event_processed_queue.put((event_data["id"], camera))
|
||||||
|
|
||||||
logger.info(f"Exiting event processor...")
|
logger.info(f"Exiting event processor...")
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ from typing import Callable, Dict
|
|||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from frigate.config import CameraConfig, FrigateConfig
|
from frigate.config import CameraConfig, SnapshotsConfig, RecordConfig, FrigateConfig
|
||||||
from frigate.const import CACHE_DIR, CLIPS_DIR, RECORD_DIR
|
from frigate.const import CACHE_DIR, CLIPS_DIR, RECORD_DIR
|
||||||
from frigate.edgetpu import load_labels
|
from frigate.edgetpu import load_labels
|
||||||
from frigate.util import (
|
from frigate.util import (
|
||||||
@ -73,6 +73,8 @@ class TrackedObject:
|
|||||||
self.current_zones = []
|
self.current_zones = []
|
||||||
self.entered_zones = set()
|
self.entered_zones = set()
|
||||||
self.false_positive = True
|
self.false_positive = True
|
||||||
|
self.has_clip = False
|
||||||
|
self.has_snapshot = False
|
||||||
self.top_score = self.computed_score = 0.0
|
self.top_score = self.computed_score = 0.0
|
||||||
self.thumbnail_data = None
|
self.thumbnail_data = None
|
||||||
self.last_updated = 0
|
self.last_updated = 0
|
||||||
@ -176,6 +178,8 @@ class TrackedObject:
|
|||||||
"region": self.obj_data["region"],
|
"region": self.obj_data["region"],
|
||||||
"current_zones": self.current_zones.copy(),
|
"current_zones": self.current_zones.copy(),
|
||||||
"entered_zones": list(self.entered_zones).copy(),
|
"entered_zones": list(self.entered_zones).copy(),
|
||||||
|
"has_clip": self.has_clip,
|
||||||
|
"has_snapshot": self.has_snapshot,
|
||||||
}
|
}
|
||||||
|
|
||||||
if include_thumbnail:
|
if include_thumbnail:
|
||||||
@ -611,9 +615,46 @@ class TrackedObjectProcessor(threading.Thread):
|
|||||||
obj.previous = after
|
obj.previous = after
|
||||||
|
|
||||||
def end(camera, obj: TrackedObject, current_frame_time):
|
def end(camera, obj: TrackedObject, current_frame_time):
|
||||||
snapshot_config = self.config.cameras[camera].snapshots
|
# populate has_snapshot
|
||||||
event_data = obj.to_dict(include_thumbnail=True)
|
obj.has_snapshot = self.should_save_snapshot(camera, obj)
|
||||||
event_data["has_snapshot"] = False
|
obj.has_clip = self.should_retain_recording(camera, obj)
|
||||||
|
|
||||||
|
# write the snapshot to disk
|
||||||
|
if obj.has_snapshot:
|
||||||
|
snapshot_config: SnapshotsConfig = self.config.cameras[camera].snapshots
|
||||||
|
jpg_bytes = obj.get_jpg_bytes(
|
||||||
|
timestamp=snapshot_config.timestamp,
|
||||||
|
bounding_box=snapshot_config.bounding_box,
|
||||||
|
crop=snapshot_config.crop,
|
||||||
|
height=snapshot_config.height,
|
||||||
|
quality=snapshot_config.quality,
|
||||||
|
)
|
||||||
|
if jpg_bytes is None:
|
||||||
|
logger.warning(f"Unable to save snapshot for {obj.obj_data['id']}.")
|
||||||
|
else:
|
||||||
|
with open(
|
||||||
|
os.path.join(CLIPS_DIR, f"{camera}-{obj.obj_data['id']}.jpg"),
|
||||||
|
"wb",
|
||||||
|
) as j:
|
||||||
|
j.write(jpg_bytes)
|
||||||
|
|
||||||
|
# write clean snapshot if enabled
|
||||||
|
if snapshot_config.clean_copy:
|
||||||
|
png_bytes = obj.get_clean_png()
|
||||||
|
if png_bytes is None:
|
||||||
|
logger.warning(
|
||||||
|
f"Unable to save clean snapshot for {obj.obj_data['id']}."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
with open(
|
||||||
|
os.path.join(
|
||||||
|
CLIPS_DIR,
|
||||||
|
f"{camera}-{obj.obj_data['id']}-clean.png",
|
||||||
|
),
|
||||||
|
"wb",
|
||||||
|
) as p:
|
||||||
|
p.write(png_bytes)
|
||||||
|
|
||||||
if not obj.false_positive:
|
if not obj.false_positive:
|
||||||
message = {
|
message = {
|
||||||
"before": obj.previous,
|
"before": obj.previous,
|
||||||
@ -623,46 +664,8 @@ class TrackedObjectProcessor(threading.Thread):
|
|||||||
self.client.publish(
|
self.client.publish(
|
||||||
f"{self.topic_prefix}/events", json.dumps(message), retain=False
|
f"{self.topic_prefix}/events", json.dumps(message), retain=False
|
||||||
)
|
)
|
||||||
# write snapshot to disk if enabled
|
|
||||||
if snapshot_config.enabled and self.should_save_snapshot(camera, obj):
|
|
||||||
jpg_bytes = obj.get_jpg_bytes(
|
|
||||||
timestamp=snapshot_config.timestamp,
|
|
||||||
bounding_box=snapshot_config.bounding_box,
|
|
||||||
crop=snapshot_config.crop,
|
|
||||||
height=snapshot_config.height,
|
|
||||||
quality=snapshot_config.quality,
|
|
||||||
)
|
|
||||||
if jpg_bytes is None:
|
|
||||||
logger.warning(
|
|
||||||
f"Unable to save snapshot for {obj.obj_data['id']}."
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
with open(
|
|
||||||
os.path.join(
|
|
||||||
CLIPS_DIR, f"{camera}-{obj.obj_data['id']}.jpg"
|
|
||||||
),
|
|
||||||
"wb",
|
|
||||||
) as j:
|
|
||||||
j.write(jpg_bytes)
|
|
||||||
event_data["has_snapshot"] = True
|
|
||||||
|
|
||||||
# write clean snapshot if enabled
|
self.event_queue.put(("end", camera, obj.to_dict(include_thumbnail=True)))
|
||||||
if snapshot_config.clean_copy:
|
|
||||||
png_bytes = obj.get_clean_png()
|
|
||||||
if png_bytes is None:
|
|
||||||
logger.warning(
|
|
||||||
f"Unable to save clean snapshot for {obj.obj_data['id']}."
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
with open(
|
|
||||||
os.path.join(
|
|
||||||
CLIPS_DIR,
|
|
||||||
f"{camera}-{obj.obj_data['id']}-clean.png",
|
|
||||||
),
|
|
||||||
"wb",
|
|
||||||
) as p:
|
|
||||||
p.write(png_bytes)
|
|
||||||
self.event_queue.put(("end", camera, event_data))
|
|
||||||
|
|
||||||
def snapshot(camera, obj: TrackedObject, current_frame_time):
|
def snapshot(camera, obj: TrackedObject, current_frame_time):
|
||||||
mqtt_config = self.config.cameras[camera].mqtt
|
mqtt_config = self.config.cameras[camera].mqtt
|
||||||
@ -711,8 +714,16 @@ class TrackedObjectProcessor(threading.Thread):
|
|||||||
self.zone_data = defaultdict(lambda: defaultdict(dict))
|
self.zone_data = defaultdict(lambda: defaultdict(dict))
|
||||||
|
|
||||||
def should_save_snapshot(self, camera, obj: TrackedObject):
|
def should_save_snapshot(self, camera, obj: TrackedObject):
|
||||||
|
if obj.false_positive:
|
||||||
|
return False
|
||||||
|
|
||||||
|
snapshot_config: SnapshotsConfig = self.config.cameras[camera].snapshots
|
||||||
|
|
||||||
|
if not snapshot_config.enabled:
|
||||||
|
return False
|
||||||
|
|
||||||
# if there are required zones and there is no overlap
|
# if there are required zones and there is no overlap
|
||||||
required_zones = self.config.cameras[camera].snapshots.required_zones
|
required_zones = snapshot_config.required_zones
|
||||||
if len(required_zones) > 0 and not obj.entered_zones & set(required_zones):
|
if len(required_zones) > 0 and not obj.entered_zones & set(required_zones):
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Not creating snapshot for {obj.obj_data['id']} because it did not enter required zones"
|
f"Not creating snapshot for {obj.obj_data['id']} because it did not enter required zones"
|
||||||
@ -721,6 +732,36 @@ class TrackedObjectProcessor(threading.Thread):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def should_retain_recording(self, camera, obj: TrackedObject):
|
||||||
|
if obj.false_positive:
|
||||||
|
return False
|
||||||
|
|
||||||
|
record_config: RecordConfig = self.config.cameras[camera].record
|
||||||
|
|
||||||
|
# Recording is disabled
|
||||||
|
if not record_config.enabled:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# If there are required zones and there is no overlap
|
||||||
|
required_zones = record_config.events.required_zones
|
||||||
|
if len(required_zones) > 0 and not set(obj.entered_zones) & set(required_zones):
|
||||||
|
logger.debug(
|
||||||
|
f"Not creating clip for {obj.obj_data['id']} because it did not enter required zones"
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
# If the required objects are not present
|
||||||
|
if (
|
||||||
|
record_config.events.objects is not None
|
||||||
|
and obj.obj_data["label"] not in record_config.events.objects
|
||||||
|
):
|
||||||
|
logger.debug(
|
||||||
|
f"Not creating clip for {obj.obj_data['id']} because it did not contain required objects"
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def should_mqtt_snapshot(self, camera, obj: TrackedObject):
|
def should_mqtt_snapshot(self, camera, obj: TrackedObject):
|
||||||
# if there are required zones and there is no overlap
|
# if there are required zones and there is no overlap
|
||||||
required_zones = self.config.cameras[camera].mqtt.required_zones
|
required_zones = self.config.cameras[camera].mqtt.required_zones
|
||||||
@ -815,17 +856,7 @@ class TrackedObjectProcessor(threading.Thread):
|
|||||||
|
|
||||||
# cleanup event finished queue
|
# cleanup event finished queue
|
||||||
while not self.event_processed_queue.empty():
|
while not self.event_processed_queue.empty():
|
||||||
event_id, camera, clip_created = self.event_processed_queue.get()
|
event_id, camera = self.event_processed_queue.get()
|
||||||
if clip_created:
|
|
||||||
obj = self.camera_states[camera].tracked_objects[event_id]
|
|
||||||
message = {
|
|
||||||
"before": obj.previous,
|
|
||||||
"after": obj.to_dict(),
|
|
||||||
"type": "clip_ready",
|
|
||||||
}
|
|
||||||
self.client.publish(
|
|
||||||
f"{self.topic_prefix}/events", json.dumps(message), retain=False
|
|
||||||
)
|
|
||||||
self.camera_states[camera].finished(event_id)
|
self.camera_states[camera].finished(event_id)
|
||||||
|
|
||||||
logger.info(f"Exiting object processor...")
|
logger.info(f"Exiting object processor...")
|
||||||
|
Loading…
Reference in New Issue
Block a user