202 lines
5.7 KiB
Python
202 lines
5.7 KiB
Python
import numpy as np
|
|
import cv2
|
|
|
|
|
|
# Todo : BoundBox & its related method extraction
|
|
class BoundBox:
|
|
def __init__(self, x, y, w, h, c = None, classes = None):
|
|
self.x = x
|
|
self.y = y
|
|
self.w = w
|
|
self.h = h
|
|
|
|
self.c = c
|
|
self.classes = classes
|
|
|
|
def get_label(self):
|
|
return np.argmax(self.classes)
|
|
|
|
def get_score(self):
|
|
return self.classes[self.get_label()]
|
|
|
|
def iou(self, bound_box):
|
|
b1 = self.as_centroid()
|
|
b2 = bound_box.as_centroid()
|
|
return centroid_box_iou(b1, b2)
|
|
|
|
def as_centroid(self):
|
|
return np.array([self.x, self.y, self.w, self.h])
|
|
|
|
|
|
def boxes_to_array(bound_boxes):
|
|
"""
|
|
# Args
|
|
boxes : list of BoundBox instances
|
|
|
|
# Returns
|
|
centroid_boxes : (N, 4)
|
|
probs : (N, nb_classes)
|
|
"""
|
|
centroid_boxes = []
|
|
probs = []
|
|
for box in bound_boxes:
|
|
centroid_boxes.append([box.x, box.y, box.w, box.h])
|
|
probs.append(box.classes)
|
|
return np.array(centroid_boxes), np.array(probs)
|
|
|
|
|
|
def nms_boxes(boxes, n_classes, nms_threshold=0.3, obj_threshold=0.3):
|
|
"""
|
|
# Args
|
|
boxes : list of BoundBox
|
|
|
|
# Returns
|
|
boxes : list of BoundBox
|
|
non maximum supressed BoundBox instances
|
|
"""
|
|
# suppress non-maximal boxes
|
|
for c in range(n_classes):
|
|
sorted_indices = list(reversed(np.argsort([box.classes[c] for box in boxes])))
|
|
|
|
for i in range(len(sorted_indices)):
|
|
index_i = sorted_indices[i]
|
|
|
|
if boxes[index_i].classes[c] == 0:
|
|
continue
|
|
else:
|
|
for j in range(i+1, len(sorted_indices)):
|
|
index_j = sorted_indices[j]
|
|
|
|
if boxes[index_i].iou(boxes[index_j]) >= nms_threshold:
|
|
boxes[index_j].classes[c] = 0
|
|
# remove the boxes which are less likely than a obj_threshold
|
|
boxes = [box for box in boxes if box.get_score() > obj_threshold]
|
|
return boxes
|
|
|
|
|
|
def draw_scaled_boxes(image, boxes, probs, labels, desired_size=400):
|
|
img_size = min(image.shape[:2])
|
|
if img_size < desired_size:
|
|
scale_factor = float(desired_size) / img_size
|
|
else:
|
|
scale_factor = 1.0
|
|
|
|
h, w = image.shape[:2]
|
|
img_scaled = cv2.resize(image, (int(w*scale_factor), int(h*scale_factor)))
|
|
if boxes != []:
|
|
boxes_scaled = boxes*scale_factor
|
|
boxes_scaled = boxes_scaled.astype(np.int)
|
|
else:
|
|
boxes_scaled = boxes
|
|
return draw_boxes(img_scaled, boxes_scaled, probs, labels)
|
|
|
|
|
|
def draw_boxes(image, boxes, probs, labels):
|
|
for box, classes in zip(boxes, probs):
|
|
x1, y1, x2, y2 = box
|
|
cv2.rectangle(image, (x1,y1), (x2,y2), (0,255,0), 3)
|
|
cv2.putText(image,
|
|
'{}: {:.2f}'.format(labels[np.argmax(classes)], classes.max()),
|
|
(x1, y1 - 13),
|
|
cv2.FONT_HERSHEY_SIMPLEX,
|
|
1e-3 * image.shape[0],
|
|
(0,255,0), 2)
|
|
return image
|
|
|
|
|
|
def centroid_box_iou(box1, box2):
|
|
def _interval_overlap(interval_a, interval_b):
|
|
x1, x2 = interval_a
|
|
x3, x4 = interval_b
|
|
|
|
if x3 < x1:
|
|
if x4 < x1:
|
|
return 0
|
|
else:
|
|
return min(x2,x4) - x1
|
|
else:
|
|
if x2 < x3:
|
|
return 0
|
|
else:
|
|
return min(x2,x4) - x3
|
|
|
|
_, _, w1, h1 = box1.reshape(-1,)
|
|
_, _, w2, h2 = box2.reshape(-1,)
|
|
x1_min, y1_min, x1_max, y1_max = to_minmax(box1.reshape(-1,4)).reshape(-1,)
|
|
x2_min, y2_min, x2_max, y2_max = to_minmax(box2.reshape(-1,4)).reshape(-1,)
|
|
|
|
intersect_w = _interval_overlap([x1_min, x1_max], [x2_min, x2_max])
|
|
intersect_h = _interval_overlap([y1_min, y1_max], [y2_min, y2_max])
|
|
intersect = intersect_w * intersect_h
|
|
union = w1 * h1 + w2 * h2 - intersect
|
|
|
|
return float(intersect) / union
|
|
|
|
|
|
def to_centroid(minmax_boxes):
|
|
"""
|
|
minmax_boxes : (N, 4)
|
|
"""
|
|
minmax_boxes = minmax_boxes.astype(np.float)
|
|
centroid_boxes = np.zeros_like(minmax_boxes)
|
|
|
|
x1 = minmax_boxes[:,0]
|
|
y1 = minmax_boxes[:,1]
|
|
x2 = minmax_boxes[:,2]
|
|
y2 = minmax_boxes[:,3]
|
|
|
|
centroid_boxes[:,0] = (x1 + x2) / 2
|
|
centroid_boxes[:,1] = (y1 + y2) / 2
|
|
centroid_boxes[:,2] = x2 - x1
|
|
centroid_boxes[:,3] = y2 - y1
|
|
return centroid_boxes
|
|
|
|
def to_minmax(centroid_boxes):
|
|
centroid_boxes = centroid_boxes.astype(np.float)
|
|
minmax_boxes = np.zeros_like(centroid_boxes)
|
|
|
|
cx = centroid_boxes[:,0]
|
|
cy = centroid_boxes[:,1]
|
|
w = centroid_boxes[:,2]
|
|
h = centroid_boxes[:,3]
|
|
|
|
minmax_boxes[:,0] = cx - w/2
|
|
minmax_boxes[:,1] = cy - h/2
|
|
minmax_boxes[:,2] = cx + w/2
|
|
minmax_boxes[:,3] = cy + h/2
|
|
return minmax_boxes
|
|
|
|
def create_anchor_boxes(anchors):
|
|
"""
|
|
# Args
|
|
anchors : list of floats
|
|
# Returns
|
|
boxes : array, shape of (len(anchors)/2, 4)
|
|
centroid-type
|
|
"""
|
|
boxes = []
|
|
n_boxes = int(len(anchors)/2)
|
|
for i in range(n_boxes):
|
|
boxes.append(np.array([0, 0, anchors[2*i], anchors[2*i+1]]))
|
|
return np.array(boxes)
|
|
|
|
def find_match_box(centroid_box, centroid_boxes):
|
|
"""Find the index of the boxes with the largest overlap among the N-boxes.
|
|
# Args
|
|
box : array, shape of (1, 4)
|
|
boxes : array, shape of (N, 4)
|
|
|
|
# Return
|
|
match_index : int
|
|
"""
|
|
match_index = -1
|
|
max_iou = -1
|
|
|
|
for i, box in enumerate(centroid_boxes):
|
|
iou = centroid_box_iou(centroid_box, box)
|
|
|
|
if max_iou < iou:
|
|
match_index = i
|
|
max_iou = iou
|
|
return match_index
|