ultralytics 8.3.138__py3-none-any.whl → 8.3.140__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.
tests/test_cuda.py CHANGED
@@ -41,7 +41,6 @@ def test_amp():
41
41
 
42
42
 
43
43
  @pytest.mark.slow
44
- @pytest.mark.skipif(IS_JETSON, reason="Temporary disable ONNX for Jetson")
45
44
  @pytest.mark.skipif(not DEVICES, reason="No CUDA devices available")
46
45
  @pytest.mark.parametrize(
47
46
  "task, dynamic, int8, half, batch, simplify, nms",
@@ -50,7 +49,9 @@ def test_amp():
50
49
  for task, dynamic, int8, half, batch, simplify, nms in product(
51
50
  TASKS, [True, False], [False], [False], [1, 2], [True, False], [True, False]
52
51
  )
53
- if not ((int8 and half) or (task == "classify" and nms) or (task == "obb" and nms and not TORCH_1_13))
52
+ if not (
53
+ (int8 and half) or (task == "classify" and nms) or (task == "obb" and nms and (not TORCH_1_13 or IS_JETSON))
54
+ )
54
55
  ],
55
56
  )
56
57
  def test_export_onnx_matrix(task, dynamic, int8, half, batch, simplify, nms):
tests/test_python.py CHANGED
@@ -198,7 +198,13 @@ def test_track_stream():
198
198
 
199
199
  def test_val():
200
200
  """Test the validation mode of the YOLO model."""
201
- YOLO(MODEL).val(data="coco8.yaml", imgsz=32)
201
+ metrics = YOLO(MODEL).val(data="coco8.yaml", imgsz=32)
202
+ metrics.to_df()
203
+ metrics.to_csv()
204
+ metrics.to_xml()
205
+ metrics.to_html()
206
+ metrics.to_json()
207
+ metrics.to_sql()
202
208
 
203
209
 
204
210
  def test_train_scratch():
@@ -264,7 +270,8 @@ def test_predict_callback_and_setup():
264
270
  @pytest.mark.parametrize("model", MODELS)
265
271
  def test_results(model):
266
272
  """Test YOLO model results processing and output in various formats."""
267
- results = YOLO(WEIGHTS_DIR / model)([SOURCE, SOURCE], imgsz=160)
273
+ temp_s = "https://ultralytics.com/images/boats.jpg" if model == "yolo11n-obb.pt" else SOURCE
274
+ results = YOLO(WEIGHTS_DIR / model)([temp_s, temp_s], imgsz=160)
268
275
  for r in results:
269
276
  r = r.cpu().numpy()
270
277
  print(r, len(r), r.path) # print numpy attributes
tests/test_solutions.py CHANGED
@@ -12,7 +12,7 @@ import pytest
12
12
 
13
13
  from tests import MODEL, TMP
14
14
  from ultralytics import solutions
15
- from ultralytics.utils import ASSETS_URL, IS_RASPBERRYPI, LINUX, checks
15
+ from ultralytics.utils import ASSETS_URL, IS_RASPBERRYPI, checks
16
16
  from ultralytics.utils.downloads import safe_download
17
17
 
18
18
  # Pre-defined arguments values
@@ -180,10 +180,7 @@ def process_video(solution, video_path, needs_frame_count=False):
180
180
  cap.release()
181
181
 
182
182
 
183
- @pytest.mark.skipif(
184
- (LINUX and checks.IS_PYTHON_3_11) or IS_RASPBERRYPI,
185
- reason="Disabled for testing due to --slow test errors after YOLOE PR.",
186
- )
183
+ @pytest.mark.skipif(IS_RASPBERRYPI, reason="Disabled for testing due to --slow test errors after YOLOE PR.")
187
184
  @pytest.mark.parametrize("name, solution_class, needs_frame_count, video, kwargs", SOLUTIONS)
188
185
  def test_solution(name, solution_class, needs_frame_count, video, kwargs):
189
186
  """Test individual Ultralytics solution."""
@@ -300,6 +297,7 @@ def test_streamlit_handle_video_upload_creates_file():
300
297
  os.remove("ultralytics.mp4")
301
298
 
302
299
 
300
+ @pytest.mark.skipif(IS_RASPBERRYPI, reason="Disabled due to slow performance on Raspberry Pi.")
303
301
  def test_similarity_search_app_init():
304
302
  """Test SearchApp initializes with required attributes."""
305
303
  app = solutions.SearchApp(device="cpu")
@@ -307,6 +305,7 @@ def test_similarity_search_app_init():
307
305
  assert hasattr(app, "run")
308
306
 
309
307
 
308
+ @pytest.mark.skipif(IS_RASPBERRYPI, reason="Disabled due to slow performance on Raspberry Pi.")
310
309
  def test_similarity_search_complete(tmp_path):
311
310
  """Test VisualAISearch end-to-end with sample image and query."""
312
311
  from PIL import Image
ultralytics/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
- __version__ = "8.3.138"
3
+ __version__ = "8.3.140"
4
4
 
5
5
  import os
6
6
 
@@ -981,6 +981,7 @@ class Exporter:
981
981
  custom_input_op_name_np_data_path=np_data,
982
982
  enable_batchmatmul_unfold=True, # fix lower no. of detected objects on GPU delegate
983
983
  output_signaturedefs=True, # fix error with Attention block group convolution
984
+ disable_group_convolution=self.args.format == "tfjs", # fix TF.js error with group convolution
984
985
  optimization_for_gpu_delegate=True,
985
986
  )
986
987
  YAML.save(f / "metadata.yaml", self.metadata) # add metadata.yaml
@@ -13,8 +13,7 @@ import numpy as np
13
13
  import torch
14
14
 
15
15
  from ultralytics.data.augment import LetterBox
16
- from ultralytics.utils import LOGGER, SimpleClass, ops
17
- from ultralytics.utils.checks import check_requirements
16
+ from ultralytics.utils import LOGGER, DataExportMixin, SimpleClass, ops
18
17
  from ultralytics.utils.plotting import Annotator, colors, save_one_box
19
18
  from ultralytics.utils.torch_utils import smart_inference_mode
20
19
 
@@ -184,7 +183,7 @@ class BaseTensor(SimpleClass):
184
183
  return self.__class__(self.data[idx], self.orig_shape)
185
184
 
186
185
 
187
- class Results(SimpleClass):
186
+ class Results(SimpleClass, DataExportMixin):
188
187
  """
189
188
  A class for storing and manipulating inference results.
190
189
 
@@ -828,212 +827,6 @@ class Results(SimpleClass):
828
827
 
829
828
  return results
830
829
 
831
- def to_df(self, normalize=False, decimals=5):
832
- """
833
- Converts detection results to a Pandas Dataframe.
834
-
835
- This method converts the detection results into Pandas Dataframe format. It includes information
836
- about detected objects such as bounding boxes, class names, confidence scores, and optionally
837
- segmentation masks and keypoints.
838
-
839
- Args:
840
- normalize (bool): Whether to normalize the bounding box coordinates by the image dimensions.
841
- If True, coordinates will be returned as float values between 0 and 1.
842
- decimals (int): Number of decimal places to round the output values to.
843
-
844
- Returns:
845
- (DataFrame): A Pandas Dataframe containing all the information in results in an organized way.
846
-
847
- Examples:
848
- >>> results = model("path/to/image.jpg")
849
- >>> for result in results:
850
- >>> df_result = result.to_df()
851
- >>> print(df_result)
852
- """
853
- import pandas as pd # scope for faster 'import ultralytics'
854
-
855
- return pd.DataFrame(self.summary(normalize=normalize, decimals=decimals))
856
-
857
- def to_csv(self, normalize=False, decimals=5, *args, **kwargs):
858
- """
859
- Converts detection results to a CSV format.
860
-
861
- This method serializes the detection results into a CSV format. It includes information
862
- about detected objects such as bounding boxes, class names, confidence scores, and optionally
863
- segmentation masks and keypoints.
864
-
865
- Args:
866
- normalize (bool): Whether to normalize the bounding box coordinates by the image dimensions.
867
- If True, coordinates will be returned as float values between 0 and 1.
868
- decimals (int): Number of decimal places to round the output values to.
869
- *args (Any): Variable length argument list to be passed to pandas.DataFrame.to_csv().
870
- **kwargs (Any): Arbitrary keyword arguments to be passed to pandas.DataFrame.to_csv().
871
-
872
-
873
- Returns:
874
- (str): CSV containing all the information in results in an organized way.
875
-
876
- Examples:
877
- >>> results = model("path/to/image.jpg")
878
- >>> for result in results:
879
- >>> csv_result = result.to_csv()
880
- >>> print(csv_result)
881
- """
882
- return self.to_df(normalize=normalize, decimals=decimals).to_csv(*args, **kwargs)
883
-
884
- def to_xml(self, normalize=False, decimals=5, *args, **kwargs):
885
- """
886
- Converts detection results to XML format.
887
-
888
- This method serializes the detection results into an XML format. It includes information
889
- about detected objects such as bounding boxes, class names, confidence scores, and optionally
890
- segmentation masks and keypoints.
891
-
892
- Args:
893
- normalize (bool): Whether to normalize the bounding box coordinates by the image dimensions.
894
- If True, coordinates will be returned as float values between 0 and 1.
895
- decimals (int): Number of decimal places to round the output values to.
896
- *args (Any): Variable length argument list to be passed to pandas.DataFrame.to_xml().
897
- **kwargs (Any): Arbitrary keyword arguments to be passed to pandas.DataFrame.to_xml().
898
-
899
- Returns:
900
- (str): An XML string containing all the information in results in an organized way.
901
-
902
- Examples:
903
- >>> results = model("path/to/image.jpg")
904
- >>> for result in results:
905
- >>> xml_result = result.to_xml()
906
- >>> print(xml_result)
907
- """
908
- check_requirements("lxml")
909
- df = self.to_df(normalize=normalize, decimals=decimals)
910
- return '<?xml version="1.0" encoding="utf-8"?>\n<root></root>' if df.empty else df.to_xml(*args, **kwargs)
911
-
912
- def to_html(self, normalize=False, decimals=5, index=False, *args, **kwargs):
913
- """
914
- Converts detection results to HTML format.
915
-
916
- This method serializes the detection results into an HTML format. It includes information
917
- about detected objects such as bounding boxes, class names, confidence scores, and optionally
918
- segmentation masks and keypoints.
919
-
920
- Args:
921
- normalize (bool): Whether to normalize the bounding box coordinates by the image dimensions.
922
- If True, coordinates will be returned as float values between 0 and 1.
923
- decimals (int): Number of decimal places to round the output values to.
924
- index (bool): Whether to include the DataFrame index in the HTML output.
925
- *args (Any): Variable length argument list to be passed to pandas.DataFrame.to_html().
926
- **kwargs (Any): Arbitrary keyword arguments to be passed to pandas.DataFrame.to_html().
927
-
928
- Returns:
929
- (str): An HTML string containing all the information in results in an organized way.
930
-
931
- Examples:
932
- >>> results = model("path/to/image.jpg")
933
- >>> for result in results:
934
- >>> html_result = result.to_html()
935
- >>> print(html_result)
936
- """
937
- df = self.to_df(normalize=normalize, decimals=decimals)
938
- return "<table></table>" if df.empty else df.to_html(index=index, *args, **kwargs)
939
-
940
- def tojson(self, normalize=False, decimals=5):
941
- """Deprecated version of to_json()."""
942
- LOGGER.warning("'result.tojson()' is deprecated, replace with 'result.to_json()'.")
943
- return self.to_json(normalize, decimals)
944
-
945
- def to_json(self, normalize=False, decimals=5):
946
- """
947
- Converts detection results to JSON format.
948
-
949
- This method serializes the detection results into a JSON-compatible format. It includes information
950
- about detected objects such as bounding boxes, class names, confidence scores, and optionally
951
- segmentation masks and keypoints.
952
-
953
- Args:
954
- normalize (bool): Whether to normalize the bounding box coordinates by the image dimensions.
955
- If True, coordinates will be returned as float values between 0 and 1.
956
- decimals (int): Number of decimal places to round the output values to.
957
-
958
- Returns:
959
- (str): A JSON string containing the serialized detection results.
960
-
961
- Examples:
962
- >>> results = model("path/to/image.jpg")
963
- >>> for result in results:
964
- >>> json_result = result.to_json()
965
- >>> print(json_result)
966
-
967
- Notes:
968
- - For classification tasks, the JSON will contain class probabilities instead of bounding boxes.
969
- - For object detection tasks, the JSON will include bounding box coordinates, class names, and
970
- confidence scores.
971
- - If available, segmentation masks and keypoints will also be included in the JSON output.
972
- - The method uses the `summary` method internally to generate the data structure before
973
- converting it to JSON.
974
- """
975
- import json
976
-
977
- return json.dumps(self.summary(normalize=normalize, decimals=decimals), indent=2)
978
-
979
- def to_sql(self, table_name="results", normalize=False, decimals=5, db_path="results.db"):
980
- """
981
- Converts detection results to an SQL-compatible format.
982
-
983
- This method serializes the detection results into a format compatible with SQL databases.
984
- It includes information about detected objects such as bounding boxes, class names, confidence scores,
985
- and optionally segmentation masks, keypoints or oriented bounding boxes.
986
-
987
- Args:
988
- table_name (str): Name of the SQL table where the data will be inserted.
989
- normalize (bool): Whether to normalize the bounding box coordinates by the image dimensions.
990
- If True, coordinates will be returned as float values between 0 and 1.
991
- decimals (int): Number of decimal places to round the bounding boxes values to.
992
- db_path (str): Path to the SQLite database file.
993
-
994
- Examples:
995
- >>> results = model("path/to/image.jpg")
996
- >>> for result in results:
997
- >>> result.to_sql()
998
- """
999
- import json
1000
- import sqlite3
1001
-
1002
- # Convert results to a list of dictionaries
1003
- data = self.summary(normalize=normalize, decimals=decimals)
1004
- if len(data) == 0:
1005
- LOGGER.warning("No results to save to SQL. Results dict is empty.")
1006
- return
1007
-
1008
- # Connect to the SQLite database
1009
- conn = sqlite3.connect(db_path)
1010
- cursor = conn.cursor()
1011
-
1012
- # Create table if it doesn't exist
1013
- columns = (
1014
- "id INTEGER PRIMARY KEY AUTOINCREMENT, class_name TEXT, confidence REAL, box TEXT, masks TEXT, kpts TEXT"
1015
- )
1016
- cursor.execute(f"CREATE TABLE IF NOT EXISTS {table_name} ({columns})")
1017
-
1018
- # Insert data into the table
1019
- for item in data:
1020
- cursor.execute(
1021
- f"INSERT INTO {table_name} (class_name, confidence, box, masks, kpts) VALUES (?, ?, ?, ?, ?)",
1022
- (
1023
- item.get("name"),
1024
- item.get("confidence"),
1025
- json.dumps(item.get("box", {})),
1026
- json.dumps(item.get("segments", {})),
1027
- json.dumps(item.get("keypoints", {})),
1028
- ),
1029
- )
1030
-
1031
- # Commit and close the connection
1032
- conn.commit()
1033
- conn.close()
1034
-
1035
- LOGGER.info(f"Detection results successfully written to SQL table '{table_name}' in database '{db_path}'.")
1036
-
1037
830
 
1038
831
  class Boxes(BaseTensor):
1039
832
  """
@@ -187,6 +187,159 @@ class TQDM(rich.tqdm if TQDM_RICH else tqdm.tqdm):
187
187
  return super().__iter__()
188
188
 
189
189
 
190
+ class DataExportMixin:
191
+ """
192
+ Mixin class for exporting validation metrics or prediction results in various formats.
193
+
194
+ This class provides utilities to export performance metrics (e.g., mAP, precision, recall) or prediction results
195
+ from classification, object detection, segmentation, or pose estimation tasks into various formats, Pandas DataFrame
196
+ CSV, XML, HTML, JSON and SQLite (SQL)
197
+
198
+ Methods:
199
+ to_df(): Convert summary to a Pandas DataFrame.
200
+ to_csv(): Export results as a CSV string.
201
+ to_xml(): Export results as an XML string (requires `lxml`).
202
+ to_html(): Export results as an HTML table.
203
+ to_json(): Export results as a JSON string.
204
+ tojson(): Deprecated alias for `to_json()`.
205
+ to_sql(): Export results to an SQLite database.
206
+
207
+ Examples:
208
+ >>> model = YOLO("yolov8n.pt")
209
+ >>> results = model("image.jpg")
210
+ >>> df = results.to_df()
211
+ >>> print(df)
212
+ >>> csv_data = results.to_csv()
213
+ >>> results.to_sql(table_name="yolo_results")
214
+ """
215
+
216
+ def to_df(self, normalize=False, decimals=5):
217
+ """
218
+ Create a pandas DataFrame from the prediction results summary or validation metrics.
219
+
220
+ Args:
221
+ normalize (bool, optional): Normalize numerical values for easier comparison. Defaults to False.
222
+ decimals (int, optional): Decimal places to round floats. Defaults to 5.
223
+
224
+ Returns:
225
+ (DataFrame): DataFrame containing the summary data.
226
+ """
227
+ import pandas as pd # scope for faster 'import ultralytics'
228
+
229
+ return pd.DataFrame(self.summary(normalize=normalize, decimals=decimals))
230
+
231
+ def to_csv(self, normalize=False, decimals=5):
232
+ """
233
+ Export results to CSV string format.
234
+
235
+ Args:
236
+ normalize (bool, optional): Normalize numeric values. Defaults to False.
237
+ decimals (int, optional): Decimal precision. Defaults to 5.
238
+
239
+ Returns:
240
+ (str): CSV content as string.
241
+ """
242
+ return self.to_df(normalize=normalize, decimals=decimals).to_csv()
243
+
244
+ def to_xml(self, normalize=False, decimals=5):
245
+ """
246
+ Export results to XML format.
247
+
248
+ Args:
249
+ normalize (bool, optional): Normalize numeric values. Defaults to False.
250
+ decimals (int, optional): Decimal precision. Defaults to 5.
251
+
252
+ Returns:
253
+ (str): XML string.
254
+
255
+ Note:
256
+ Requires `lxml` package to be installed.
257
+ """
258
+ from ultralytics.utils.checks import check_requirements
259
+
260
+ check_requirements("lxml")
261
+ df = self.to_df(normalize=normalize, decimals=decimals)
262
+ return '<?xml version="1.0" encoding="utf-8"?>\n<root></root>' if df.empty else df.to_xml()
263
+
264
+ def to_html(self, normalize=False, decimals=5, index=False):
265
+ """
266
+ Export results to HTML table format.
267
+
268
+ Args:
269
+ normalize (bool, optional): Normalize numeric values. Defaults to False.
270
+ decimals (int, optional): Decimal precision. Defaults to 5.
271
+ index (bool, optional): Whether to include index column in the HTML table. Defaults to False.
272
+
273
+ Returns:
274
+ (str): HTML representation of the results.
275
+ """
276
+ df = self.to_df(normalize=normalize, decimals=decimals)
277
+ return "<table></table>" if df.empty else df.to_html(index=index)
278
+
279
+ def tojson(self, normalize=False, decimals=5):
280
+ """Deprecated version of to_json()."""
281
+ LOGGER.warning("'result.tojson()' is deprecated, replace with 'result.to_json()'.")
282
+ return self.to_json(normalize, decimals)
283
+
284
+ def to_json(self, normalize=False, decimals=5):
285
+ """
286
+ Export results to JSON format.
287
+
288
+ Args:
289
+ normalize (bool, optional): Normalize numeric values. Defaults to False.
290
+ decimals (int, optional): Decimal precision. Defaults to 5.
291
+
292
+ Returns:
293
+ (str): JSON-formatted string of the results.
294
+ """
295
+ return self.to_df(normalize=normalize, decimals=decimals).to_json(orient="records", indent=2)
296
+
297
+ def to_sql(self, normalize=False, decimals=5, table_name="results", db_path="results.db"):
298
+ """
299
+ Save results to an SQLite database.
300
+
301
+ Args:
302
+ normalize (bool, optional): Normalize numeric values. Defaults to False.
303
+ decimals (int, optional): Decimal precision. Defaults to 5.
304
+ table_name (str, optional): Name of the SQL table. Defaults to "results".
305
+ db_path (str, optional): SQLite database file path. Defaults to "results.db".
306
+ """
307
+ df = self.to_df(normalize, decimals)
308
+ if df.empty or df.columns.empty: # Exit if df is None or has no columns (i.e., no schema)
309
+ return
310
+
311
+ import sqlite3
312
+
313
+ conn = sqlite3.connect(db_path)
314
+ cursor = conn.cursor()
315
+
316
+ # Dynamically create table schema based on summary to support prediction and validation results export
317
+ columns = []
318
+ for col in df.columns:
319
+ sample_val = df[col].dropna().iloc[0] if not df[col].dropna().empty else ""
320
+ if isinstance(sample_val, dict):
321
+ col_type = "TEXT"
322
+ elif isinstance(sample_val, (float, int)):
323
+ col_type = "REAL"
324
+ else:
325
+ col_type = "TEXT"
326
+ columns.append(f'"{col}" {col_type}') # Quote column names to handle special characters like hyphens
327
+
328
+ # Create table (Drop table from db if it's already exist)
329
+ cursor.execute(f'DROP TABLE IF EXISTS "{table_name}"')
330
+ cursor.execute(f'CREATE TABLE "{table_name}" (id INTEGER PRIMARY KEY AUTOINCREMENT, {", ".join(columns)})')
331
+
332
+ for _, row in df.iterrows():
333
+ values = [json.dumps(v) if isinstance(v, dict) else v for v in row]
334
+ column_names = ", ".join(f'"{col}"' for col in df.columns)
335
+ placeholders = ", ".join("?" for _ in df.columns)
336
+ cursor.execute(f'INSERT INTO "{table_name}" ({column_names}) VALUES ({placeholders})', values)
337
+
338
+ conn.commit()
339
+ conn.close()
340
+ LOGGER.info(f"Results saved to SQL table '{table_name}' in '{db_path}'.")
341
+
342
+
190
343
  class SimpleClass:
191
344
  """
192
345
  A simple base class for creating objects with string representations of their attributes.
@@ -23,6 +23,7 @@ TensorFlow.js | `tfjs` | yolo11n_web_model/
23
23
  PaddlePaddle | `paddle` | yolo11n_paddle_model/
24
24
  MNN | `mnn` | yolo11n.mnn
25
25
  NCNN | `ncnn` | yolo11n_ncnn_model/
26
+ IMX | `imx` | yolo11n_imx_model/
26
27
  RKNN | `rknn` | yolo11n_rknn_model/
27
28
  """
28
29
 
@@ -165,6 +165,15 @@ def check_imgsz(imgsz, stride=32, min_dim=1, max_dim=2, floor=0):
165
165
  return sz
166
166
 
167
167
 
168
+ @functools.lru_cache
169
+ def check_uv():
170
+ """Check if uv is installed and can run successfully."""
171
+ try:
172
+ return subprocess.run(["uv", "-V"], capture_output=True).returncode == 0
173
+ except FileNotFoundError:
174
+ return False
175
+
176
+
168
177
  @functools.lru_cache
169
178
  def check_version(
170
179
  current: str = "0.0.0",
@@ -388,21 +397,27 @@ def check_requirements(requirements=ROOT.parent / "requirements.txt", exclude=()
388
397
  pkgs.append(r)
389
398
 
390
399
  @Retry(times=2, delay=1)
391
- def attempt_install(packages, commands):
392
- """Attempt pip install command with retries on failure."""
393
- return subprocess.check_output(f"pip install --no-cache-dir {packages} {commands}", shell=True).decode()
400
+ def attempt_install(packages, commands, use_uv):
401
+ """Attempt package installation with uv if available, falling back to pip."""
402
+ if use_uv:
403
+ # Note requires --break-system-packages on ARM64 dockerfile
404
+ cmd = f"uv pip install --system --no-cache-dir {packages} {commands} --index-strategy=unsafe-best-match --break-system-packages --prerelease=allow"
405
+ else:
406
+ cmd = f"pip install --no-cache-dir {packages} {commands}"
407
+ return subprocess.check_output(cmd, shell=True).decode()
394
408
 
395
409
  s = " ".join(f'"{x}"' for x in pkgs) # console string
396
410
  if s:
397
411
  if install and AUTOINSTALL: # check environment variable
412
+ # Note uv fails on arm64 macOS and Raspberry Pi runners
398
413
  n = len(pkgs) # number of packages updates
399
414
  LOGGER.info(f"{prefix} Ultralytics requirement{'s' * (n > 1)} {pkgs} not found, attempting AutoUpdate...")
400
415
  try:
401
416
  t = time.time()
402
417
  assert ONLINE, "AutoUpdate skipped (offline)"
403
- LOGGER.info(attempt_install(s, cmds))
418
+ LOGGER.info(attempt_install(s, cmds, use_uv=not ARM64 and check_uv()))
404
419
  dt = time.time() - t
405
- LOGGER.info(f"{prefix} AutoUpdate success ✅ {dt:.1f}s, installed {n} package{'s' * (n > 1)}: {pkgs}")
420
+ LOGGER.info(f"{prefix} AutoUpdate success ✅ {dt:.1f}s")
406
421
  LOGGER.warning(
407
422
  f"{prefix} {colorstr('bold', 'Restart runtime or rerun command for updates to take effect')}\n"
408
423
  )
@@ -894,7 +909,6 @@ check_torchvision() # check torch-torchvision compatibility
894
909
 
895
910
  # Define constants
896
911
  IS_PYTHON_3_8 = PYTHON_VERSION.startswith("3.8")
897
- IS_PYTHON_3_11 = PYTHON_VERSION.startswith("3.11")
898
912
  IS_PYTHON_3_12 = PYTHON_VERSION.startswith("3.12")
899
913
  IS_PYTHON_3_13 = PYTHON_VERSION.startswith("3.13")
900
914
 
@@ -8,7 +8,7 @@ from pathlib import Path
8
8
  import numpy as np
9
9
  import torch
10
10
 
11
- from ultralytics.utils import LOGGER, SimpleClass, TryExcept, checks, plt_settings
11
+ from ultralytics.utils import LOGGER, DataExportMixin, SimpleClass, TryExcept, checks, plt_settings
12
12
 
13
13
  OKS_SIGMA = (
14
14
  np.array([0.26, 0.25, 0.25, 0.35, 0.35, 0.79, 0.79, 0.72, 0.72, 0.62, 0.62, 1.07, 1.07, 0.87, 0.87, 0.89, 0.89])
@@ -865,7 +865,7 @@ class Metric(SimpleClass):
865
865
  ]
866
866
 
867
867
 
868
- class DetMetrics(SimpleClass):
868
+ class DetMetrics(SimpleClass, DataExportMixin):
869
869
  """
870
870
  Utility class for computing detection metrics such as precision, recall, and mean average precision (mAP).
871
871
 
@@ -961,8 +961,29 @@ class DetMetrics(SimpleClass):
961
961
  """Return dictionary of computed performance metrics and statistics."""
962
962
  return self.box.curves_results
963
963
 
964
+ def summary(self, **kwargs):
965
+ """Returns per-class detection metrics with shared scalar values included."""
966
+ scalars = {
967
+ "box-map": self.box.map,
968
+ "box-map50": self.box.map50,
969
+ "box-map75": self.box.map75,
970
+ }
971
+ per_class = {
972
+ "box-p": self.box.p,
973
+ "box-r": self.box.r,
974
+ "box-f1": self.box.f1,
975
+ }
976
+ return [
977
+ {
978
+ "class_name": self.names[i] if hasattr(self, "names") and i in self.names else str(i),
979
+ **{k: v[i] for k, v in per_class.items()},
980
+ **scalars,
981
+ }
982
+ for i in range(len(next(iter(per_class.values()), [])))
983
+ ]
964
984
 
965
- class SegmentMetrics(SimpleClass):
985
+
986
+ class SegmentMetrics(SimpleClass, DataExportMixin):
966
987
  """
967
988
  Calculates and aggregates detection and segmentation metrics over a given set of classes.
968
989
 
@@ -1097,6 +1118,29 @@ class SegmentMetrics(SimpleClass):
1097
1118
  """Return dictionary of computed performance metrics and statistics."""
1098
1119
  return self.box.curves_results + self.seg.curves_results
1099
1120
 
1121
+ def summary(self, **kwargs):
1122
+ """Returns per-class segmentation metrics with shared scalar values included (box + mask)."""
1123
+ scalars = {
1124
+ "box-map": self.box.map,
1125
+ "box-map50": self.box.map50,
1126
+ "box-map75": self.box.map75,
1127
+ "mask-map": self.seg.map,
1128
+ "mask-map50": self.seg.map50,
1129
+ "mask-map75": self.seg.map75,
1130
+ }
1131
+ per_class = {
1132
+ "box-p": self.box.p,
1133
+ "box-r": self.box.r,
1134
+ "box-f1": self.box.f1,
1135
+ "mask-p": self.seg.p,
1136
+ "mask-r": self.seg.r,
1137
+ "mask-f1": self.seg.f1,
1138
+ }
1139
+ return [
1140
+ {"class_name": self.names[i], **{k: v[i] for k, v in per_class.items()}, **scalars}
1141
+ for i in range(len(next(iter(per_class.values()), [])))
1142
+ ]
1143
+
1100
1144
 
1101
1145
  class PoseMetrics(SegmentMetrics):
1102
1146
  """
@@ -1229,8 +1273,31 @@ class PoseMetrics(SegmentMetrics):
1229
1273
  """Return dictionary of computed performance metrics and statistics."""
1230
1274
  return self.box.curves_results + self.pose.curves_results
1231
1275
 
1276
+ def summary(self, **kwargs):
1277
+ """Returns per-class pose metrics with shared scalar values included (box + pose)."""
1278
+ scalars = {
1279
+ "box-map": self.box.map,
1280
+ "box-map50": self.box.map50,
1281
+ "box-map75": self.box.map75,
1282
+ "pose-map": self.pose.map,
1283
+ "pose-map50": self.pose.map50,
1284
+ "pose-map75": self.pose.map75,
1285
+ }
1286
+ per_class = {
1287
+ "box-p": self.box.p,
1288
+ "box-r": self.box.r,
1289
+ "box-f1": self.box.f1,
1290
+ "pose-p": self.pose.p,
1291
+ "pose-r": self.pose.r,
1292
+ "pose-f1": self.pose.f1,
1293
+ }
1294
+ return [
1295
+ {"class_name": self.names[i], **{k: v[i] for k, v in per_class.items()}, **scalars}
1296
+ for i in range(len(next(iter(per_class.values()), [])))
1297
+ ]
1298
+
1232
1299
 
1233
- class ClassifyMetrics(SimpleClass):
1300
+ class ClassifyMetrics(SimpleClass, DataExportMixin):
1234
1301
  """
1235
1302
  Class for computing classification metrics including top-1 and top-5 accuracy.
1236
1303
 
@@ -1286,8 +1353,12 @@ class ClassifyMetrics(SimpleClass):
1286
1353
  """Return a list of curves for accessing specific metrics curves."""
1287
1354
  return []
1288
1355
 
1356
+ def summary(self, **kwargs):
1357
+ """Returns a single-row summary for classification metrics (top1/top5)."""
1358
+ return [{"classify-top1": self.top1, "classify-top5": self.top5}]
1359
+
1289
1360
 
1290
- class OBBMetrics(SimpleClass):
1361
+ class OBBMetrics(SimpleClass, DataExportMixin):
1291
1362
  """
1292
1363
  Metrics for evaluating oriented bounding box (OBB) detection.
1293
1364
 
@@ -1316,6 +1387,7 @@ class OBBMetrics(SimpleClass):
1316
1387
  self.names = names
1317
1388
  self.box = Metric()
1318
1389
  self.speed = {"preprocess": 0.0, "inference": 0.0, "loss": 0.0, "postprocess": 0.0}
1390
+ self.task = "obb"
1319
1391
 
1320
1392
  def process(self, tp, conf, pred_cls, target_cls, on_plot=None):
1321
1393
  """
@@ -1383,3 +1455,16 @@ class OBBMetrics(SimpleClass):
1383
1455
  def curves_results(self):
1384
1456
  """Return a list of curves for accessing specific metrics curves."""
1385
1457
  return []
1458
+
1459
+ def summary(self, **kwargs):
1460
+ """Returns per-class detection metrics with shared scalar values included."""
1461
+ scalars = {
1462
+ "box-map": self.box.map,
1463
+ "box-map50": self.box.map50,
1464
+ "box-map75": self.box.map75,
1465
+ }
1466
+ per_class = {"box-p": self.box.p, "box-r": self.box.r, "box-f1": self.box.f1}
1467
+ return [
1468
+ {"class_name": self.names[i], **{k: v[i] for k, v in per_class.items()}, **scalars}
1469
+ for i in range(len(next(iter(per_class.values()), [])))
1470
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ultralytics
3
- Version: 8.3.138
3
+ Version: 8.3.140
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>
@@ -1,13 +1,13 @@
1
1
  tests/__init__.py,sha256=xnMhv3O_DF1YrW4zk__ZywQzAaoTDjPKPoiI1Ktss1w,670
2
2
  tests/conftest.py,sha256=rsIAipRKfrVNoTaJ1LdpYue8AbcJ_fr3d3WIlM_6uXY,2982
3
3
  tests/test_cli.py,sha256=vXUC_EK0fa87JRhHsCOZf7AJQ5_Jm1sL8u-yhmsaQh0,5851
4
- tests/test_cuda.py,sha256=L_2xp2TH-pInsdI8UrbZ5onRtHQGdUVoPXnyX6Ot4_U,7950
4
+ tests/test_cuda.py,sha256=k4i-6lrp_hczJjbLk_uJOTfMeZZN5o3Dj9jbPA0TzB4,7912
5
5
  tests/test_engine.py,sha256=aGqZ8P7QO5C_nOa1b4FOyk92Ysdk5WiP-ST310Vyxys,4962
6
6
  tests/test_exports.py,sha256=dhZn86LdbapW15RthQF870LGxDjC1MUZhlGdBgPmgIQ,9716
7
7
  tests/test_integrations.py,sha256=dQteeRsRVuT_p5-T88-7jqT65Zm9iAXkyKg-KQ1_TQ8,6341
8
- tests/test_python.py,sha256=KWsncKpeDdRmjRftmJpsMl7bBLI3TG_I7Lb4kuemZzQ,25618
9
- tests/test_solutions.py,sha256=4_Ce7VmsAoALN79a72nEpRgIJqx1oGnCY7yre9a-5vk,12671
10
- ultralytics/__init__.py,sha256=dNL32IT6IwReXAZE3Gp8ZeYZDxmgnF_xLn60OR9wXzg,730
8
+ tests/test_python.py,sha256=Zx9OlPN11_D1WSLpi9nPFqORNHNz0lEn6mxVNL2ZHjE,25852
9
+ tests/test_solutions.py,sha256=7n4CqKj2guj09UFKe4jufrrC16xRBUIjiRAfGDoAMI8,12808
10
+ ultralytics/__init__.py,sha256=27HzhLPFDZjawOY0VuVN8Sn9RoiF9R4NJj3_CBuiuIs,730
11
11
  ultralytics/assets/bus.jpg,sha256=wCAZxJecGR63Od3ZRERe9Aja1Weayrb9Ug751DS_vGM,137419
12
12
  ultralytics/assets/zidane.jpg,sha256=Ftc4aeMmen1O0A3o6GCDO9FlfBslLpTAw0gnetx7bts,50427
13
13
  ultralytics/cfg/__init__.py,sha256=mpvLR68Iff4J59zYGhysSl8VwIVVzV_VMOYeVdqnYj4,39544
@@ -118,10 +118,10 @@ ultralytics/data/scripts/get_coco.sh,sha256=UuJpJeo3qQpTHVINeOpmP0NYmg8PhEFE3A8J
118
118
  ultralytics/data/scripts/get_coco128.sh,sha256=qmRQl_hOKrsdHrTrnyQuFIH01oDz3lfaz138OgGfLt8,650
119
119
  ultralytics/data/scripts/get_imagenet.sh,sha256=hr42H16bM47iT27rgS7MpEo-GeOZAYUQXgr0B2cwn48,1705
120
120
  ultralytics/engine/__init__.py,sha256=lm6MckFYCPTbqIoX7w0s_daxdjNeBeKW6DXppv1-QUM,70
121
- ultralytics/engine/exporter.py,sha256=tJTl6AkJ-OCgE1pstsu2iOSHqidkuqejPetNCg8S64k,70841
121
+ ultralytics/engine/exporter.py,sha256=BZWa7Mnl1BPvbPiD-RJs6M5Bca4sm3_MQgjoHesvXEs,70949
122
122
  ultralytics/engine/model.py,sha256=BtC5KYNrdfhryrS7b6ZXDIsmtObEeIDTePCv1gO4br4,52952
123
123
  ultralytics/engine/predictor.py,sha256=rZ5mIPeejkxUerpTfUf_1rSAklOR3THqoejlil4C04w,21651
124
- ultralytics/engine/results.py,sha256=MhbyMCwgslmtV53fqii4UJUaLQ4gKTKdkXi7vvmJDAE,79628
124
+ ultralytics/engine/results.py,sha256=2sNNhAc2zaIRaQBXl_36gAKK31V8tgNDcgC4ZPiGqKI,70072
125
125
  ultralytics/engine/trainer.py,sha256=xdgNAgq6umJ6915tiCK3U22NeY7w1HnvmAhXlwS_hYI,38955
126
126
  ultralytics/engine/tuner.py,sha256=zEW1UpLlZ6N4xbvS7MxICkshRlaFgLNfuADA0VfRpao,12629
127
127
  ultralytics/engine/validator.py,sha256=f9UUv3QqQStLrO1nojrHkdS58qYQxKXaoIQQria6WyA,17054
@@ -233,11 +233,11 @@ ultralytics/trackers/utils/__init__.py,sha256=lm6MckFYCPTbqIoX7w0s_daxdjNeBeKW6D
233
233
  ultralytics/trackers/utils/gmc.py,sha256=843LlmqWuXdUULBNpxVCZlil-_2QG-UwvscUCFbpGjA,14541
234
234
  ultralytics/trackers/utils/kalman_filter.py,sha256=A0CqOnnaKH6kr0XwuHzyHmIU6aJAjJYxF9jVlNBKZHo,21326
235
235
  ultralytics/trackers/utils/matching.py,sha256=7eIufSdeN7cXuFMjvcfvz0Ldq84m4YKZl5IGxBR8IIo,7169
236
- ultralytics/utils/__init__.py,sha256=9RF8KyUHd_YyovvZzlcnZzxx-jkxBLrxfXfkFVj64Iw,52882
236
+ ultralytics/utils/__init__.py,sha256=7VT2VSCIgDPInuNKO0sy2_3-qUwuCafLG0wF4wAyjBg,59059
237
237
  ultralytics/utils/autobatch.py,sha256=kg05q2qKg74y_Uq2vvr01i3KhLfpVR7sT0IXBt3_kyI,4921
238
238
  ultralytics/utils/autodevice.py,sha256=OKZfTbswg6SlsYGCGMqROkA-451CXGG47oeyC5Q1kFM,7232
239
- ultralytics/utils/benchmarks.py,sha256=lDNNnLeLUzmqKrqrqlCOiau-q7A-gcLooZP2dbxCu-U,30214
240
- ultralytics/utils/checks.py,sha256=GJbfMl608ihHSjN78Xjjky10Cglv-CYsxJ7Ra7HeH1U,33204
239
+ ultralytics/utils/benchmarks.py,sha256=iqjxD29srcCpimtAhbSidpsjnUlMhNR5S6QGPZyz16I,30287
240
+ ultralytics/utils/checks.py,sha256=SinI5gY-znVbQ-JXk1JaHIlSp2kuBv92Rv99NWFzOFg,33763
241
241
  ultralytics/utils/dist.py,sha256=aytW0JEkcA5ZTZucV92ot7Bn-apiej8aLk3QNWicjAc,4103
242
242
  ultralytics/utils/downloads.py,sha256=G1nd7c7Gwjf58nZzDVpXDtoFtzhZYbjKBnwbZVMWRG0,22333
243
243
  ultralytics/utils/errors.py,sha256=vY9h2evFSrHnZdHJVVrmm8Zzw4qVDLyo9DeYW5g0dFk,1573
@@ -245,7 +245,7 @@ ultralytics/utils/export.py,sha256=Rr5R3GdJBapJJt1XHkH6VQwYN52-L_7wGiRDCgnb7BY,8
245
245
  ultralytics/utils/files.py,sha256=0K4O1cgqRiXaDw7EQK13TqA5SME_RrvfDVQSPetNr5w,8042
246
246
  ultralytics/utils/instance.py,sha256=UOEsXR9V-bXNRk6BTonASBEgeMqvzzAk4S7VdXZJUAM,18090
247
247
  ultralytics/utils/loss.py,sha256=Woc_rj7ptCyezHdylEygXMeSEgivYu_B9jJHD4UwxWE,37607
248
- ultralytics/utils/metrics.py,sha256=n8guPEADBMRNpeXNShEX-fxVv9xck8S4QaOIiaW_kl0,56037
248
+ ultralytics/utils/metrics.py,sha256=8x4S7y-rBKRkM47f_o7jfMHA1Bz8SDq3t-R1FXlQNEM,59267
249
249
  ultralytics/utils/ops.py,sha256=YFwPrKlPcgEmgAWqnJVR0Ccx5NQgp5e3P-YYHwVSP0k,34779
250
250
  ultralytics/utils/patches.py,sha256=_dhIU_eDklQE-aWIjpyjPHl_wOwZoGuIUQnXgdSwk_A,5020
251
251
  ultralytics/utils/plotting.py,sha256=oFq19c3tRng-dKHEH-j-S_wLG4CZ_mk8wqE_Gab2H8A,47221
@@ -264,9 +264,9 @@ ultralytics/utils/callbacks/neptune.py,sha256=yYUgEgSv6L39sSev6vjwhAWU3DlPDsbSDV
264
264
  ultralytics/utils/callbacks/raytune.py,sha256=A8amUGpux7dYES-L1iSeMoMXBySGWCD1aUqT7vcG-pU,1284
265
265
  ultralytics/utils/callbacks/tensorboard.py,sha256=jgYnym3cUQFAgN1GzTyO7l3jINtfAh8zhrllDvnLuVQ,5339
266
266
  ultralytics/utils/callbacks/wb.py,sha256=iDRFXI4IIDm8R5OI89DMTmjs8aHLo1HRCLkOFKdaMG4,7507
267
- ultralytics-8.3.138.dist-info/licenses/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
268
- ultralytics-8.3.138.dist-info/METADATA,sha256=106NtnFyYbqCGa4Qz3VS2r9kAit_FObW2-HtID3Ozv4,37200
269
- ultralytics-8.3.138.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
270
- ultralytics-8.3.138.dist-info/entry_points.txt,sha256=YM_wiKyTe9yRrsEfqvYolNO5ngwfoL4-NwgKzc8_7sI,93
271
- ultralytics-8.3.138.dist-info/top_level.txt,sha256=XP49TwiMw4QGsvTLSYiJhz1xF_k7ev5mQ8jJXaXi45Q,12
272
- ultralytics-8.3.138.dist-info/RECORD,,
267
+ ultralytics-8.3.140.dist-info/licenses/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
268
+ ultralytics-8.3.140.dist-info/METADATA,sha256=ubcLXOXQF7RbhxNBCUSmLJxf6IhihoPeoaBx5LU1r0M,37200
269
+ ultralytics-8.3.140.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
270
+ ultralytics-8.3.140.dist-info/entry_points.txt,sha256=YM_wiKyTe9yRrsEfqvYolNO5ngwfoL4-NwgKzc8_7sI,93
271
+ ultralytics-8.3.140.dist-info/top_level.txt,sha256=XP49TwiMw4QGsvTLSYiJhz1xF_k7ev5mQ8jJXaXi45Q,12
272
+ ultralytics-8.3.140.dist-info/RECORD,,