dgenerate-ultralytics-headless 8.3.137__py3-none-any.whl → 8.3.138__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.138.dist-info}/METADATA +1 -1
  2. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.138.dist-info}/RECORD +33 -33
  3. tests/test_solutions.py +181 -8
  4. ultralytics/__init__.py +1 -1
  5. ultralytics/cfg/__init__.py +1 -1
  6. ultralytics/data/base.py +1 -1
  7. ultralytics/data/build.py +4 -3
  8. ultralytics/data/loaders.py +2 -2
  9. ultralytics/engine/exporter.py +4 -5
  10. ultralytics/engine/model.py +2 -2
  11. ultralytics/engine/predictor.py +3 -10
  12. ultralytics/engine/trainer.py +1 -1
  13. ultralytics/engine/validator.py +1 -1
  14. ultralytics/hub/auth.py +2 -2
  15. ultralytics/hub/utils.py +8 -3
  16. ultralytics/models/yolo/classify/predict.py +11 -0
  17. ultralytics/models/yolo/obb/val.py +1 -1
  18. ultralytics/models/yolo/world/train.py +1 -1
  19. ultralytics/models/yolo/yoloe/val.py +3 -3
  20. ultralytics/solutions/similarity_search.py +3 -6
  21. ultralytics/solutions/streamlit_inference.py +1 -1
  22. ultralytics/utils/__init__.py +1 -1
  23. ultralytics/utils/callbacks/hub.py +5 -4
  24. ultralytics/utils/checks.py +13 -13
  25. ultralytics/utils/downloads.py +7 -5
  26. ultralytics/utils/export.py +1 -1
  27. ultralytics/utils/plotting.py +1 -1
  28. ultralytics/utils/torch_utils.py +3 -0
  29. ultralytics/utils/triton.py +1 -1
  30. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.138.dist-info}/WHEEL +0 -0
  31. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.138.dist-info}/entry_points.txt +0 -0
  32. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.138.dist-info}/licenses/LICENSE +0 -0
  33. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.138.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.137
3
+ Version: 8.3.138
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.137.dist-info/licenses/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
1
+ dgenerate_ultralytics_headless-8.3.138.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
@@ -7,11 +7,11 @@ 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
9
  tests/test_python.py,sha256=KWsncKpeDdRmjRftmJpsMl7bBLI3TG_I7Lb4kuemZzQ,25618
10
- tests/test_solutions.py,sha256=IFlqyOUCvGbLe_YZqWmNCe_afg4as0p-SfAv3j7VURI,6205
11
- ultralytics/__init__.py,sha256=8hzZtbr1IMQwOTdqbcNED-RHZiqww--zXivCgQOzujQ,730
10
+ tests/test_solutions.py,sha256=4_Ce7VmsAoALN79a72nEpRgIJqx1oGnCY7yre9a-5vk,12671
11
+ ultralytics/__init__.py,sha256=dNL32IT6IwReXAZE3Gp8ZeYZDxmgnF_xLn60OR9wXzg,730
12
12
  ultralytics/assets/bus.jpg,sha256=wCAZxJecGR63Od3ZRERe9Aja1Weayrb9Ug751DS_vGM,137419
13
13
  ultralytics/assets/zidane.jpg,sha256=Ftc4aeMmen1O0A3o6GCDO9FlfBslLpTAw0gnetx7bts,50427
14
- ultralytics/cfg/__init__.py,sha256=h0UVCvX6DIpoR4_pthpZD_Ihq7eCaS8HbXsPOm82G0E,39540
14
+ ultralytics/cfg/__init__.py,sha256=mpvLR68Iff4J59zYGhysSl8VwIVVzV_VMOYeVdqnYj4,39544
15
15
  ultralytics/cfg/default.yaml,sha256=oFG6llJO-Py5H-cR9qs-7FieJamroDLwpbrkhmfROOM,8307
16
16
  ultralytics/cfg/datasets/Argoverse.yaml,sha256=_xlEDIJ9XkUo0v_iNL7FW079BoSeZtKSuLteKTtGbA8,3275
17
17
  ultralytics/cfg/datasets/DOTAv1.5.yaml,sha256=SHND_CFkojxw5iQD5Mcgju2kCZIl0gW2ajuzv1cqoL0,1224
@@ -106,11 +106,11 @@ ultralytics/cfg/trackers/bytetrack.yaml,sha256=6u-tiZlk16EqEwkNXaMrza6PAQmWj_ypg
106
106
  ultralytics/data/__init__.py,sha256=nAXaL1puCc7z_NjzQNlJnhbVhT9Fla2u7Dsqo7q1dAc,644
107
107
  ultralytics/data/annotator.py,sha256=VEwb11FsEZm75qlEp8XDHFGKW0_rGsEaFDaBVd771Kw,2902
108
108
  ultralytics/data/augment.py,sha256=5O02Um483j7VAutLUz13IGpuuEdvyD9mhTMxFCFwCas,129342
109
- ultralytics/data/base.py,sha256=bsASjxdkvojkFjas-JfFNSpBjo0GRAbYKDh64Y2hCH4,19015
110
- ultralytics/data/build.py,sha256=Ez_HSx-ZpL3Z1C4mDnyGPi107saG3TLR4PC7iv2sz_4,9807
109
+ ultralytics/data/base.py,sha256=Yn0pRz1E_yIx2IJtQClA0FuWkYrlpJfuOGPlg3QUGiI,19020
110
+ ultralytics/data/build.py,sha256=J1aP7qYioSiP2xq3QefiRWk3-c7lKzhNCR0hqXLQFos,9850
111
111
  ultralytics/data/converter.py,sha256=znXH2XTdo0Q4NDHMny1ydVBvrxKn2kbbwI-X5bn1MlQ,26890
112
112
  ultralytics/data/dataset.py,sha256=uc5OMkaQtWQHBd_KST_WXO6FEoeF4xUhKDDJBKkQ354,34916
113
- ultralytics/data/loaders.py,sha256=Wn_93-niQZg57VuX-vXF9MmcdHrGs5RlevdyO_V5J0s,29951
113
+ ultralytics/data/loaders.py,sha256=ybkN2q9nFtjl-YQYCy_fvlTBuA19ARDSeoag2Gg7aTU,29961
114
114
  ultralytics/data/split.py,sha256=6UFXcbVrzYVAPmFbl4FeZFJOkdbN3jQFepJxi_pD-I0,4748
115
115
  ultralytics/data/split_dota.py,sha256=ihG56YfNFZJDq1r7Zcgk8fKzde3gn21W0f67ub6nT68,11879
116
116
  ultralytics/data/utils.py,sha256=5vD6Nea2SE14Ap9nFTHkJgzOgVKJy-P8-bcqqxa_UB0,35551
@@ -119,17 +119,17 @@ 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=JucFVR_RAfzrRWM9kJK6MHALEbdzrf93ReTnAhiRTBo,70823
123
- ultralytics/engine/model.py,sha256=fWhPNWUQzjjWfTEXzTaqSSearV4THRkEa_fl4dDvzWw,52930
124
- ultralytics/engine/predictor.py,sha256=AwKpOGY2G-thNNiRw4Kf_MBLamq5tbRhXLNSMRArqFo,21803
122
+ ultralytics/engine/exporter.py,sha256=tJTl6AkJ-OCgE1pstsu2iOSHqidkuqejPetNCg8S64k,70841
123
+ ultralytics/engine/model.py,sha256=BtC5KYNrdfhryrS7b6ZXDIsmtObEeIDTePCv1gO4br4,52952
124
+ ultralytics/engine/predictor.py,sha256=rZ5mIPeejkxUerpTfUf_1rSAklOR3THqoejlil4C04w,21651
125
125
  ultralytics/engine/results.py,sha256=MhbyMCwgslmtV53fqii4UJUaLQ4gKTKdkXi7vvmJDAE,79628
126
- ultralytics/engine/trainer.py,sha256=c_iGyt6bwIf4aRUeVcVEuOKG9ZpixJsZUbI2eMqQXto,38951
126
+ ultralytics/engine/trainer.py,sha256=xdgNAgq6umJ6915tiCK3U22NeY7w1HnvmAhXlwS_hYI,38955
127
127
  ultralytics/engine/tuner.py,sha256=zEW1UpLlZ6N4xbvS7MxICkshRlaFgLNfuADA0VfRpao,12629
128
- ultralytics/engine/validator.py,sha256=jfV81wuFDgrVVXEcPzgOpxAPrAZn-1LgpKwu9l_1-ts,17050
128
+ ultralytics/engine/validator.py,sha256=f9UUv3QqQStLrO1nojrHkdS58qYQxKXaoIQQria6WyA,17054
129
129
  ultralytics/hub/__init__.py,sha256=wDtAUKdfqob95tfFHgDJFXcsNSDSdoIQkJTm-CfIUTI,6616
130
- ultralytics/hub/auth.py,sha256=_bGQVLTgP-ina4fQxq2M7qkj9zKKfxb99_VWgN3S_4k,5549
130
+ ultralytics/hub/auth.py,sha256=cykVsFR5xjqZdf8_TagHceGc1BzrfkiSHrzQdoa0nOQ,5555
131
131
  ultralytics/hub/session.py,sha256=Hohzn2L2QJTYszIHqwxnsK4V-0MOU-8ldMIfpxMtLSE,18708
132
- ultralytics/hub/utils.py,sha256=luSqI4Ym7A1NRFrDsryPTDrlFL8FJdWQ9Zyrl9d-Abs,9661
132
+ ultralytics/hub/utils.py,sha256=Hh_ND38R7ssflXh9ndG739-8283oej_EZzlOftIDFEU,9936
133
133
  ultralytics/hub/google/__init__.py,sha256=rV9_KoRBwYlwyx3QLaBp1opw5Sjrbgl0YoDHtXoHIMw,8429
134
134
  ultralytics/models/__init__.py,sha256=DqQFFYJ4IQlqIDb61H1HzcnZU7SuHN-43bw94-l-YAQ,309
135
135
  ultralytics/models/fastsam/__init__.py,sha256=HGJ8EKlBAsdF-e2aIwQLjSDAFI_r0yHR0A1gzrp4vqE,231
@@ -166,7 +166,7 @@ ultralytics/models/utils/ops.py,sha256=SuBnwwgUTqByNHpufobGLW72yO2cyfZFi14KAFWSj
166
166
  ultralytics/models/yolo/__init__.py,sha256=or0j5xvcM0usMlsFTYhNAOcQUri7reD0cD9JR5b7zDk,307
167
167
  ultralytics/models/yolo/model.py,sha256=oGPLPkgym0kfFhkLgyriR5KbKTQyJH-1Uggup5wFgw0,14296
168
168
  ultralytics/models/yolo/classify/__init__.py,sha256=9--HVaNOfI1K7rn_rRqclL8FUAnpfeBrRqEQIaQw2xM,383
169
- ultralytics/models/yolo/classify/predict.py,sha256=JV9szginTQ9Lpob0FozhKMiEIu1vVaYg4YItuVK2AFM,4081
169
+ ultralytics/models/yolo/classify/predict.py,sha256=QckEv4qNiCSjIbHFxq9KVKpYHL4TbuLWfT5zXoMQEpU,4576
170
170
  ultralytics/models/yolo/classify/train.py,sha256=rv2CJv9fzvtHf2q4l5g0RsjplWKeLpz637kKqjtrLNY,9737
171
171
  ultralytics/models/yolo/classify/val.py,sha256=xk-YwSQdl_oqyCBV0OOAOcXFL6CchebFOc36AkRSyjE,9992
172
172
  ultralytics/models/yolo/detect/__init__.py,sha256=GIRsLYR-kT4JJx7lh4ZZAFGBZj0aebokuU0A7JbjDVA,257
@@ -176,7 +176,7 @@ ultralytics/models/yolo/detect/val.py,sha256=7AB_wZi7aQ9_V1pZQSWk5qiJYS34fuO3P5a
176
176
  ultralytics/models/yolo/obb/__init__.py,sha256=tQmpG8wVHsajWkZdmD6cjGohJ4ki64iSXQT8JY_dydo,221
177
177
  ultralytics/models/yolo/obb/predict.py,sha256=L40iamQgTY7VDn0WggG2jeJK8cVUo1qsNuFSbK67ry0,2974
178
178
  ultralytics/models/yolo/obb/train.py,sha256=NBSpXCyIn2qxtaG7gvolUzXOB0mf3oEFIpQZHTES1_s,3458
179
- ultralytics/models/yolo/obb/val.py,sha256=dkXUh2JfffILVRkfXycQGImQQssUDgKMtfDRP7jUpV0,13981
179
+ ultralytics/models/yolo/obb/val.py,sha256=Qzer8to_DhPmJ56BNDZh6d9f4o5TY-OgebZpzY8lUxY,13984
180
180
  ultralytics/models/yolo/pose/__init__.py,sha256=63xmuHZLNzV8I76HhVXAq4f2W0KTk8Oi9eL-Y204LyQ,227
181
181
  ultralytics/models/yolo/pose/predict.py,sha256=sY-yMVl-hW8tGVSKt-5Pl1Bhdhj9exnmGIeb4n9wUDc,3836
182
182
  ultralytics/models/yolo/pose/train.py,sha256=dKa1Vzt4GoZ9yqdK6olqLEg-qhYaPUh29Qg62bHAVi8,6502
@@ -186,13 +186,13 @@ ultralytics/models/yolo/segment/predict.py,sha256=mIC3aHI7Jg4dU1k2UZnjVj4unE-5TW
186
186
  ultralytics/models/yolo/segment/train.py,sha256=EIyIAjYp127Mb-DomyjPORaONu57OY_gOTK9p2MwW6E,5359
187
187
  ultralytics/models/yolo/segment/val.py,sha256=cXJM1JNuzDraU0SJQRIdzNxabd0bfcxiRE8wozHZChY,18415
188
188
  ultralytics/models/yolo/world/__init__.py,sha256=nlh8I6t8hMGz_vZg8QSlsUW1R-2eKvn9CGUoPPQEGhA,131
189
- ultralytics/models/yolo/world/train.py,sha256=4e54RghcrpdtpxG3n2Nicwo-tcj-wI4nLcUo8_4cf30,6898
189
+ ultralytics/models/yolo/world/train.py,sha256=2R0h36kggT8ZBpwaACqgg9vd34rNU-tbPsfPuxuBV4g,6901
190
190
  ultralytics/models/yolo/world/train_world.py,sha256=fFhhI-toaEy1_-XcPM1_mF395WRQ26gZ4UxqyUAZmWw,8461
191
191
  ultralytics/models/yolo/yoloe/__init__.py,sha256=6SLytdJtwu37qewf7CobG7C7Wl1m-xtNdvCXEasfPDE,760
192
192
  ultralytics/models/yolo/yoloe/predict.py,sha256=N0oYcr_mdw8wyUAWprAwJhrA0r23BaTeYXEjw2e8_mI,6993
193
193
  ultralytics/models/yolo/yoloe/train.py,sha256=xRPDJ3nUWxtqjESfmUtsZslVhpgzrZRw8z_QU5hV6nc,11710
194
194
  ultralytics/models/yolo/yoloe/train_seg.py,sha256=BYFBd04k5WQaJPcFbCvVIbEf2IOQyW8_sGeoVT_74j0,4632
195
- ultralytics/models/yolo/yoloe/val.py,sha256=oA8cVT3pBXF6aPZy7ITq0mDcktRuIgks8tTtqMRISyY,8431
195
+ ultralytics/models/yolo/yoloe/val.py,sha256=g6GK5NgVEV9bhXzo1zes0NGa4JEZS3UB-5sPN8fGyZw,8440
196
196
  ultralytics/nn/__init__.py,sha256=rjociYD9lo_K-d-1s6TbdWklPLjTcEHk7OIlRDJstIE,615
197
197
  ultralytics/nn/autobackend.py,sha256=X2cxCytBu9fmniy8uJ5aZb28IukQ-uxV1INXeS1lclA,39368
198
198
  ultralytics/nn/tasks.py,sha256=iJWpwRr4yZg1dTT-9jXuzIqkdFmbZm1b7hejnO-CiZk,64337
@@ -218,10 +218,10 @@ ultralytics/solutions/parking_management.py,sha256=BV-2lpSfgmK7fib3DnPSZ5rtLdy11
218
218
  ultralytics/solutions/queue_management.py,sha256=p1-cuI_rs4ygtlBryXjE65NYG2bnZXhp3ylggFnWcRs,4344
219
219
  ultralytics/solutions/region_counter.py,sha256=Zn35YRXNzhBk27D9MLOHBYe2L1o6H2ey3mEwCXofB_E,5418
220
220
  ultralytics/solutions/security_alarm.py,sha256=JdkQUjqJl3iCd2MLVYkh1L7askvhi3_gp0RLXG6s390,6247
221
- ultralytics/solutions/similarity_search.py,sha256=WTYmHNHfFrRiJ6mrZhJvGPsjt3szQUiM6VRpw2eBRjA,7332
221
+ ultralytics/solutions/similarity_search.py,sha256=NVjrlxWStXPhSaE_tGW0g1_j8vD0evaT9IjGOHYERFg,7323
222
222
  ultralytics/solutions/solutions.py,sha256=1iZIj3Z5bs14WbVT8MIDXABfW-pBmfvQNdBJ6l21uVY,32696
223
223
  ultralytics/solutions/speed_estimation.py,sha256=r7S5nGIx8PTV-zC4zCI36lQD2DVy5cen5cTXItfQIHo,5318
224
- ultralytics/solutions/streamlit_inference.py,sha256=M0ppTFInqSPrdytZBLH8x-XoA7zFc7PaRQ51wHG9ppU,9846
224
+ ultralytics/solutions/streamlit_inference.py,sha256=p1bBKTtmvB6zStXdOzS0CGYurm4zu82WKii5rJriizA,9849
225
225
  ultralytics/solutions/trackzone.py,sha256=mfklnZcVRqI3bbhPiHF2iSoV6INcd10wwwGP4tlK7L0,3854
226
226
  ultralytics/solutions/vision_eye.py,sha256=LCb-2YPVvEks9e7xqZtNGftpAXNaZhEUb5yb3N0ni_U,2952
227
227
  ultralytics/solutions/templates/similarity-search.html,sha256=DPoAO-1H-KXNt_T8mGtSCsYUEi_5Nrx01p0cZfX-E8Q,3790
@@ -234,39 +234,39 @@ 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=vac0M-Hx55QXl6Vod3QPjnLBlt87Hwxu1784RXPmeQA,52879
237
+ ultralytics/utils/__init__.py,sha256=9RF8KyUHd_YyovvZzlcnZzxx-jkxBLrxfXfkFVj64Iw,52882
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=TGhnnNVT3NEBhSeckWIe1rGlXUyYI3xhFqK6CR0oBiE,33192
241
+ ultralytics/utils/checks.py,sha256=GJbfMl608ihHSjN78Xjjky10Cglv-CYsxJ7Ra7HeH1U,33204
242
242
  ultralytics/utils/dist.py,sha256=aytW0JEkcA5ZTZucV92ot7Bn-apiej8aLk3QNWicjAc,4103
243
- ultralytics/utils/downloads.py,sha256=Rn8xDwn2bzgBqiYz3Xn0rm3MWjk4T-QUd2Ajlu1EpQ4,22312
243
+ ultralytics/utils/downloads.py,sha256=G1nd7c7Gwjf58nZzDVpXDtoFtzhZYbjKBnwbZVMWRG0,22333
244
244
  ultralytics/utils/errors.py,sha256=vY9h2evFSrHnZdHJVVrmm8Zzw4qVDLyo9DeYW5g0dFk,1573
245
- ultralytics/utils/export.py,sha256=XInnl9AQeik7EuR1492nzDvgDqaV43FlnM5CLamrgd4,8814
245
+ ultralytics/utils/export.py,sha256=Rr5R3GdJBapJJt1XHkH6VQwYN52-L_7wGiRDCgnb7BY,8817
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
249
  ultralytics/utils/metrics.py,sha256=n8guPEADBMRNpeXNShEX-fxVv9xck8S4QaOIiaW_kl0,56037
250
250
  ultralytics/utils/ops.py,sha256=YFwPrKlPcgEmgAWqnJVR0Ccx5NQgp5e3P-YYHwVSP0k,34779
251
251
  ultralytics/utils/patches.py,sha256=_dhIU_eDklQE-aWIjpyjPHl_wOwZoGuIUQnXgdSwk_A,5020
252
- ultralytics/utils/plotting.py,sha256=GKic2OMavjJPT3pOPdU0UcvQTrG1LVt0vHJM-Zuy9Bs,47217
252
+ ultralytics/utils/plotting.py,sha256=oFq19c3tRng-dKHEH-j-S_wLG4CZ_mk8wqE_Gab2H8A,47221
253
253
  ultralytics/utils/tal.py,sha256=P5nPoR9qNnFuDIda0fsn8WP6m1V8r7EbvXUuhNRFFTA,20805
254
- ultralytics/utils/torch_utils.py,sha256=2SJxxg8Qr0YqOoQ-8qAYn6VrzZdQMObqiw3CJZ-rAY0,39611
255
- ultralytics/utils/triton.py,sha256=xK9Db_ZUVDnIK1u76S2G-6ulIBsLfj9HN_YOaSrnMuU,5304
254
+ ultralytics/utils/torch_utils.py,sha256=xQgznbCdnuEkCajUpx5q8SfUM8wh9Bb-PcHOMl2g1KI,39670
255
+ ultralytics/utils/triton.py,sha256=9P2rlQcGCTMFVKLA5S5mTYzU9cKbR5HF9ruVkPpVBE8,5307
256
256
  ultralytics/utils/tuner.py,sha256=0Bp7l5dWZe1RzdvAIa11wQoX6eoAaoNRcA-EAnpofbk,6755
257
257
  ultralytics/utils/callbacks/__init__.py,sha256=hzL63Rce6VkZhP4Lcim9LKjadixaQG86nKqPhk7IkS0,242
258
258
  ultralytics/utils/callbacks/base.py,sha256=p8YCeYDp4GLcyHWFZxC2Wxr2IXLw_MfIE5ef1fOQcWk,6848
259
259
  ultralytics/utils/callbacks/clearml.py,sha256=z-MmCALz1FcNSec8CmDiFHkRd_zTzzuPDCidq_xkUXY,5990
260
260
  ultralytics/utils/callbacks/comet.py,sha256=_j8tKKxGlxDcw_Rx4Ow2PjZ3UpBHm9gLJlYSVU0WJ_E,22221
261
261
  ultralytics/utils/callbacks/dvc.py,sha256=NywyiMqJfnK_UfJ_f1IK31puyIXZy0iVJQ4bB9uyu08,7532
262
- ultralytics/utils/callbacks/hub.py,sha256=dPSeSStRE1x-WYyqrUghCp_VtBxNZ5-Bmb4wW2KYV2Y,4073
262
+ ultralytics/utils/callbacks/hub.py,sha256=1RmGiCaog1GoTya9OAyGELbQ2Lk5X3EWh7RYMxns0so,4177
263
263
  ultralytics/utils/callbacks/mlflow.py,sha256=rcjjN_QVg6XoL4Kbw8YqC28RDCQMs0LxfsXRpAc8BgY,5430
264
264
  ultralytics/utils/callbacks/neptune.py,sha256=yYUgEgSv6L39sSev6vjwhAWU3DlPDsbSDVFoR24NYio,4664
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.137.dist-info/METADATA,sha256=8ui4ivOJaSEgzcD9bZTlWkJ3-Q_44TdABJCrfpEeLRM,38296
269
- dgenerate_ultralytics_headless-8.3.137.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
270
- dgenerate_ultralytics_headless-8.3.137.dist-info/entry_points.txt,sha256=YM_wiKyTe9yRrsEfqvYolNO5ngwfoL4-NwgKzc8_7sI,93
271
- dgenerate_ultralytics_headless-8.3.137.dist-info/top_level.txt,sha256=XP49TwiMw4QGsvTLSYiJhz1xF_k7ev5mQ8jJXaXi45Q,12
272
- dgenerate_ultralytics_headless-8.3.137.dist-info/RECORD,,
268
+ dgenerate_ultralytics_headless-8.3.138.dist-info/METADATA,sha256=nEE7PWdXEk2qcDj95YSk9gnmx_YRfC-qzikx69OBVYY,38296
269
+ dgenerate_ultralytics_headless-8.3.138.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
270
+ dgenerate_ultralytics_headless-8.3.138.dist-info/entry_points.txt,sha256=YM_wiKyTe9yRrsEfqvYolNO5ngwfoL4-NwgKzc8_7sI,93
271
+ dgenerate_ultralytics_headless-8.3.138.dist-info/top_level.txt,sha256=XP49TwiMw4QGsvTLSYiJhz1xF_k7ev5mQ8jJXaXi45Q,12
272
+ dgenerate_ultralytics_headless-8.3.138.dist-info/RECORD,,
tests/test_solutions.py CHANGED
@@ -3,7 +3,11 @@
3
3
  # Tests Ultralytics Solutions: https://docs.ultralytics.com/solutions/,
4
4
  # including every solution excluding DistanceCalculation and Security Alarm System.
5
5
 
6
+ import os
7
+ from unittest.mock import patch
8
+
6
9
  import cv2
10
+ import numpy as np
7
11
  import pytest
8
12
 
9
13
  from tests import MODEL, TMP
@@ -19,7 +23,10 @@ POSE_VIDEO = "solution_ci_pose_demo.mp4" # only for workouts monitoring solutio
19
23
  PARKING_VIDEO = "solution_ci_parking_demo.mp4" # only for parking management solution
20
24
  PARKING_AREAS_JSON = "solution_ci_parking_areas.json" # only for parking management solution
21
25
  PARKING_MODEL = "solutions_ci_parking_model.pt" # only for parking management solution
26
+ VERTICAL_VIDEO = "solution_vertical_demo.mp4" # only for vertical line counting
22
27
  REGION = [(10, 200), (540, 200), (540, 180), (10, 180)] # for object counting, speed estimation and queue management
28
+ HORIZONTAL_LINE = [(10, 200), (540, 200)] # for object counting
29
+ VERTICAL_LINE = [(320, 0), (320, 400)] # for object counting
23
30
 
24
31
  # Test configs for each solution : (name, class, needs_frame_count, video, kwargs)
25
32
  SOLUTIONS = [
@@ -30,6 +37,27 @@ SOLUTIONS = [
30
37
  DEMO_VIDEO,
31
38
  {"region": REGION, "model": MODEL, "show": SHOW},
32
39
  ),
40
+ (
41
+ "ObjectCounter",
42
+ solutions.ObjectCounter,
43
+ False,
44
+ DEMO_VIDEO,
45
+ {"region": HORIZONTAL_LINE, "model": MODEL, "show": SHOW},
46
+ ),
47
+ (
48
+ "ObjectCounterVertical",
49
+ solutions.ObjectCounter,
50
+ False,
51
+ DEMO_VIDEO,
52
+ {"region": VERTICAL_LINE, "model": MODEL, "show": SHOW},
53
+ ),
54
+ (
55
+ "ObjectCounterwithOBB",
56
+ solutions.ObjectCounter,
57
+ False,
58
+ DEMO_VIDEO,
59
+ {"region": REGION, "model": "yolo11n-obb.pt", "show": SHOW},
60
+ ),
33
61
  (
34
62
  "Heatmap",
35
63
  solutions.Heatmap,
@@ -63,28 +91,28 @@ SOLUTIONS = [
63
91
  solutions.Analytics,
64
92
  True,
65
93
  DEMO_VIDEO,
66
- {"analytics_type": "line", "model": MODEL, "show": SHOW},
94
+ {"analytics_type": "line", "model": MODEL, "show": SHOW, "figsize": (6.4, 3.2)},
67
95
  ),
68
96
  (
69
97
  "PieAnalytics",
70
98
  solutions.Analytics,
71
99
  True,
72
100
  DEMO_VIDEO,
73
- {"analytics_type": "pie", "model": MODEL, "show": SHOW},
101
+ {"analytics_type": "pie", "model": MODEL, "show": SHOW, "figsize": (6.4, 3.2)},
74
102
  ),
75
103
  (
76
104
  "BarAnalytics",
77
105
  solutions.Analytics,
78
106
  True,
79
107
  DEMO_VIDEO,
80
- {"analytics_type": "bar", "model": MODEL, "show": SHOW},
108
+ {"analytics_type": "bar", "model": MODEL, "show": SHOW, "figsize": (6.4, 3.2)},
81
109
  ),
82
110
  (
83
111
  "AreaAnalytics",
84
112
  solutions.Analytics,
85
113
  True,
86
114
  DEMO_VIDEO,
87
- {"analytics_type": "area", "model": MODEL, "show": SHOW},
115
+ {"analytics_type": "area", "model": MODEL, "show": SHOW, "figsize": (6.4, 3.2)},
88
116
  ),
89
117
  ("TrackZone", solutions.TrackZone, False, DEMO_VIDEO, {"region": REGION, "model": MODEL, "show": SHOW}),
90
118
  (
@@ -99,7 +127,7 @@ SOLUTIONS = [
99
127
  solutions.ObjectBlurrer,
100
128
  False,
101
129
  DEMO_VIDEO,
102
- {"blur_ratio": 0.5, "model": MODEL, "show": SHOW},
130
+ {"blur_ratio": 0.02, "model": MODEL, "show": SHOW},
103
131
  ),
104
132
  (
105
133
  "InstanceSegmentation",
@@ -160,7 +188,10 @@ def process_video(solution, video_path, needs_frame_count=False):
160
188
  def test_solution(name, solution_class, needs_frame_count, video, kwargs):
161
189
  """Test individual Ultralytics solution."""
162
190
  if video:
163
- safe_download(url=f"{ASSETS_URL}/{video}", dir=TMP)
191
+ if name != "ObjectCounterVertical":
192
+ safe_download(url=f"{ASSETS_URL}/{video}", dir=TMP)
193
+ else:
194
+ safe_download(url=f"{ASSETS_URL}/{VERTICAL_VIDEO}", dir=TMP)
164
195
  if name == "ParkingManager":
165
196
  safe_download(url=f"{ASSETS_URL}/{PARKING_AREAS_JSON}", dir=TMP)
166
197
  safe_download(url=f"{ASSETS_URL}/{PARKING_MODEL}", dir=TMP)
@@ -169,6 +200,7 @@ def test_solution(name, solution_class, needs_frame_count, video, kwargs):
169
200
  solution_class(**kwargs).inference() # requires interactive GUI environment
170
201
  return
171
202
 
203
+ video = VERTICAL_VIDEO if name == "ObjectCounterVertical" else video
172
204
  process_video(
173
205
  solution=solution_class(**kwargs),
174
206
  video_path=str(TMP / video),
@@ -181,7 +213,148 @@ def test_solution(name, solution_class, needs_frame_count, video, kwargs):
181
213
  @pytest.mark.skipif(IS_RASPBERRYPI, reason="Disabled due to slow performance on Raspberry Pi.")
182
214
  def test_similarity_search():
183
215
  """Test similarity search solution."""
184
- from ultralytics import solutions
185
-
186
216
  searcher = solutions.VisualAISearch()
187
217
  _ = searcher("a dog sitting on a bench") # Returns the results in format "- img name | similarity score"
218
+
219
+
220
+ def test_left_click_selection():
221
+ """Test distance calculation left click."""
222
+ dc = solutions.DistanceCalculation()
223
+ dc.boxes, dc.track_ids = [[10, 10, 50, 50]], [1]
224
+ dc.mouse_event_for_distance(cv2.EVENT_LBUTTONDOWN, 30, 30, None, None)
225
+ assert 1 in dc.selected_boxes
226
+
227
+
228
+ def test_right_click_reset():
229
+ """Test distance calculation right click."""
230
+ dc = solutions.DistanceCalculation()
231
+ dc.selected_boxes, dc.left_mouse_count = {1: [10, 10, 50, 50]}, 1
232
+ dc.mouse_event_for_distance(cv2.EVENT_RBUTTONDOWN, 0, 0, None, None)
233
+ assert dc.selected_boxes == {}
234
+ assert dc.left_mouse_count == 0
235
+
236
+
237
+ def test_parking_json_none():
238
+ """Test that ParkingManagement skips or errors cleanly when no JSON is provided."""
239
+ im0 = np.zeros((640, 480, 3), dtype=np.uint8)
240
+ try:
241
+ parkingmanager = solutions.ParkingManagement(json_path=None)
242
+ parkingmanager(im0)
243
+ except ValueError:
244
+ pytest.skip("Skipping test due to missing JSON.")
245
+
246
+
247
+ def test_analytics_graph_not_supported():
248
+ """Test that unsupported analytics type raises ModuleNotFoundError."""
249
+ try:
250
+ analytics = solutions.Analytics(analytics_type="test") # 'test' is unsupported
251
+ analytics.process(im0=None, frame_number=0)
252
+ assert False, "Expected ModuleNotFoundError for unsupported chart type"
253
+ except ModuleNotFoundError as e:
254
+ assert "test chart is not supported" in str(e)
255
+
256
+
257
+ def test_area_chart_padding():
258
+ """Test area chart graph update with dynamic class padding logic."""
259
+ analytics = solutions.Analytics(analytics_type="area")
260
+ analytics.update_graph(frame_number=1, count_dict={"car": 2}, plot="area")
261
+ plot_im = analytics.update_graph(frame_number=2, count_dict={"car": 3, "person": 1}, plot="area")
262
+ assert plot_im is not None
263
+
264
+
265
+ def test_config_update_method_with_invalid_argument():
266
+ """Test that update() raises ValueError for invalid config keys."""
267
+ obj = solutions.config.SolutionConfig()
268
+ try:
269
+ obj.update(invalid_key=123)
270
+ assert False, "Expected ValueError for invalid update argument"
271
+ except ValueError as e:
272
+ assert "❌ invalid_key is not a valid solution argument" in str(e)
273
+
274
+
275
+ def test_plot_with_no_masks():
276
+ """Test that instance segmentation handles cases with no masks."""
277
+ im0 = np.zeros((640, 480, 3), dtype=np.uint8)
278
+ isegment = solutions.InstanceSegmentation(model="yolo11n-seg.pt")
279
+ results = isegment(im0)
280
+ assert results.plot_im is not None
281
+
282
+
283
+ def test_streamlit_handle_video_upload_creates_file():
284
+ """Test Streamlit video upload logic saves file correctly."""
285
+ import io
286
+
287
+ fake_file = io.BytesIO(b"fake video content")
288
+ fake_file.read = fake_file.getvalue
289
+ if fake_file is not None:
290
+ g = io.BytesIO(fake_file.read())
291
+ with open("ultralytics.mp4", "wb") as out:
292
+ out.write(g.read())
293
+ output_path = "ultralytics.mp4"
294
+ else:
295
+ output_path = None
296
+ assert output_path == "ultralytics.mp4"
297
+ assert os.path.exists("ultralytics.mp4")
298
+ with open("ultralytics.mp4", "rb") as f:
299
+ assert f.read() == b"fake video content"
300
+ os.remove("ultralytics.mp4")
301
+
302
+
303
+ def test_similarity_search_app_init():
304
+ """Test SearchApp initializes with required attributes."""
305
+ app = solutions.SearchApp(device="cpu")
306
+ assert hasattr(app, "searcher")
307
+ assert hasattr(app, "run")
308
+
309
+
310
+ def test_similarity_search_complete(tmp_path):
311
+ """Test VisualAISearch end-to-end with sample image and query."""
312
+ from PIL import Image
313
+
314
+ image_dir = tmp_path / "images"
315
+ os.makedirs(image_dir, exist_ok=True)
316
+ for i in range(2):
317
+ img = Image.fromarray(np.uint8(np.random.rand(224, 224, 3) * 255))
318
+ img.save(image_dir / f"test_image_{i}.jpg")
319
+ searcher = solutions.VisualAISearch(data=str(image_dir))
320
+ results = searcher("a red and white object")
321
+ assert results
322
+
323
+
324
+ def test_distance_calculation_process_method():
325
+ """Test DistanceCalculation.process() computes distance between selected boxes."""
326
+ from ultralytics.solutions.solutions import SolutionResults
327
+
328
+ dc = solutions.DistanceCalculation()
329
+ dc.boxes, dc.track_ids, dc.clss, dc.confs = (
330
+ [[100, 100, 200, 200], [300, 300, 400, 400]],
331
+ [1, 2],
332
+ [0, 0],
333
+ [0.9, 0.95],
334
+ )
335
+ dc.selected_boxes = {1: dc.boxes[0], 2: dc.boxes[1]}
336
+ frame = np.zeros((480, 640, 3), dtype=np.uint8)
337
+ with patch.object(dc, "extract_tracks"), patch.object(dc, "display_output"), patch("cv2.setMouseCallback"):
338
+ result = dc.process(frame)
339
+ assert isinstance(result, SolutionResults)
340
+ assert result.total_tracks == 2
341
+ assert result.pixels_distance > 0
342
+
343
+
344
+ def test_object_crop_with_show_True():
345
+ """Test ObjectCropper init with show=True to cover display warning."""
346
+ solutions.ObjectCropper(show=True)
347
+
348
+
349
+ def test_display_output_method():
350
+ """Test that display_output triggers imshow, waitKey, and destroyAllWindows when enabled."""
351
+ counter = solutions.ObjectCounter(show=True)
352
+ counter.env_check = True
353
+ frame = np.zeros((100, 100, 3), dtype=np.uint8)
354
+ with patch("cv2.imshow") as mock_imshow, patch("cv2.waitKey", return_value=ord("q")) as mock_wait, patch(
355
+ "cv2.destroyAllWindows"
356
+ ) as mock_destroy:
357
+ counter.display_output(frame)
358
+ mock_imshow.assert_called_once()
359
+ mock_wait.assert_called_once()
360
+ mock_destroy.assert_called_once()
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.137"
3
+ __version__ = "8.3.138"
4
4
 
5
5
  import os
6
6
 
@@ -311,7 +311,7 @@ def get_cfg(cfg: Union[str, Path, Dict, SimpleNamespace] = DEFAULT_CFG_DICT, ove
311
311
  if k in cfg and isinstance(cfg[k], (int, float)):
312
312
  cfg[k] = str(cfg[k])
313
313
  if cfg.get("name") == "model": # assign model to 'name' arg
314
- cfg["name"] = str(cfg.get("model", "")).split(".")[0]
314
+ cfg["name"] = str(cfg.get("model", "")).partition(".")[0]
315
315
  LOGGER.warning(f"'name=model' automatically updated to 'name={cfg['name']}'.")
316
316
 
317
317
  # Type and Value checks
ultralytics/data/base.py CHANGED
@@ -170,7 +170,7 @@ class BaseDataset(Dataset):
170
170
  # F += [p.parent / x.lstrip(os.sep) for x in t] # local to global path (pathlib)
171
171
  else:
172
172
  raise FileNotFoundError(f"{self.prefix}{p} does not exist")
173
- im_files = sorted(x.replace("/", os.sep) for x in f if x.split(".")[-1].lower() in IMG_FORMATS)
173
+ im_files = sorted(x.replace("/", os.sep) for x in f if x.rpartition(".")[-1].lower() in IMG_FORMATS)
174
174
  # self.img_files = sorted([x for x in f if x.suffix[1:].lower() in IMG_FORMATS]) # pathlib
175
175
  assert im_files, f"{self.prefix}No images found in {img_path}. {FORMATS_HELP_MSG}"
176
176
  except Exception as e:
ultralytics/data/build.py CHANGED
@@ -200,10 +200,11 @@ def check_source(source):
200
200
  webcam, screenshot, from_img, in_memory, tensor = False, False, False, False, False
201
201
  if isinstance(source, (str, int, Path)): # int for local usb camera
202
202
  source = str(source)
203
- is_file = Path(source).suffix[1:] in (IMG_FORMATS | VID_FORMATS)
204
- is_url = source.lower().startswith(("https://", "http://", "rtsp://", "rtmp://", "tcp://"))
203
+ source_lower = source.lower()
204
+ is_file = source_lower.rpartition(".")[-1] in (IMG_FORMATS | VID_FORMATS)
205
+ is_url = source_lower.startswith(("https://", "http://", "rtsp://", "rtmp://", "tcp://"))
205
206
  webcam = source.isnumeric() or source.endswith(".streams") or (is_url and not is_file)
206
- screenshot = source.lower() == "screen"
207
+ screenshot = source_lower == "screen"
207
208
  if is_url and is_file:
208
209
  source = check_file(source) # download
209
210
  elif isinstance(source, LOADERS):
@@ -353,7 +353,7 @@ class LoadImagesAndVideos:
353
353
  # Define files as images or videos
354
354
  images, videos = [], []
355
355
  for f in files:
356
- suffix = f.split(".")[-1].lower() # Get file extension without the dot and lowercase
356
+ suffix = f.rpartition(".")[-1].lower() # Get file extension without the dot and lowercase
357
357
  if suffix in IMG_FORMATS:
358
358
  images.append(f)
359
359
  elif suffix in VID_FORMATS:
@@ -427,7 +427,7 @@ class LoadImagesAndVideos:
427
427
  else:
428
428
  # Handle image files (including HEIC)
429
429
  self.mode = "image"
430
- if path.split(".")[-1].lower() == "heic":
430
+ if path.rpartition(".")[-1].lower() == "heic":
431
431
  # Load HEIC image using Pillow with pillow-heif
432
432
  check_requirements("pillow-heif")
433
433
 
@@ -244,7 +244,6 @@ class Exporter:
244
244
 
245
245
  def __call__(self, model=None) -> str:
246
246
  """Return list of exported files/dirs after running callbacks."""
247
- self.run_callbacks("on_export_start")
248
247
  t = time.time()
249
248
  fmt = self.args.format.lower() # to lowercase
250
249
  if fmt in {"tensorrt", "trt"}: # 'engine' aliases
@@ -277,7 +276,7 @@ class Exporter:
277
276
  LOGGER.warning("TensorRT requires GPU export, automatically assigning device=0")
278
277
  self.args.device = "0"
279
278
  if fmt == "engine" and "dla" in str(self.args.device): # convert int/list to str first
280
- dla = self.args.device.split(":")[-1]
279
+ dla = self.args.device.rsplit(":", 1)[-1]
281
280
  self.args.device = "0" # update device to "0"
282
281
  assert dla in {"0", "1"}, f"Expected self.args.device='dla:0' or 'dla:1, but got {self.args.device}."
283
282
  if imx and self.args.device is None and torch.cuda.is_available():
@@ -450,7 +449,7 @@ class Exporter:
450
449
  f"\n{colorstr('PyTorch:')} starting from '{file}' with input shape {tuple(im.shape)} BCHW and "
451
450
  f"output shape(s) {self.output_shape} ({file_size(file):.1f} MB)"
452
451
  )
453
-
452
+ self.run_callbacks("on_export_start")
454
453
  # Exports
455
454
  f = [""] * len(fmts) # exported filenames
456
455
  if jit or ncnn: # TorchScript
@@ -789,7 +788,7 @@ class Exporter:
789
788
  subprocess.run(cmd, check=True)
790
789
 
791
790
  # Remove debug files
792
- pnnx_files = [x.split("=")[-1] for x in pnnx_args]
791
+ pnnx_files = [x.rsplit("=", 1)[-1] for x in pnnx_args]
793
792
  for f_debug in ("debug.bin", "debug.param", "debug2.bin", "debug2.param", *pnnx_files):
794
793
  Path(f_debug).unlink(missing_ok=True)
795
794
 
@@ -1048,7 +1047,7 @@ class Exporter:
1048
1047
  "sudo apt-get install edgetpu-compiler",
1049
1048
  ):
1050
1049
  subprocess.run(c if is_sudo_available() else c.replace("sudo ", ""), shell=True, check=True)
1051
- ver = subprocess.run(cmd, shell=True, capture_output=True, check=True).stdout.decode().split()[-1]
1050
+ ver = subprocess.run(cmd, shell=True, capture_output=True, check=True).stdout.decode().rsplit(maxsplit=1)[-1]
1052
1051
 
1053
1052
  LOGGER.info(f"\n{prefix} starting export with Edge TPU compiler {ver}...")
1054
1053
  f = str(tflite_model).replace(".tflite", "_edgetpu.tflite") # Edge TPU model
@@ -288,7 +288,7 @@ class Model(torch.nn.Module):
288
288
  weights = checks.check_file(weights, download_dir=SETTINGS["weights_dir"]) # download and return local file
289
289
  weights = checks.check_model_file_from_stem(weights) # add suffix, i.e. yolo11n -> yolo11n.pt
290
290
 
291
- if Path(weights).suffix == ".pt":
291
+ if str(weights).rpartition(".")[-1] == "pt":
292
292
  self.model, self.ckpt = attempt_load_one_weight(weights)
293
293
  self.task = self.model.args["task"]
294
294
  self.overrides = self.model.args = self._reset_ckpt_args(self.model.args)
@@ -319,7 +319,7 @@ class Model(torch.nn.Module):
319
319
  >>> model = Model("yolo11n.onnx")
320
320
  >>> model._check_is_pytorch_model() # Raises TypeError
321
321
  """
322
- pt_str = isinstance(self.model, (str, Path)) and Path(self.model).suffix == ".pt"
322
+ pt_str = isinstance(self.model, (str, Path)) and str(self.model).rpartition(".")[-1] == "pt"
323
323
  pt_module = isinstance(self.model, torch.nn.Module)
324
324
  if not (pt_module or pt_str):
325
325
  raise TypeError(
@@ -43,7 +43,7 @@ import torch
43
43
 
44
44
  from ultralytics.cfg import get_cfg, get_save_dir
45
45
  from ultralytics.data import load_inference_source
46
- from ultralytics.data.augment import LetterBox, classify_transforms
46
+ from ultralytics.data.augment import LetterBox
47
47
  from ultralytics.nn.autobackend import AutoBackend
48
48
  from ultralytics.utils import DEFAULT_CFG, LOGGER, MACOS, WINDOWS, callbacks, colorstr, ops
49
49
  from ultralytics.utils.checks import check_imgsz, check_imshow
@@ -247,15 +247,6 @@ class BasePredictor:
247
247
  Source for inference.
248
248
  """
249
249
  self.imgsz = check_imgsz(self.args.imgsz, stride=self.model.stride, min_dim=2) # check image size
250
- self.transforms = (
251
- getattr(
252
- self.model.model,
253
- "transforms",
254
- classify_transforms(self.imgsz[0]),
255
- )
256
- if self.args.task == "classify"
257
- else None
258
- )
259
250
  self.dataset = load_inference_source(
260
251
  source=source,
261
252
  batch=self.args.batch,
@@ -395,6 +386,8 @@ class BasePredictor:
395
386
 
396
387
  self.device = self.model.device # update device
397
388
  self.args.half = self.model.fp16 # update half
389
+ if hasattr(self.model, "imgsz"):
390
+ self.args.imgsz = self.model.imgsz # reuse imgsz from export metadata
398
391
  self.model.eval()
399
392
 
400
393
  def write_results(self, i, p, im, s):
@@ -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.split(".")[-1] in {"yaml", "yml"} or self.args.task in {
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",
@@ -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).split(".")[-1] in {"yaml", "yml"}:
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": ".".join(platform.python_version_tuple()[:2]), # i.e. 3.10
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..."):
@@ -1387,7 +1387,7 @@ def deprecation_warn(arg, new_arg=None):
1387
1387
  def clean_url(url):
1388
1388
  """Strip auth from URL, i.e. https://url.com/file.txt?auth -> https://url.com/file.txt."""
1389
1389
  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
1390
+ return unquote(url).split("?", 1)[0] # '%2F' to '/', split https://url.com/file.txt?auth
1391
1391
 
1392
1392
 
1393
1393
  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
- events(validator.args)
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 = (
@@ -73,7 +73,7 @@ def parse_requirements(file_path=ROOT.parent / "requirements.txt", package=""):
73
73
  for line in requires:
74
74
  line = line.strip()
75
75
  if line and not line.startswith("#"):
76
- line = line.split("#")[0].strip() # ignore inline comments
76
+ line = line.partition("#")[0].strip() # ignore inline comments
77
77
  if match := re.match(r"([a-zA-Z0-9-_]+)\s*([<>!=~]+.*)?", line):
78
78
  requirements.append(SimpleNamespace(name=match[1], specifier=match[2].strip() if match[2] else ""))
79
79
 
@@ -379,7 +379,7 @@ def check_requirements(requirements=ROOT.parent / "requirements.txt", exclude=()
379
379
 
380
380
  pkgs = []
381
381
  for r in requirements:
382
- r_stripped = r.split("/")[-1].replace(".git", "") # replace git+https://org/repo.git -> 'repo'
382
+ r_stripped = r.rpartition("/")[-1].replace(".git", "") # replace git+https://org/repo.git -> 'repo'
383
383
  match = re.match(r"([a-zA-Z0-9-_]+)([<>!=~]+.*)?", r_stripped)
384
384
  name, required = match[1], match[2].strip() if match[2] else ""
385
385
  try:
@@ -423,6 +423,7 @@ def check_torchvision():
423
423
  to the compatibility table based on: https://github.com/pytorch/vision#installation.
424
424
  """
425
425
  compatibility_table = {
426
+ "2.7": ["0.22"],
426
427
  "2.6": ["0.21"],
427
428
  "2.5": ["0.20"],
428
429
  "2.4": ["0.19"],
@@ -435,10 +436,10 @@ def check_torchvision():
435
436
  }
436
437
 
437
438
  # Check major and minor versions
438
- v_torch = ".".join(torch.__version__.split("+")[0].split(".")[:2])
439
+ v_torch = ".".join(torch.__version__.split("+", 1)[0].split(".")[:2])
439
440
  if v_torch in compatibility_table:
440
441
  compatible_versions = compatibility_table[v_torch]
441
- v_torchvision = ".".join(TORCHVISION_VERSION.split("+")[0].split(".")[:2])
442
+ v_torchvision = ".".join(TORCHVISION_VERSION.split("+", 1)[0].split(".")[:2])
442
443
  if all(v_torchvision != v for v in compatible_versions):
443
444
  LOGGER.warning(
444
445
  f"torchvision=={v_torchvision} is incompatible with torch=={v_torch}.\n"
@@ -461,9 +462,8 @@ def check_suffix(file="yolo11n.pt", suffix=".pt", msg=""):
461
462
  if isinstance(suffix, str):
462
463
  suffix = {suffix}
463
464
  for f in file if isinstance(file, (list, tuple)) else [file]:
464
- s = Path(f).suffix.lower().strip() # file suffix
465
- if len(s):
466
- assert s in suffix, f"{msg}{f} acceptable suffix is {suffix}, not {s}"
465
+ if s := str(f).rpartition(".")[-1].lower().strip(): # file suffix
466
+ assert f".{s}" in suffix, f"{msg}{f} acceptable suffix is {suffix}, not .{s}"
467
467
 
468
468
 
469
469
  def check_yolov5u_filename(file: str, verbose: bool = True):
@@ -504,10 +504,10 @@ def check_model_file_from_stem(model="yolo11n"):
504
504
  Returns:
505
505
  (str | Path): Model filename with appropriate suffix.
506
506
  """
507
- if model and not Path(model).suffix and Path(model).stem in downloads.GITHUB_ASSETS_STEMS:
508
- return Path(model).with_suffix(".pt") # add suffix, i.e. yolo11n -> yolo11n.pt
509
- else:
510
- return model
507
+ path = Path(model)
508
+ if not path.suffix and path.stem in downloads.GITHUB_ASSETS_STEMS:
509
+ return path.with_suffix(".pt") # add suffix, i.e. yolo11n -> yolo11n.pt
510
+ return model
511
511
 
512
512
 
513
513
  def check_file(file, suffix="", download=True, download_dir=".", hard=True):
@@ -655,7 +655,7 @@ def collect_system_info():
655
655
  from ultralytics.utils.torch_utils import get_cpu_info, get_gpu_info
656
656
 
657
657
  gib = 1 << 30 # bytes per GiB
658
- cuda = torch and torch.cuda.is_available()
658
+ cuda = torch.cuda.is_available()
659
659
  check_yolo()
660
660
  total, used, free = shutil.disk_usage("/")
661
661
 
@@ -837,7 +837,7 @@ def cuda_device_count() -> int:
837
837
  )
838
838
 
839
839
  # Take the first line and strip any leading/trailing white space
840
- first_line = output.strip().split("\n")[0]
840
+ first_line = output.strip().split("\n", 1)[0]
841
841
 
842
842
  return int(first_line)
843
843
  except (subprocess.CalledProcessError, FileNotFoundError, ValueError):
@@ -32,11 +32,13 @@ GITHUB_ASSETS_NAMES = frozenset(
32
32
  + [f"sam2.1_{k}.pt" for k in "blst"]
33
33
  + [f"FastSAM-{k}.pt" for k in "sx"]
34
34
  + [f"rtdetr-{k}.pt" for k in "lx"]
35
- + ["mobile_sam.pt"]
36
- + ["mobileclip_blt.ts"]
37
- + ["calibration_image_sample_data_20x128x128x3_float32.npy.zip"]
35
+ + [
36
+ "mobile_sam.pt",
37
+ "mobileclip_blt.ts",
38
+ "calibration_image_sample_data_20x128x128x3_float32.npy.zip",
39
+ ]
38
40
  )
39
- GITHUB_ASSETS_STEMS = frozenset(k.rsplit(".", 1)[0] for k in GITHUB_ASSETS_NAMES)
41
+ GITHUB_ASSETS_STEMS = frozenset(k.rpartition(".")[0] for k in GITHUB_ASSETS_NAMES)
40
42
 
41
43
 
42
44
  def is_url(url, check=False):
@@ -247,7 +249,7 @@ def get_google_drive_file_info(link):
247
249
  """
248
250
  import requests # slow import
249
251
 
250
- file_id = link.split("/d/")[1].split("/view")[0]
252
+ file_id = link.split("/d/")[1].split("/view", 1)[0]
251
253
  drive_url = f"https://drive.google.com/uc?export=download&id={file_id}"
252
254
  filename = None
253
255
 
@@ -97,7 +97,7 @@ def export_engine(
97
97
  builder = trt.Builder(logger)
98
98
  config = builder.create_builder_config()
99
99
  workspace = int((workspace or 0) * (1 << 30))
100
- is_trt10 = int(trt.__version__.split(".")[0]) >= 10 # is TensorRT >= 10
100
+ is_trt10 = int(trt.__version__.split(".", 1)[0]) >= 10 # is TensorRT >= 10
101
101
  if is_trt10 and workspace > 0:
102
102
  config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, workspace)
103
103
  elif workspace > 0: # TensorRT versions 7, 8
@@ -1000,7 +1000,7 @@ def feature_visualization(x, module_type, stage, n=32, save_dir=Path("runs/detec
1000
1000
  if isinstance(x, torch.Tensor):
1001
1001
  _, channels, height, width = x.shape # batch, channels, height, width
1002
1002
  if height > 1 and width > 1:
1003
- f = save_dir / f"stage{stage}_{module_type.split('.')[-1]}_features.png" # filename
1003
+ f = save_dir / f"stage{stage}_{module_type.rsplit('.', 1)[-1]}_features.png" # filename
1004
1004
 
1005
1005
  blocks = torch.chunk(x[0].cpu(), channels, dim=0) # select batch index 0, block by channels
1006
1006
  n = min(n, channels) # number of plots
@@ -1,5 +1,6 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
+ import functools
3
4
  import gc
4
5
  import math
5
6
  import os
@@ -101,6 +102,7 @@ def autocast(enabled: bool, device: str = "cuda"):
101
102
  return torch.cuda.amp.autocast(enabled)
102
103
 
103
104
 
105
+ @functools.lru_cache
104
106
  def get_cpu_info():
105
107
  """Return a string with system CPU information, i.e. 'Apple M2'."""
106
108
  from ultralytics.utils import PERSISTENT_CACHE # avoid circular import error
@@ -118,6 +120,7 @@ def get_cpu_info():
118
120
  return PERSISTENT_CACHE.get("cpu_info", "unknown")
119
121
 
120
122
 
123
+ @functools.lru_cache
121
124
  def get_gpu_info(index):
122
125
  """Return a string with system GPU information, i.e. 'Tesla T4, 15102MiB'."""
123
126
  properties = torch.cuda.get_device_properties(index)
@@ -53,7 +53,7 @@ class TritonRemoteModel:
53
53
  """
54
54
  if not endpoint and not scheme: # Parse all args from URL string
55
55
  splits = urlsplit(url)
56
- endpoint = splits.path.strip("/").split("/")[0]
56
+ endpoint = splits.path.strip("/").split("/", 1)[0]
57
57
  scheme = splits.scheme
58
58
  url = splits.netloc
59
59