mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-09-26 19:41:29 +08:00
False positives (#6217)
* add false positive submission * switch timeline events to x,y,w,h * update docs * fix type checks * convert to upsert * fix config test
This commit is contained in:
@@ -3,6 +3,7 @@ import { route } from 'preact-router';
|
||||
import ActivityIndicator from '../components/ActivityIndicator';
|
||||
import Heading from '../components/Heading';
|
||||
import { Tabs, TextTab } from '../components/Tabs';
|
||||
import Link from '../components/Link';
|
||||
import { useApiHost } from '../api';
|
||||
import useSWR from 'swr';
|
||||
import useSWRInfinite from 'swr/infinite';
|
||||
@@ -57,7 +58,12 @@ export default function Events({ path, ...props }) {
|
||||
showDownloadMenu: false,
|
||||
showDatePicker: false,
|
||||
showCalendar: false,
|
||||
showPlusConfig: false,
|
||||
showPlusSubmit: false,
|
||||
});
|
||||
const [plusSubmitEvent, setPlusSubmitEvent] = useState({
|
||||
id: null,
|
||||
label: null,
|
||||
validBox: null,
|
||||
});
|
||||
const [uploading, setUploading] = useState([]);
|
||||
const [viewEvent, setViewEvent] = useState();
|
||||
@@ -65,6 +71,8 @@ export default function Events({ path, ...props }) {
|
||||
const [eventDetailType, setEventDetailType] = useState('clip');
|
||||
const [downloadEvent, setDownloadEvent] = useState({
|
||||
id: null,
|
||||
label: null,
|
||||
box: null,
|
||||
has_clip: false,
|
||||
has_snapshot: false,
|
||||
plus_id: undefined,
|
||||
@@ -198,6 +206,8 @@ export default function Events({ path, ...props }) {
|
||||
e.stopPropagation();
|
||||
setDownloadEvent((_prev) => ({
|
||||
id: event.id,
|
||||
box: event.box,
|
||||
label: event.label,
|
||||
has_clip: event.has_clip,
|
||||
has_snapshot: event.has_snapshot,
|
||||
plus_id: event.plus_id,
|
||||
@@ -207,6 +217,16 @@ export default function Events({ path, ...props }) {
|
||||
setState({ ...state, showDownloadMenu: true });
|
||||
};
|
||||
|
||||
const showSubmitToPlus = (event_id, label, box, e) => {
|
||||
if (e) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
// if any of the box coordinates are > 1, then the box data is from an older version
|
||||
// and not valid to submit to plus with the snapshot image
|
||||
setPlusSubmitEvent({ id: event_id, label, validBox: !box.some((d) => d > 1) });
|
||||
setState({ ...state, showDownloadMenu: false, showPlusSubmit: true });
|
||||
};
|
||||
|
||||
const handleSelectDateRange = useCallback(
|
||||
(dates) => {
|
||||
setSearchParams({ ...searchParams, before: dates.before, after: dates.after });
|
||||
@@ -251,23 +271,16 @@ export default function Events({ path, ...props }) {
|
||||
[size, setSize, isValidating, isDone]
|
||||
);
|
||||
|
||||
const onSendToPlus = async (id, e) => {
|
||||
if (e) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
const onSendToPlus = async (id, false_positive, validBox) => {
|
||||
if (uploading.includes(id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!config.plus.enabled) {
|
||||
setState({ ...state, showDownloadMenu: false, showPlusConfig: true });
|
||||
return;
|
||||
}
|
||||
|
||||
setUploading((prev) => [...prev, id]);
|
||||
|
||||
const response = await axios.post(`events/${id}/plus`);
|
||||
const response = false_positive
|
||||
? await axios.put(`events/${id}/false_positive`)
|
||||
: await axios.post(`events/${id}/plus`, validBox ? { include_annotation: 1 } : {});
|
||||
|
||||
if (response.status === 200) {
|
||||
mutate(
|
||||
@@ -289,6 +302,8 @@ export default function Events({ path, ...props }) {
|
||||
if (state.showDownloadMenu && downloadEvent.id === id) {
|
||||
setState({ ...state, showDownloadMenu: false });
|
||||
}
|
||||
|
||||
setState({ ...state, showPlusSubmit: false });
|
||||
};
|
||||
|
||||
const handleEventDetailTabChange = (index) => {
|
||||
@@ -375,12 +390,12 @@ export default function Events({ path, ...props }) {
|
||||
download
|
||||
/>
|
||||
)}
|
||||
{(downloadEvent.end_time && downloadEvent.has_snapshot && !downloadEvent.plus_id) && (
|
||||
{downloadEvent.end_time && downloadEvent.has_snapshot && !downloadEvent.plus_id && (
|
||||
<MenuItem
|
||||
icon={UploadPlus}
|
||||
label={uploading.includes(downloadEvent.id) ? 'Uploading...' : 'Send to Frigate+'}
|
||||
value="plus"
|
||||
onSelect={() => onSendToPlus(downloadEvent.id)}
|
||||
onSelect={() => showSubmitToPlus(downloadEvent.id, downloadEvent.label, downloadEvent.box)}
|
||||
/>
|
||||
)}
|
||||
{downloadEvent.plus_id && (
|
||||
@@ -435,25 +450,96 @@ export default function Events({ path, ...props }) {
|
||||
/>
|
||||
</Menu>
|
||||
)}
|
||||
{state.showPlusConfig && (
|
||||
{state.showPlusSubmit && (
|
||||
<Dialog>
|
||||
<div className="p-4">
|
||||
<Heading size="lg">Setup a Frigate+ Account</Heading>
|
||||
<p className="mb-2">In order to submit images to Frigate+, you first need to setup an account.</p>
|
||||
<a
|
||||
className="text-blue-500 hover:underline"
|
||||
href="https://plus.frigate.video"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
https://plus.frigate.video
|
||||
</a>
|
||||
</div>
|
||||
<div className="p-2 flex justify-start flex-row-reverse space-x-2">
|
||||
<Button className="ml-2" onClick={() => setState({ ...state, showPlusConfig: false })} type="text">
|
||||
Close
|
||||
</Button>
|
||||
</div>
|
||||
{config.plus.enabled ? (
|
||||
<>
|
||||
<div className="p-4">
|
||||
<Heading size="lg">Submit to Frigate+</Heading>
|
||||
|
||||
<img
|
||||
className="flex-grow-0"
|
||||
src={`${apiHost}/api/events/${plusSubmitEvent.id}/snapshot.jpg`}
|
||||
alt={`${plusSubmitEvent.label}`}
|
||||
/>
|
||||
|
||||
{plusSubmitEvent.validBox ? (
|
||||
<p className="mb-2">
|
||||
Objects in locations you want to avoid are not false positives. Submitting them as false positives
|
||||
will confuse the model.
|
||||
</p>
|
||||
) : (
|
||||
<p className="mb-2">
|
||||
Events prior to version 0.13 can only be submitted to Frigate+ without annotations.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{plusSubmitEvent.validBox ? (
|
||||
<div className="p-2 flex justify-start flex-row-reverse space-x-2">
|
||||
<Button className="ml-2" onClick={() => setState({ ...state, showPlusSubmit: false })} type="text">
|
||||
{uploading.includes(plusSubmitEvent.id) ? 'Close' : 'Cancel'}
|
||||
</Button>
|
||||
<Button
|
||||
className="ml-2"
|
||||
color="red"
|
||||
onClick={() => onSendToPlus(plusSubmitEvent.id, true, plusSubmitEvent.validBox)}
|
||||
disabled={uploading.includes(plusSubmitEvent.id)}
|
||||
type="text"
|
||||
>
|
||||
This is not a {plusSubmitEvent.label}
|
||||
</Button>
|
||||
<Button
|
||||
className="ml-2"
|
||||
color="green"
|
||||
onClick={() => onSendToPlus(plusSubmitEvent.id, false, plusSubmitEvent.validBox)}
|
||||
disabled={uploading.includes(plusSubmitEvent.id)}
|
||||
type="text"
|
||||
>
|
||||
This is a {plusSubmitEvent.label}
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="p-2 flex justify-start flex-row-reverse space-x-2">
|
||||
<Button
|
||||
className="ml-2"
|
||||
onClick={() => setState({ ...state, showPlusSubmit: false })}
|
||||
disabled={uploading.includes(plusSubmitEvent.id)}
|
||||
type="text"
|
||||
>
|
||||
{uploading.includes(plusSubmitEvent.id) ? 'Close' : 'Cancel'}
|
||||
</Button>
|
||||
<Button
|
||||
className="ml-2"
|
||||
onClick={() => onSendToPlus(plusSubmitEvent.id, false, plusSubmitEvent.validBox)}
|
||||
disabled={uploading.includes(plusSubmitEvent.id)}
|
||||
type="text"
|
||||
>
|
||||
Submit to Frigate+
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="p-4">
|
||||
<Heading size="lg">Setup a Frigate+ Account</Heading>
|
||||
<p className="mb-2">In order to submit images to Frigate+, you first need to setup an account.</p>
|
||||
<a
|
||||
className="text-blue-500 hover:underline"
|
||||
href="https://plus.frigate.video"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
https://plus.frigate.video
|
||||
</a>
|
||||
</div>
|
||||
<div className="p-2 flex justify-start flex-row-reverse space-x-2">
|
||||
<Button className="ml-2" onClick={() => setState({ ...state, showPlusSubmit: false })} type="text">
|
||||
Close
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Dialog>
|
||||
)}
|
||||
{deleteFavoriteState.showDeleteFavorite && (
|
||||
@@ -539,12 +625,20 @@ export default function Events({ path, ...props }) {
|
||||
{event.end_time && event.has_snapshot && (
|
||||
<Fragment>
|
||||
{event.plus_id ? (
|
||||
<div className="uppercase text-xs">Sent to Frigate+</div>
|
||||
<div className="uppercase text-xs underline">
|
||||
<Link
|
||||
href={`https://plus.frigate.video/dashboard/edit-image/?id=${event.plus_id}`}
|
||||
target="_blank"
|
||||
rel="nofollow"
|
||||
>
|
||||
Edit in Frigate+
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
color="gray"
|
||||
disabled={uploading.includes(event.id)}
|
||||
onClick={(e) => onSendToPlus(event.id, e)}
|
||||
onClick={(e) => showSubmitToPlus(event.id, event.label, event.box, e)}
|
||||
>
|
||||
{uploading.includes(event.id) ? 'Uploading...' : 'Send to Frigate+'}
|
||||
</Button>
|
||||
@@ -617,8 +711,12 @@ export default function Events({ path, ...props }) {
|
||||
style={{
|
||||
left: `${Math.round(eventOverlay.data.box[0] * 100)}%`,
|
||||
top: `${Math.round(eventOverlay.data.box[1] * 100)}%`,
|
||||
right: `${Math.round((1 - eventOverlay.data.box[2]) * 100)}%`,
|
||||
bottom: `${Math.round((1 - eventOverlay.data.box[3]) * 100)}%`,
|
||||
right: `${Math.round(
|
||||
(1 - eventOverlay.data.box[2] - eventOverlay.data.box[0]) * 100
|
||||
)}%`,
|
||||
bottom: `${Math.round(
|
||||
(1 - eventOverlay.data.box[3] - eventOverlay.data.box[1]) * 100
|
||||
)}%`,
|
||||
}}
|
||||
>
|
||||
{eventOverlay.class_type == 'entered_zone' ? (
|
||||
|
Reference in New Issue
Block a user