mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
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:
parent
89f843cf95
commit
4d522be7fb
@ -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),
|
||||||
[],
|
[],
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
|
@ -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
|
||||||
|
@ -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) {
|
||||||
|
@ -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>
|
||||||
|
@ -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[];
|
||||||
};
|
};
|
||||||
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user