mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-12-23 19:11:14 +01:00
Attribute scores (#7100)
* rework attributes to get scores * show sublabel score * formatting
This commit is contained in:
parent
5e772c3625
commit
7c0d25f9da
@ -63,7 +63,9 @@ Message published for each changed event. The first message is published when th
|
|||||||
"stationary": false, // whether or not the object is considered stationary
|
"stationary": false, // whether or not the object is considered stationary
|
||||||
"motionless_count": 0, // number of frames the object has been motionless
|
"motionless_count": 0, // number of frames the object has been motionless
|
||||||
"position_changes": 2, // number of times the object has moved from a stationary position
|
"position_changes": 2, // number of times the object has moved from a stationary position
|
||||||
"attributes": [], // set of unique attributes that have been identified on the object
|
"attributes": {
|
||||||
|
"face": 0.64
|
||||||
|
}, // attributes with top score that have been identified on the object at any point
|
||||||
"current_attributes": [] // detailed data about the current attributes in this frame
|
"current_attributes": [] // detailed data about the current attributes in this frame
|
||||||
},
|
},
|
||||||
"after": {
|
"after": {
|
||||||
@ -90,13 +92,15 @@ Message published for each changed event. The first message is published when th
|
|||||||
"stationary": false, // whether or not the object is considered stationary
|
"stationary": false, // whether or not the object is considered stationary
|
||||||
"motionless_count": 0, // number of frames the object has been motionless
|
"motionless_count": 0, // number of frames the object has been motionless
|
||||||
"position_changes": 2, // number of times the object has changed position
|
"position_changes": 2, // number of times the object has changed position
|
||||||
"attributes": ["face"], // set of unique attributes that have been identified on the object
|
"attributes": {
|
||||||
|
"face": 0.86
|
||||||
|
}, // attributes with top score that have been identified on the object at any point
|
||||||
"current_attributes": [
|
"current_attributes": [
|
||||||
// detailed data about the current attributes in this frame
|
// detailed data about the current attributes in this frame
|
||||||
{
|
{
|
||||||
"label": "face",
|
"label": "face",
|
||||||
"box": [442, 506, 534, 524],
|
"box": [442, 506, 534, 524],
|
||||||
"score": 0.64
|
"score": 0.86
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,8 @@ class EventProcessor(threading.Thread):
|
|||||||
|
|
||||||
# only overwrite the sub_label in the database if it's set
|
# only overwrite the sub_label in the database if it's set
|
||||||
if event_data.get("sub_label") is not None:
|
if event_data.get("sub_label") is not None:
|
||||||
event[Event.sub_label] = event_data["sub_label"]
|
event[Event.sub_label] = event_data["sub_label"][0]
|
||||||
|
event[Event.data]["sub_label_score"] = event_data["sub_label"][1]
|
||||||
|
|
||||||
(
|
(
|
||||||
Event.insert(event)
|
Event.insert(event)
|
||||||
|
@ -38,7 +38,9 @@ class Event(Model): # type: ignore[misc]
|
|||||||
IntegerField()
|
IntegerField()
|
||||||
) # TODO remove when columns can be dropped without rebuilding table
|
) # TODO remove when columns can be dropped without rebuilding table
|
||||||
retain_indefinitely = BooleanField(default=False)
|
retain_indefinitely = BooleanField(default=False)
|
||||||
ratio = FloatField(default=1.0)
|
ratio = FloatField(
|
||||||
|
default=1.0
|
||||||
|
) # TODO remove when columns can be dropped without rebuilding table
|
||||||
plus_id = CharField(max_length=30)
|
plus_id = CharField(max_length=30)
|
||||||
model_hash = CharField(max_length=32)
|
model_hash = CharField(max_length=32)
|
||||||
detector_type = CharField(max_length=32)
|
detector_type = CharField(max_length=32)
|
||||||
|
@ -112,7 +112,7 @@ class TrackedObject:
|
|||||||
self.zone_presence = {}
|
self.zone_presence = {}
|
||||||
self.current_zones = []
|
self.current_zones = []
|
||||||
self.entered_zones = []
|
self.entered_zones = []
|
||||||
self.attributes = set()
|
self.attributes = defaultdict(float)
|
||||||
self.false_positive = True
|
self.false_positive = True
|
||||||
self.has_clip = False
|
self.has_clip = False
|
||||||
self.has_snapshot = False
|
self.has_snapshot = False
|
||||||
@ -207,15 +207,19 @@ class TrackedObject:
|
|||||||
|
|
||||||
# maintain attributes
|
# maintain attributes
|
||||||
for attr in obj_data["attributes"]:
|
for attr in obj_data["attributes"]:
|
||||||
self.attributes.add(attr["label"])
|
if self.attributes[attr["label"]] < attr["score"]:
|
||||||
|
self.attributes[attr["label"]] = attr["score"]
|
||||||
|
|
||||||
# populate the sub_label for car with first logo if it exists
|
# populate the sub_label for car with highest scoring logo
|
||||||
if self.obj_data["label"] == "car" and "sub_label" not in self.obj_data:
|
if self.obj_data["label"] == "car":
|
||||||
recognized_logos = self.attributes.intersection(
|
recognized_logos = {
|
||||||
set(["ups", "fedex", "amazon"])
|
k: self.attributes[k]
|
||||||
)
|
for k in ["ups", "fedex", "amazon"]
|
||||||
|
if k in self.attributes
|
||||||
|
}
|
||||||
if len(recognized_logos) > 0:
|
if len(recognized_logos) > 0:
|
||||||
self.obj_data["sub_label"] = recognized_logos.pop()
|
max_logo = max(recognized_logos, key=recognized_logos.get)
|
||||||
|
self.obj_data["sub_label"] = (max_logo, recognized_logos[max_logo])
|
||||||
|
|
||||||
# check for significant change
|
# check for significant change
|
||||||
if not self.false_positive:
|
if not self.false_positive:
|
||||||
@ -274,7 +278,7 @@ class TrackedObject:
|
|||||||
"entered_zones": self.entered_zones.copy(),
|
"entered_zones": self.entered_zones.copy(),
|
||||||
"has_clip": self.has_clip,
|
"has_clip": self.has_clip,
|
||||||
"has_snapshot": self.has_snapshot,
|
"has_snapshot": self.has_snapshot,
|
||||||
"attributes": list(self.attributes),
|
"attributes": self.attributes,
|
||||||
"current_attributes": self.obj_data["attributes"],
|
"current_attributes": self.obj_data["attributes"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
20
web/src/icons/Score.jsx
Normal file
20
web/src/icons/Score.jsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { h } from 'preact';
|
||||||
|
import { memo } from 'preact/compat';
|
||||||
|
|
||||||
|
export function Score({ className = 'h-6 w-6', stroke = 'currentColor', fill = 'currentColor', onClick = () => {} }) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
className={className}
|
||||||
|
fill={fill}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke={stroke}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<title>percent</title>
|
||||||
|
<path d="M12,9A3,3 0 0,0 9,12A3,3 0 0,0 12,15A3,3 0 0,0 15,12A3,3 0 0,0 12,9M19,19H15V21H19A2,2 0 0,0 21,19V15H19M19,3H15V5H19V9H21V5A2,2 0 0,0 19,3M5,5H9V3H5A2,2 0 0,0 3,5V9H5M5,15H3V19A2,2 0 0,0 5,21H9V19H5V15Z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(Score);
|
@ -30,6 +30,7 @@ import TimeAgo from '../components/TimeAgo';
|
|||||||
import Timepicker from '../components/TimePicker';
|
import Timepicker from '../components/TimePicker';
|
||||||
import TimelineSummary from '../components/TimelineSummary';
|
import TimelineSummary from '../components/TimelineSummary';
|
||||||
import TimelineEventOverlay from '../components/TimelineEventOverlay';
|
import TimelineEventOverlay from '../components/TimelineEventOverlay';
|
||||||
|
import { Score } from '../icons/Score';
|
||||||
|
|
||||||
const API_LIMIT = 25;
|
const API_LIMIT = 25;
|
||||||
|
|
||||||
@ -602,13 +603,10 @@ export default function Events({ path, ...props }) {
|
|||||||
<div className="m-2 flex grow">
|
<div className="m-2 flex grow">
|
||||||
<div className="flex flex-col grow">
|
<div className="flex flex-col grow">
|
||||||
<div className="capitalize text-lg font-bold">
|
<div className="capitalize text-lg font-bold">
|
||||||
{event.sub_label
|
{event.label.replaceAll('_', ' ')}
|
||||||
? `${event.label.replaceAll('_', ' ')}: ${event.sub_label.replaceAll('_', ' ')}`
|
{event.sub_label ? `: ${event.sub_label.replaceAll('_', ' ')}` : null}
|
||||||
: event.label.replaceAll('_', ' ')}
|
|
||||||
{(event?.data?.top_score || event.top_score || 0) == 0
|
|
||||||
? null
|
|
||||||
: ` (${((event?.data?.top_score || event.top_score) * 100).toFixed(0)}%)`}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-sm flex">
|
<div className="text-sm flex">
|
||||||
<Clock className="h-5 w-5 mr-2 inline" />
|
<Clock className="h-5 w-5 mr-2 inline" />
|
||||||
{formatUnixTimestampToDateTime(event.start_time, { ...config.ui })}
|
{formatUnixTimestampToDateTime(event.start_time, { ...config.ui })}
|
||||||
@ -628,6 +626,15 @@ export default function Events({ path, ...props }) {
|
|||||||
<Zone className="w-5 h-5 mr-2 inline" />
|
<Zone className="w-5 h-5 mr-2 inline" />
|
||||||
{event.zones.join(', ').replaceAll('_', ' ')}
|
{event.zones.join(', ').replaceAll('_', ' ')}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="capitalize text-sm flex align-center">
|
||||||
|
<Score className="w-5 h-5 mr-2 inline" />
|
||||||
|
{(event?.data?.top_score || event.top_score || 0) == 0
|
||||||
|
? null
|
||||||
|
: `Label: ${((event?.data?.top_score || event.top_score) * 100).toFixed(0)}%`}
|
||||||
|
{(event?.data?.sub_label_score || 0) == 0
|
||||||
|
? null
|
||||||
|
: `, Sub Label: ${(event?.data?.sub_label_score * 100).toFixed(0)}%`}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="hidden sm:flex flex-col justify-end mr-2">
|
<div class="hidden sm:flex flex-col justify-end mr-2">
|
||||||
{event.end_time && event.has_snapshot && (
|
{event.end_time && event.has_snapshot && (
|
||||||
|
Loading…
Reference in New Issue
Block a user