Source code for motrackers.utils.misc

import numpy as np
import cv2 as cv


[docs]def get_centroid(bboxes): """ Calculate centroids for multiple bounding boxes. Args: bboxes (numpy.ndarray): Array of shape `(n, 4)` or of shape `(4,)` where each row contains `(xmin, ymin, width, height)`. Returns: numpy.ndarray: Centroid (x, y) coordinates of shape `(n, 2)` or `(2,)`. """ one_bbox = False if len(bboxes.shape) == 1: one_bbox = True bboxes = bboxes[None, :] xmin = bboxes[:, 0] ymin = bboxes[:, 1] w, h = bboxes[:, 2], bboxes[:, 3] xc = xmin + 0.5*w yc = ymin + 0.5*h x = np.hstack([xc[:, None], yc[:, None]]) if one_bbox: x = x.flatten() return x
[docs]def iou(bbox1, bbox2): """ Calculates the intersection-over-union of two bounding boxes. Source: https://github.com/bochinski/iou-tracker/blob/master/util.py Args: bbox1 (numpy.array or list[floats]): Bounding box of length 4 containing ``(x-top-left, y-top-left, x-bottom-right, y-bottom-right)``. bbox2 (numpy.array or list[floats]): Bounding box of length 4 containing ``(x-top-left, y-top-left, x-bottom-right, y-bottom-right)``. Returns: float: intersection-over-onion of bbox1, bbox2. """ bbox1 = [float(x) for x in bbox1] bbox2 = [float(x) for x in bbox2] (x0_1, y0_1, x1_1, y1_1), (x0_2, y0_2, x1_2, y1_2) = bbox1, bbox2 # get the overlap rectangle overlap_x0 = max(x0_1, x0_2) overlap_y0 = max(y0_1, y0_2) overlap_x1 = min(x1_1, x1_2) overlap_y1 = min(y1_1, y1_2) # check if there is an overlap if overlap_x1 - overlap_x0 <= 0 or overlap_y1 - overlap_y0 <= 0: return 0.0 # if yes, calculate the ratio of the overlap to each ROI size and the unified size size_1 = (x1_1 - x0_1) * (y1_1 - y0_1) size_2 = (x1_2 - x0_2) * (y1_2 - y0_2) size_intersection = (overlap_x1 - overlap_x0) * (overlap_y1 - overlap_y0) size_union = size_1 + size_2 - size_intersection iou_ = size_intersection / size_union return iou_
[docs]def iou_xywh(bbox1, bbox2): """ Calculates the intersection-over-union of two bounding boxes. Source: https://github.com/bochinski/iou-tracker/blob/master/util.py Args: bbox1 (numpy.array or list[floats]): bounding box of length 4 containing ``(x-top-left, y-top-left, width, height)``. bbox2 (numpy.array or list[floats]): bounding box of length 4 containing ``(x-top-left, y-top-left, width, height)``. Returns: float: intersection-over-onion of bbox1, bbox2. """ bbox1 = bbox1[0], bbox1[1], bbox1[0]+bbox1[2], bbox1[1]+bbox1[3] bbox2 = bbox2[0], bbox2[1], bbox2[0]+bbox2[2], bbox2[1]+bbox2[3] iou_ = iou(bbox1, bbox2) return iou_
[docs]def xyxy2xywh(xyxy): """ Convert bounding box coordinates from (xmin, ymin, xmax, ymax) format to (xmin, ymin, width, height). Args: xyxy (numpy.ndarray): Returns: numpy.ndarray: Bounding box coordinates (xmin, ymin, width, height). """ if len(xyxy.shape) == 2: w, h = xyxy[:, 2] - xyxy[:, 0] + 1, xyxy[:, 3] - xyxy[:, 1] + 1 xywh = np.concatenate((xyxy[:, 0:2], w[:, None], h[:, None]), axis=1) return xywh.astype("int") elif len(xyxy.shape) == 1: (left, top, right, bottom) = xyxy width = right - left + 1 height = bottom - top + 1 return np.array([left, top, width, height]).astype('int') else: raise ValueError("Input shape not compatible.")
[docs]def xywh2xyxy(xywh): """ Convert bounding box coordinates from (xmin, ymin, width, height) to (xmin, ymin, xmax, ymax) format. Args: xywh (numpy.ndarray): Bounding box coordinates as `(xmin, ymin, width, height)`. Returns: numpy.ndarray : Bounding box coordinates as `(xmin, ymin, xmax, ymax)`. """ if len(xywh.shape) == 2: x = xywh[:, 0] + xywh[:, 2] y = xywh[:, 1] + xywh[:, 3] xyxy = np.concatenate((xywh[:, 0:2], x[:, None], y[:, None]), axis=1).astype('int') return xyxy if len(xywh.shape) == 1: x, y, w, h = xywh xr = x + w yb = y + h return np.array([x, y, xr, yb]).astype('int')
[docs]def midwh2xywh(midwh): """ Convert bounding box coordinates from (xmid, ymid, width, height) to (xmin, ymin, width, height) format. Args: midwh (numpy.ndarray): Bounding box coordinates (xmid, ymid, width, height). Returns: numpy.ndarray: Bounding box coordinates (xmin, ymin, width, height). """ if len(midwh.shape) == 2: xymin = midwh[:, 0:2] - midwh[:, 2:] * 0.5 wh = midwh[:, 2:] xywh = np.concatenate([xymin, wh], axis=1).astype('int') return xywh if len(midwh.shape) == 1: xmid, ymid, w, h = midwh xywh = np.array([xmid-w*0.5, ymid-h*0.5, w, h]).astype('int') return xywh
[docs]def intersection_complement_indices(big_set_indices, small_set_indices): """ Get the complement of intersection of two sets of indices. Args: big_set_indices (numpy.ndarray): Indices of big set. small_set_indices (numpy.ndarray): Indices of small set. Returns: numpy.ndarray: Indices of set which is complementary to intersection of two input sets. """ assert big_set_indices.shape[0] >= small_set_indices.shape[1] n = len(big_set_indices) mask = np.ones((n,), dtype=bool) mask[small_set_indices] = False intersection_complement = big_set_indices[mask] return intersection_complement
[docs]def nms(boxes, scores, overlapThresh, classes=None): """ Non-maximum suppression. based on Malisiewicz et al. Args: boxes (numpy.ndarray): Boxes to process (xmin, ymin, xmax, ymax) scores (numpy.ndarray): Corresponding scores for each box overlapThresh (float): Overlap threshold for boxes to merge classes (numpy.ndarray, optional): Class ids for each box. Returns: tuple: a tuple containing: - boxes (list): nms boxes - scores (list): nms scores - classes (list, optional): nms classes if specified """ if boxes.dtype.kind == "i": boxes = boxes.astype("float") if scores.dtype.kind == "i": scores = scores.astype("float") pick = [] x1 = boxes[:, 0] y1 = boxes[:, 1] x2 = boxes[:, 2] y2 = boxes[:, 3] area = (x2 - x1 + 1) * (y2 - y1 + 1) idxs = np.argsort(scores) while len(idxs) > 0: last = len(idxs) - 1 i = idxs[last] pick.append(i) xx1 = np.maximum(x1[i], x1[idxs[:last]]) yy1 = np.maximum(y1[i], y1[idxs[:last]]) xx2 = np.minimum(x2[i], x2[idxs[:last]]) yy2 = np.minimum(y2[i], y2[idxs[:last]]) w = np.maximum(0, xx2 - xx1 + 1) h = np.maximum(0, yy2 - yy1 + 1) overlap = (w * h) / area[idxs[:last]] # delete all indexes from the index list that have idxs = np.delete(idxs, np.concatenate(([last], np.where(overlap > overlapThresh)[0]))) if classes is not None: return boxes[pick], scores[pick], classes[pick] else: return boxes[pick], scores[pick]
[docs]def draw_tracks(image, tracks): """ Draw on input image. Args: image (numpy.ndarray): image tracks (list): list of tracks to be drawn on the image. Returns: numpy.ndarray: image with the track-ids drawn on it. """ for trk in tracks: trk_id = trk[1] xmin = trk[2] ymin = trk[3] width = trk[4] height = trk[5] xcentroid, ycentroid = int(xmin + 0.5*width), int(ymin + 0.5*height) text = "ID {}".format(trk_id) cv.putText(image, text, (xcentroid - 10, ycentroid - 10), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) cv.circle(image, (xcentroid, ycentroid), 4, (0, 255, 0), -1) return image
[docs]def load_labelsjson(json_file): import json with open(json_file) as file: data = json.load(file) labels = {int(k): v for k, v in data.items()} return labels
[docs]def dict2jsonfile(dict_data, json_file_path): import json with open(json_file_path, 'w') as outfile: json.dump(dict_data, outfile)
if __name__ == '__main__': bb = np.random.random_integers(0, 100, size=(20,)).reshape((5, 4)) c = get_centroid(bb) print(bb, c) bb2 = np.array([1, 2, 3, 4]) c2 = get_centroid(bb2) print(bb2, c2) data = { 0: 'background', 1: 'aeroplane', 2: 'bicycle', 3: 'bird', 4: 'boat', 5: 'bottle', 6: 'bus', 7: 'car', 8: 'cat', 9: 'chair', 10: 'cow', 11: 'diningtable', 12: 'dog', 13: 'horse', 14: 'motorbike', 15: 'person', 16: 'pottedplant', 17: 'sheep', 18: 'sofa', 19: 'train', 20: 'tvmonitor' } dict2jsonfile(data, '../../examples/pretrained_models/caffemodel_weights/ssd_mobilenet_caffe_names.json')