Improve review book keeping (#10735)

* Improve review book keeping

* Cleanup

* Cleanup for new labels

* Final cleanup

* Fix sub label checking
This commit is contained in:
Nicolas Mowen 2024-03-30 12:45:42 -06:00 committed by GitHub
parent 89f843cf95
commit 4d522be7fb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 47 additions and 57 deletions

View File

@ -17,7 +17,7 @@ from frigate.comms.config_updater import ConfigSubscriber
from frigate.comms.detections_updater import DetectionSubscriber, DetectionTypeEnum from frigate.comms.detections_updater import DetectionSubscriber, DetectionTypeEnum
from frigate.comms.inter_process import InterProcessRequestor from frigate.comms.inter_process import InterProcessRequestor
from frigate.config import CameraConfig, FrigateConfig from frigate.config import CameraConfig, FrigateConfig
from frigate.const import CLIPS_DIR, UPSERT_REVIEW_SEGMENT from frigate.const import ALL_ATTRIBUTE_LABELS, CLIPS_DIR, UPSERT_REVIEW_SEGMENT
from frigate.models import ReviewSegment from frigate.models import ReviewSegment
from frigate.object_processing import TrackedObject from frigate.object_processing import TrackedObject
from frigate.util.image import SharedMemoryFrameManager, calculate_16_9_crop from frigate.util.image import SharedMemoryFrameManager, calculate_16_9_crop
@ -45,9 +45,7 @@ class PendingReviewSegment:
camera: str, camera: str,
frame_time: float, frame_time: float,
severity: SeverityEnum, severity: SeverityEnum,
detections: set[str] = set(), detections: dict[str, str],
objects: set[str] = set(),
sub_labels: set[str] = set(),
zones: set[str] = set(), zones: set[str] = set(),
audio: set[str] = set(), audio: set[str] = set(),
motion: list[int] = [], motion: list[int] = [],
@ -58,8 +56,6 @@ class PendingReviewSegment:
self.start_time = frame_time self.start_time = frame_time
self.severity = severity self.severity = severity
self.detections = detections self.detections = detections
self.objects = objects
self.sub_labels = sub_labels
self.zones = zones self.zones = zones
self.audio = audio self.audio = audio
self.sig_motion_areas = motion self.sig_motion_areas = motion
@ -114,9 +110,8 @@ class PendingReviewSegment:
ReviewSegment.severity: self.severity.value, ReviewSegment.severity: self.severity.value,
ReviewSegment.thumb_path: path, ReviewSegment.thumb_path: path,
ReviewSegment.data: { ReviewSegment.data: {
"detections": list(self.detections), "detections": list(set(self.detections.keys())),
"objects": list(self.objects), "objects": list(set(self.detections.values())),
"sub_labels": list(self.sub_labels),
"zones": list(self.zones), "zones": list(self.zones),
"audio": list(self.audio), "audio": list(self.audio),
"significant_motion_areas": self.sig_motion_areas, "significant_motion_areas": self.sig_motion_areas,
@ -180,11 +175,12 @@ class ReviewSegmentMaintainer(threading.Thread):
self.frame_manager.close(frame_id) self.frame_manager.close(frame_id)
for object in active_objects: for object in active_objects:
segment.detections.add(object["id"]) if not object["sub_label"]:
segment.objects.add(object["label"]) segment.detections[object["id"]] = object["label"]
elif object["sub_label"][0] in ALL_ATTRIBUTE_LABELS:
if object["sub_label"]: segment.detections[object["id"]] = object["sub_label"][0]
segment.sub_labels.add(object["sub_label"][0]) else:
segment.detections[object["id"]] = f'{object["label"]}-verified'
# if object is alert label and has qualified for recording # if object is alert label and has qualified for recording
# mark this review as alert # mark this review as alert
@ -224,9 +220,7 @@ class ReviewSegmentMaintainer(threading.Thread):
if len(active_objects) > 0: if len(active_objects) > 0:
has_sig_object = False has_sig_object = False
detections: set = set() detections: dict[str, str] = {}
objects: set = set()
sub_labels: set = set()
zones: set = set() zones: set = set()
for object in active_objects: for object in active_objects:
@ -237,11 +231,12 @@ class ReviewSegmentMaintainer(threading.Thread):
): ):
has_sig_object = True has_sig_object = True
detections.add(object["id"]) if not object["sub_label"]:
objects.add(object["label"]) detections[object["id"]] = object["label"]
elif object["sub_label"][0] in ALL_ATTRIBUTE_LABELS:
if object["sub_label"]: detections[object["id"]] = object["sub_label"][0]
sub_labels.add(object["sub_label"][0]) else:
detections[object["id"]] = f'{object["label"]}-verified'
zones.update(object["current_zones"]) zones.update(object["current_zones"])
@ -250,8 +245,6 @@ class ReviewSegmentMaintainer(threading.Thread):
frame_time, frame_time,
SeverityEnum.alert if has_sig_object else SeverityEnum.detection, SeverityEnum.alert if has_sig_object else SeverityEnum.detection,
detections, detections,
objects=objects,
sub_labels=sub_labels,
audio=set(), audio=set(),
zones=zones, zones=zones,
motion=[], motion=[],
@ -268,9 +261,8 @@ class ReviewSegmentMaintainer(threading.Thread):
camera, camera,
frame_time, frame_time,
SeverityEnum.signification_motion, SeverityEnum.signification_motion,
detections=set(), detections={},
objects=set(), audio=set(),
sub_labels=set(),
motion=motion, motion=motion,
zones=set(), zones=set(),
) )
@ -340,9 +332,7 @@ class ReviewSegmentMaintainer(threading.Thread):
camera, camera,
frame_time, frame_time,
SeverityEnum.detection, SeverityEnum.detection,
set(), {},
set(),
set(),
set(), set(),
set(audio_detections), set(audio_detections),
[], [],

View File

@ -83,7 +83,7 @@ export function AnimatedEventCard({ event }: AnimatedEventCardProps) {
</div> </div>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
{`${[...event.data.objects, ...event.data.audio, ...(event.data.sub_labels || [])].join(", ")} detected`} {`${[...event.data.objects, ...event.data.audio].join(", ").replaceAll("-verified", "")} detected`}
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
); );

View File

@ -2,7 +2,7 @@ import { baseUrl } from "@/api/baseUrl";
import { useFormattedTimestamp } from "@/hooks/use-date-utils"; import { useFormattedTimestamp } from "@/hooks/use-date-utils";
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
import { ReviewSegment } from "@/types/review"; import { ReviewSegment } from "@/types/review";
import { getIconForLabel, getIconForSubLabel } from "@/utils/iconUtil"; import { getIconForLabel } from "@/utils/iconUtil";
import { isSafari } from "react-device-detect"; import { isSafari } from "react-device-detect";
import useSWR from "swr"; import useSWR from "swr";
import TimeAgo from "../dynamic/TimeAgo"; import TimeAgo from "../dynamic/TimeAgo";
@ -57,9 +57,6 @@ export default function ReviewCard({
{event.data.audio.map((audio) => { {event.data.audio.map((audio) => {
return getIconForLabel(audio, "size-3 text-white"); return getIconForLabel(audio, "size-3 text-white");
})} })}
{event.data.sub_labels?.map((sub) => {
return getIconForSubLabel(sub, "size-3 text-white");
})}
<div className="font-extra-light text-xs">{formattedDate}</div> <div className="font-extra-light text-xs">{formattedDate}</div>
</div> </div>
<TimeAgo <TimeAgo

View File

@ -30,7 +30,6 @@ import MobileReviewSettingsDrawer, {
DrawerFeatures, DrawerFeatures,
} from "../overlay/MobileReviewSettingsDrawer"; } from "../overlay/MobileReviewSettingsDrawer";
const ATTRIBUTES = ["amazon", "face", "fedex", "license_plate", "ups"];
const REVIEW_FILTERS = [ const REVIEW_FILTERS = [
"cameras", "cameras",
"reviewed", "reviewed",
@ -77,9 +76,7 @@ export default function ReviewFilterGroup({
cameras.forEach((camera) => { cameras.forEach((camera) => {
const cameraConfig = config.cameras[camera]; const cameraConfig = config.cameras[camera];
cameraConfig.objects.track.forEach((label) => { cameraConfig.objects.track.forEach((label) => {
if (!ATTRIBUTES.includes(label)) { labels.add(label);
labels.add(label);
}
}); });
if (cameraConfig.audio.enabled_in_config) { if (cameraConfig.audio.enabled_in_config) {

View File

@ -9,7 +9,7 @@ import { useApiHost } from "@/api";
import { isCurrentHour } from "@/utils/dateUtil"; import { isCurrentHour } from "@/utils/dateUtil";
import { ReviewSegment } from "@/types/review"; import { ReviewSegment } from "@/types/review";
import { Slider } from "../ui/slider-no-thumb"; import { Slider } from "../ui/slider-no-thumb";
import { getIconForLabel, getIconForSubLabel } from "@/utils/iconUtil"; import { getIconForLabel } from "@/utils/iconUtil";
import TimeAgo from "../dynamic/TimeAgo"; import TimeAgo from "../dynamic/TimeAgo";
import useSWR from "swr"; import useSWR from "swr";
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
@ -227,9 +227,6 @@ export default function PreviewThumbnailPlayer({
{review.data.audio.map((audio) => { {review.data.audio.map((audio) => {
return getIconForLabel(audio, "size-3 text-white"); return getIconForLabel(audio, "size-3 text-white");
})} })}
{review.data.sub_labels?.map((sub) => {
return getIconForSubLabel(sub, "size-3 text-white");
})}
</Chip> </Chip>
</> </>
)} )}
@ -237,13 +234,10 @@ export default function PreviewThumbnailPlayer({
</TooltipTrigger> </TooltipTrigger>
</div> </div>
<TooltipContent className="capitalize"> <TooltipContent className="capitalize">
{[ {[...(review.data.objects || []), ...(review.data.audio || [])]
...(review.data.objects || []),
...(review.data.audio || []),
...(review.data.sub_labels || []),
]
.filter((item) => item !== undefined) .filter((item) => item !== undefined)
.join(", ")} .join(", ")
.replaceAll("-verified", "")}
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</div> </div>

View File

@ -15,7 +15,6 @@ export type ReviewData = {
audio: string[]; audio: string[];
detections: string[]; detections: string[];
objects: string[]; objects: string[];
sub_labels?: string[];
significant_motion_areas: number[]; significant_motion_areas: number[];
zones: string[]; zones: string[];
}; };

View File

@ -3,6 +3,7 @@ import {
FaAmazon, FaAmazon,
FaCarSide, FaCarSide,
FaCat, FaCat,
FaCheckCircle,
FaCircle, FaCircle,
FaDog, FaDog,
FaFedex, FaFedex,
@ -34,6 +35,10 @@ export function getIconForGroup(icon: string, className: string = "size-4") {
} }
export function getIconForLabel(label: string, className?: string) { export function getIconForLabel(label: string, className?: string) {
if (label.endsWith("-verified")) {
return getVerifiedIcon(label, className);
}
switch (label) { switch (label) {
case "car": case "car":
return <FaCarSide key={label} className={className} />; return <FaCarSide key={label} className={className} />;
@ -48,24 +53,32 @@ export function getIconForLabel(label: string, className?: string) {
return <LuBox key={label} className={className} />; return <LuBox key={label} className={className} />;
case "person": case "person":
return <BsPersonWalking key={label} className={className} />; return <BsPersonWalking key={label} className={className} />;
// audio
case "crying": case "crying":
case "laughter": case "laughter":
case "scream": case "scream":
case "speech": case "speech":
case "yell": case "yell":
return <MdRecordVoiceOver key={label} className={className} />; return <MdRecordVoiceOver key={label} className={className} />;
default: // sub labels
return <LuLassoSelect key={label} className={className} />;
}
}
export function getIconForSubLabel(label: string, className?: string) {
switch (label) {
case "amazon": case "amazon":
return <FaAmazon key={label} className={className} />; return <FaAmazon key={label} className={className} />;
case "fedex": case "fedex":
return <FaFedex key={label} className={className} />; return <FaFedex key={label} className={className} />;
case "ups": case "ups":
return <FaUps key={label} className={className} />; return <FaUps key={label} className={className} />;
default:
return <LuLassoSelect key={label} className={className} />;
} }
} }
function getVerifiedIcon(label: string, className?: string) {
const simpleLabel = label.substring(0, label.lastIndexOf("-"));
return (
<div className="flex items-center">
{getIconForLabel(simpleLabel, className)}
<FaCheckCircle className="absolute size-2 translate-x-[80%] translate-y-3/4" />
</div>
);
}