Merge in RR/fb_detr_prediction_container from non_max_supprs to master
Squashed commit of the following:
commit 9cc31b70e39412b3613a117228554608d947dbb5
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Fri Feb 4 17:41:00 2022 +0100
refactoring, renaming
commit ebc37299df598b71f7569d8e8473bdb66bbbbd1a
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Fri Feb 4 17:34:26 2022 +0100
renaming
commit d694866e1e98e6129f37eaf4c1950b962fed437f
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Fri Feb 4 17:33:07 2022 +0100
applied black
commit 381fe2dbf5d88f008d87bd807b84174376c5bcfe
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Fri Feb 4 17:32:22 2022 +0100
duplicate detection removal completed
commit ef2bab300322da3b12326d470f1c41263779e4a0
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date: Fri Feb 4 09:58:49 2022 +0100
box merging algo WIP
commit d770e56a7f31a28dea635816cae3b7b75fed0e24
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date: Fri Feb 4 09:37:17 2022 +0100
refactor & box dropping working but algo is faulty & drops too much WIP
commit 289848871caadb4438f889b8a030f30cfb64201a
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Thu Feb 3 23:56:04 2022 +0100
non max supprs WIP
commit 2f1ec100b2d33409e9178af8d53218b57d9bb0e2
Author: Matthias Bisping <matthias.bisping@iqser.com>
Date: Thu Feb 3 13:32:22 2022 +0100
changed Flask to not listen on public IP
97 lines
2.4 KiB
Python
97 lines
2.4 KiB
Python
from collections import namedtuple
|
|
from itertools import starmap, combinations
|
|
from operator import attrgetter, itemgetter
|
|
|
|
from frozendict import frozendict
|
|
|
|
Rectangle = namedtuple("Rectangle", "xmin ymin xmax ymax")
|
|
|
|
|
|
def make_box(x1, y1, x2, y2):
|
|
keys = "x1", "y1", "x2", "y2"
|
|
return dict(zip(keys, [x1, y1, x2, y2]))
|
|
|
|
|
|
def compute_intersection(a, b): # returns None if rectangles don't intersect
|
|
|
|
a = Rectangle(*a.values())
|
|
b = Rectangle(*b.values())
|
|
|
|
dx = min(a.xmax, b.xmax) - max(a.xmin, b.xmin)
|
|
dy = min(a.ymax, b.ymax) - max(a.ymin, b.ymin)
|
|
|
|
return dx * dy if (dx >= 0) and (dy >= 0) else 0
|
|
|
|
|
|
def compute_union(a, b):
|
|
def area(box):
|
|
r = Rectangle(*box.values())
|
|
return (r.xmax - r.xmin) * (r.ymax - r.ymin)
|
|
|
|
return (area(a) + area(b)) - compute_intersection(a, b)
|
|
|
|
|
|
def compute_iou(a, b):
|
|
return compute_intersection(a, b) / compute_union(a, b)
|
|
|
|
|
|
LPBox = namedtuple("LPBox", "label proba box")
|
|
|
|
|
|
def less_likely(a, b):
|
|
return min([a, b], key=attrgetter("proba"))
|
|
|
|
|
|
def overlap_too_much(a, b, iou_thresh):
|
|
iou = compute_iou(a.box, b.box)
|
|
return iou > iou_thresh
|
|
|
|
|
|
def __greedy_non_max_supprs(lpboxes, iou_thresh=0.1):
|
|
def remove_less_likely(a, b):
|
|
try:
|
|
ll = less_likely(a, b)
|
|
current_boxes.remove(ll)
|
|
except KeyError:
|
|
pass
|
|
|
|
current_boxes = {*lpboxes}
|
|
|
|
while True:
|
|
n = len(current_boxes)
|
|
for a, b in combinations(current_boxes, r=2):
|
|
if len({a, b} & current_boxes) != 2:
|
|
continue
|
|
if overlap_too_much(a, b, iou_thresh):
|
|
remove_less_likely(a, b)
|
|
|
|
if n == len(current_boxes):
|
|
break
|
|
|
|
return current_boxes
|
|
|
|
|
|
def lpboxes_to_dict(lpboxes):
|
|
|
|
boxes = map(dict, map(attrgetter("box"), lpboxes))
|
|
classes = map(attrgetter("label"), lpboxes)
|
|
probas = map(attrgetter("proba"), lpboxes)
|
|
|
|
boxes, classes, probas = map(list, [boxes, classes, probas])
|
|
|
|
return {"bboxes": boxes, "classes": classes, "probas": probas}
|
|
|
|
|
|
def greedy_non_max_supprs(predictions):
|
|
|
|
boxes, classes, probas = itemgetter("bboxes", "classes", "probas")(predictions)
|
|
boxes = map(frozendict, boxes)
|
|
lpboxes = list(starmap(LPBox, zip(classes, probas, boxes)))
|
|
|
|
lpboxes = __greedy_non_max_supprs(lpboxes)
|
|
|
|
merged_predictions = lpboxes_to_dict(lpboxes)
|
|
predictions.update(merged_predictions)
|
|
|
|
return predictions
|