VerokeAI/Object_tracking_boxmot
0
1# Mikel Broström 🔥 Yolo Tracking 🧾 AGPL-3.0 license2 3import numpy as np4import torch5import cv26from typing import Tuple, Union7 8 9def xyxy2xywh(x):10 """11 Convert bounding box coordinates from (x1, y1, x2, y2) format to (x, y, width, height) format.12 13 Args:14 x (np.ndarray) or (torch.Tensor): The input bounding box coordinates in (x1, y1, x2, y2) format.15 Returns:16 y (np.ndarray) or (torch.Tensor): The bounding box coordinates in (x, y, width, height) format.17 """18 y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)19 y[..., 0] = (x[..., 0] + x[..., 2]) / 2 # x center20 y[..., 1] = (x[..., 1] + x[..., 3]) / 2 # y center21 y[..., 2] = x[..., 2] - x[..., 0] # width22 y[..., 3] = x[..., 3] - x[..., 1] # height23 return y24 25 26def xywh2xyxy(x):27 """28 Convert bounding box coordinates from (x_c, y_c, width, height) format to29 (x1, y1, x2, y2) format where (x1, y1) is the top-left corner and (x2, y2)30 is the bottom-right corner.31 32 Args:33 x (np.ndarray) or (torch.Tensor): The input bounding box coordinates in (x, y, width, height) format.34 Returns:35 y (np.ndarray) or (torch.Tensor): The bounding box coordinates in (x1, y1, x2, y2) format.36 """37 y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)38 y[..., 0] = x[..., 0] - x[..., 2] / 2 # top left x39 y[..., 1] = x[..., 1] - x[..., 3] / 2 # top left y40 y[..., 2] = x[..., 0] + x[..., 2] / 2 # bottom right x41 y[..., 3] = x[..., 1] + x[..., 3] / 2 # bottom right y42 return y43 44 45def xywh2tlwh(x):46 """47 Convert bounding box coordinates from (x c, y c, w, h) format to (t, l, w, h) format where (t, l) is the48 top-left corner and (w, h) is width and height.49 50 Args:51 x (np.ndarray) or (torch.Tensor): The input bounding box coordinates in (x, y, width, height) format.52 Returns:53 y (np.ndarray) or (torch.Tensor): The bounding box coordinates in (x1, y1, x2, y2) format.54 """55 y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)56 y[..., 0] = x[..., 0] - x[..., 2] / 2.0 # xc --> t57 y[..., 1] = x[..., 1] - x[..., 3] / 2.0 # yc --> l58 y[..., 2] = x[..., 2] # width59 y[..., 3] = x[..., 3] # height60 return y61 62 63def tlwh2xyxy(x):64 """65 Convert bounding box coordinates from (t, l ,w ,h) format to (t, l, w, h) format where (t, l) is the66 top-left corner and (w, h) is width and height.67 """68 y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)69 y[..., 0] = x[..., 0]70 y[..., 1] = x[..., 1]71 y[..., 2] = x[..., 0] + x[..., 2]72 y[..., 3] = x[..., 1] + x[..., 3]73 return y74 75 76def xyxy2tlwh(x):77 """78 Convert bounding box coordinates from (t, l ,w ,h) format to (t, l, w, h) format where (t, l) is the79 top-left corner and (w, h) is width and height.80 """81 y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)82 y[..., 0] = x[..., 0]83 y[..., 1] = x[..., 1]84 y[..., 2] = x[..., 2] - x[..., 0]85 y[..., 3] = x[..., 3] - x[..., 1]86 return y87 88 89def tlwh2xyah(x):90 """91 Convert bounding box coordinates from (t, l ,w ,h)92 to (center x, center y, aspect ratio, height)`, where the aspect ratio is `width / height`.93 """94 y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)95 y[..., 0] = x[..., 0] + (x[..., 2] / 2)96 y[..., 1] = x[..., 1] + (x[..., 3] / 2)97 y[..., 2] = x[..., 2] / x[..., 3]98 y[..., 3] = x[..., 3]99 return y100 101 102def xyxy2xysr(x):103 """104 Converts bounding box coordinates from (x1, y1, x2, y2) format to (x, y, s, r) format.105 106 Args:107 bbox (np.ndarray) or (torch.Tensor): The input bounding box coordinates in (x1, y1, x2, y2) format.108 Returns:109 z (np.ndarray) or (torch.Tensor): The bounding box coordinates in (x, y, s, r) format, where110 x, y is the center of the box,111 s is the scale (area), and112 r is the aspect ratio.113 """114 x = x[0:4]115 y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)116 w = y[..., 2] - y[..., 0] # width117 h = y[..., 3] - y[..., 1] # height118 y[..., 0] = y[..., 0] + w / 2.0 # x center119 y[..., 1] = y[..., 1] + h / 2.0 # y center120 y[..., 2] = w * h # scale (area)121 y[..., 3] = w / (h + 1e-6) # aspect ratio122 y = y.reshape((4, 1))123 return y124 125 126def letterbox(127 img: np.ndarray,128 new_shape: Union[int, Tuple[int, int]] = (640, 640),129 color: Tuple[int, int, int] = (114, 114, 114),130 auto: bool = True,131 scaleFill: bool = False,132 scaleup: bool = True133) -> Tuple[np.ndarray, Tuple[float, float], Tuple[float, float]]:134 """135 Resizes an image to a new shape while maintaining aspect ratio, padding with color if needed.136 137 Args:138 img (np.ndarray): The original image in BGR format.139 new_shape (Union[int, Tuple[int, int]], optional): Desired size as an integer (e.g., 640) 140 or tuple (width, height). Default is (640, 640).141 color (Tuple[int, int, int], optional): Padding color in BGR format. Default is (114, 114, 114).142 auto (bool, optional): If True, adjusts padding to be a multiple of 32. Default is True.143 scaleFill (bool, optional): If True, stretches the image to fill the new shape. Default is False.144 scaleup (bool, optional): If True, allows scaling up; otherwise, only scales down. Default is True.145 146 Returns:147 Tuple[np.ndarray, Tuple[float, float], Tuple[float, float]]:148 - Resized and padded image as np.ndarray.149 - Scaling ratio used for width and height as (width_ratio, height_ratio).150 - Padding applied to width and height as (width_padding, height_padding).151 """152 shape = img.shape[:2] # current shape [height, width]153 154 # Ensure new_shape is a tuple (width, height)155 if isinstance(new_shape, int):156 new_shape = (new_shape, new_shape)157 158 # Calculate scale ratio159 r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])160 if not scaleup:161 r = min(r, 1.0) # only scale down162 163 # Calculate new dimensions and padding164 ratio = (r, r)165 new_unpad = (int(round(shape[1] * r)), int(round(shape[0] * r)))166 dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]167 168 if auto: # minimum rectangle169 dw, dh = np.mod(dw, 32), np.mod(dh, 32)170 elif scaleFill: # stretch to fill171 dw, dh = 0.0, 0.0172 new_unpad = new_shape173 ratio = (new_shape[1] / shape[1], new_shape[0] / shape[0])174 175 # Divide padding by 2 for even distribution176 dw /= 2177 dh /= 2178 179 # Resize image if necessary180 if shape[::-1] != new_unpad:181 img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)182 183 # Add border to the image184 top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))185 left, right = int(round(dw - 0.1)), int(round(dw + 0.1))186 img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)187 188 return img, ratio, (dw, dh)189 