dgenerate-ultralytics-headless 8.3.137__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.137.dist-info → dgenerate_ultralytics_headless-8.3.139.dist-info}/METADATA +1 -1
- {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.139.dist-info}/RECORD +36 -36
- tests/test_python.py +6 -1
- tests/test_solutions.py +183 -8
- ultralytics/__init__.py +1 -1
- ultralytics/cfg/__init__.py +1 -1
- ultralytics/data/base.py +1 -1
- ultralytics/data/build.py +4 -3
- ultralytics/data/loaders.py +2 -2
- ultralytics/engine/exporter.py +5 -5
- ultralytics/engine/model.py +2 -2
- ultralytics/engine/predictor.py +3 -10
- ultralytics/engine/results.py +2 -209
- ultralytics/engine/trainer.py +1 -1
- ultralytics/engine/validator.py +1 -1
- ultralytics/hub/auth.py +2 -2
- ultralytics/hub/utils.py +8 -3
- ultralytics/models/yolo/classify/predict.py +11 -0
- ultralytics/models/yolo/obb/val.py +1 -1
- ultralytics/models/yolo/world/train.py +1 -1
- ultralytics/models/yolo/yoloe/val.py +3 -3
- ultralytics/solutions/similarity_search.py +3 -6
- ultralytics/solutions/streamlit_inference.py +1 -1
- ultralytics/utils/__init__.py +159 -1
- ultralytics/utils/callbacks/hub.py +5 -4
- ultralytics/utils/checks.py +25 -18
- ultralytics/utils/downloads.py +7 -5
- ultralytics/utils/export.py +1 -1
- ultralytics/utils/metrics.py +90 -5
- ultralytics/utils/plotting.py +1 -1
- ultralytics/utils/torch_utils.py +3 -0
- ultralytics/utils/triton.py +1 -1
- {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.139.dist-info}/WHEEL +0 -0
- {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.139.dist-info}/entry_points.txt +0 -0
- {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.139.dist-info}/licenses/LICENSE +0 -0
- {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.139.dist-info}/top_level.txt +0 -0
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/engine/trainer.py
CHANGED
@@ -578,7 +578,7 @@ class BaseTrainer:
|
|
578
578
|
try:
|
579
579
|
if self.args.task == "classify":
|
580
580
|
data = check_cls_dataset(self.args.data)
|
581
|
-
elif self.args.data.
|
581
|
+
elif self.args.data.rsplit(".", 1)[-1] in {"yaml", "yml"} or self.args.task in {
|
582
582
|
"detect",
|
583
583
|
"segment",
|
584
584
|
"pose",
|
ultralytics/engine/validator.py
CHANGED
@@ -175,7 +175,7 @@ class BaseValidator:
|
|
175
175
|
self.args.batch = model.metadata.get("batch", 1) # export.py models default to batch-size 1
|
176
176
|
LOGGER.info(f"Setting batch={self.args.batch} input of shape ({self.args.batch}, 3, {imgsz}, {imgsz})")
|
177
177
|
|
178
|
-
if str(self.args.data).
|
178
|
+
if str(self.args.data).rsplit(".", 1)[-1] in {"yaml", "yml"}:
|
179
179
|
self.data = check_det_dataset(self.args.data)
|
180
180
|
elif self.args.task == "classify":
|
181
181
|
self.data = check_cls_dataset(self.args.data, split=self.args.split)
|
ultralytics/hub/auth.py
CHANGED
@@ -37,7 +37,7 @@ class Auth:
|
|
37
37
|
verbose (bool): Enable verbose logging.
|
38
38
|
"""
|
39
39
|
# Split the input API key in case it contains a combined key_model and keep only the API key part
|
40
|
-
api_key = api_key.split("_")[0]
|
40
|
+
api_key = api_key.split("_", 1)[0]
|
41
41
|
|
42
42
|
# Set API key attribute as value passed or SETTINGS API key if none passed
|
43
43
|
self.api_key = api_key or SETTINGS.get("api_key", "")
|
@@ -77,7 +77,7 @@ class Auth:
|
|
77
77
|
for attempts in range(max_attempts):
|
78
78
|
LOGGER.info(f"{PREFIX}Login. Attempt {attempts + 1} of {max_attempts}")
|
79
79
|
input_key = getpass.getpass(f"Enter API key from {API_KEY_URL} ")
|
80
|
-
self.api_key = input_key.split("_")[0] # remove model id if present
|
80
|
+
self.api_key = input_key.split("_", 1)[0] # remove model id if present
|
81
81
|
if self.authenticate():
|
82
82
|
return True
|
83
83
|
raise ConnectionError(emojis(f"{PREFIX}Failed to authenticate ❌"))
|
ultralytics/hub/utils.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
2
2
|
|
3
3
|
import os
|
4
|
-
import platform
|
5
4
|
import random
|
6
5
|
import threading
|
7
6
|
import time
|
@@ -18,6 +17,7 @@ from ultralytics.utils import (
|
|
18
17
|
IS_PIP_PACKAGE,
|
19
18
|
LOGGER,
|
20
19
|
ONLINE,
|
20
|
+
PYTHON_VERSION,
|
21
21
|
RANK,
|
22
22
|
SETTINGS,
|
23
23
|
TESTS_RUNNING,
|
@@ -27,6 +27,7 @@ from ultralytics.utils import (
|
|
27
27
|
get_git_origin_url,
|
28
28
|
)
|
29
29
|
from ultralytics.utils.downloads import GITHUB_ASSETS_NAMES
|
30
|
+
from ultralytics.utils.torch_utils import get_cpu_info
|
30
31
|
|
31
32
|
HUB_API_ROOT = os.environ.get("ULTRALYTICS_HUB_API", "https://api.ultralytics.com")
|
32
33
|
HUB_WEB_ROOT = os.environ.get("ULTRALYTICS_HUB_WEB", "https://hub.ultralytics.com")
|
@@ -191,7 +192,9 @@ class Events:
|
|
191
192
|
self.metadata = {
|
192
193
|
"cli": Path(ARGV[0]).name == "yolo",
|
193
194
|
"install": "git" if IS_GIT_DIR else "pip" if IS_PIP_PACKAGE else "other",
|
194
|
-
"python": "."
|
195
|
+
"python": PYTHON_VERSION.rsplit(".", 1)[0], # i.e. 3.13
|
196
|
+
"CPU": get_cpu_info(),
|
197
|
+
# "GPU": get_gpu_info(index=0) if cuda else None,
|
195
198
|
"version": __version__,
|
196
199
|
"env": ENVIRONMENT,
|
197
200
|
"session_id": round(random.random() * 1e15),
|
@@ -205,12 +208,13 @@ class Events:
|
|
205
208
|
and (IS_PIP_PACKAGE or get_git_origin_url() == "https://github.com/ultralytics/ultralytics.git")
|
206
209
|
)
|
207
210
|
|
208
|
-
def __call__(self, cfg):
|
211
|
+
def __call__(self, cfg, device=None):
|
209
212
|
"""
|
210
213
|
Attempt to add a new event to the events list and send events if the rate limit is reached.
|
211
214
|
|
212
215
|
Args:
|
213
216
|
cfg (IterableSimpleNamespace): The configuration object containing mode and task information.
|
217
|
+
device (torch.device | str): The device type (e.g., 'cpu', 'cuda').
|
214
218
|
"""
|
215
219
|
if not self.enabled:
|
216
220
|
# Events disabled, do nothing
|
@@ -222,6 +226,7 @@ class Events:
|
|
222
226
|
**self.metadata,
|
223
227
|
"task": cfg.task,
|
224
228
|
"model": cfg.model if cfg.model in GITHUB_ASSETS_NAMES else "custom",
|
229
|
+
"device": str(device),
|
225
230
|
}
|
226
231
|
if cfg.mode == "export":
|
227
232
|
params["format"] = cfg.format
|
@@ -4,6 +4,7 @@ import cv2
|
|
4
4
|
import torch
|
5
5
|
from PIL import Image
|
6
6
|
|
7
|
+
from ultralytics.data.augment import classify_transforms
|
7
8
|
from ultralytics.engine.predictor import BasePredictor
|
8
9
|
from ultralytics.engine.results import Results
|
9
10
|
from ultralytics.utils import DEFAULT_CFG, ops
|
@@ -51,6 +52,16 @@ class ClassificationPredictor(BasePredictor):
|
|
51
52
|
self.args.task = "classify"
|
52
53
|
self._legacy_transform_name = "ultralytics.yolo.data.augment.ToTensor"
|
53
54
|
|
55
|
+
def setup_source(self, source):
|
56
|
+
"""Sets up source and inference mode and classify transforms."""
|
57
|
+
super().setup_source(source)
|
58
|
+
updated = (
|
59
|
+
self.model.model.transforms.transforms[0].size != max(self.imgsz)
|
60
|
+
if hasattr(self.model.model, "transforms")
|
61
|
+
else True
|
62
|
+
)
|
63
|
+
self.transforms = self.model.model.transforms if not updated else classify_transforms(self.imgsz)
|
64
|
+
|
54
65
|
def preprocess(self, img):
|
55
66
|
"""Convert input images to model-compatible tensor format with appropriate normalization."""
|
56
67
|
if not isinstance(img, torch.Tensor):
|
@@ -252,7 +252,7 @@ class OBBValidator(DetectionValidator):
|
|
252
252
|
merged_results = defaultdict(list)
|
253
253
|
LOGGER.info(f"Saving merged predictions with DOTA format to {pred_merged_txt}...")
|
254
254
|
for d in data:
|
255
|
-
image_id = d["image_id"].split("__")[0]
|
255
|
+
image_id = d["image_id"].split("__", 1)[0]
|
256
256
|
pattern = re.compile(r"\d+___\d+")
|
257
257
|
x, y = (int(c) for c in re.findall(pattern, d["image_id"])[0].split("___"))
|
258
258
|
bbox, score, cls = d["rbox"], d["score"], d["category_id"] - 1
|
@@ -16,7 +16,7 @@ def on_pretrain_routine_end(trainer):
|
|
16
16
|
"""Callback to set up model classes and text encoder at the end of the pretrain routine."""
|
17
17
|
if RANK in {-1, 0}:
|
18
18
|
# Set class names for evaluation
|
19
|
-
names = [name.split("/")[0] for name in list(trainer.test_loader.dataset.data["names"].values())]
|
19
|
+
names = [name.split("/", 1)[0] for name in list(trainer.test_loader.dataset.data["names"].values())]
|
20
20
|
de_parallel(trainer.ema.ema).set_classes(names, cache_clip_model=False)
|
21
21
|
|
22
22
|
|
@@ -47,7 +47,7 @@ class YOLOEDetectValidator(DetectionValidator):
|
|
47
47
|
(torch.Tensor): Visual prompt embeddings with shape (1, num_classes, embed_dim).
|
48
48
|
"""
|
49
49
|
assert isinstance(model, YOLOEModel)
|
50
|
-
names = [name.split("/")[0] for name in list(dataloader.dataset.data["names"].values())]
|
50
|
+
names = [name.split("/", 1)[0] for name in list(dataloader.dataset.data["names"].values())]
|
51
51
|
visual_pe = torch.zeros(len(names), model.model[-1].embed, device=self.device)
|
52
52
|
cls_visual_num = torch.zeros(len(names))
|
53
53
|
|
@@ -140,7 +140,7 @@ class YOLOEDetectValidator(DetectionValidator):
|
|
140
140
|
if trainer is not None:
|
141
141
|
self.device = trainer.device
|
142
142
|
model = trainer.ema.ema
|
143
|
-
names = [name.split("/")[0] for name in list(self.dataloader.dataset.data["names"].values())]
|
143
|
+
names = [name.split("/", 1)[0] for name in list(self.dataloader.dataset.data["names"].values())]
|
144
144
|
|
145
145
|
if load_vp:
|
146
146
|
LOGGER.info("Validate using the visual prompt.")
|
@@ -164,7 +164,7 @@ class YOLOEDetectValidator(DetectionValidator):
|
|
164
164
|
model = attempt_load_weights(model, device=self.device, inplace=True)
|
165
165
|
model.eval().to(self.device)
|
166
166
|
data = check_det_dataset(refer_data or self.args.data)
|
167
|
-
names = [name.split("/")[0] for name in list(data["names"].values())]
|
167
|
+
names = [name.split("/", 1)[0] for name in list(data["names"].values())]
|
168
168
|
|
169
169
|
if load_vp:
|
170
170
|
LOGGER.info("Validate using the visual prompt.")
|
@@ -30,12 +30,9 @@ class VisualAISearch(BaseSolution):
|
|
30
30
|
"""Initializes the VisualAISearch class with the FAISS index file and CLIP model."""
|
31
31
|
super().__init__(**kwargs)
|
32
32
|
check_requirements(["git+https://github.com/ultralytics/CLIP.git", "faiss-cpu"])
|
33
|
-
import clip
|
34
|
-
import faiss
|
35
|
-
|
36
|
-
self.faiss = faiss
|
37
|
-
self.clip = clip
|
38
33
|
|
34
|
+
self.faiss = __import__("faiss")
|
35
|
+
self.clip = __import__("clip")
|
39
36
|
self.faiss_index = "faiss.index"
|
40
37
|
self.data_path_npy = "paths.npy"
|
41
38
|
self.model_name = "ViT-B/32"
|
@@ -51,7 +48,7 @@ class VisualAISearch(BaseSolution):
|
|
51
48
|
safe_download(url=f"{ASSETS_URL}/images.zip", unzip=True, retry=3)
|
52
49
|
self.data_dir = Path("images")
|
53
50
|
|
54
|
-
self.model, self.preprocess = clip.load(self.model_name, device=self.device)
|
51
|
+
self.model, self.preprocess = self.clip.load(self.model_name, device=self.device)
|
55
52
|
|
56
53
|
self.index = None
|
57
54
|
self.image_paths = []
|
@@ -130,7 +130,7 @@ class Inference:
|
|
130
130
|
# Add dropdown menu for model selection
|
131
131
|
available_models = [x.replace("yolo", "YOLO") for x in GITHUB_ASSETS_STEMS if x.startswith("yolo11")]
|
132
132
|
if self.model_path: # If user provided the custom model, insert model without suffix as *.pt is added later
|
133
|
-
available_models.insert(0, self.model_path.split(".pt")[0])
|
133
|
+
available_models.insert(0, self.model_path.split(".pt", 1)[0])
|
134
134
|
selected_model = self.st.sidebar.selectbox("Model", available_models)
|
135
135
|
|
136
136
|
with self.st.spinner("Model is downloading..."):
|
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.
|
@@ -1387,7 +1545,7 @@ def deprecation_warn(arg, new_arg=None):
|
|
1387
1545
|
def clean_url(url):
|
1388
1546
|
"""Strip auth from URL, i.e. https://url.com/file.txt?auth -> https://url.com/file.txt."""
|
1389
1547
|
url = Path(url).as_posix().replace(":/", "://") # Pathlib turns :// -> :/, as_posix() for Windows
|
1390
|
-
return unquote(url).split("?")[0] # '%2F' to '/', split https://url.com/file.txt?auth
|
1548
|
+
return unquote(url).split("?", 1)[0] # '%2F' to '/', split https://url.com/file.txt?auth
|
1391
1549
|
|
1392
1550
|
|
1393
1551
|
def url2file(url):
|
@@ -73,22 +73,23 @@ def on_train_end(trainer):
|
|
73
73
|
|
74
74
|
def on_train_start(trainer):
|
75
75
|
"""Run events on train start."""
|
76
|
-
events(trainer.args)
|
76
|
+
events(trainer.args, trainer.device)
|
77
77
|
|
78
78
|
|
79
79
|
def on_val_start(validator):
|
80
80
|
"""Run events on validation start."""
|
81
|
-
|
81
|
+
if not validator.training:
|
82
|
+
events(validator.args, validator.device)
|
82
83
|
|
83
84
|
|
84
85
|
def on_predict_start(predictor):
|
85
86
|
"""Run events on predict start."""
|
86
|
-
events(predictor.args)
|
87
|
+
events(predictor.args, predictor.device)
|
87
88
|
|
88
89
|
|
89
90
|
def on_export_start(exporter):
|
90
91
|
"""Run events on export start."""
|
91
|
-
events(exporter.args)
|
92
|
+
events(exporter.args, exporter.device)
|
92
93
|
|
93
94
|
|
94
95
|
callbacks = (
|