dgenerate-ultralytics-headless 8.3.138__py3-none-any.whl → 8.3.139__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.
- {dgenerate_ultralytics_headless-8.3.138.dist-info → dgenerate_ultralytics_headless-8.3.139.dist-info}/METADATA +1 -1
- {dgenerate_ultralytics_headless-8.3.138.dist-info → dgenerate_ultralytics_headless-8.3.139.dist-info}/RECORD +14 -14
- tests/test_python.py +6 -1
- tests/test_solutions.py +2 -0
- ultralytics/__init__.py +1 -1
- ultralytics/engine/exporter.py +1 -0
- ultralytics/engine/results.py +2 -209
- ultralytics/utils/__init__.py +158 -0
- ultralytics/utils/checks.py +12 -5
- ultralytics/utils/metrics.py +90 -5
- {dgenerate_ultralytics_headless-8.3.138.dist-info → dgenerate_ultralytics_headless-8.3.139.dist-info}/WHEEL +0 -0
- {dgenerate_ultralytics_headless-8.3.138.dist-info → dgenerate_ultralytics_headless-8.3.139.dist-info}/entry_points.txt +0 -0
- {dgenerate_ultralytics_headless-8.3.138.dist-info → dgenerate_ultralytics_headless-8.3.139.dist-info}/licenses/LICENSE +0 -0
- {dgenerate_ultralytics_headless-8.3.138.dist-info → dgenerate_ultralytics_headless-8.3.139.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: dgenerate-ultralytics-headless
|
3
|
-
Version: 8.3.
|
3
|
+
Version: 8.3.139
|
4
4
|
Summary: Automatically built Ultralytics package with python-opencv-headless dependency instead of python-opencv
|
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,4 +1,4 @@
|
|
1
|
-
dgenerate_ultralytics_headless-8.3.
|
1
|
+
dgenerate_ultralytics_headless-8.3.139.dist-info/licenses/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
2
2
|
tests/__init__.py,sha256=xnMhv3O_DF1YrW4zk__ZywQzAaoTDjPKPoiI1Ktss1w,670
|
3
3
|
tests/conftest.py,sha256=rsIAipRKfrVNoTaJ1LdpYue8AbcJ_fr3d3WIlM_6uXY,2982
|
4
4
|
tests/test_cli.py,sha256=vXUC_EK0fa87JRhHsCOZf7AJQ5_Jm1sL8u-yhmsaQh0,5851
|
@@ -6,9 +6,9 @@ tests/test_cuda.py,sha256=L_2xp2TH-pInsdI8UrbZ5onRtHQGdUVoPXnyX6Ot4_U,7950
|
|
6
6
|
tests/test_engine.py,sha256=aGqZ8P7QO5C_nOa1b4FOyk92Ysdk5WiP-ST310Vyxys,4962
|
7
7
|
tests/test_exports.py,sha256=dhZn86LdbapW15RthQF870LGxDjC1MUZhlGdBgPmgIQ,9716
|
8
8
|
tests/test_integrations.py,sha256=dQteeRsRVuT_p5-T88-7jqT65Zm9iAXkyKg-KQ1_TQ8,6341
|
9
|
-
tests/test_python.py,sha256=
|
10
|
-
tests/test_solutions.py,sha256=
|
11
|
-
ultralytics/__init__.py,sha256=
|
9
|
+
tests/test_python.py,sha256=C1T9nODEyw1AUtpwmpTYO3-yx5ceQj1pfuWX1o7jXpU,25734
|
10
|
+
tests/test_solutions.py,sha256=Vscth8_3n9yGPQv2nrcloQYnPjB7V_oDdDKIb1pfUHI,12863
|
11
|
+
ultralytics/__init__.py,sha256=evr9ZL63t1w8IbSYB-xf-mDRjQo4ZpAHAtsrfWUghoU,730
|
12
12
|
ultralytics/assets/bus.jpg,sha256=wCAZxJecGR63Od3ZRERe9Aja1Weayrb9Ug751DS_vGM,137419
|
13
13
|
ultralytics/assets/zidane.jpg,sha256=Ftc4aeMmen1O0A3o6GCDO9FlfBslLpTAw0gnetx7bts,50427
|
14
14
|
ultralytics/cfg/__init__.py,sha256=mpvLR68Iff4J59zYGhysSl8VwIVVzV_VMOYeVdqnYj4,39544
|
@@ -119,10 +119,10 @@ ultralytics/data/scripts/get_coco.sh,sha256=UuJpJeo3qQpTHVINeOpmP0NYmg8PhEFE3A8J
|
|
119
119
|
ultralytics/data/scripts/get_coco128.sh,sha256=qmRQl_hOKrsdHrTrnyQuFIH01oDz3lfaz138OgGfLt8,650
|
120
120
|
ultralytics/data/scripts/get_imagenet.sh,sha256=hr42H16bM47iT27rgS7MpEo-GeOZAYUQXgr0B2cwn48,1705
|
121
121
|
ultralytics/engine/__init__.py,sha256=lm6MckFYCPTbqIoX7w0s_daxdjNeBeKW6DXppv1-QUM,70
|
122
|
-
ultralytics/engine/exporter.py,sha256=
|
122
|
+
ultralytics/engine/exporter.py,sha256=BZWa7Mnl1BPvbPiD-RJs6M5Bca4sm3_MQgjoHesvXEs,70949
|
123
123
|
ultralytics/engine/model.py,sha256=BtC5KYNrdfhryrS7b6ZXDIsmtObEeIDTePCv1gO4br4,52952
|
124
124
|
ultralytics/engine/predictor.py,sha256=rZ5mIPeejkxUerpTfUf_1rSAklOR3THqoejlil4C04w,21651
|
125
|
-
ultralytics/engine/results.py,sha256=
|
125
|
+
ultralytics/engine/results.py,sha256=2sNNhAc2zaIRaQBXl_36gAKK31V8tgNDcgC4ZPiGqKI,70072
|
126
126
|
ultralytics/engine/trainer.py,sha256=xdgNAgq6umJ6915tiCK3U22NeY7w1HnvmAhXlwS_hYI,38955
|
127
127
|
ultralytics/engine/tuner.py,sha256=zEW1UpLlZ6N4xbvS7MxICkshRlaFgLNfuADA0VfRpao,12629
|
128
128
|
ultralytics/engine/validator.py,sha256=f9UUv3QqQStLrO1nojrHkdS58qYQxKXaoIQQria6WyA,17054
|
@@ -234,11 +234,11 @@ ultralytics/trackers/utils/__init__.py,sha256=lm6MckFYCPTbqIoX7w0s_daxdjNeBeKW6D
|
|
234
234
|
ultralytics/trackers/utils/gmc.py,sha256=843LlmqWuXdUULBNpxVCZlil-_2QG-UwvscUCFbpGjA,14541
|
235
235
|
ultralytics/trackers/utils/kalman_filter.py,sha256=A0CqOnnaKH6kr0XwuHzyHmIU6aJAjJYxF9jVlNBKZHo,21326
|
236
236
|
ultralytics/trackers/utils/matching.py,sha256=7eIufSdeN7cXuFMjvcfvz0Ldq84m4YKZl5IGxBR8IIo,7169
|
237
|
-
ultralytics/utils/__init__.py,sha256=
|
237
|
+
ultralytics/utils/__init__.py,sha256=4U7xwGn3zbnmTm_P8pnySaY0l_yovbh6PvXJkD9P6r4,58774
|
238
238
|
ultralytics/utils/autobatch.py,sha256=kg05q2qKg74y_Uq2vvr01i3KhLfpVR7sT0IXBt3_kyI,4921
|
239
239
|
ultralytics/utils/autodevice.py,sha256=OKZfTbswg6SlsYGCGMqROkA-451CXGG47oeyC5Q1kFM,7232
|
240
240
|
ultralytics/utils/benchmarks.py,sha256=lDNNnLeLUzmqKrqrqlCOiau-q7A-gcLooZP2dbxCu-U,30214
|
241
|
-
ultralytics/utils/checks.py,sha256=
|
241
|
+
ultralytics/utils/checks.py,sha256=F02ASeClT_HbYaLQEvddL5ZFRursRWSTNTrSG0EWixQ,33671
|
242
242
|
ultralytics/utils/dist.py,sha256=aytW0JEkcA5ZTZucV92ot7Bn-apiej8aLk3QNWicjAc,4103
|
243
243
|
ultralytics/utils/downloads.py,sha256=G1nd7c7Gwjf58nZzDVpXDtoFtzhZYbjKBnwbZVMWRG0,22333
|
244
244
|
ultralytics/utils/errors.py,sha256=vY9h2evFSrHnZdHJVVrmm8Zzw4qVDLyo9DeYW5g0dFk,1573
|
@@ -246,7 +246,7 @@ ultralytics/utils/export.py,sha256=Rr5R3GdJBapJJt1XHkH6VQwYN52-L_7wGiRDCgnb7BY,8
|
|
246
246
|
ultralytics/utils/files.py,sha256=0K4O1cgqRiXaDw7EQK13TqA5SME_RrvfDVQSPetNr5w,8042
|
247
247
|
ultralytics/utils/instance.py,sha256=UOEsXR9V-bXNRk6BTonASBEgeMqvzzAk4S7VdXZJUAM,18090
|
248
248
|
ultralytics/utils/loss.py,sha256=Woc_rj7ptCyezHdylEygXMeSEgivYu_B9jJHD4UwxWE,37607
|
249
|
-
ultralytics/utils/metrics.py,sha256=
|
249
|
+
ultralytics/utils/metrics.py,sha256=8x4S7y-rBKRkM47f_o7jfMHA1Bz8SDq3t-R1FXlQNEM,59267
|
250
250
|
ultralytics/utils/ops.py,sha256=YFwPrKlPcgEmgAWqnJVR0Ccx5NQgp5e3P-YYHwVSP0k,34779
|
251
251
|
ultralytics/utils/patches.py,sha256=_dhIU_eDklQE-aWIjpyjPHl_wOwZoGuIUQnXgdSwk_A,5020
|
252
252
|
ultralytics/utils/plotting.py,sha256=oFq19c3tRng-dKHEH-j-S_wLG4CZ_mk8wqE_Gab2H8A,47221
|
@@ -265,8 +265,8 @@ ultralytics/utils/callbacks/neptune.py,sha256=yYUgEgSv6L39sSev6vjwhAWU3DlPDsbSDV
|
|
265
265
|
ultralytics/utils/callbacks/raytune.py,sha256=A8amUGpux7dYES-L1iSeMoMXBySGWCD1aUqT7vcG-pU,1284
|
266
266
|
ultralytics/utils/callbacks/tensorboard.py,sha256=jgYnym3cUQFAgN1GzTyO7l3jINtfAh8zhrllDvnLuVQ,5339
|
267
267
|
ultralytics/utils/callbacks/wb.py,sha256=iDRFXI4IIDm8R5OI89DMTmjs8aHLo1HRCLkOFKdaMG4,7507
|
268
|
-
dgenerate_ultralytics_headless-8.3.
|
269
|
-
dgenerate_ultralytics_headless-8.3.
|
270
|
-
dgenerate_ultralytics_headless-8.3.
|
271
|
-
dgenerate_ultralytics_headless-8.3.
|
272
|
-
dgenerate_ultralytics_headless-8.3.
|
268
|
+
dgenerate_ultralytics_headless-8.3.139.dist-info/METADATA,sha256=39D1VzPdrUmxMBKSS9idrLRY8BcpB39cBXjRzPLyc0A,38296
|
269
|
+
dgenerate_ultralytics_headless-8.3.139.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
270
|
+
dgenerate_ultralytics_headless-8.3.139.dist-info/entry_points.txt,sha256=YM_wiKyTe9yRrsEfqvYolNO5ngwfoL4-NwgKzc8_7sI,93
|
271
|
+
dgenerate_ultralytics_headless-8.3.139.dist-info/top_level.txt,sha256=XP49TwiMw4QGsvTLSYiJhz1xF_k7ev5mQ8jJXaXi45Q,12
|
272
|
+
dgenerate_ultralytics_headless-8.3.139.dist-info/RECORD,,
|
tests/test_python.py
CHANGED
@@ -198,7 +198,12 @@ 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()
|
202
207
|
|
203
208
|
|
204
209
|
def test_train_scratch():
|
tests/test_solutions.py
CHANGED
@@ -300,6 +300,7 @@ def test_streamlit_handle_video_upload_creates_file():
|
|
300
300
|
os.remove("ultralytics.mp4")
|
301
301
|
|
302
302
|
|
303
|
+
@pytest.mark.skipif(IS_RASPBERRYPI, reason="Disabled due to slow performance on Raspberry Pi.")
|
303
304
|
def test_similarity_search_app_init():
|
304
305
|
"""Test SearchApp initializes with required attributes."""
|
305
306
|
app = solutions.SearchApp(device="cpu")
|
@@ -307,6 +308,7 @@ def test_similarity_search_app_init():
|
|
307
308
|
assert hasattr(app, "run")
|
308
309
|
|
309
310
|
|
311
|
+
@pytest.mark.skipif(IS_RASPBERRYPI, reason="Disabled due to slow performance on Raspberry Pi.")
|
310
312
|
def test_similarity_search_complete(tmp_path):
|
311
313
|
"""Test VisualAISearch end-to-end with sample image and query."""
|
312
314
|
from PIL import Image
|
ultralytics/__init__.py
CHANGED
ultralytics/engine/exporter.py
CHANGED
@@ -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
|
ultralytics/engine/results.py
CHANGED
@@ -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
|
"""
|
ultralytics/utils/__init__.py
CHANGED
@@ -187,6 +187,164 @@ 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())
|
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
|
+
if not hasattr(self, "summary"):
|
308
|
+
LOGGER.warning("SQL export is only supported for detection results with `summary()`.")
|
309
|
+
return
|
310
|
+
|
311
|
+
import sqlite3
|
312
|
+
|
313
|
+
df = self.to_df(normalize, decimals)
|
314
|
+
if df.empty:
|
315
|
+
LOGGER.warning("No results to save to SQL. DataFrame is empty.")
|
316
|
+
return
|
317
|
+
|
318
|
+
conn = sqlite3.connect(db_path)
|
319
|
+
cursor = conn.cursor()
|
320
|
+
cursor.execute(
|
321
|
+
f"""CREATE TABLE IF NOT EXISTS {table_name} (
|
322
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
323
|
+
class_name TEXT,
|
324
|
+
confidence REAL,
|
325
|
+
box TEXT,
|
326
|
+
masks TEXT,
|
327
|
+
kpts TEXT
|
328
|
+
)"""
|
329
|
+
)
|
330
|
+
|
331
|
+
for _, row in df.iterrows():
|
332
|
+
cursor.execute(
|
333
|
+
f"INSERT INTO {table_name} (class_name, confidence, box, masks, kpts) VALUES (?, ?, ?, ?, ?)",
|
334
|
+
(
|
335
|
+
row.get("name"),
|
336
|
+
row.get("confidence"),
|
337
|
+
json.dumps(row.get("box", {})),
|
338
|
+
json.dumps(row.get("segments", {})),
|
339
|
+
json.dumps(row.get("keypoints", {})),
|
340
|
+
),
|
341
|
+
)
|
342
|
+
|
343
|
+
conn.commit()
|
344
|
+
conn.close()
|
345
|
+
LOGGER.info(f"Results saved to SQL table '{table_name}' in '{db_path}'.")
|
346
|
+
|
347
|
+
|
190
348
|
class SimpleClass:
|
191
349
|
"""
|
192
350
|
A simple base class for creating objects with string representations of their attributes.
|
ultralytics/utils/checks.py
CHANGED
@@ -388,21 +388,28 @@ def check_requirements(requirements=ROOT.parent / "requirements.txt", exclude=()
|
|
388
388
|
pkgs.append(r)
|
389
389
|
|
390
390
|
@Retry(times=2, delay=1)
|
391
|
-
def attempt_install(packages, commands):
|
392
|
-
"""Attempt
|
393
|
-
|
391
|
+
def attempt_install(packages, commands, use_uv):
|
392
|
+
"""Attempt package installation with uv if available, falling back to pip."""
|
393
|
+
if use_uv:
|
394
|
+
# Note requires --break-system-packages on ARM64 dockerfile
|
395
|
+
cmd = f"uv pip install --system --no-cache-dir {packages} {commands} --index-strategy=unsafe-best-match --break-system-packages --prerelease=allow"
|
396
|
+
else:
|
397
|
+
cmd = f"pip install --no-cache-dir {packages} {commands}"
|
398
|
+
return subprocess.check_output(cmd, shell=True).decode()
|
394
399
|
|
395
400
|
s = " ".join(f'"{x}"' for x in pkgs) # console string
|
396
401
|
if s:
|
397
402
|
if install and AUTOINSTALL: # check environment variable
|
403
|
+
# Note uv fails on arm64 macOS and Raspberry Pi runners
|
404
|
+
uv = not ARM64 and subprocess.run(["command", "-v", "uv"], capture_output=True, shell=True).returncode == 0
|
398
405
|
n = len(pkgs) # number of packages updates
|
399
406
|
LOGGER.info(f"{prefix} Ultralytics requirement{'s' * (n > 1)} {pkgs} not found, attempting AutoUpdate...")
|
400
407
|
try:
|
401
408
|
t = time.time()
|
402
409
|
assert ONLINE, "AutoUpdate skipped (offline)"
|
403
|
-
LOGGER.info(attempt_install(s, cmds))
|
410
|
+
LOGGER.info(attempt_install(s, cmds, use_uv=uv))
|
404
411
|
dt = time.time() - t
|
405
|
-
LOGGER.info(f"{prefix} AutoUpdate success ✅ {dt:.1f}s
|
412
|
+
LOGGER.info(f"{prefix} AutoUpdate success ✅ {dt:.1f}s")
|
406
413
|
LOGGER.warning(
|
407
414
|
f"{prefix} {colorstr('bold', 'Restart runtime or rerun command for updates to take effect')}\n"
|
408
415
|
)
|
ultralytics/utils/metrics.py
CHANGED
@@ -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
|
-
|
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
|
+
]
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|