""" Help functions for SSD """ import cv2 import numpy as np ############## preprocess image ################## # whiten the image def whiten_image(image, means=(123., 117., 104.)): """Subtracts the given means from each image channel""" if image.ndim != 3: raise ValueError('Input must be of size [height, width, C>0]') num_channels = image.shape[-1] if len(means) != num_channels: raise ValueError('len(means) must match the number of channels') mean = np.array(means, dtype=image.dtype) image = image - mean return image def resize_image(image, size=(300, 300)): return cv2.resize(image, size) def preprocess_image(image): """Preprocess a image to inference""" image_cp = np.copy(image).astype(np.float32) # whiten the image image_whitened = whiten_image(image_cp) # resize the image image_resized = resize_image(image_whitened) # expand the batch_size dim image_expanded = np.expand_dims(image_resized, axis=0) return image_expanded ############## process bboxes ################## def bboxes_clip(bbox_ref, bboxes): """Clip bounding boxes with respect to reference bbox. """ bboxes = np.copy(bboxes) bboxes = np.transpose(bboxes) bbox_ref = np.transpose(bbox_ref) bboxes[0] = np.maximum(bboxes[0], bbox_ref[0]) bboxes[1] = np.maximum(bboxes[1], bbox_ref[1]) bboxes[2] = np.minimum(bboxes[2], bbox_ref[2]) bboxes[3] = np.minimum(bboxes[3], bbox_ref[3]) bboxes = np.transpose(bboxes) return bboxes def bboxes_sort(classes, scores, bboxes, top_k=400): """Sort bounding boxes by decreasing order and keep only the top_k """ # if priority_inside: # inside = (bboxes[:, 0] > margin) & (bboxes[:, 1] > margin) & \ # (bboxes[:, 2] < 1-margin) & (bboxes[:, 3] < 1-margin) # idxes = np.argsort(-scores) # inside = inside[idxes] # idxes = np.concatenate([idxes[inside], idxes[~inside]]) idxes = np.argsort(-scores) classes = classes[idxes][:top_k] scores = scores[idxes][:top_k] bboxes = bboxes[idxes][:top_k] return classes, scores, bboxes def bboxes_iou(bboxes1, bboxes2): """Computing iou between bboxes1 and bboxes2. Note: bboxes1 and bboxes2 can be multi-dimensional, but should broacastable. """ bboxes1 = np.transpose(bboxes1) bboxes2 = np.transpose(bboxes2) # Intersection bbox and volume. int_ymin = np.maximum(bboxes1[0], bboxes2[0]) int_xmin = np.maximum(bboxes1[1], bboxes2[1]) int_ymax = np.minimum(bboxes1[2], bboxes2[2]) int_xmax = np.minimum(bboxes1[3], bboxes2[3]) int_h = np.maximum(int_ymax - int_ymin, 0.) int_w = np.maximum(int_xmax - int_xmin, 0.) int_vol = int_h * int_w # Union volume. vol1 = (bboxes1[2] - bboxes1[0]) * (bboxes1[3] - bboxes1[1]) vol2 = (bboxes2[2] - bboxes2[0]) * (bboxes2[3] - bboxes2[1]) iou = int_vol / (vol1 + vol2 - int_vol) return iou def bboxes_nms(classes, scores, bboxes, nms_threshold=0.5): """Apply non-maximum selection to bounding boxes. """ keep_bboxes = np.ones(scores.shape, dtype=np.bool) for i in range(scores.size-1): if keep_bboxes[i]: # Computer overlap with bboxes which are following. overlap = bboxes_iou(bboxes[i], bboxes[(i+1):]) # Overlap threshold for keeping + checking part of the same class keep_overlap = np.logical_or(overlap < nms_threshold, classes[(i+1):] != classes[i]) keep_bboxes[(i+1):] = np.logical_and(keep_bboxes[(i+1):], keep_overlap) idxes = np.where(keep_bboxes) return classes[idxes], scores[idxes], bboxes[idxes] def bboxes_resize(bbox_ref, bboxes): """Resize bounding boxes based on a reference bounding box, assuming that the latter is [0, 0, 1, 1] after transform. """ bboxes = np.copy(bboxes) # Translate. bboxes[:, 0] -= bbox_ref[0] bboxes[:, 1] -= bbox_ref[1] bboxes[:, 2] -= bbox_ref[0] bboxes[:, 3] -= bbox_ref[1] # Resize. resize = [bbox_ref[2] - bbox_ref[0], bbox_ref[3] - bbox_ref[1]] bboxes[:, 0] /= resize[0] bboxes[:, 1] /= resize[1] bboxes[:, 2] /= resize[0] bboxes[:, 3] /= resize[1] return bboxes def process_bboxes(rclasses, rscores, rbboxes, rbbox_img = (0.0, 0.0, 1.0, 1.0), top_k=400, nms_threshold=0.5): """Process the bboxes including sort and nms""" rbboxes = bboxes_clip(rbbox_img, rbboxes) rclasses, rscores, rbboxes = bboxes_sort(rclasses, rscores, rbboxes, top_k) rclasses, rscores, rbboxes = bboxes_nms(rclasses, rscores, rbboxes, nms_threshold) rbboxes = bboxes_resize(rbbox_img, rbboxes) return rclasses, rscores, rbboxes