mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-12-06 13:34:13 +03:00
Store and show boxes for attributes in timeline (#20513)
* Store and show boxes for attributes in timeline * Simplify
This commit is contained in:
parent
4e99ee0c33
commit
2e7a2fd780
@ -142,6 +142,11 @@ class TimelineProcessor(threading.Thread):
|
|||||||
timeline_entry[Timeline.data]["attribute"] = list(
|
timeline_entry[Timeline.data]["attribute"] = list(
|
||||||
event_data["attributes"].keys()
|
event_data["attributes"].keys()
|
||||||
)[0]
|
)[0]
|
||||||
|
timeline_entry[Timeline.data]["attribute_box"] = to_relative_box(
|
||||||
|
camera_config.detect.width,
|
||||||
|
camera_config.detect.height,
|
||||||
|
event_data["current_attributes"][0]["box"],
|
||||||
|
)
|
||||||
save = True
|
save = True
|
||||||
elif event_type == EventStateEnum.end:
|
elif event_type == EventStateEnum.end:
|
||||||
timeline_entry[Timeline.class_type] = "gone"
|
timeline_entry[Timeline.class_type] = "gone"
|
||||||
|
|||||||
@ -1,101 +0,0 @@
|
|||||||
import { ObjectLifecycleSequence } from "@/types/timeline";
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
type TimelineEventOverlayProps = {
|
|
||||||
timeline: ObjectLifecycleSequence;
|
|
||||||
cameraConfig: {
|
|
||||||
detect: {
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function TimelineEventOverlay({
|
|
||||||
timeline,
|
|
||||||
cameraConfig,
|
|
||||||
}: TimelineEventOverlayProps) {
|
|
||||||
const [isHovering, setIsHovering] = useState<boolean>(false);
|
|
||||||
const getHoverStyle = () => {
|
|
||||||
if (!timeline.data.box) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (boxLeftEdge < 15) {
|
|
||||||
// show object stats on right side
|
|
||||||
return {
|
|
||||||
left: `${boxLeftEdge + timeline.data.box[2] * 100 + 1}%`,
|
|
||||||
top: `${boxTopEdge}%`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
right: `${boxRightEdge + timeline.data.box[2] * 100 + 1}%`,
|
|
||||||
top: `${boxTopEdge}%`,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const getObjectArea = () => {
|
|
||||||
if (!timeline.data.box) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const width = timeline.data.box[2] * cameraConfig.detect.width;
|
|
||||||
const height = timeline.data.box[3] * cameraConfig.detect.height;
|
|
||||||
return Math.round(width * height);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getObjectRatio = () => {
|
|
||||||
if (!timeline.data.box) {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const width = timeline.data.box[2] * cameraConfig.detect.width;
|
|
||||||
const height = timeline.data.box[3] * cameraConfig.detect.height;
|
|
||||||
return Math.round(100 * (width / height)) / 100;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!timeline.data.box) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const boxLeftEdge = Math.round(timeline.data.box[0] * 100);
|
|
||||||
const boxTopEdge = Math.round(timeline.data.box[1] * 100);
|
|
||||||
const boxRightEdge = Math.round(
|
|
||||||
(1 - timeline.data.box[2] - timeline.data.box[0]) * 100,
|
|
||||||
);
|
|
||||||
const boxBottomEdge = Math.round(
|
|
||||||
(1 - timeline.data.box[3] - timeline.data.box[1]) * 100,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div
|
|
||||||
className="absolute border-4 border-red-600"
|
|
||||||
onMouseEnter={() => setIsHovering(true)}
|
|
||||||
onMouseLeave={() => setIsHovering(false)}
|
|
||||||
onTouchStart={() => setIsHovering(true)}
|
|
||||||
onTouchEnd={() => setIsHovering(false)}
|
|
||||||
style={{
|
|
||||||
left: `${boxLeftEdge}%`,
|
|
||||||
top: `${boxTopEdge}%`,
|
|
||||||
right: `${boxRightEdge}%`,
|
|
||||||
bottom: `${boxBottomEdge}%`,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{timeline.class_type == "entered_zone" ? (
|
|
||||||
<div className="absolute bottom-0 left-[50%] h-2 w-2 -translate-x-1/2 translate-y-3/4 bg-yellow-500" />
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
{isHovering && (
|
|
||||||
<div
|
|
||||||
className="absolute block bg-white p-4 text-lg text-black dark:bg-slate-800 dark:text-white"
|
|
||||||
style={getHoverStyle()}
|
|
||||||
>
|
|
||||||
<div>{`Area: ${getObjectArea()} px`}</div>
|
|
||||||
<div>{`Ratio: ${getObjectRatio()}`}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -151,6 +151,8 @@ export default function ObjectLifecycle({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const [boxStyle, setBoxStyle] = useState<React.CSSProperties | null>(null);
|
const [boxStyle, setBoxStyle] = useState<React.CSSProperties | null>(null);
|
||||||
|
const [attributeBoxStyle, setAttributeBoxStyle] =
|
||||||
|
useState<React.CSSProperties | null>(null);
|
||||||
|
|
||||||
const configAnnotationOffset = useMemo(() => {
|
const configAnnotationOffset = useMemo(() => {
|
||||||
if (!config) {
|
if (!config) {
|
||||||
@ -218,7 +220,7 @@ export default function ObjectLifecycle({
|
|||||||
const [timeIndex, setTimeIndex] = useState(0);
|
const [timeIndex, setTimeIndex] = useState(0);
|
||||||
|
|
||||||
const handleSetBox = useCallback(
|
const handleSetBox = useCallback(
|
||||||
(box: number[]) => {
|
(box: number[], attrBox: number[] | undefined) => {
|
||||||
if (imgRef.current && Array.isArray(box) && box.length === 4) {
|
if (imgRef.current && Array.isArray(box) && box.length === 4) {
|
||||||
const imgElement = imgRef.current;
|
const imgElement = imgRef.current;
|
||||||
const imgRect = imgElement.getBoundingClientRect();
|
const imgRect = imgElement.getBoundingClientRect();
|
||||||
@ -231,6 +233,19 @@ export default function ObjectLifecycle({
|
|||||||
borderColor: `rgb(${getObjectColor(event.label)?.join(",")})`,
|
borderColor: `rgb(${getObjectColor(event.label)?.join(",")})`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (attrBox) {
|
||||||
|
const attrStyle = {
|
||||||
|
left: `${attrBox[0] * imgRect.width}px`,
|
||||||
|
top: `${attrBox[1] * imgRect.height}px`,
|
||||||
|
width: `${attrBox[2] * imgRect.width}px`,
|
||||||
|
height: `${attrBox[3] * imgRect.height}px`,
|
||||||
|
borderColor: `rgb(${getObjectColor(event.label)?.join(",")})`,
|
||||||
|
};
|
||||||
|
setAttributeBoxStyle(attrStyle);
|
||||||
|
} else {
|
||||||
|
setAttributeBoxStyle(null);
|
||||||
|
}
|
||||||
|
|
||||||
setBoxStyle(style);
|
setBoxStyle(style);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -292,7 +307,10 @@ export default function ObjectLifecycle({
|
|||||||
} else {
|
} else {
|
||||||
// lifecycle point
|
// lifecycle point
|
||||||
setTimeIndex(eventSequence?.[current].timestamp);
|
setTimeIndex(eventSequence?.[current].timestamp);
|
||||||
handleSetBox(eventSequence?.[current].data.box ?? []);
|
handleSetBox(
|
||||||
|
eventSequence?.[current].data.box ?? [],
|
||||||
|
eventSequence?.[current].data?.attribute_box,
|
||||||
|
);
|
||||||
setLifecycleZones(eventSequence?.[current].data.zones);
|
setLifecycleZones(eventSequence?.[current].data.zones);
|
||||||
}
|
}
|
||||||
setSelectedZone("");
|
setSelectedZone("");
|
||||||
@ -448,6 +466,9 @@ export default function ObjectLifecycle({
|
|||||||
<div className="absolute bottom-[-3px] left-1/2 h-[5px] w-[5px] -translate-x-1/2 transform bg-yellow-500" />
|
<div className="absolute bottom-[-3px] left-1/2 h-[5px] w-[5px] -translate-x-1/2 transform bg-yellow-500" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{attributeBoxStyle && (
|
||||||
|
<div className="absolute border-2" style={attributeBoxStyle} />
|
||||||
|
)}
|
||||||
{imgRef.current?.width &&
|
{imgRef.current?.width &&
|
||||||
imgRef.current?.height &&
|
imgRef.current?.height &&
|
||||||
pathPoints &&
|
pathPoints &&
|
||||||
|
|||||||
@ -20,6 +20,7 @@ export type ObjectLifecycleSequence = {
|
|||||||
box?: [number, number, number, number];
|
box?: [number, number, number, number];
|
||||||
region: [number, number, number, number];
|
region: [number, number, number, number];
|
||||||
attribute: string;
|
attribute: string;
|
||||||
|
attribute_box?: [number, number, number, number];
|
||||||
zones: string[];
|
zones: string[];
|
||||||
};
|
};
|
||||||
class_type: LifecycleClassType;
|
class_type: LifecycleClassType;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user