mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-05 18:55:23 +03:00
Fix bug in intersection logic
This commit is contained in:
parent
ff90db30e6
commit
ee5b59a61c
@ -10,6 +10,7 @@ from frigate.video import (
|
|||||||
get_cluster_candidates,
|
get_cluster_candidates,
|
||||||
get_cluster_region,
|
get_cluster_region,
|
||||||
)
|
)
|
||||||
|
from frigate.util import intersection
|
||||||
|
|
||||||
|
|
||||||
def draw_box(frame, box, color=(255, 0, 0), thickness=2):
|
def draw_box(frame, box, color=(255, 0, 0), thickness=2):
|
||||||
@ -63,7 +64,7 @@ def save_cluster_boundary_image(name, boxes, bounding_boxes):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestConfig(unittest.TestCase):
|
class TestRegion(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.frame_shape = (1000, 2000)
|
self.frame_shape = (1000, 2000)
|
||||||
self.min_region_size = 160
|
self.min_region_size = 160
|
||||||
@ -176,3 +177,79 @@ class TestConfig(unittest.TestCase):
|
|||||||
|
|
||||||
# save_clusters_image("dont_combine", boxes, cluster_candidates, regions)
|
# save_clusters_image("dont_combine", boxes, cluster_candidates, regions)
|
||||||
assert len(regions) == 2
|
assert len(regions) == 2
|
||||||
|
|
||||||
|
|
||||||
|
class TestObjectBoundingBoxes(unittest.TestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_box_intersection(self):
|
||||||
|
box_a = [2012, 191, 2031, 205]
|
||||||
|
box_b = [887, 92, 985, 151]
|
||||||
|
box_c = [899, 128, 1080, 175]
|
||||||
|
|
||||||
|
assert intersection(box_a, box_b) == None
|
||||||
|
assert intersection(box_b, box_c) == [899, 128, 985, 151]
|
||||||
|
|
||||||
|
def test_consolidate_objects(self):
|
||||||
|
selected_objects = [
|
||||||
|
(
|
||||||
|
"car", # valid detection, should not be consolidated
|
||||||
|
0.82421875,
|
||||||
|
(2044, 240, 2134, 297),
|
||||||
|
5130,
|
||||||
|
1.5789473684210527,
|
||||||
|
(2008, 188, 2168, 348),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"car", # valid detection, should not be consolidated
|
||||||
|
0.76953125,
|
||||||
|
(124, 239, 967, 719),
|
||||||
|
404640,
|
||||||
|
1.75625,
|
||||||
|
(0, 0, 2968, 2968),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"car", # valid detection, should not be consolidated
|
||||||
|
0.71484375,
|
||||||
|
(890, 93, 976, 150),
|
||||||
|
4902,
|
||||||
|
1.5087719298245614,
|
||||||
|
(854, 40, 1014, 200),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"car", # valid detection, should not be consolidated
|
||||||
|
0.6640625,
|
||||||
|
(1959, 188, 2005, 222),
|
||||||
|
1564,
|
||||||
|
1.3529411764705883,
|
||||||
|
(1768, 0, 2560, 792),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"car", # invalid detection, should be consolidated
|
||||||
|
0.65234375,
|
||||||
|
(2033, 193, 2048, 203),
|
||||||
|
150,
|
||||||
|
1.5,
|
||||||
|
(2008, 188, 2168, 348),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"car",
|
||||||
|
0.82421875,
|
||||||
|
(2044, 240, 2134, 297),
|
||||||
|
5130,
|
||||||
|
1.5789473684210527,
|
||||||
|
(2008, 188, 2168, 348),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"car",
|
||||||
|
0.76953125,
|
||||||
|
(124, 239, 967, 719),
|
||||||
|
404640,
|
||||||
|
1.75625,
|
||||||
|
(0, 0, 2968, 2968),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|||||||
@ -572,7 +572,16 @@ def yuv_region_2_bgr(frame, region):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def intersection(box_a, box_b):
|
def intersection(box_a, box_b) -> Optional[list[int]]:
|
||||||
|
"""Return intersection box or None if boxes do not intersect."""
|
||||||
|
if (
|
||||||
|
box_a[2] < box_b[0]
|
||||||
|
or box_a[0] > box_b[2]
|
||||||
|
or box_a[1] > box_b[3]
|
||||||
|
or box_a[3] < box_b[1]
|
||||||
|
):
|
||||||
|
return None
|
||||||
|
|
||||||
return (
|
return (
|
||||||
max(box_a[0], box_b[0]),
|
max(box_a[0], box_b[0]),
|
||||||
max(box_a[1], box_b[1]),
|
max(box_a[1], box_b[1]),
|
||||||
@ -589,6 +598,9 @@ def intersection_over_union(box_a, box_b):
|
|||||||
# determine the (x, y)-coordinates of the intersection rectangle
|
# determine the (x, y)-coordinates of the intersection rectangle
|
||||||
intersect = intersection(box_a, box_b)
|
intersect = intersection(box_a, box_b)
|
||||||
|
|
||||||
|
if intersect is None:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
# compute the area of intersection rectangle
|
# compute the area of intersection rectangle
|
||||||
inter_area = max(0, intersect[2] - intersect[0] + 1) * max(
|
inter_area = max(0, intersect[2] - intersect[0] + 1) * max(
|
||||||
0, intersect[3] - intersect[1] + 1
|
0, intersect[3] - intersect[1] + 1
|
||||||
|
|||||||
@ -669,6 +669,40 @@ def get_cluster_region(frame_shape, min_region, cluster, boxes):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_consolidated_object_detections(detected_object_groups):
|
||||||
|
"""Drop detections that overlap too much"""
|
||||||
|
consolidated_detections = []
|
||||||
|
for group in detected_object_groups.values():
|
||||||
|
# if the group only has 1 item, skip
|
||||||
|
if len(group) == 1:
|
||||||
|
consolidated_detections.append(group[0])
|
||||||
|
continue
|
||||||
|
|
||||||
|
# sort smallest to largest by area
|
||||||
|
sorted_by_area = sorted(group, key=lambda g: g[3])
|
||||||
|
|
||||||
|
for current_detection_idx in range(0, len(sorted_by_area)):
|
||||||
|
current_detection = sorted_by_area[current_detection_idx][2]
|
||||||
|
overlap = 0
|
||||||
|
for to_check_idx in range(
|
||||||
|
min(current_detection_idx + 1, len(sorted_by_area)),
|
||||||
|
len(sorted_by_area),
|
||||||
|
):
|
||||||
|
to_check = sorted_by_area[to_check_idx][2]
|
||||||
|
intersect_box = intersection(current_detection, to_check)
|
||||||
|
# if 90% of smaller detection is inside of another detection, consolidate
|
||||||
|
if (
|
||||||
|
intersect_box is not None
|
||||||
|
and area(intersect_box) / area(current_detection) > 0.9
|
||||||
|
):
|
||||||
|
overlap = 1
|
||||||
|
break
|
||||||
|
if overlap == 0:
|
||||||
|
consolidated_detections.append(sorted_by_area[current_detection_idx])
|
||||||
|
|
||||||
|
return consolidated_detections
|
||||||
|
|
||||||
|
|
||||||
def process_frames(
|
def process_frames(
|
||||||
camera_name: str,
|
camera_name: str,
|
||||||
frame_queue: mp.Queue,
|
frame_queue: mp.Queue,
|
||||||
@ -849,9 +883,6 @@ def process_frames(
|
|||||||
# set the detections list to only include top objects
|
# set the detections list to only include top objects
|
||||||
detections = selected_objects
|
detections = selected_objects
|
||||||
|
|
||||||
## drop detections that overlap too much
|
|
||||||
consolidated_detections = []
|
|
||||||
|
|
||||||
# if detection was run on this frame, consolidate
|
# if detection was run on this frame, consolidate
|
||||||
if len(regions) > 0:
|
if len(regions) > 0:
|
||||||
# group by name
|
# group by name
|
||||||
@ -859,35 +890,8 @@ def process_frames(
|
|||||||
for detection in detections:
|
for detection in detections:
|
||||||
detected_object_groups[detection[0]].append(detection)
|
detected_object_groups[detection[0]].append(detection)
|
||||||
|
|
||||||
# loop over detections grouped by label
|
consolidated_detections = get_consolidated_object_detections(
|
||||||
for group in detected_object_groups.values():
|
detected_object_groups
|
||||||
# if the group only has 1 item, skip
|
|
||||||
if len(group) == 1:
|
|
||||||
consolidated_detections.append(group[0])
|
|
||||||
continue
|
|
||||||
|
|
||||||
# sort smallest to largest by area
|
|
||||||
sorted_by_area = sorted(group, key=lambda g: g[3])
|
|
||||||
|
|
||||||
for current_detection_idx in range(0, len(sorted_by_area)):
|
|
||||||
current_detection = sorted_by_area[current_detection_idx][2]
|
|
||||||
overlap = 0
|
|
||||||
for to_check_idx in range(
|
|
||||||
min(current_detection_idx + 1, len(sorted_by_area)),
|
|
||||||
len(sorted_by_area),
|
|
||||||
):
|
|
||||||
to_check = sorted_by_area[to_check_idx][2]
|
|
||||||
# if 90% of smaller detection is inside of another detection, consolidate
|
|
||||||
if (
|
|
||||||
area(intersection(current_detection, to_check))
|
|
||||||
/ area(current_detection)
|
|
||||||
> 0.9
|
|
||||||
):
|
|
||||||
overlap = 1
|
|
||||||
break
|
|
||||||
if overlap == 0:
|
|
||||||
consolidated_detections.append(
|
|
||||||
sorted_by_area[current_detection_idx]
|
|
||||||
)
|
)
|
||||||
# now that we have refined our detections, we need to track objects
|
# now that we have refined our detections, we need to track objects
|
||||||
object_tracker.match_and_update(frame_time, consolidated_detections)
|
object_tracker.match_and_update(frame_time, consolidated_detections)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user