Snap points to edges and create object mask from bounding box (#16488)

This commit is contained in:
Josh Hawkins
2025-02-11 09:08:28 -07:00
committed by GitHub
parent b594f198a9
commit a3ede3cf8a
10 changed files with 329 additions and 65 deletions
+54 -3
View File
@@ -37,6 +37,7 @@ import PolygonItem from "@/components/settings/PolygonItem";
import { Link } from "react-router-dom";
import { isDesktop } from "react-device-detect";
import { StatusBarMessagesContext } from "@/context/statusbar-provider";
import { useSearchEffect } from "@/hooks/use-overlay-state";
type MasksAndZoneViewProps = {
selectedCamera: string;
@@ -62,6 +63,7 @@ export default function MasksAndZonesView({
const containerRef = useRef<HTMLDivElement | null>(null);
const [editPane, setEditPane] = useState<PolygonType | undefined>(undefined);
const [activeLine, setActiveLine] = useState<number | undefined>();
const [snapPoints, setSnapPoints] = useState(false);
const { addMessage } = useContext(StatusBarMessagesContext)!;
@@ -142,7 +144,7 @@ export default function MasksAndZonesView({
}
}, [scaledHeight, aspectRatio]);
const handleNewPolygon = (type: PolygonType) => {
const handleNewPolygon = (type: PolygonType, coordinates?: number[][]) => {
if (!cameraConfig) {
return;
}
@@ -161,9 +163,9 @@ export default function MasksAndZonesView({
setEditingPolygons([
...(allPolygons || []),
{
points: [],
points: coordinates ?? [],
distances: [],
isFinished: false,
isFinished: coordinates ? true : false,
type,
typeIndex: 9999,
name: "",
@@ -373,6 +375,48 @@ export default function MasksAndZonesView({
}
}, [selectedCamera]);
useSearchEffect("object_mask", (coordinates: string) => {
if (!scaledWidth || !scaledHeight || isLoading) {
return false;
}
// convert box points string to points array
const points = coordinates.split(",").map((p) => parseFloat(p));
const [x1, y1, w, h] = points;
// bottom center
const centerX = x1 + w / 2;
const bottomY = y1 + h;
const centerXAbs = centerX * scaledWidth;
const bottomYAbs = bottomY * scaledHeight;
// padding and clamp
const minPadding = 0.1 * w * scaledWidth;
const maxPadding = 0.3 * w * scaledWidth;
const padding = Math.min(
Math.max(minPadding, 0.15 * w * scaledWidth),
maxPadding,
);
const top = Math.max(0, bottomYAbs - padding);
const bottom = Math.min(scaledHeight, bottomYAbs + padding);
const left = Math.max(0, centerXAbs - padding);
const right = Math.min(scaledWidth, centerXAbs + padding);
const paddedBox = [
[left, top],
[right, top],
[right, bottom],
[left, bottom],
];
setEditPane("object_mask");
setActivePolygonIndex(undefined);
handleNewPolygon("object_mask", paddedBox);
return true;
});
useEffect(() => {
document.title = "Mask and Zone Editor - Frigate";
}, []);
@@ -399,6 +443,8 @@ export default function MasksAndZonesView({
onCancel={handleCancel}
onSave={handleSave}
setActiveLine={setActiveLine}
snapPoints={snapPoints}
setSnapPoints={setSnapPoints}
/>
)}
{editPane == "motion_mask" && (
@@ -412,6 +458,8 @@ export default function MasksAndZonesView({
setIsLoading={setIsLoading}
onCancel={handleCancel}
onSave={handleSave}
snapPoints={snapPoints}
setSnapPoints={setSnapPoints}
/>
)}
{editPane == "object_mask" && (
@@ -425,6 +473,8 @@ export default function MasksAndZonesView({
setIsLoading={setIsLoading}
onCancel={handleCancel}
onSave={handleSave}
snapPoints={snapPoints}
setSnapPoints={setSnapPoints}
/>
)}
{editPane === undefined && (
@@ -662,6 +712,7 @@ export default function MasksAndZonesView({
hoveredPolygonIndex={hoveredPolygonIndex}
selectedZoneMask={selectedZoneMask}
activeLine={activeLine}
snapPoints={true}
/>
) : (
<Skeleton className="size-full" />