mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-02 17:25:22 +03:00
parent
83481afee1
commit
43fd76f4ec
@ -197,6 +197,10 @@ objects:
|
|||||||
min_area: 5000
|
min_area: 5000
|
||||||
# Optional: maximum width*height of the bounding box for the detected object (default: 24000000)
|
# Optional: maximum width*height of the bounding box for the detected object (default: 24000000)
|
||||||
max_area: 100000
|
max_area: 100000
|
||||||
|
# Optional: minimum width/height of the bounding box for the detected object (default: 0)
|
||||||
|
min_ratio: 0.5
|
||||||
|
# Optional: maximum width/height of the bounding box for the detected object (default: 24000000)
|
||||||
|
max_ratio: 2.0
|
||||||
# Optional: minimum score for the object to initiate tracking (default: shown below)
|
# Optional: minimum score for the object to initiate tracking (default: shown below)
|
||||||
min_score: 0.5
|
min_score: 0.5
|
||||||
# Optional: minimum decimal percentage for tracked object's computed score to be considered a true positive (default: shown below)
|
# Optional: minimum decimal percentage for tracked object's computed score to be considered a true positive (default: shown below)
|
||||||
|
|||||||
@ -3,7 +3,11 @@ id: false_positives
|
|||||||
title: Reducing false positives
|
title: Reducing false positives
|
||||||
---
|
---
|
||||||
|
|
||||||
Tune your object filters to adjust false positives: `min_area`, `max_area`, `min_score`, `threshold`.
|
Tune your object filters to adjust false positives: `min_area`, `max_area`, `min_ratio`, `max_ratio`, `min_score`, `threshold`.
|
||||||
|
|
||||||
|
The `min_area` and `max_area` values are compared against the area (number of pixels) from a given detected object. If the area is outside this range, the object will be ignored as a false positive. This allows objects that must be too small or too large to be ignored.
|
||||||
|
|
||||||
|
Similarly, the `min_ratio` and `max_ratio` values are compared against a given detected object's width/height ratio (in pixels). If the ratio is outside this range, the object will be ignored as a false positive. This allows objects that are proportionally too short-and-wide (higher ratio) or too tall-and-narrow (smaller ratio) to be ignored.
|
||||||
|
|
||||||
For object filters in your configuration, any single detection below `min_score` will be ignored as a false positive. `threshold` is based on the median of the history of scores (padded to 3 values) for a tracked object. Consider the following frames when `min_score` is set to 0.6 and threshold is set to 0.85:
|
For object filters in your configuration, any single detection below `min_score` will be ignored as a false positive. `threshold` is based on the median of the history of scores (padded to 3 values) for a tracked object. Consider the following frames when `min_score` is set to 0.6 and threshold is set to 0.85:
|
||||||
|
|
||||||
|
|||||||
@ -50,6 +50,7 @@ Message published for each changed event. The first message is published when th
|
|||||||
"score": 0.7890625,
|
"score": 0.7890625,
|
||||||
"box": [424, 500, 536, 712],
|
"box": [424, 500, 536, 712],
|
||||||
"area": 23744,
|
"area": 23744,
|
||||||
|
"ratio": 2.113207,
|
||||||
"region": [264, 450, 667, 853],
|
"region": [264, 450, 667, 853],
|
||||||
"current_zones": ["driveway"],
|
"current_zones": ["driveway"],
|
||||||
"entered_zones": ["yard", "driveway"],
|
"entered_zones": ["yard", "driveway"],
|
||||||
@ -73,6 +74,7 @@ Message published for each changed event. The first message is published when th
|
|||||||
"score": 0.87890625,
|
"score": 0.87890625,
|
||||||
"box": [432, 496, 544, 854],
|
"box": [432, 496, 544, 854],
|
||||||
"area": 40096,
|
"area": 40096,
|
||||||
|
"ratio": 1.251397,
|
||||||
"region": [218, 440, 693, 915],
|
"region": [218, 440, 693, 915],
|
||||||
"current_zones": ["yard", "driveway"],
|
"current_zones": ["yard", "driveway"],
|
||||||
"entered_zones": ["yard", "driveway"],
|
"entered_zones": ["yard", "driveway"],
|
||||||
|
|||||||
@ -209,6 +209,14 @@ class FilterConfig(FrigateBaseModel):
|
|||||||
max_area: int = Field(
|
max_area: int = Field(
|
||||||
default=24000000, title="Maximum area of bounding box for object to be counted."
|
default=24000000, title="Maximum area of bounding box for object to be counted."
|
||||||
)
|
)
|
||||||
|
min_ratio: float = Field(
|
||||||
|
default=0,
|
||||||
|
title="Minimum ratio of bounding box's width/height for object to be counted.",
|
||||||
|
)
|
||||||
|
max_ratio: float = Field(
|
||||||
|
default=24000000, # @todo: Is there a way to make this `math.inf` but still load from YAML?
|
||||||
|
title="Maximum ratio of bounding box's width/height for object to be counted.",
|
||||||
|
)
|
||||||
threshold: float = Field(
|
threshold: float = Field(
|
||||||
default=0.7,
|
default=0.7,
|
||||||
title="Average detection confidence threshold for object to be counted.",
|
title="Average detection confidence threshold for object to be counted.",
|
||||||
|
|||||||
@ -77,6 +77,7 @@ class EventProcessor(threading.Thread):
|
|||||||
region=event_data["region"],
|
region=event_data["region"],
|
||||||
box=event_data["box"],
|
box=event_data["box"],
|
||||||
area=event_data["area"],
|
area=event_data["area"],
|
||||||
|
ratio=event_data["ratio"],
|
||||||
has_clip=event_data["has_clip"],
|
has_clip=event_data["has_clip"],
|
||||||
has_snapshot=event_data["has_snapshot"],
|
has_snapshot=event_data["has_snapshot"],
|
||||||
).execute()
|
).execute()
|
||||||
@ -96,6 +97,7 @@ class EventProcessor(threading.Thread):
|
|||||||
region=event_data["region"],
|
region=event_data["region"],
|
||||||
box=event_data["box"],
|
box=event_data["box"],
|
||||||
area=event_data["area"],
|
area=event_data["area"],
|
||||||
|
ratio=event_data["ratio"],
|
||||||
has_clip=event_data["has_clip"],
|
has_clip=event_data["has_clip"],
|
||||||
has_snapshot=event_data["has_snapshot"],
|
has_snapshot=event_data["has_snapshot"],
|
||||||
).execute()
|
).execute()
|
||||||
|
|||||||
@ -18,6 +18,7 @@ class Event(Model):
|
|||||||
region = JSONField()
|
region = JSONField()
|
||||||
box = JSONField()
|
box = JSONField()
|
||||||
area = IntegerField()
|
area = IntegerField()
|
||||||
|
ratio = FloatField()
|
||||||
|
|
||||||
|
|
||||||
class Recordings(Model):
|
class Recordings(Model):
|
||||||
|
|||||||
@ -192,6 +192,7 @@ class TrackedObject:
|
|||||||
"score": self.obj_data["score"],
|
"score": self.obj_data["score"],
|
||||||
"box": self.obj_data["box"],
|
"box": self.obj_data["box"],
|
||||||
"area": self.obj_data["area"],
|
"area": self.obj_data["area"],
|
||||||
|
"ratio": self.obj_data["ratio"],
|
||||||
"region": self.obj_data["region"],
|
"region": self.obj_data["region"],
|
||||||
"stationary": self.obj_data["motionless_count"]
|
"stationary": self.obj_data["motionless_count"]
|
||||||
> self.camera_config.detect.stationary.threshold,
|
> self.camera_config.detect.stationary.threshold,
|
||||||
@ -341,6 +342,14 @@ def zone_filtered(obj: TrackedObject, object_config):
|
|||||||
if obj_settings.threshold > obj.computed_score:
|
if obj_settings.threshold > obj.computed_score:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# if the object is not proportionally wide enough
|
||||||
|
if obj_settings.min_ratio > obj.obj_data["ratio"]:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# if the object is not proportionally narrow enough
|
||||||
|
if obj_settings.max_ratio < obj.obj_data["ratio"]:
|
||||||
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -149,7 +149,8 @@ class ObjectTracker:
|
|||||||
"score": obj[1],
|
"score": obj[1],
|
||||||
"box": obj[2],
|
"box": obj[2],
|
||||||
"area": obj[3],
|
"area": obj[3],
|
||||||
"region": obj[4],
|
"ratio": obj[4],
|
||||||
|
"region": obj[5],
|
||||||
"frame_time": frame_time,
|
"frame_time": frame_time,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@ -38,6 +38,9 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
def filtered(obj, objects_to_track, object_filters):
|
def filtered(obj, objects_to_track, object_filters):
|
||||||
object_name = obj[0]
|
object_name = obj[0]
|
||||||
|
object_score = obj[1]
|
||||||
|
object_bounds = obj[2]
|
||||||
|
object_area = obj[3]
|
||||||
|
|
||||||
if not object_name in objects_to_track:
|
if not object_name in objects_to_track:
|
||||||
return True
|
return True
|
||||||
@ -47,24 +50,27 @@ def filtered(obj, objects_to_track, object_filters):
|
|||||||
|
|
||||||
# if the min area is larger than the
|
# if the min area is larger than the
|
||||||
# detected object, don't add it to detected objects
|
# detected object, don't add it to detected objects
|
||||||
if obj_settings.min_area > obj[3]:
|
if obj_settings.min_area > object_area:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# if the detected object is larger than the
|
# if the detected object is larger than the
|
||||||
# max area, don't add it to detected objects
|
# max area, don't add it to detected objects
|
||||||
if obj_settings.max_area < obj[3]:
|
if obj_settings.max_area < object_area:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# if the score is lower than the min_score, skip
|
# if the score is lower than the min_score, skip
|
||||||
if obj_settings.min_score > obj[1]:
|
if obj_settings.min_score > object_score:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if not obj_settings.mask is None:
|
if not obj_settings.mask is None:
|
||||||
# compute the coordinates of the object and make sure
|
# compute the coordinates of the object and make sure
|
||||||
# the location isnt outside the bounds of the image (can happen from rounding)
|
# the location isn't outside the bounds of the image (can happen from rounding)
|
||||||
y_location = min(int(obj[2][3]), len(obj_settings.mask) - 1)
|
object_xmin = object_bounds[0]
|
||||||
|
object_xmax = object_bounds[2]
|
||||||
|
object_ymax = object_bounds[3]
|
||||||
|
y_location = min(int(object_ymax), len(obj_settings.mask) - 1)
|
||||||
x_location = min(
|
x_location = min(
|
||||||
int((obj[2][2] - obj[2][0]) / 2.0) + obj[2][0],
|
int((object_xmax + object_xmin) / 2.0),
|
||||||
len(obj_settings.mask[0]) - 1,
|
len(obj_settings.mask[0]) - 1,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -429,11 +435,16 @@ def detect(
|
|||||||
y_min = int((box[0] * size) + region[1])
|
y_min = int((box[0] * size) + region[1])
|
||||||
x_max = int((box[3] * size) + region[0])
|
x_max = int((box[3] * size) + region[0])
|
||||||
y_max = int((box[2] * size) + region[1])
|
y_max = int((box[2] * size) + region[1])
|
||||||
|
width = x_max - x_min
|
||||||
|
height = y_max - y_min
|
||||||
|
area = width * height
|
||||||
|
ratio = width / height
|
||||||
det = (
|
det = (
|
||||||
d[0],
|
d[0],
|
||||||
d[1],
|
d[1],
|
||||||
(x_min, y_min, x_max, y_max),
|
(x_min, y_min, x_max, y_max),
|
||||||
(x_max - x_min) * (y_max - y_min),
|
area,
|
||||||
|
ratio,
|
||||||
region,
|
region,
|
||||||
)
|
)
|
||||||
# apply object filters
|
# apply object filters
|
||||||
@ -615,8 +626,14 @@ def process_frames(
|
|||||||
for group in detected_object_groups.values():
|
for group in detected_object_groups.values():
|
||||||
|
|
||||||
# apply non-maxima suppression to suppress weak, overlapping bounding boxes
|
# apply non-maxima suppression to suppress weak, overlapping bounding boxes
|
||||||
|
bounds = o[2] # xmin, ymin, xmax, ymax
|
||||||
boxes = [
|
boxes = [
|
||||||
(o[2][0], o[2][1], o[2][2] - o[2][0], o[2][3] - o[2][1])
|
(
|
||||||
|
bounds[0],
|
||||||
|
bounds[1],
|
||||||
|
bounds[2] - bounds[0],
|
||||||
|
bounds[3] - bounds[1],
|
||||||
|
)
|
||||||
for o in group
|
for o in group
|
||||||
]
|
]
|
||||||
confidences = [o[1] for o in group]
|
confidences = [o[1] for o in group]
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user