ultralytics-opencv-headless 8.4.7__py3-none-any.whl → 8.4.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. tests/test_cli.py +10 -3
  2. tests/test_cuda.py +1 -1
  3. tests/test_exports.py +64 -43
  4. tests/test_python.py +16 -12
  5. ultralytics/__init__.py +1 -1
  6. ultralytics/cfg/__init__.py +1 -0
  7. ultralytics/cfg/default.yaml +1 -0
  8. ultralytics/data/augment.py +2 -2
  9. ultralytics/data/converter.py +11 -0
  10. ultralytics/engine/exporter.py +13 -16
  11. ultralytics/engine/predictor.py +5 -0
  12. ultralytics/engine/trainer.py +3 -3
  13. ultralytics/engine/tuner.py +2 -2
  14. ultralytics/engine/validator.py +5 -0
  15. ultralytics/models/sam/predict.py +2 -2
  16. ultralytics/models/yolo/classify/train.py +14 -1
  17. ultralytics/models/yolo/detect/train.py +4 -2
  18. ultralytics/models/yolo/pose/train.py +2 -1
  19. ultralytics/models/yolo/world/train_world.py +21 -1
  20. ultralytics/models/yolo/yoloe/train.py +1 -2
  21. ultralytics/nn/autobackend.py +22 -6
  22. ultralytics/nn/modules/head.py +13 -2
  23. ultralytics/nn/tasks.py +18 -0
  24. ultralytics/solutions/security_alarm.py +1 -1
  25. ultralytics/utils/benchmarks.py +3 -9
  26. ultralytics/utils/checks.py +18 -3
  27. ultralytics/utils/dist.py +9 -3
  28. ultralytics/utils/loss.py +4 -5
  29. ultralytics/utils/tal.py +15 -5
  30. ultralytics/utils/torch_utils.py +2 -1
  31. {ultralytics_opencv_headless-8.4.7.dist-info → ultralytics_opencv_headless-8.4.9.dist-info}/METADATA +3 -3
  32. {ultralytics_opencv_headless-8.4.7.dist-info → ultralytics_opencv_headless-8.4.9.dist-info}/RECORD +36 -36
  33. {ultralytics_opencv_headless-8.4.7.dist-info → ultralytics_opencv_headless-8.4.9.dist-info}/WHEEL +1 -1
  34. {ultralytics_opencv_headless-8.4.7.dist-info → ultralytics_opencv_headless-8.4.9.dist-info}/entry_points.txt +0 -0
  35. {ultralytics_opencv_headless-8.4.7.dist-info → ultralytics_opencv_headless-8.4.9.dist-info}/licenses/LICENSE +0 -0
  36. {ultralytics_opencv_headless-8.4.7.dist-info → ultralytics_opencv_headless-8.4.9.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,14 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from pathlib import Path
4
6
 
5
7
  from ultralytics.data import YOLOConcatDataset, build_grounding, build_yolo_dataset
6
8
  from ultralytics.data.utils import check_det_dataset
7
9
  from ultralytics.models.yolo.world import WorldTrainer
8
10
  from ultralytics.utils import DATASETS_DIR, DEFAULT_CFG, LOGGER
11
+ from ultralytics.utils.checks import check_file
9
12
  from ultralytics.utils.torch_utils import unwrap_model
10
13
 
11
14
 
@@ -100,6 +103,23 @@ class WorldTrainerFromScratch(WorldTrainer):
100
103
  self.set_text_embeddings(datasets, batch) # cache text embeddings to accelerate training
101
104
  return YOLOConcatDataset(datasets) if len(datasets) > 1 else datasets[0]
102
105
 
106
+ @staticmethod
107
+ def check_data_config(data: dict | str | Path) -> dict:
108
+ """Check and load the data configuration from a YAML file or dictionary.
109
+
110
+ Args:
111
+ data (dict | str | Path): Data configuration as a dictionary or path to a YAML file.
112
+
113
+ Returns:
114
+ (dict): Data configuration dictionary loaded from YAML file or passed directly.
115
+ """
116
+ # If string, load from YAML file
117
+ if not isinstance(data, dict):
118
+ from ultralytics.utils import YAML
119
+
120
+ return YAML.load(check_file(data))
121
+ return data
122
+
103
123
  def get_dataset(self):
104
124
  """Get train and validation paths from data dictionary.
105
125
 
@@ -114,7 +134,7 @@ class WorldTrainerFromScratch(WorldTrainer):
114
134
  AssertionError: If train or validation datasets are not found, or if validation has multiple datasets.
115
135
  """
116
136
  final_data = {}
117
- data_yaml = self.args.data
137
+ self.args.data = data_yaml = self.check_data_config(self.args.data)
118
138
  assert data_yaml.get("train", False), "train dataset not found" # object365.yaml
119
139
  assert data_yaml.get("val", False), "validation dataset not found" # lvis.yaml
120
140
  data = {k: [check_det_dataset(d) for d in v.get("yolo_data", [])] for k, v in data_yaml.items()}
@@ -196,7 +196,7 @@ class YOLOETrainerFromScratch(YOLOETrainer, WorldTrainerFromScratch):
196
196
  Returns:
197
197
  (dict): Dictionary mapping text samples to their embeddings.
198
198
  """
199
- model = "mobileclip:blt"
199
+ model = unwrap_model(self.model).text_model
200
200
  cache_path = cache_dir / f"text_embeddings_{model.replace(':', '_').replace('/', '_')}.pt"
201
201
  if cache_path.exists():
202
202
  LOGGER.info(f"Reading existed cache from '{cache_path}'")
@@ -204,7 +204,6 @@ class YOLOETrainerFromScratch(YOLOETrainer, WorldTrainerFromScratch):
204
204
  if sorted(txt_map.keys()) == sorted(texts):
205
205
  return txt_map
206
206
  LOGGER.info(f"Caching text embeddings to '{cache_path}'")
207
- assert self.model is not None
208
207
  txt_feats = unwrap_model(self.model).get_text_pe(texts, batch, without_reprta=True, cache_clip_model=False)
209
208
  txt_map = dict(zip(texts, txt_feats.squeeze(0)))
210
209
  torch.save(txt_map, cache_path)
@@ -16,8 +16,24 @@ import torch
16
16
  import torch.nn as nn
17
17
  from PIL import Image
18
18
 
19
- from ultralytics.utils import ARM64, IS_JETSON, LINUX, LOGGER, PYTHON_VERSION, ROOT, YAML, is_jetson
20
- from ultralytics.utils.checks import check_requirements, check_suffix, check_version, check_yaml, is_rockchip
19
+ from ultralytics.utils import (
20
+ ARM64,
21
+ IS_JETSON,
22
+ LINUX,
23
+ LOGGER,
24
+ PYTHON_VERSION,
25
+ ROOT,
26
+ YAML,
27
+ is_jetson,
28
+ )
29
+ from ultralytics.utils.checks import (
30
+ check_executorch_requirements,
31
+ check_requirements,
32
+ check_suffix,
33
+ check_version,
34
+ check_yaml,
35
+ is_rockchip,
36
+ )
21
37
  from ultralytics.utils.downloads import attempt_download_asset, is_url
22
38
  from ultralytics.utils.nms import non_max_suppression
23
39
 
@@ -616,9 +632,9 @@ class AutoBackend(nn.Module):
616
632
  # ExecuTorch
617
633
  elif pte:
618
634
  LOGGER.info(f"Loading {w} for ExecuTorch inference...")
619
- # TorchAO release compatibility table bug https://github.com/pytorch/ao/issues/2919
620
- check_requirements("setuptools<71.0.0") # Setuptools bug: https://github.com/pypa/setuptools/issues/4483
621
- check_requirements(("executorch==1.0.1", "flatbuffers"))
635
+
636
+ check_executorch_requirements()
637
+
622
638
  from executorch.runtime import Runtime
623
639
 
624
640
  w = Path(w)
@@ -648,7 +664,7 @@ class AutoBackend(nn.Module):
648
664
  for k, v in metadata.items():
649
665
  if k in {"stride", "batch", "channels"}:
650
666
  metadata[k] = int(v)
651
- elif k in {"imgsz", "names", "kpt_shape", "kpt_names", "args"} and isinstance(v, str):
667
+ elif k in {"imgsz", "names", "kpt_shape", "kpt_names", "args", "end2end"} and isinstance(v, str):
652
668
  metadata[k] = ast.literal_eval(v)
653
669
  stride = metadata["stride"]
654
670
  task = metadata["task"]
@@ -69,6 +69,7 @@ class Detect(nn.Module):
69
69
  export = False # export mode
70
70
  format = None # export format
71
71
  max_det = 300 # max_det
72
+ agnostic_nms = False
72
73
  shape = None
73
74
  anchors = torch.empty(0) # init
74
75
  strides = torch.empty(0) # init
@@ -125,7 +126,12 @@ class Detect(nn.Module):
125
126
  @property
126
127
  def end2end(self):
127
128
  """Checks if the model has one2one for v5/v5/v8/v9/11 backward compatibility."""
128
- return hasattr(self, "one2one")
129
+ return getattr(self, "_end2end", True) and hasattr(self, "one2one")
130
+
131
+ @end2end.setter
132
+ def end2end(self, value):
133
+ """Override the end-to-end detection mode."""
134
+ self._end2end = value
129
135
 
130
136
  def forward_head(
131
137
  self, x: list[torch.Tensor], box_head: torch.nn.Module = None, cls_head: torch.nn.Module = None
@@ -230,6 +236,11 @@ class Detect(nn.Module):
230
236
  # Use max_det directly during export for TensorRT compatibility (requires k to be constant),
231
237
  # otherwise use min(max_det, anchors) for safety with small inputs during Python inference
232
238
  k = max_det if self.export else min(max_det, anchors)
239
+ if self.agnostic_nms:
240
+ scores, labels = scores.max(dim=-1, keepdim=True)
241
+ scores, indices = scores.topk(k, dim=1)
242
+ labels = labels.gather(1, indices)
243
+ return scores, labels, indices
233
244
  ori_index = scores.max(dim=-1)[0].topk(k)[1].unsqueeze(-1)
234
245
  scores = scores.gather(dim=1, index=ori_index.repeat(1, 1, nc))
235
246
  scores, index = scores.flatten(1).topk(k)
@@ -1098,7 +1109,7 @@ class YOLOEDetect(Detect):
1098
1109
  boxes, scores, index = [], [], []
1099
1110
  bs = x[0].shape[0]
1100
1111
  cv2 = self.cv2 if not self.end2end else self.one2one_cv2
1101
- cv3 = self.cv3 if not self.end2end else self.one2one_cv2
1112
+ cv3 = self.cv3 if not self.end2end else self.one2one_cv3
1102
1113
  for i in range(self.nl):
1103
1114
  cls_feat = cv3[i](x[i])
1104
1115
  loc_feat = cv2[i](x[i])
ultralytics/nn/tasks.py CHANGED
@@ -425,6 +425,24 @@ class DetectionModel(BaseModel):
425
425
  """Return whether the model uses end-to-end NMS-free detection."""
426
426
  return getattr(self.model[-1], "end2end", False)
427
427
 
428
+ @end2end.setter
429
+ def end2end(self, value):
430
+ """Override the end-to-end detection mode."""
431
+ self.set_head_attr(end2end=value)
432
+
433
+ def set_head_attr(self, **kwargs):
434
+ """Set attributes of the model head (last layer).
435
+
436
+ Args:
437
+ **kwargs: Arbitrary keyword arguments representing attributes to set.
438
+ """
439
+ head = self.model[-1]
440
+ for k, v in kwargs.items():
441
+ if not hasattr(head, k):
442
+ LOGGER.warning(f"Head has no attribute '{k}'.")
443
+ continue
444
+ setattr(head, k, v)
445
+
428
446
  def _predict_augment(self, x):
429
447
  """Perform augmentations on input image x and return augmented inference and train outputs.
430
448
 
@@ -62,7 +62,7 @@ class SecurityAlarm(BaseSolution):
62
62
  """
63
63
  import smtplib
64
64
 
65
- self.server = smtplib.SMTP("smtp.gmail.com: 587")
65
+ self.server = smtplib.SMTP("smtp.gmail.com", 587)
66
66
  self.server.starttls()
67
67
  self.server.login(from_email, password)
68
68
  self.to_email = to_email
@@ -36,6 +36,7 @@ import platform
36
36
  import re
37
37
  import shutil
38
38
  import time
39
+ from copy import deepcopy
39
40
  from pathlib import Path
40
41
 
41
42
  import numpy as np
@@ -101,7 +102,6 @@ def benchmark(
101
102
  device = select_device(device, verbose=False)
102
103
  if isinstance(model, (str, Path)):
103
104
  model = YOLO(model)
104
- is_end2end = getattr(model.model.model[-1], "end2end", False)
105
105
  data = data or TASK2DATA[model.task] # task to dataset, i.e. coco8.yaml for task=detect
106
106
  key = TASK2METRIC[model.task] # task to metric, i.e. metrics/mAP50-95(B) for task=detect
107
107
 
@@ -135,14 +135,12 @@ def benchmark(
135
135
  if format == "paddle":
136
136
  assert not isinstance(model, YOLOWorld), "YOLOWorldv2 Paddle exports not supported yet"
137
137
  assert model.task != "obb", "Paddle OBB bug https://github.com/PaddlePaddle/Paddle/issues/72024"
138
- assert not is_end2end, "End-to-end models not supported by PaddlePaddle yet"
139
138
  assert (LINUX and not IS_JETSON) or MACOS, "Windows and Jetson Paddle exports not supported yet"
140
139
  if format == "mnn":
141
140
  assert not isinstance(model, YOLOWorld), "YOLOWorldv2 MNN exports not supported yet"
142
141
  if format == "ncnn":
143
142
  assert not isinstance(model, YOLOWorld), "YOLOWorldv2 NCNN exports not supported yet"
144
143
  if format == "imx":
145
- assert not is_end2end
146
144
  assert not isinstance(model, YOLOWorld), "YOLOWorldv2 IMX exports not supported"
147
145
  assert model.task in {"detect", "classify", "pose"}, (
148
146
  "IMX export is only supported for detection, classification and pose estimation tasks"
@@ -150,25 +148,21 @@ def benchmark(
150
148
  assert "C2f" in model.__str__(), "IMX only supported for YOLOv8n and YOLO11n"
151
149
  if format == "rknn":
152
150
  assert not isinstance(model, YOLOWorld), "YOLOWorldv2 RKNN exports not supported yet"
153
- assert not is_end2end, "End-to-end models not supported by RKNN yet"
154
151
  assert LINUX, "RKNN only supported on Linux"
155
152
  assert not is_rockchip(), "RKNN Inference only supported on Rockchip devices"
156
153
  if format == "executorch":
157
154
  assert not isinstance(model, YOLOWorld), "YOLOWorldv2 ExecuTorch exports not supported yet"
158
- assert not is_end2end, "End-to-end models not supported by ExecuTorch yet"
159
155
  if "cpu" in device.type:
160
156
  assert cpu, "inference not supported on CPU"
161
157
  if "cuda" in device.type:
162
158
  assert gpu, "inference not supported on GPU"
163
- if format == "ncnn":
164
- assert not is_end2end, "End-to-end torch.topk operation is not supported for NCNN prediction yet"
165
159
 
166
160
  # Export
167
161
  if format == "-":
168
162
  filename = model.pt_path or model.ckpt_path or model.model_name
169
- exported_model = model # PyTorch format
163
+ exported_model = deepcopy(model) # PyTorch format
170
164
  else:
171
- filename = model.export(
165
+ filename = deepcopy(model).export(
172
166
  imgsz=imgsz, format=format, half=half, int8=int8, data=data, device=device, verbose=False, **kwargs
173
167
  )
174
168
  exported_model = YOLO(filename, task=model.task)
@@ -29,6 +29,7 @@ from ultralytics.utils import (
29
29
  AUTOINSTALL,
30
30
  GIT,
31
31
  IS_COLAB,
32
+ IS_DOCKER,
32
33
  IS_JETSON,
33
34
  IS_KAGGLE,
34
35
  IS_PIP_PACKAGE,
@@ -495,6 +496,17 @@ def check_requirements(requirements=ROOT.parent / "requirements.txt", exclude=()
495
496
  return True
496
497
 
497
498
 
499
+ def check_executorch_requirements():
500
+ """Check and install ExecuTorch requirements including platform-specific dependencies."""
501
+ # BUG executorch build on arm64 Docker requires packaging>=22.0 https://github.com/pypa/setuptools/issues/4483
502
+ if LINUX and ARM64 and IS_DOCKER:
503
+ check_requirements("packaging>=22.0")
504
+
505
+ check_requirements("executorch", cmds=f"torch=={TORCH_VERSION.split('+')[0]}")
506
+ # Pin numpy to avoid coremltools errors with numpy>=2.4.0, must be separate
507
+ check_requirements("numpy<=2.3.5")
508
+
509
+
498
510
  def check_torchvision():
499
511
  """Check the installed versions of PyTorch and Torchvision to ensure they're compatible.
500
512
 
@@ -546,7 +558,7 @@ def check_suffix(file="yolo26n.pt", suffix=".pt", msg=""):
546
558
  assert f".{s}" in suffix, f"{msg}{f} acceptable suffix is {suffix}, not .{s}"
547
559
 
548
560
 
549
- def check_yolov5u_filename(file: str, verbose: bool = True):
561
+ def check_yolov5u_filename(file: str, verbose: bool = True) -> str:
550
562
  """Replace legacy YOLOv5 filenames with updated YOLOv5u filenames.
551
563
 
552
564
  Args:
@@ -573,7 +585,7 @@ def check_yolov5u_filename(file: str, verbose: bool = True):
573
585
  return file
574
586
 
575
587
 
576
- def check_model_file_from_stem(model="yolo11n"):
588
+ def check_model_file_from_stem(model: str = "yolo11n") -> str | Path:
577
589
  """Return a model filename from a valid model stem.
578
590
 
579
591
  Args:
@@ -619,6 +631,9 @@ def check_file(file, suffix="", download=True, download_dir=".", hard=True):
619
631
  # Use URI path for unique directory structure: ul://user/project/model -> user/project/model/filename.pt
620
632
  uri_path = file[5:] # Remove "ul://"
621
633
  local_file = Path(download_dir) / uri_path / url2file(url)
634
+ # Always re-download NDJSON datasets (cheap, ensures fresh data after updates)
635
+ if local_file.suffix == ".ndjson":
636
+ local_file.unlink(missing_ok=True)
622
637
  if local_file.exists():
623
638
  LOGGER.info(f"Found {clean_url(url)} locally at {local_file}")
624
639
  else:
@@ -660,7 +675,7 @@ def check_yaml(file, suffix=(".yaml", ".yml"), hard=True):
660
675
  return check_file(file, suffix, hard=hard)
661
676
 
662
677
 
663
- def check_is_path_safe(basedir, path):
678
+ def check_is_path_safe(basedir: Path | str, path: Path | str) -> bool:
664
679
  """Check if the resolved path is under the intended directory to prevent path traversal.
665
680
 
666
681
  Args:
ultralytics/utils/dist.py CHANGED
@@ -1,13 +1,19 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import os
4
6
  import shutil
5
7
  import sys
6
8
  import tempfile
9
+ from typing import TYPE_CHECKING
7
10
 
8
11
  from . import USER_CONFIG_DIR
9
12
  from .torch_utils import TORCH_1_9
10
13
 
14
+ if TYPE_CHECKING:
15
+ from ultralytics.engine.trainer import BaseTrainer
16
+
11
17
 
12
18
  def find_free_network_port() -> int:
13
19
  """Find a free port on localhost.
@@ -25,7 +31,7 @@ def find_free_network_port() -> int:
25
31
  return s.getsockname()[1] # port
26
32
 
27
33
 
28
- def generate_ddp_file(trainer):
34
+ def generate_ddp_file(trainer: BaseTrainer) -> str:
29
35
  """Generate a DDP (Distributed Data Parallel) file for multi-GPU training.
30
36
 
31
37
  This function creates a temporary Python file that enables distributed training across multiple GPUs. The file
@@ -75,7 +81,7 @@ if __name__ == "__main__":
75
81
  return file.name
76
82
 
77
83
 
78
- def generate_ddp_command(trainer):
84
+ def generate_ddp_command(trainer: BaseTrainer) -> tuple[list[str], str]:
79
85
  """Generate command for distributed training.
80
86
 
81
87
  Args:
@@ -105,7 +111,7 @@ def generate_ddp_command(trainer):
105
111
  return cmd, file
106
112
 
107
113
 
108
- def ddp_cleanup(trainer, file):
114
+ def ddp_cleanup(trainer: BaseTrainer, file: str) -> None:
109
115
  """Delete temporary file if created during distributed data parallel (DDP) training.
110
116
 
111
117
  This function checks if the provided file contains the trainer's ID in its name, indicating it was created as a
ultralytics/utils/loss.py CHANGED
@@ -1105,7 +1105,7 @@ class v8OBBLoss(v8DetectionLoss):
1105
1105
  pred_theta = pred_bboxes[..., 4]
1106
1106
  target_theta = target_bboxes[..., 4]
1107
1107
 
1108
- log_ar = torch.log(w_gt / h_gt)
1108
+ log_ar = torch.log((w_gt + 1e-9) / (h_gt + 1e-9))
1109
1109
  scale_weight = torch.exp(-(log_ar**2) / (lambda_val**2))
1110
1110
 
1111
1111
  delta_theta = pred_theta - target_theta
@@ -1174,9 +1174,9 @@ class E2ELoss:
1174
1174
  class TVPDetectLoss:
1175
1175
  """Criterion class for computing training losses for text-visual prompt detection."""
1176
1176
 
1177
- def __init__(self, model, tal_topk=10):
1177
+ def __init__(self, model, tal_topk=10, tal_topk2: int | None = None):
1178
1178
  """Initialize TVPDetectLoss with task-prompt and visual-prompt criteria using the provided model."""
1179
- self.vp_criterion = v8DetectionLoss(model, tal_topk)
1179
+ self.vp_criterion = v8DetectionLoss(model, tal_topk, tal_topk2)
1180
1180
  # NOTE: store following info as it's changeable in __call__
1181
1181
  self.hyp = self.vp_criterion.hyp
1182
1182
  self.ori_nc = self.vp_criterion.nc
@@ -1206,8 +1206,7 @@ class TVPDetectLoss:
1206
1206
 
1207
1207
  def _get_vp_features(self, preds: dict[str, torch.Tensor]) -> list[torch.Tensor]:
1208
1208
  """Extract visual-prompt features from the model output."""
1209
- # NOTE: remove empty placeholder
1210
- scores = preds["scores"][:, self.ori_nc :, :]
1209
+ scores = preds["scores"]
1211
1210
  vnc = scores.shape[1]
1212
1211
 
1213
1212
  self.vp_criterion.nc = vnc
ultralytics/utils/tal.py CHANGED
@@ -24,6 +24,7 @@ class TaskAlignedAssigner(nn.Module):
24
24
  alpha (float): The alpha parameter for the classification component of the task-aligned metric.
25
25
  beta (float): The beta parameter for the localization component of the task-aligned metric.
26
26
  stride (list): List of stride values for different feature levels.
27
+ stride_val (int): The stride value used for select_candidates_in_gts.
27
28
  eps (float): A small value to prevent division by zero.
28
29
  """
29
30
 
@@ -55,6 +56,7 @@ class TaskAlignedAssigner(nn.Module):
55
56
  self.alpha = alpha
56
57
  self.beta = beta
57
58
  self.stride = stride
59
+ self.stride_val = self.stride[1] if len(self.stride) > 1 else self.stride[0]
58
60
  self.eps = eps
59
61
 
60
62
  @torch.no_grad()
@@ -302,8 +304,11 @@ class TaskAlignedAssigner(nn.Module):
302
304
  """
303
305
  gt_bboxes_xywh = xyxy2xywh(gt_bboxes)
304
306
  wh_mask = gt_bboxes_xywh[..., 2:] < self.stride[0] # the smallest stride
305
- stride_val = torch.tensor(self.stride[1], dtype=gt_bboxes_xywh.dtype, device=gt_bboxes_xywh.device)
306
- gt_bboxes_xywh[..., 2:] = torch.where((wh_mask * mask_gt).bool(), stride_val, gt_bboxes_xywh[..., 2:])
307
+ gt_bboxes_xywh[..., 2:] = torch.where(
308
+ (wh_mask * mask_gt).bool(),
309
+ torch.tensor(self.stride_val, dtype=gt_bboxes_xywh.dtype, device=gt_bboxes_xywh.device),
310
+ gt_bboxes_xywh[..., 2:],
311
+ )
307
312
  gt_bboxes = xywh2xyxy(gt_bboxes_xywh)
308
313
 
309
314
  n_anchors = xy_centers.shape[0]
@@ -357,19 +362,24 @@ class RotatedTaskAlignedAssigner(TaskAlignedAssigner):
357
362
  """Calculate IoU for rotated bounding boxes."""
358
363
  return probiou(gt_bboxes, pd_bboxes).squeeze(-1).clamp_(0)
359
364
 
360
- @staticmethod
361
- def select_candidates_in_gts(xy_centers, gt_bboxes, mask_gt):
365
+ def select_candidates_in_gts(self, xy_centers, gt_bboxes, mask_gt):
362
366
  """Select the positive anchor center in gt for rotated bounding boxes.
363
367
 
364
368
  Args:
365
369
  xy_centers (torch.Tensor): Anchor center coordinates with shape (h*w, 2).
366
370
  gt_bboxes (torch.Tensor): Ground truth bounding boxes with shape (b, n_boxes, 5).
367
371
  mask_gt (torch.Tensor): Mask for valid ground truth boxes with shape (b, n_boxes, 1).
368
- stride (list[int]): List of stride values for each feature map level.
369
372
 
370
373
  Returns:
371
374
  (torch.Tensor): Boolean mask of positive anchors with shape (b, n_boxes, h*w).
372
375
  """
376
+ wh_mask = gt_bboxes[..., 2:4] < self.stride[0]
377
+ gt_bboxes[..., 2:4] = torch.where(
378
+ (wh_mask * mask_gt).bool(),
379
+ torch.tensor(self.stride_val, dtype=gt_bboxes.dtype, device=gt_bboxes.device),
380
+ gt_bboxes[..., 2:4],
381
+ )
382
+
373
383
  # (b, n_boxes, 5) --> (b, n_boxes, 4, 2)
374
384
  corners = xywhr2xyxyxyxy(gt_bboxes)
375
385
  # (b, n_boxes, 1, 2)
@@ -46,6 +46,7 @@ TORCH_2_1 = check_version(TORCH_VERSION, "2.1.0")
46
46
  TORCH_2_4 = check_version(TORCH_VERSION, "2.4.0")
47
47
  TORCH_2_8 = check_version(TORCH_VERSION, "2.8.0")
48
48
  TORCH_2_9 = check_version(TORCH_VERSION, "2.9.0")
49
+ TORCH_2_10 = check_version(TORCH_VERSION, "2.10.0")
49
50
  TORCHVISION_0_10 = check_version(TORCHVISION_VERSION, "0.10.0")
50
51
  TORCHVISION_0_11 = check_version(TORCHVISION_VERSION, "0.11.0")
51
52
  TORCHVISION_0_13 = check_version(TORCHVISION_VERSION, "0.13.0")
@@ -78,7 +79,7 @@ def smart_inference_mode():
78
79
  if TORCH_1_9 and torch.is_inference_mode_enabled():
79
80
  return fn # already in inference_mode, act as a pass-through
80
81
  else:
81
- return (torch.inference_mode if TORCH_1_9 else torch.no_grad)()(fn)
82
+ return (torch.inference_mode if TORCH_1_10 else torch.no_grad)()(fn)
82
83
 
83
84
  return decorate
84
85
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ultralytics-opencv-headless
3
- Version: 8.4.7
3
+ Version: 8.4.9
4
4
  Summary: Ultralytics YOLO 🚀 for SOTA object detection, multi-object tracking, instance segmentation, pose estimation and image classification.
5
5
  Author-email: Glenn Jocher <glenn.jocher@ultralytics.com>, Jing Qiu <jing.qiu@ultralytics.com>
6
6
  Maintainer-email: Ultralytics <hello@ultralytics.com>
@@ -39,8 +39,8 @@ Requires-Dist: pillow>=7.1.2
39
39
  Requires-Dist: pyyaml>=5.3.1
40
40
  Requires-Dist: requests>=2.23.0
41
41
  Requires-Dist: scipy>=1.4.1
42
- Requires-Dist: torch<2.10,>=1.8.0
43
- Requires-Dist: torch!=2.4.0,<2.10,>=1.8.0; sys_platform == "win32"
42
+ Requires-Dist: torch>=1.8.0
43
+ Requires-Dist: torch!=2.4.0,>=1.8.0; sys_platform == "win32"
44
44
  Requires-Dist: torchvision>=0.9.0
45
45
  Requires-Dist: psutil>=5.8.0
46
46
  Requires-Dist: polars>=0.20.0