From 8f79ce45c16f22cb313c9242af83984e56b8e7d8 Mon Sep 17 00:00:00 2001 From: Laughing <61612323+Laughing-q@users.noreply.github.com> Date: Sun, 20 Aug 2023 19:15:37 +0800 Subject: [PATCH] Fix conversion ops using `clone` and `copy` (#4438) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Glenn Jocher --- ultralytics/utils/downloads.py | 3 +-- ultralytics/utils/instance.py | 36 +++++++------------------------- ultralytics/utils/ops.py | 38 +++++++++++++++++++--------------- 3 files changed, 29 insertions(+), 48 deletions(-) diff --git a/ultralytics/utils/downloads.py b/ultralytics/utils/downloads.py index 2de31a9..f32a039 100644 --- a/ultralytics/utils/downloads.py +++ b/ultralytics/utils/downloads.py @@ -13,7 +13,7 @@ import requests import torch from tqdm import tqdm -from ultralytics.utils import LOGGER, checks, clean_url, emojis, is_online, url2file +from ultralytics.utils import LOGGER, TQDM_BAR_FORMAT, checks, clean_url, emojis, is_online, url2file GITHUB_ASSET_NAMES = [f'yolov8{k}{suffix}.pt' for k in 'nsmlx' for suffix in ('', '6', '-cls', '-seg', '-pose')] + \ [f'yolov5{k}u.pt' for k in 'nsmlx'] + \ @@ -287,7 +287,6 @@ def safe_download(url, if method == 'torch': torch.hub.download_url_to_file(url, f, progress=progress) else: - from ultralytics.utils import TQDM_BAR_FORMAT with request.urlopen(url) as response, tqdm(total=int(response.getheader('Content-Length', 0)), desc=desc, disable=not progress, diff --git a/ultralytics/utils/instance.py b/ultralytics/utils/instance.py index 68f9613..4e2e438 100644 --- a/ultralytics/utils/instance.py +++ b/ultralytics/utils/instance.py @@ -24,15 +24,15 @@ to_2tuple = _ntuple(2) to_4tuple = _ntuple(4) # `xyxy` means left top and right bottom -# `xywh` means center x, center y and width, height(yolo format) -# `ltwh` means left top and width, height(coco format) +# `xywh` means center x, center y and width, height(YOLO format) +# `ltwh` means left top and width, height(COCO format) _formats = ['xyxy', 'xywh', 'ltwh'] __all__ = 'Bboxes', # tuple or list class Bboxes: - """Now only numpy is supported.""" + """Bounding Boxes class. Only numpy variables are supported.""" def __init__(self, bboxes, format='xyxy') -> None: assert format in _formats, f'Invalid bounding box format: {format}, format must be one of {_formats}' @@ -43,40 +43,18 @@ class Bboxes: self.format = format # self.normalized = normalized - # def convert(self, format): - # assert format in _formats - # if self.format == format: - # bboxes = self.bboxes - # elif self.format == "xyxy": - # if format == "xywh": - # bboxes = xyxy2xywh(self.bboxes) - # else: - # bboxes = xyxy2ltwh(self.bboxes) - # elif self.format == "xywh": - # if format == "xyxy": - # bboxes = xywh2xyxy(self.bboxes) - # else: - # bboxes = xywh2ltwh(self.bboxes) - # else: - # if format == "xyxy": - # bboxes = ltwh2xyxy(self.bboxes) - # else: - # bboxes = ltwh2xywh(self.bboxes) - # - # return Bboxes(bboxes, format) - def convert(self, format): """Converts bounding box format from one type to another.""" assert format in _formats, f'Invalid bounding box format: {format}, format must be one of {_formats}' if self.format == format: return elif self.format == 'xyxy': - bboxes = xyxy2xywh(self.bboxes) if format == 'xywh' else xyxy2ltwh(self.bboxes) + func = xyxy2xywh if format == 'xywh' else xyxy2ltwh elif self.format == 'xywh': - bboxes = xywh2xyxy(self.bboxes) if format == 'xyxy' else xywh2ltwh(self.bboxes) + func = xywh2xyxy if format == 'xyxy' else xywh2ltwh else: - bboxes = ltwh2xyxy(self.bboxes) if format == 'xyxy' else ltwh2xywh(self.bboxes) - self.bboxes = bboxes + func = ltwh2xyxy if format == 'xyxy' else ltwh2xywh + self.bboxes = func(self.bboxes) self.format = format def areas(self): diff --git a/ultralytics/utils/ops.py b/ultralytics/utils/ops.py index 1042e3c..75a3270 100644 --- a/ultralytics/utils/ops.py +++ b/ultralytics/utils/ops.py @@ -344,7 +344,8 @@ def xyxy2xywh(x): Returns: y (np.ndarray | torch.Tensor): The bounding box coordinates in (x, y, width, height) format. """ - y = torch.empty_like(x) if isinstance(x, torch.Tensor) else np.empty_like(x) + assert x.shape[-1] == 4, f'input shape last dimension expected 4 but input shape is {x.shape}' + y = torch.empty_like(x) if isinstance(x, torch.Tensor) else np.empty_like(x) # faster than clone/copy y[..., 0] = (x[..., 0] + x[..., 2]) / 2 # x center y[..., 1] = (x[..., 1] + x[..., 3]) / 2 # y center y[..., 2] = x[..., 2] - x[..., 0] # width @@ -362,7 +363,8 @@ def xywh2xyxy(x): Returns: y (np.ndarray | torch.Tensor): The bounding box coordinates in (x1, y1, x2, y2) format. """ - y = torch.empty_like(x) if isinstance(x, torch.Tensor) else np.empty_like(x) + assert x.shape[-1] == 4, f'input shape last dimension expected 4 but input shape is {x.shape}' + y = torch.empty_like(x) if isinstance(x, torch.Tensor) else np.empty_like(x) # faster than clone/copy dw = x[..., 2] / 2 # half-width dh = x[..., 3] / 2 # half-height y[..., 0] = x[..., 0] - dw # top left x @@ -386,7 +388,8 @@ def xywhn2xyxy(x, w=640, h=640, padw=0, padh=0): y (np.ndarray | torch.Tensor): The coordinates of the bounding box in the format [x1, y1, x2, y2] where x1,y1 is the top-left corner, x2,y2 is the bottom-right corner of the bounding box. """ - y = torch.empty_like(x) if isinstance(x, torch.Tensor) else np.empty_like(x) + assert x.shape[-1] == 4, f'input shape last dimension expected 4 but input shape is {x.shape}' + y = torch.empty_like(x) if isinstance(x, torch.Tensor) else np.empty_like(x) # faster than clone/copy y[..., 0] = w * (x[..., 0] - x[..., 2] / 2) + padw # top left x y[..., 1] = h * (x[..., 1] - x[..., 3] / 2) + padh # top left y y[..., 2] = w * (x[..., 0] + x[..., 2] / 2) + padw # bottom right x @@ -410,7 +413,8 @@ def xyxy2xywhn(x, w=640, h=640, clip=False, eps=0.0): """ if clip: clip_boxes(x, (h - eps, w - eps)) # warning: inplace clip - y = torch.empty_like(x) if isinstance(x, torch.Tensor) else np.empty_like(x) + assert x.shape[-1] == 4, f'input shape last dimension expected 4 but input shape is {x.shape}' + y = torch.empty_like(x) if isinstance(x, torch.Tensor) else np.empty_like(x) # faster than clone/copy y[..., 0] = ((x[..., 0] + x[..., 2]) / 2) / w # x center y[..., 1] = ((x[..., 1] + x[..., 3]) / 2) / h # y center y[..., 2] = (x[..., 2] - x[..., 0]) / w # width @@ -431,7 +435,7 @@ def xyn2xy(x, w=640, h=640, padw=0, padh=0): Returns: y (np.ndarray | torch.Tensor): The x and y coordinates of the top left corner of the bounding box """ - y = torch.empty_like(x) if isinstance(x, torch.Tensor) else np.empty_like(x) + y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x) y[..., 0] = w * x[..., 0] + padw # top left x y[..., 1] = h * x[..., 1] + padh # top left y return y @@ -446,9 +450,9 @@ def xywh2ltwh(x): Returns: y (np.ndarray | torch.Tensor): The bounding box coordinates in the xyltwh format """ - y = torch.empty_like(x) if isinstance(x, torch.Tensor) else np.empty_like(x) - y[:, 0] = x[:, 0] - x[:, 2] / 2 # top left x - y[:, 1] = x[:, 1] - x[:, 3] / 2 # top left y + y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x) + y[..., 0] = x[..., 0] - x[..., 2] / 2 # top left x + y[..., 1] = x[..., 1] - x[..., 3] / 2 # top left y return y @@ -461,9 +465,9 @@ def xyxy2ltwh(x): Returns: y (np.ndarray | torch.Tensor): The bounding box coordinates in the xyltwh format. """ - y = torch.empty_like(x) if isinstance(x, torch.Tensor) else np.empty_like(x) - y[:, 2] = x[:, 2] - x[:, 0] # width - y[:, 3] = x[:, 3] - x[:, 1] # height + y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x) + y[..., 2] = x[..., 2] - x[..., 0] # width + y[..., 3] = x[..., 3] - x[..., 1] # height return y @@ -474,9 +478,9 @@ def ltwh2xywh(x): Args: x (torch.Tensor): the input tensor """ - y = torch.empty_like(x) if isinstance(x, torch.Tensor) else np.empty_like(x) - y[:, 0] = x[:, 0] + x[:, 2] / 2 # center x - y[:, 1] = x[:, 1] + x[:, 3] / 2 # center y + y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x) + y[..., 0] = x[..., 0] + x[..., 2] / 2 # center x + y[..., 1] = x[..., 1] + x[..., 3] / 2 # center y return y @@ -569,9 +573,9 @@ def ltwh2xyxy(x): Returns: y (np.ndarray | torch.Tensor): the xyxy coordinates of the bounding boxes. """ - y = torch.empty_like(x) if isinstance(x, torch.Tensor) else np.empty_like(x) - y[:, 2] = x[:, 2] + x[:, 0] # width - y[:, 3] = x[:, 3] + x[:, 1] # height + y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x) + y[..., 2] = x[..., 2] + x[..., 0] # width + y[..., 3] = x[..., 3] + x[..., 1] # height return y