remove clip_ready event type

this doesnt really mean anything more than "end" anymore. new has_clip property added
This commit is contained in:
Blake Blackshear 2021-09-15 07:16:52 -05:00
parent 86a5b46c68
commit 1b2134c49e
3 changed files with 96 additions and 95 deletions

View File

@ -36,7 +36,7 @@ Message published for each changed event. The first message is published when th
```json
{
"type": "update", // new, update, end or clip_ready
"type": "update", // new, update, end
"before": {
"id": "1607123955.475377-mxklsc",
"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],
"current_zones": ["driveway"],
"entered_zones": ["yard", "driveway"],
"thumbnail": null
"thumbnail": null,
"has_snapshot": false,
"has_clip": false
},
"after": {
"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],
"current_zones": ["yard", "driveway"],
"entered_zones": ["yard", "driveway"],
"thumbnail": null
"thumbnail": null,
"has_snapshot": false,
"has_clip": false
}
}
```

View File

@ -29,38 +29,6 @@ class EventProcessor(threading.Thread):
self.events_in_process = {}
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):
while not self.stop_event.is_set():
try:
@ -74,11 +42,9 @@ class EventProcessor(threading.Thread):
self.events_in_process[event_data["id"]] = event_data
if event_type == "end":
has_clip = self.should_create_clip(camera, event_data)
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(
id=event_data["id"],
label=event_data["label"],
@ -89,12 +55,12 @@ class EventProcessor(threading.Thread):
false_positive=event_data["false_positive"],
zones=list(event_data["entered_zones"]),
thumbnail=event_data["thumbnail"],
has_clip=has_clip,
has_clip=event_data["has_clip"],
has_snapshot=event_data["has_snapshot"],
)
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...")

View File

@ -16,7 +16,7 @@ from typing import Callable, Dict
import cv2
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.edgetpu import load_labels
from frigate.util import (
@ -73,6 +73,8 @@ class TrackedObject:
self.current_zones = []
self.entered_zones = set()
self.false_positive = True
self.has_clip = False
self.has_snapshot = False
self.top_score = self.computed_score = 0.0
self.thumbnail_data = None
self.last_updated = 0
@ -176,6 +178,8 @@ class TrackedObject:
"region": self.obj_data["region"],
"current_zones": self.current_zones.copy(),
"entered_zones": list(self.entered_zones).copy(),
"has_clip": self.has_clip,
"has_snapshot": self.has_snapshot,
}
if include_thumbnail:
@ -611,9 +615,46 @@ class TrackedObjectProcessor(threading.Thread):
obj.previous = after
def end(camera, obj: TrackedObject, current_frame_time):
snapshot_config = self.config.cameras[camera].snapshots
event_data = obj.to_dict(include_thumbnail=True)
event_data["has_snapshot"] = False
# populate has_snapshot
obj.has_snapshot = self.should_save_snapshot(camera, obj)
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:
message = {
"before": obj.previous,
@ -623,46 +664,8 @@ class TrackedObjectProcessor(threading.Thread):
self.client.publish(
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
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))
self.event_queue.put(("end", camera, obj.to_dict(include_thumbnail=True)))
def snapshot(camera, obj: TrackedObject, current_frame_time):
mqtt_config = self.config.cameras[camera].mqtt
@ -711,8 +714,16 @@ class TrackedObjectProcessor(threading.Thread):
self.zone_data = defaultdict(lambda: defaultdict(dict))
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
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):
logger.debug(
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
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):
# if there are required zones and there is no overlap
required_zones = self.config.cameras[camera].mqtt.required_zones
@ -815,17 +856,7 @@ class TrackedObjectProcessor(threading.Thread):
# cleanup event finished queue
while not self.event_processed_queue.empty():
event_id, camera, clip_created = 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
)
event_id, camera = self.event_processed_queue.get()
self.camera_states[camera].finished(event_id)
logger.info(f"Exiting object processor...")