dgenerate-ultralytics-headless 8.3.184__py3-none-any.whl → 8.3.186__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.184.dist-info → dgenerate_ultralytics_headless-8.3.186.dist-info}/METADATA +4 -5
- {dgenerate_ultralytics_headless-8.3.184.dist-info → dgenerate_ultralytics_headless-8.3.186.dist-info}/RECORD +24 -23
- ultralytics/__init__.py +1 -1
- ultralytics/cfg/datasets/Argoverse.yaml +2 -2
- ultralytics/cfg/datasets/Objects365.yaml +3 -3
- ultralytics/cfg/datasets/SKU-110K.yaml +2 -2
- ultralytics/cfg/datasets/VOC.yaml +2 -4
- ultralytics/cfg/datasets/VisDrone.yaml +2 -2
- ultralytics/cfg/datasets/xView.yaml +2 -2
- ultralytics/data/build.py +2 -2
- ultralytics/data/converter.py +1 -1
- ultralytics/data/dataset.py +1 -1
- ultralytics/data/utils.py +0 -2
- ultralytics/engine/exporter.py +4 -1
- ultralytics/solutions/region_counter.py +3 -2
- ultralytics/utils/__init__.py +1 -62
- ultralytics/utils/autodevice.py +1 -1
- ultralytics/utils/downloads.py +29 -19
- ultralytics/utils/logger.py +21 -22
- ultralytics/utils/tqdm.py +462 -0
- {dgenerate_ultralytics_headless-8.3.184.dist-info → dgenerate_ultralytics_headless-8.3.186.dist-info}/WHEEL +0 -0
- {dgenerate_ultralytics_headless-8.3.184.dist-info → dgenerate_ultralytics_headless-8.3.186.dist-info}/entry_points.txt +0 -0
- {dgenerate_ultralytics_headless-8.3.184.dist-info → dgenerate_ultralytics_headless-8.3.186.dist-info}/licenses/LICENSE +0 -0
- {dgenerate_ultralytics_headless-8.3.184.dist-info → dgenerate_ultralytics_headless-8.3.186.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.186
|
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>
|
@@ -42,7 +42,6 @@ Requires-Dist: scipy>=1.4.1
|
|
42
42
|
Requires-Dist: torch>=1.8.0
|
43
43
|
Requires-Dist: torch!=2.4.0,>=1.8.0; sys_platform == "win32"
|
44
44
|
Requires-Dist: torchvision>=0.9.0
|
45
|
-
Requires-Dist: tqdm>=4.64.0
|
46
45
|
Requires-Dist: psutil
|
47
46
|
Requires-Dist: py-cpuinfo
|
48
47
|
Requires-Dist: pandas>=1.1.4
|
@@ -55,7 +54,7 @@ Requires-Dist: coverage[toml]; extra == "dev"
|
|
55
54
|
Requires-Dist: mkdocs>=1.6.0; extra == "dev"
|
56
55
|
Requires-Dist: mkdocs-material>=9.5.9; extra == "dev"
|
57
56
|
Requires-Dist: mkdocstrings[python]; extra == "dev"
|
58
|
-
Requires-Dist: mkdocs-ultralytics-plugin>=0.1.
|
57
|
+
Requires-Dist: mkdocs-ultralytics-plugin>=0.1.28; extra == "dev"
|
59
58
|
Requires-Dist: mkdocs-macros-plugin>=1.0.5; extra == "dev"
|
60
59
|
Provides-Extra: export
|
61
60
|
Requires-Dist: numpy<2.0.0; extra == "export"
|
@@ -131,7 +130,7 @@ The workflow runs automatically every day at midnight UTC to check for new Ultra
|
|
131
130
|
|
132
131
|
<div>
|
133
132
|
<a href="https://github.com/ultralytics/ultralytics/actions/workflows/ci.yml"><img src="https://github.com/ultralytics/ultralytics/actions/workflows/ci.yml/badge.svg" alt="Ultralytics CI"></a>
|
134
|
-
<a href="https://
|
133
|
+
<a href="https://clickpy.clickhouse.com/dashboard/ultralytics"><img src="https://static.pepy.tech/badge/ultralytics" alt="Ultralytics Downloads"></a>
|
135
134
|
<a href="https://zenodo.org/badge/latestdoi/264818686"><img src="https://zenodo.org/badge/264818686.svg" alt="Ultralytics YOLO Citation"></a>
|
136
135
|
<a href="https://discord.com/invite/ultralytics"><img alt="Ultralytics Discord" src="https://img.shields.io/discord/1089800235347353640?logo=discord&logoColor=white&label=Discord&color=blue"></a>
|
137
136
|
<a href="https://community.ultralytics.com/"><img alt="Ultralytics Forums" src="https://img.shields.io/discourse/users?server=https%3A%2F%2Fcommunity.ultralytics.com&logo=discourse&label=Forums&color=blue"></a>
|
@@ -180,7 +179,7 @@ See below for quickstart installation and usage examples. For comprehensive guid
|
|
180
179
|
|
181
180
|
Install the `ultralytics` package, including all [requirements](https://github.com/ultralytics/ultralytics/blob/main/pyproject.toml), in a [**Python>=3.8**](https://www.python.org/) environment with [**PyTorch>=1.8**](https://pytorch.org/get-started/locally/).
|
182
181
|
|
183
|
-
[](https://pypi.org/project/ultralytics/) [](https://
|
182
|
+
[](https://pypi.org/project/ultralytics/) [](https://clickpy.clickhouse.com/dashboard/ultralytics) [](https://pypi.org/project/ultralytics/)
|
184
183
|
|
185
184
|
```bash
|
186
185
|
pip install ultralytics
|
@@ -1,4 +1,4 @@
|
|
1
|
-
dgenerate_ultralytics_headless-8.3.
|
1
|
+
dgenerate_ultralytics_headless-8.3.186.dist-info/licenses/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
2
2
|
tests/__init__.py,sha256=b4KP5_q-2IO8Br8YHOSLYnn7IwZS81l_vfEF2YPa2lM,894
|
3
3
|
tests/conftest.py,sha256=LXtQJcFNWPGuzauTGkiXgsvVC3llJKfg22WcmhRzuQc,2593
|
4
4
|
tests/test_cli.py,sha256=EMf5gTAopOnIz8VvzaM-Qb044o7D0flnUHYQ-2ffOM4,5670
|
@@ -8,22 +8,22 @@ tests/test_exports.py,sha256=CY-4xVZlVM16vdyIC0mSR3Ix59aiZm1qjFGIhSNmB20,11007
|
|
8
8
|
tests/test_integrations.py,sha256=kl_AKmE_Qs1GB0_91iVwbzNxofm_hFTt0zzU6JF-pg4,6323
|
9
9
|
tests/test_python.py,sha256=JbOB6pbTkoQtPCjkl_idagV0_W2QLWGbsh2IvGmru0M,28274
|
10
10
|
tests/test_solutions.py,sha256=tuf6n_fsI8KvSdJrnc-cqP2qYdiYqCWuVrx0z9dOz3Q,13213
|
11
|
-
ultralytics/__init__.py,sha256=
|
11
|
+
ultralytics/__init__.py,sha256=CCcYcTlUEFxDB3syD60I3oQ4B2UFVPb4gCZ-jatuAsU,730
|
12
12
|
ultralytics/py.typed,sha256=la67KBlbjXN-_-DfGNcdOcjYumVpKG_Tkw-8n5dnGB4,8
|
13
13
|
ultralytics/assets/bus.jpg,sha256=wCAZxJecGR63Od3ZRERe9Aja1Weayrb9Ug751DS_vGM,137419
|
14
14
|
ultralytics/assets/zidane.jpg,sha256=Ftc4aeMmen1O0A3o6GCDO9FlfBslLpTAw0gnetx7bts,50427
|
15
15
|
ultralytics/cfg/__init__.py,sha256=Uj1br3-NVFvP6VY5CL4PK63mAQAom93XFC5cqSbM6t4,39887
|
16
16
|
ultralytics/cfg/default.yaml,sha256=1SspGAK_K_DT7DBfEScJh4jsJUTOxahehZYj92xmj7o,8347
|
17
|
-
ultralytics/cfg/datasets/Argoverse.yaml,sha256=
|
17
|
+
ultralytics/cfg/datasets/Argoverse.yaml,sha256=J4ItoUlE_EiYTmp1DFKYHfbqHkj8j4wUtRJQhaMIlBM,3275
|
18
18
|
ultralytics/cfg/datasets/DOTAv1.5.yaml,sha256=VZ_KKFX0H2YvlFVJ8JHcLWYBZ2xiQ6Z-ROSTiKWpS7c,1211
|
19
19
|
ultralytics/cfg/datasets/DOTAv1.yaml,sha256=JrDuYcQ0JU9lJlCA-dCkMNko_jaj6MAVGHjsfjeZ_u0,1181
|
20
20
|
ultralytics/cfg/datasets/GlobalWheat2020.yaml,sha256=dnr_loeYSE6Eo_f7V1yubILsMRBMRm1ozyC5r7uT-iY,2144
|
21
21
|
ultralytics/cfg/datasets/HomeObjects-3K.yaml,sha256=xEtSqEad-rtfGuIrERjjhdISggmPlvaX-315ZzKz50I,934
|
22
22
|
ultralytics/cfg/datasets/ImageNet.yaml,sha256=GvDWypLVG_H3H67Ai8IC1pvK6fwcTtF5FRhzO1OXXDU,42530
|
23
|
-
ultralytics/cfg/datasets/Objects365.yaml,sha256=
|
24
|
-
ultralytics/cfg/datasets/SKU-110K.yaml,sha256=
|
25
|
-
ultralytics/cfg/datasets/VOC.yaml,sha256=
|
26
|
-
ultralytics/cfg/datasets/VisDrone.yaml,sha256=
|
23
|
+
ultralytics/cfg/datasets/Objects365.yaml,sha256=eMQuA8B4ZGp_GsmMNKFP4CziMSVduyuAK1IANkAZaJw,9367
|
24
|
+
ultralytics/cfg/datasets/SKU-110K.yaml,sha256=25M1xoJRqw-UEHmeAiyLKCzk0kTLj0FSlwpZ9dRKwIw,2555
|
25
|
+
ultralytics/cfg/datasets/VOC.yaml,sha256=NhVLvsmLOwMIteW4DPKxetURP5bTaJvYc7w08-HYAUs,3785
|
26
|
+
ultralytics/cfg/datasets/VisDrone.yaml,sha256=RauTGwmGetLjamcPCiBL7FEWwd8mAA1Y4ARlozX6-E8,3613
|
27
27
|
ultralytics/cfg/datasets/african-wildlife.yaml,sha256=SuloMp9WAZBigGC8az-VLACsFhTM76_O29yhTvUqdnU,915
|
28
28
|
ultralytics/cfg/datasets/brain-tumor.yaml,sha256=qrxPO_t9wxbn2kHFwP3vGTzSWj2ELTLelUwYL3_b6nc,800
|
29
29
|
ultralytics/cfg/datasets/carparts-seg.yaml,sha256=A4e9hM1unTY2jjZIXGiKSarF6R-Ad9R99t57OgRJ37w,1253
|
@@ -47,7 +47,7 @@ ultralytics/cfg/datasets/open-images-v7.yaml,sha256=wK9v3OAGdHORkFdqoBi0hS0fa1b7
|
|
47
47
|
ultralytics/cfg/datasets/package-seg.yaml,sha256=V4uyTDWWzgft24y9HJWuELKuZ5AndAHXbanxMI6T8GU,849
|
48
48
|
ultralytics/cfg/datasets/signature.yaml,sha256=gBvU3715gVxVAafI_yaYczGX3kfEfA4BttbiMkgOXNk,774
|
49
49
|
ultralytics/cfg/datasets/tiger-pose.yaml,sha256=Y_8htA4--6hmpqHTW-Ix4t9SdaWenSSyl_FUtI2A7n8,926
|
50
|
-
ultralytics/cfg/datasets/xView.yaml,sha256=
|
50
|
+
ultralytics/cfg/datasets/xView.yaml,sha256=P347BJlmb7AG7YC29JyyOtNy52QqZ87Sn7gFP8Dx86s,5353
|
51
51
|
ultralytics/cfg/models/11/yolo11-cls-resnet18.yaml,sha256=1Ycp9qMrwpb8rq7cqht3Q-1gMN0R87U35nm2j_isdro,524
|
52
52
|
ultralytics/cfg/models/11/yolo11-cls.yaml,sha256=17l5GdN-Vst4LvafsK2-q6Li9VX9UlUcT5ClCtikweE,1412
|
53
53
|
ultralytics/cfg/models/11/yolo11-obb.yaml,sha256=3M_c06B-y8da4tunHVxQQ-iFUNLKUfofqCZTpnH5FEU,2034
|
@@ -109,19 +109,19 @@ ultralytics/data/__init__.py,sha256=nAXaL1puCc7z_NjzQNlJnhbVhT9Fla2u7Dsqo7q1dAc,
|
|
109
109
|
ultralytics/data/annotator.py,sha256=uAgd7K-yudxiwdNqHz0ubfFg5JsfNlae4cgxdvCMyuY,3030
|
110
110
|
ultralytics/data/augment.py,sha256=Ps1s-ug_oXdyAz4Jyur6OmxzRlyzwP3VP-3hDalSxj8,132959
|
111
111
|
ultralytics/data/base.py,sha256=mRcuehK1thNuuzQGL6D1AaZkod71oHRdYTod_zdQZQg,19688
|
112
|
-
ultralytics/data/build.py,sha256=
|
113
|
-
ultralytics/data/converter.py,sha256=
|
114
|
-
ultralytics/data/dataset.py,sha256=
|
112
|
+
ultralytics/data/build.py,sha256=v2dHe52m_cqKnRSWZhEcpGynKMCB-dgw4SyVnfTNAXA,11464
|
113
|
+
ultralytics/data/converter.py,sha256=h-0liMb7OkxoR7P0h_mOUpEu5KUsocH3fVEAz3_-p-I,32096
|
114
|
+
ultralytics/data/dataset.py,sha256=0GyB6PPsUXMxpf88RyvhGcsREDCenS7Xvc8CrMWivco,36759
|
115
115
|
ultralytics/data/loaders.py,sha256=u9sExTGPy1iiqVd_p29zVoEkQ3C36g2rE0FEbYPET0A,31767
|
116
116
|
ultralytics/data/split.py,sha256=F6O73bAbESj70FQZzqkydXQeXgPXGHGiC06b5MkLHjQ,5109
|
117
117
|
ultralytics/data/split_dota.py,sha256=rr-lLpTUVaFZMggV_fUYZdFVIJk_zbbSOpgB_Qp50_M,12893
|
118
|
-
ultralytics/data/utils.py,sha256=
|
118
|
+
ultralytics/data/utils.py,sha256=Zt01BBVwpdHBLwkJC_qTUpaokhF_74hmBYQC3d9Ic8w,36675
|
119
119
|
ultralytics/data/scripts/download_weights.sh,sha256=0y8XtZxOru7dVThXDFUXLHBuICgOIqZNUwpyL4Rh6lg,595
|
120
120
|
ultralytics/data/scripts/get_coco.sh,sha256=UuJpJeo3qQpTHVINeOpmP0NYmg8PhEFE3A8J3jKrnPw,1768
|
121
121
|
ultralytics/data/scripts/get_coco128.sh,sha256=qmRQl_hOKrsdHrTrnyQuFIH01oDz3lfaz138OgGfLt8,650
|
122
122
|
ultralytics/data/scripts/get_imagenet.sh,sha256=hr42H16bM47iT27rgS7MpEo-GeOZAYUQXgr0B2cwn48,1705
|
123
123
|
ultralytics/engine/__init__.py,sha256=lm6MckFYCPTbqIoX7w0s_daxdjNeBeKW6DXppv1-QUM,70
|
124
|
-
ultralytics/engine/exporter.py,sha256
|
124
|
+
ultralytics/engine/exporter.py,sha256=-AUku73LwK0l_Gt71evXQIJg3WpC2jr73S-87vw5T6g,75277
|
125
125
|
ultralytics/engine/model.py,sha256=877u2n0ISz2COOYtEMUqQe0E-HHB4Atb2DuH1XCE98k,53530
|
126
126
|
ultralytics/engine/predictor.py,sha256=iXnUB-tvBHtVpKbB-5EKs1wSREBIerdUxWx39MaFYuk,22485
|
127
127
|
ultralytics/engine/results.py,sha256=QcHcbPVlLBiy_APwABr-T5K65HR8Bl1rRzxawjjP76E,71873
|
@@ -218,7 +218,7 @@ ultralytics/solutions/object_counter.py,sha256=zD-EYIxu_y7qCFEkv6aqV60oMCZ4q6b_k
|
|
218
218
|
ultralytics/solutions/object_cropper.py,sha256=lRKtWINAe9GDxau1Xejbjydsqg2hrpGZXPtZwTgvyKQ,3603
|
219
219
|
ultralytics/solutions/parking_management.py,sha256=IfPUn15aelxz6YZNo9WYkVEl5IOVSw8VD0OrpKtExPE,13613
|
220
220
|
ultralytics/solutions/queue_management.py,sha256=gTkILx4dVcsKRZXSCXtelkEjCRiDS5iznb3FnddC61c,4390
|
221
|
-
ultralytics/solutions/region_counter.py,sha256=
|
221
|
+
ultralytics/solutions/region_counter.py,sha256=gaBN5piMyIJSk0DBycKxm7HXHOfixA0meITcMxbwHOg,6031
|
222
222
|
ultralytics/solutions/security_alarm.py,sha256=czEaMcy04q-iBkKqT_14d8H20CFB6zcKH_31nBGQnyw,6345
|
223
223
|
ultralytics/solutions/similarity_search.py,sha256=c18TK0qW5AvanXU28nAX4o_WtB1SDAJStUtyLDuEBHQ,9505
|
224
224
|
ultralytics/solutions/solutions.py,sha256=9dTkAx1W-0oaZGwKyysXTxKCYNBEV4kThRjqsQea2VQ,36059
|
@@ -236,18 +236,18 @@ ultralytics/trackers/utils/__init__.py,sha256=lm6MckFYCPTbqIoX7w0s_daxdjNeBeKW6D
|
|
236
236
|
ultralytics/trackers/utils/gmc.py,sha256=9IvCf5MhBYY9ppVHykN02_oBWHmE98R8EaYFKaykdV0,14032
|
237
237
|
ultralytics/trackers/utils/kalman_filter.py,sha256=PPmM0lwBMdT_hGojvfLoUsBUFMBBMNRAxKbMcQa3wJ0,21619
|
238
238
|
ultralytics/trackers/utils/matching.py,sha256=uSYtywqi1lE_uNN1FwuBFPyISfDQXHMu8K5KH69nrRI,7160
|
239
|
-
ultralytics/utils/__init__.py,sha256=
|
239
|
+
ultralytics/utils/__init__.py,sha256=jI8xbKM4OrRFvYbT7j1qAlRmvKTnVSHyHzY-On3yAjI,56982
|
240
240
|
ultralytics/utils/autobatch.py,sha256=33m8YgggLIhltDqMXZ5OE-FGs2QiHrl2-LfgY1mI4cw,5119
|
241
|
-
ultralytics/utils/autodevice.py,sha256=
|
241
|
+
ultralytics/utils/autodevice.py,sha256=1wwjkO2tmyR5IAYa6t8G9QJgGrm00niPY4bTbTRH0Uk,8861
|
242
242
|
ultralytics/utils/benchmarks.py,sha256=btsi_B0mfLPfhE8GrsBpi79vl7SRam0YYngNFAsY8Ak,31035
|
243
243
|
ultralytics/utils/checks.py,sha256=q64U5wKyejD-2W2fCPqJ0Oiaa4_4vq2pVxV9wp6lMz4,34707
|
244
244
|
ultralytics/utils/dist.py,sha256=A9lDGtGefTjSVvVS38w86GOdbtLzNBDZuDGK0MT4PRI,4170
|
245
|
-
ultralytics/utils/downloads.py,sha256=
|
245
|
+
ultralytics/utils/downloads.py,sha256=5p9X5XN3I4RzZYGv8wP8Iehm3fDR4KXtN7KgGsJ0iAg,22621
|
246
246
|
ultralytics/utils/errors.py,sha256=XT9Ru7ivoBgofK6PlnyigGoa7Fmf5nEhyHtnD-8TRXI,1584
|
247
247
|
ultralytics/utils/export.py,sha256=LK-wlTlyb_zIKtSvOmfmvR70RcUU9Ct9UBDt5wn9_rY,9880
|
248
248
|
ultralytics/utils/files.py,sha256=ZCbLGleiF0f-PqYfaxMFAWop88w7U1hpreHXl8b2ko0,8238
|
249
249
|
ultralytics/utils/instance.py,sha256=dC83rHvQXciAED3rOiScFs3BOX9OI06Ey1mj9sjUKvs,19070
|
250
|
-
ultralytics/utils/logger.py,sha256=
|
250
|
+
ultralytics/utils/logger.py,sha256=KDHLdpBe3su3OkMpLSUgDk3-cXMkRgH3oK0hhxsxxvM,15137
|
251
251
|
ultralytics/utils/loss.py,sha256=fbOWc3Iu0QOJiWbi-mXWA9-1otTYlehtmUsI7os7ydM,39799
|
252
252
|
ultralytics/utils/metrics.py,sha256=Q0cD4J1_7WRElv_En6YUM94l4SjE7XTF9LdZUMvrGys,68853
|
253
253
|
ultralytics/utils/ops.py,sha256=8d60fbpntrexK3gPoLUS6mWAYGrtrQaQCOYyRJsCjuI,34521
|
@@ -255,6 +255,7 @@ ultralytics/utils/patches.py,sha256=PPWiKzwGbCvuawLzDKVR8tWOQAlZbJBi8g_-A6eTCYA,
|
|
255
255
|
ultralytics/utils/plotting.py,sha256=4TG_J8rz9VVPrOXbdjRHPJZVgJrFYVmEYE0BcVDdolc,47745
|
256
256
|
ultralytics/utils/tal.py,sha256=aXawOnhn8ni65tJWIW-PYqWr_TRvltbHBjrTo7o6lDQ,20924
|
257
257
|
ultralytics/utils/torch_utils.py,sha256=D76Pvmw5OKh-vd4aJkOMO0dSLbM5WzGr7Hmds54hPEk,39233
|
258
|
+
ultralytics/utils/tqdm.py,sha256=cJSzlv6NP72kN7_J0PETA3h4bwGh5a_YHA2gdmZqL8U,16535
|
258
259
|
ultralytics/utils/triton.py,sha256=M7qe4RztiADBJQEWQKaIQsp94ERFJ_8_DUHDR6TXEOM,5410
|
259
260
|
ultralytics/utils/tuner.py,sha256=bHr09Fz-0-t0ei55gX5wJh-obyiAQoicP7HUVM2I8qA,6826
|
260
261
|
ultralytics/utils/callbacks/__init__.py,sha256=hzL63Rce6VkZhP4Lcim9LKjadixaQG86nKqPhk7IkS0,242
|
@@ -269,8 +270,8 @@ ultralytics/utils/callbacks/platform.py,sha256=gdbEuedXEs1VjdU0IiedjPFwttZJUiI0d
|
|
269
270
|
ultralytics/utils/callbacks/raytune.py,sha256=S6Bq16oQDQ8BQgnZzA0zJHGN_BBr8iAM_WtGoLiEcwg,1283
|
270
271
|
ultralytics/utils/callbacks/tensorboard.py,sha256=MDPBW7aDes-66OE6YqKXXvqA_EocjzEMHWGM-8z9vUQ,5281
|
271
272
|
ultralytics/utils/callbacks/wb.py,sha256=Tm_-aRr2CN32MJkY9tylpMBJkb007-MSRNSQ7rDJ5QU,7521
|
272
|
-
dgenerate_ultralytics_headless-8.3.
|
273
|
-
dgenerate_ultralytics_headless-8.3.
|
274
|
-
dgenerate_ultralytics_headless-8.3.
|
275
|
-
dgenerate_ultralytics_headless-8.3.
|
276
|
-
dgenerate_ultralytics_headless-8.3.
|
273
|
+
dgenerate_ultralytics_headless-8.3.186.dist-info/METADATA,sha256=tc5kxyFm0pFjeLSyNe-BkQrg_2NM5SYxzhi2SLsMbXs,38723
|
274
|
+
dgenerate_ultralytics_headless-8.3.186.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
275
|
+
dgenerate_ultralytics_headless-8.3.186.dist-info/entry_points.txt,sha256=YM_wiKyTe9yRrsEfqvYolNO5ngwfoL4-NwgKzc8_7sI,93
|
276
|
+
dgenerate_ultralytics_headless-8.3.186.dist-info/top_level.txt,sha256=XP49TwiMw4QGsvTLSYiJhz1xF_k7ev5mQ8jJXaXi45Q,12
|
277
|
+
dgenerate_ultralytics_headless-8.3.186.dist-info/RECORD,,
|
ultralytics/__init__.py
CHANGED
@@ -30,14 +30,14 @@ download: |
|
|
30
30
|
import json
|
31
31
|
from pathlib import Path
|
32
32
|
|
33
|
-
from
|
33
|
+
from ultralytics.utils import TQDM
|
34
34
|
from ultralytics.utils.downloads import download
|
35
35
|
|
36
36
|
def argoverse2yolo(set):
|
37
37
|
"""Convert Argoverse dataset annotations to YOLO format for object detection tasks."""
|
38
38
|
labels = {}
|
39
39
|
a = json.load(open(set, "rb"))
|
40
|
-
for annot in
|
40
|
+
for annot in TQDM(a["annotations"], desc=f"Converting {set} to YOLOv5 format..."):
|
41
41
|
img_id = annot["image_id"]
|
42
42
|
img_name = a["images"][img_id]["name"]
|
43
43
|
img_label_name = f"{img_name[:-3]}txt"
|
@@ -387,8 +387,8 @@ download: |
|
|
387
387
|
from pathlib import Path
|
388
388
|
|
389
389
|
import numpy as np
|
390
|
-
from tqdm import tqdm
|
391
390
|
|
391
|
+
from ultralytics.utils import TQDM
|
392
392
|
from ultralytics.utils.checks import check_requirements
|
393
393
|
from ultralytics.utils.downloads import download
|
394
394
|
from ultralytics.utils.ops import xyxy2xywhn
|
@@ -419,7 +419,7 @@ download: |
|
|
419
419
|
download([f"{url}images/v2/patch{i}.tar.gz" for i in range(16, patches)], dir=images, curl=True, threads=8)
|
420
420
|
|
421
421
|
# Move
|
422
|
-
for f in
|
422
|
+
for f in TQDM(images.rglob("*.jpg"), desc=f"Moving {split} images"):
|
423
423
|
f.rename(images / f.name) # move to /images/{split}
|
424
424
|
|
425
425
|
# Labels
|
@@ -428,7 +428,7 @@ download: |
|
|
428
428
|
for cid, cat in enumerate(names):
|
429
429
|
catIds = coco.getCatIds(catNms=[cat])
|
430
430
|
imgIds = coco.getImgIds(catIds=catIds)
|
431
|
-
for im in
|
431
|
+
for im in TQDM(coco.loadImgs(imgIds), desc=f"Class {cid + 1}/{len(names)} {cat}"):
|
432
432
|
width, height = im["width"], im["height"]
|
433
433
|
path = Path(im["file_name"]) # image filename
|
434
434
|
try:
|
@@ -25,8 +25,8 @@ download: |
|
|
25
25
|
|
26
26
|
import numpy as np
|
27
27
|
import pandas as pd
|
28
|
-
from tqdm import tqdm
|
29
28
|
|
29
|
+
from ultralytics.utils import TQDM
|
30
30
|
from ultralytics.utils.downloads import download
|
31
31
|
from ultralytics.utils.ops import xyxy2xywh
|
32
32
|
|
@@ -49,7 +49,7 @@ download: |
|
|
49
49
|
images, unique_images = x[:, 0], np.unique(x[:, 0])
|
50
50
|
with open((dir / d).with_suffix(".txt").__str__().replace("annotations_", ""), "w", encoding="utf-8") as f:
|
51
51
|
f.writelines(f"./images/{s}\n" for s in unique_images)
|
52
|
-
for im in
|
52
|
+
for im in TQDM(unique_images, desc=f"Converting {dir / d}"):
|
53
53
|
cls = 0 # single-class dataset
|
54
54
|
with open((dir / "labels" / im).with_suffix(".txt"), "a", encoding="utf-8") as f:
|
55
55
|
for r in x[images == im]:
|
@@ -48,10 +48,8 @@ download: |
|
|
48
48
|
import xml.etree.ElementTree as ET
|
49
49
|
from pathlib import Path
|
50
50
|
|
51
|
-
from tqdm import tqdm
|
52
|
-
|
53
51
|
from ultralytics.utils.downloads import download
|
54
|
-
|
52
|
+
from ultralytics.utils import TQDM
|
55
53
|
|
56
54
|
def convert_label(path, lb_path, year, image_id):
|
57
55
|
"""Converts XML annotations from VOC format to YOLO format by extracting bounding boxes and class IDs."""
|
@@ -99,7 +97,7 @@ download: |
|
|
99
97
|
|
100
98
|
with open(path / f"VOC{year}/ImageSets/Main/{image_set}.txt") as f:
|
101
99
|
image_ids = f.read().strip().split()
|
102
|
-
for id in
|
100
|
+
for id in TQDM(image_ids, desc=f"{image_set}{year}"):
|
103
101
|
f = path / f"VOC{year}/JPEGImages/{id}.jpg" # old img path
|
104
102
|
lb_path = (lbs_path / f.name).with_suffix(".txt") # new label path
|
105
103
|
f.rename(imgs_path / f.name) # move image
|
@@ -34,12 +34,12 @@ download: |
|
|
34
34
|
import shutil
|
35
35
|
|
36
36
|
from ultralytics.utils.downloads import download
|
37
|
+
from ultralytics.utils import TQDM
|
37
38
|
|
38
39
|
|
39
40
|
def visdrone2yolo(dir, split, source_name=None):
|
40
41
|
"""Convert VisDrone annotations to YOLO format with images/{split} and labels/{split} structure."""
|
41
42
|
from PIL import Image
|
42
|
-
from tqdm import tqdm
|
43
43
|
|
44
44
|
source_dir = dir / (source_name or f"VisDrone2019-DET-{split}")
|
45
45
|
images_dir = dir / "images" / split
|
@@ -52,7 +52,7 @@ download: |
|
|
52
52
|
for img in source_images_dir.glob("*.jpg"):
|
53
53
|
img.rename(images_dir / img.name)
|
54
54
|
|
55
|
-
for f in
|
55
|
+
for f in TQDM((source_dir / "annotations").glob("*.txt"), desc=f"Converting {split}"):
|
56
56
|
img_size = Image.open(images_dir / f.with_suffix(".jpg").name).size
|
57
57
|
dw, dh = 1.0 / img_size[0], 1.0 / img_size[1]
|
58
58
|
lines = []
|
@@ -85,8 +85,8 @@ download: |
|
|
85
85
|
|
86
86
|
import numpy as np
|
87
87
|
from PIL import Image
|
88
|
-
from tqdm import tqdm
|
89
88
|
|
89
|
+
from ultralytics.utils import TQDM
|
90
90
|
from ultralytics.data.utils import autosplit
|
91
91
|
from ultralytics.utils.ops import xyxy2xywhn
|
92
92
|
|
@@ -110,7 +110,7 @@ download: |
|
|
110
110
|
47, 48, 49, -1, 50, 51, -1, 52, -1, -1, -1, 53, 54, -1, 55, -1, -1, 56, -1, 57, -1, 58, 59]
|
111
111
|
|
112
112
|
shapes = {}
|
113
|
-
for feature in
|
113
|
+
for feature in TQDM(data["features"], desc=f"Converting {fname}"):
|
114
114
|
p = feature["properties"]
|
115
115
|
if p["bounds_imcoords"]:
|
116
116
|
id = p["image_id"]
|
ultralytics/data/build.py
CHANGED
@@ -22,7 +22,7 @@ from ultralytics.data.loaders import (
|
|
22
22
|
SourceTypes,
|
23
23
|
autocast_list,
|
24
24
|
)
|
25
|
-
from ultralytics.data.utils import IMG_FORMATS,
|
25
|
+
from ultralytics.data.utils import IMG_FORMATS, VID_FORMATS
|
26
26
|
from ultralytics.utils import RANK, colorstr
|
27
27
|
from ultralytics.utils.checks import check_file
|
28
28
|
|
@@ -206,7 +206,7 @@ def build_dataloader(dataset, batch: int, workers: int, shuffle: bool = True, ra
|
|
206
206
|
shuffle=shuffle and sampler is None,
|
207
207
|
num_workers=nw,
|
208
208
|
sampler=sampler,
|
209
|
-
pin_memory=
|
209
|
+
pin_memory=nd > 0,
|
210
210
|
collate_fn=getattr(dataset, "collate_fn", None),
|
211
211
|
worker_init_fn=seed_worker,
|
212
212
|
generator=generator,
|
ultralytics/data/converter.py
CHANGED
@@ -613,7 +613,7 @@ def yolo_bbox2segment(
|
|
613
613
|
from ultralytics.utils.ops import xywh2xyxy
|
614
614
|
|
615
615
|
# NOTE: add placeholder to pass class index check
|
616
|
-
dataset = YOLODataset(im_dir, data=dict(names=list(range(1000))))
|
616
|
+
dataset = YOLODataset(im_dir, data=dict(names=list(range(1000)), channels=3))
|
617
617
|
if len(dataset.labels[0]["segments"]) > 0: # if it's segment data
|
618
618
|
LOGGER.info("Segmentation labels detected, no need to generate new ones!")
|
619
619
|
return
|
ultralytics/data/dataset.py
CHANGED
@@ -85,7 +85,7 @@ class YOLODataset(BaseDataset):
|
|
85
85
|
self.use_obb = task == "obb"
|
86
86
|
self.data = data
|
87
87
|
assert not (self.use_segments and self.use_keypoints), "Can not use both segments and keypoints."
|
88
|
-
super().__init__(*args, channels=self.data
|
88
|
+
super().__init__(*args, channels=self.data.get("channels", 3), **kwargs)
|
89
89
|
|
90
90
|
def cache_labels(self, path: Path = Path("./labels.cache")) -> Dict:
|
91
91
|
"""
|
ultralytics/data/utils.py
CHANGED
@@ -19,7 +19,6 @@ from ultralytics.nn.autobackend import check_class_names
|
|
19
19
|
from ultralytics.utils import (
|
20
20
|
DATASETS_DIR,
|
21
21
|
LOGGER,
|
22
|
-
MACOS,
|
23
22
|
NUM_THREADS,
|
24
23
|
ROOT,
|
25
24
|
SETTINGS_FILE,
|
@@ -37,7 +36,6 @@ from ultralytics.utils.ops import segments2boxes
|
|
37
36
|
HELP_URL = "See https://docs.ultralytics.com/datasets for dataset formatting guidance."
|
38
37
|
IMG_FORMATS = {"bmp", "dng", "jpeg", "jpg", "mpo", "png", "tif", "tiff", "webp", "pfm", "heic"} # image suffixes
|
39
38
|
VID_FORMATS = {"asf", "avi", "gif", "m4v", "mkv", "mov", "mp4", "mpeg", "mpg", "ts", "wmv", "webm"} # video suffixes
|
40
|
-
PIN_MEMORY = str(os.getenv("PIN_MEMORY", not MACOS)).lower() == "true" # global pin_memory for dataloaders
|
41
39
|
FORMATS_HELP_MSG = f"Supported formats are:\nimages: {IMG_FORMATS}\nvideos: {VID_FORMATS}"
|
42
40
|
|
43
41
|
|
ultralytics/engine/exporter.py
CHANGED
@@ -1479,7 +1479,10 @@ class IOSDetectModel(torch.nn.Module):
|
|
1479
1479
|
if w == h:
|
1480
1480
|
self.normalize = 1.0 / w # scalar
|
1481
1481
|
else:
|
1482
|
-
self.normalize = torch.tensor(
|
1482
|
+
self.normalize = torch.tensor(
|
1483
|
+
[1.0 / w, 1.0 / h, 1.0 / w, 1.0 / h], # broadcast (slower, smaller)
|
1484
|
+
device=next(model.parameters()).device,
|
1485
|
+
)
|
1483
1486
|
|
1484
1487
|
def forward(self, x):
|
1485
1488
|
"""Normalize predictions of object detection model with input size-dependent factors."""
|
@@ -115,8 +115,9 @@ class RegionCounter(BaseSolution):
|
|
115
115
|
|
116
116
|
# Display region counts
|
117
117
|
for region in self.counting_regions:
|
118
|
-
|
119
|
-
pts =
|
118
|
+
poly = region["polygon"]
|
119
|
+
pts = list(map(tuple, np.array(poly.exterior.coords, dtype=np.int32)))
|
120
|
+
(x1, y1), (x2, y2) = [(int(poly.centroid.x), int(poly.centroid.y))] * 2
|
120
121
|
annotator.draw_region(pts, region["region_color"], self.line_width * 2)
|
121
122
|
annotator.adaptive_label(
|
122
123
|
[x1, y1, x2, y2],
|
ultralytics/utils/__init__.py
CHANGED
@@ -12,7 +12,6 @@ import subprocess
|
|
12
12
|
import sys
|
13
13
|
import threading
|
14
14
|
import time
|
15
|
-
import warnings
|
16
15
|
from pathlib import Path
|
17
16
|
from threading import Lock
|
18
17
|
from types import SimpleNamespace
|
@@ -22,10 +21,10 @@ from urllib.parse import unquote
|
|
22
21
|
import cv2
|
23
22
|
import numpy as np
|
24
23
|
import torch
|
25
|
-
import tqdm
|
26
24
|
|
27
25
|
from ultralytics import __version__
|
28
26
|
from ultralytics.utils.patches import imread, imshow, imwrite, torch_save # for patches
|
27
|
+
from ultralytics.utils.tqdm import TQDM # noqa
|
29
28
|
|
30
29
|
# PyTorch Multi-GPU DDP Constants
|
31
30
|
RANK = int(os.getenv("RANK", -1))
|
@@ -41,7 +40,6 @@ DEFAULT_CFG_PATH = ROOT / "cfg/default.yaml"
|
|
41
40
|
NUM_THREADS = min(8, max(1, os.cpu_count() - 1)) # number of YOLO multiprocessing threads
|
42
41
|
AUTOINSTALL = str(os.getenv("YOLO_AUTOINSTALL", True)).lower() == "true" # global auto-install mode
|
43
42
|
VERBOSE = str(os.getenv("YOLO_VERBOSE", True)).lower() == "true" # global verbose mode
|
44
|
-
TQDM_BAR_FORMAT = "{l_bar}{bar:10}{r_bar}" if VERBOSE else None # tqdm bar format
|
45
43
|
LOGGING_NAME = "ultralytics"
|
46
44
|
MACOS, LINUX, WINDOWS = (platform.system() == x for x in ["Darwin", "Linux", "Windows"]) # environment booleans
|
47
45
|
MACOS_VERSION = platform.mac_ver()[0] if MACOS else None
|
@@ -130,65 +128,6 @@ os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" # suppress verbose TF compiler warning
|
|
130
128
|
os.environ["TORCH_CPP_LOG_LEVEL"] = "ERROR" # suppress "NNPACK.cpp could not initialize NNPACK" warnings
|
131
129
|
os.environ["KINETO_LOG_LEVEL"] = "5" # suppress verbose PyTorch profiler output when computing FLOPs
|
132
130
|
|
133
|
-
if TQDM_RICH := str(os.getenv("YOLO_TQDM_RICH", False)).lower() == "true":
|
134
|
-
from tqdm import rich
|
135
|
-
|
136
|
-
|
137
|
-
class TQDM(rich.tqdm if TQDM_RICH else tqdm.tqdm):
|
138
|
-
"""
|
139
|
-
A custom TQDM progress bar class that extends the original tqdm functionality.
|
140
|
-
|
141
|
-
This class modifies the behavior of the original tqdm progress bar based on global settings and provides
|
142
|
-
additional customization options for Ultralytics projects. The progress bar is automatically disabled when
|
143
|
-
VERBOSE is False or when explicitly disabled.
|
144
|
-
|
145
|
-
Attributes:
|
146
|
-
disable (bool): Whether to disable the progress bar. Determined by the global VERBOSE setting and
|
147
|
-
any passed 'disable' argument.
|
148
|
-
bar_format (str): The format string for the progress bar. Uses the global TQDM_BAR_FORMAT if not
|
149
|
-
explicitly set.
|
150
|
-
|
151
|
-
Methods:
|
152
|
-
__init__: Initialize the TQDM object with custom settings.
|
153
|
-
__iter__: Return self as iterator to satisfy Iterable interface.
|
154
|
-
|
155
|
-
Examples:
|
156
|
-
>>> from ultralytics.utils import TQDM
|
157
|
-
>>> for i in TQDM(range(100)):
|
158
|
-
... # Your processing code here
|
159
|
-
... pass
|
160
|
-
"""
|
161
|
-
|
162
|
-
def __init__(self, *args, **kwargs):
|
163
|
-
"""
|
164
|
-
Initialize a custom TQDM progress bar with Ultralytics-specific settings.
|
165
|
-
|
166
|
-
Args:
|
167
|
-
*args (Any): Variable length argument list to be passed to the original tqdm constructor.
|
168
|
-
**kwargs (Any): Arbitrary keyword arguments to be passed to the original tqdm constructor.
|
169
|
-
|
170
|
-
Notes:
|
171
|
-
- The progress bar is disabled if VERBOSE is False or if 'disable' is explicitly set to True in kwargs.
|
172
|
-
- The default bar format is set to TQDM_BAR_FORMAT unless overridden in kwargs.
|
173
|
-
- In GitHub Actions, progress bars only update at completion to keep CI logs clean.
|
174
|
-
|
175
|
-
Examples:
|
176
|
-
>>> from ultralytics.utils import TQDM
|
177
|
-
>>> for i in TQDM(range(100)):
|
178
|
-
... # Your code here
|
179
|
-
... pass
|
180
|
-
"""
|
181
|
-
warnings.filterwarnings("ignore", category=tqdm.TqdmExperimentalWarning) # suppress tqdm.rich warning
|
182
|
-
if is_github_action_running():
|
183
|
-
kwargs["mininterval"] = 60 # only update every 60 seconds
|
184
|
-
kwargs["disable"] = not VERBOSE or kwargs.get("disable", False) or LOGGER.getEffectiveLevel() > 20
|
185
|
-
kwargs.setdefault("bar_format", TQDM_BAR_FORMAT) # override default value if passed
|
186
|
-
super().__init__(*args, **kwargs)
|
187
|
-
|
188
|
-
def __iter__(self):
|
189
|
-
"""Return self as iterator to satisfy Iterable interface."""
|
190
|
-
return super().__iter__()
|
191
|
-
|
192
131
|
|
193
132
|
class DataExportMixin:
|
194
133
|
"""
|
ultralytics/utils/autodevice.py
CHANGED
ultralytics/utils/downloads.py
CHANGED
@@ -118,11 +118,11 @@ def zip_directory(directory, compress: bool = True, exclude=(".DS_Store", "__MAC
|
|
118
118
|
raise FileNotFoundError(f"Directory '{directory}' does not exist.")
|
119
119
|
|
120
120
|
# Zip with progress bar
|
121
|
-
|
121
|
+
files = [f for f in directory.rglob("*") if f.is_file() and all(x not in f.name for x in exclude)] # files to zip
|
122
122
|
zip_file = directory.with_suffix(".zip")
|
123
123
|
compression = ZIP_DEFLATED if compress else ZIP_STORED
|
124
124
|
with ZipFile(zip_file, "w", compression) as f:
|
125
|
-
for file in TQDM(
|
125
|
+
for file in TQDM(files, desc=f"Zipping {directory} to {zip_file}...", unit="files", disable=not progress):
|
126
126
|
f.write(file, file.relative_to(directory))
|
127
127
|
|
128
128
|
return zip_file # return path to zip file
|
@@ -187,7 +187,7 @@ def unzip_file(
|
|
187
187
|
LOGGER.warning(f"Skipping {file} unzip as destination directory {path} is not empty.")
|
188
188
|
return path
|
189
189
|
|
190
|
-
for f in TQDM(files, desc=f"Unzipping {file} to {Path(path).resolve()}...", unit="
|
190
|
+
for f in TQDM(files, desc=f"Unzipping {file} to {Path(path).resolve()}...", unit="files", disable=not progress):
|
191
191
|
# Ensure the file is within the extract_path to avoid path traversal security vulnerability
|
192
192
|
if ".." in Path(f).parts:
|
193
193
|
LOGGER.warning(f"Potentially insecure file path: {f}, skipping extraction.")
|
@@ -295,7 +295,8 @@ def safe_download(
|
|
295
295
|
progress: bool = True,
|
296
296
|
):
|
297
297
|
"""
|
298
|
-
Download files from a URL with options for retrying, unzipping, and deleting the downloaded file.
|
298
|
+
Download files from a URL with options for retrying, unzipping, and deleting the downloaded file. Enhanced with
|
299
|
+
robust partial download detection using Content-Length validation.
|
299
300
|
|
300
301
|
Args:
|
301
302
|
url (str): The URL of the file to be downloaded.
|
@@ -342,24 +343,33 @@ def safe_download(
|
|
342
343
|
s = "sS" * (not progress) # silent
|
343
344
|
r = subprocess.run(["curl", "-#", f"-{s}L", url, "-o", f, "--retry", "3", "-C", "-"]).returncode
|
344
345
|
assert r == 0, f"Curl return value {r}"
|
346
|
+
expected_size = None # Can't get size with curl
|
345
347
|
else: # urllib download
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
348
|
+
with request.urlopen(url) as response:
|
349
|
+
expected_size = int(response.getheader("Content-Length", 0))
|
350
|
+
with TQDM(
|
351
|
+
total=expected_size,
|
352
|
+
desc=desc,
|
353
|
+
disable=not progress,
|
354
|
+
unit="B",
|
355
|
+
unit_scale=True,
|
356
|
+
unit_divisor=1024,
|
357
|
+
) as pbar:
|
358
|
+
with open(f, "wb") as f_opened:
|
359
|
+
for data in response:
|
360
|
+
f_opened.write(data)
|
361
|
+
pbar.update(len(data))
|
359
362
|
|
360
363
|
if f.exists():
|
361
|
-
|
362
|
-
|
364
|
+
file_size = f.stat().st_size
|
365
|
+
if file_size > min_bytes:
|
366
|
+
# Check if download is complete (only if we have expected_size)
|
367
|
+
if expected_size and file_size != expected_size:
|
368
|
+
LOGGER.warning(
|
369
|
+
f"Partial download: {file_size}/{expected_size} bytes ({file_size / expected_size * 100:.1f}%)"
|
370
|
+
)
|
371
|
+
else:
|
372
|
+
break # success
|
363
373
|
f.unlink() # remove partial downloads
|
364
374
|
except Exception as e:
|
365
375
|
if i == 0 and not is_online():
|
ultralytics/utils/logger.py
CHANGED
@@ -77,7 +77,7 @@ class ConsoleLogger:
|
|
77
77
|
# State tracking
|
78
78
|
self.last_line = ""
|
79
79
|
self.last_time = 0.0
|
80
|
-
self.last_progress_line = "" # Track
|
80
|
+
self.last_progress_line = "" # Track last progress line for deduplication
|
81
81
|
self.last_was_progress = False # Track if last line was a progress bar
|
82
82
|
|
83
83
|
def start_capture(self):
|
@@ -127,15 +127,14 @@ class ConsoleLogger:
|
|
127
127
|
for line in lines:
|
128
128
|
line = line.rstrip()
|
129
129
|
|
130
|
-
#
|
131
|
-
if
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
progress_core
|
138
|
-
if progress_core == self.last_progress_line:
|
130
|
+
# Skip lines with only thin progress bars (partial progress)
|
131
|
+
if "─" in line: # Has thin lines but no thick lines
|
132
|
+
continue
|
133
|
+
|
134
|
+
# Deduplicate completed progress bars only if they match the previous progress line
|
135
|
+
if " ━━" in line:
|
136
|
+
progress_core = line.split(" ━━")[0].strip()
|
137
|
+
if progress_core == self.last_progress_line and self.last_was_progress:
|
139
138
|
continue
|
140
139
|
self.last_progress_line = progress_core
|
141
140
|
self.last_was_progress = True
|
@@ -271,7 +270,7 @@ class SystemLogger:
|
|
271
270
|
"""Initialize NVIDIA GPU monitoring with pynvml."""
|
272
271
|
try:
|
273
272
|
assert not MACOS
|
274
|
-
check_requirements("
|
273
|
+
check_requirements("nvidia-ml-py>=12.0.0")
|
275
274
|
self.pynvml = __import__("pynvml")
|
276
275
|
self.pynvml.nvmlInit()
|
277
276
|
return True
|
@@ -285,16 +284,16 @@ class SystemLogger:
|
|
285
284
|
Collects comprehensive system metrics including CPU usage, RAM usage, disk I/O statistics,
|
286
285
|
network I/O statistics, and GPU metrics (if available). Example output:
|
287
286
|
|
288
|
-
```
|
289
|
-
{
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
0: {
|
296
|
-
1: {
|
297
|
-
}
|
287
|
+
```python
|
288
|
+
metrics = {
|
289
|
+
"cpu": 45.2,
|
290
|
+
"ram": 78.9,
|
291
|
+
"disk": {"read_mb": 156.7, "write_mb": 89.3, "used_gb": 256.8},
|
292
|
+
"network": {"recv_mb": 157.2, "sent_mb": 89.1},
|
293
|
+
"gpus": {
|
294
|
+
0: {"usage": 95.6, "memory": 85.4, "temp": 72, "power": 285},
|
295
|
+
1: {"usage": 94.1, "memory": 82.7, "temp": 70, "power": 278},
|
296
|
+
},
|
298
297
|
}
|
299
298
|
```
|
300
299
|
|
@@ -314,7 +313,7 @@ class SystemLogger:
|
|
314
313
|
- power (int): GPU power consumption in watts
|
315
314
|
|
316
315
|
Returns:
|
317
|
-
(dict): System metrics containing 'cpu', 'ram', 'disk', 'network', 'gpus' with respective usage data.
|
316
|
+
metrics (dict): System metrics containing 'cpu', 'ram', 'disk', 'network', 'gpus' with respective usage data.
|
318
317
|
"""
|
319
318
|
net = psutil.net_io_counters()
|
320
319
|
disk = psutil.disk_io_counters()
|
@@ -0,0 +1,462 @@
|
|
1
|
+
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import os
|
6
|
+
import sys
|
7
|
+
import time
|
8
|
+
from functools import lru_cache
|
9
|
+
from typing import IO, Any
|
10
|
+
|
11
|
+
|
12
|
+
@lru_cache(maxsize=1)
|
13
|
+
def is_noninteractive_console():
|
14
|
+
"""Check for known non-interactive console environments."""
|
15
|
+
return "GITHUB_ACTIONS" in os.environ or "RUNPOD_POD_ID" in os.environ
|
16
|
+
|
17
|
+
|
18
|
+
class TQDM:
|
19
|
+
"""
|
20
|
+
Lightweight zero-dependency progress bar for Ultralytics.
|
21
|
+
|
22
|
+
Provides clean, rich-style progress bars suitable for various environments including Weights & Biases,
|
23
|
+
console outputs, and other logging systems. Features zero external dependencies, clean single-line output,
|
24
|
+
rich-style progress bars with Unicode block characters, context manager support, iterator protocol support,
|
25
|
+
and dynamic description updates.
|
26
|
+
|
27
|
+
Attributes:
|
28
|
+
iterable (object): Iterable to wrap with progress bar.
|
29
|
+
desc (str): Prefix description for the progress bar.
|
30
|
+
total (int): Expected number of iterations.
|
31
|
+
disable (bool): Whether to disable the progress bar.
|
32
|
+
unit (str): String for units of iteration.
|
33
|
+
unit_scale (bool): Auto-scale units flag.
|
34
|
+
unit_divisor (int): Divisor for unit scaling.
|
35
|
+
leave (bool): Whether to leave the progress bar after completion.
|
36
|
+
mininterval (float): Minimum time interval between updates.
|
37
|
+
initial (int): Initial counter value.
|
38
|
+
n (int): Current iteration count.
|
39
|
+
closed (bool): Whether the progress bar is closed.
|
40
|
+
bar_format (str): Custom bar format string.
|
41
|
+
file (object): Output file stream.
|
42
|
+
|
43
|
+
Methods:
|
44
|
+
update: Update progress by n steps.
|
45
|
+
set_description: Set or update the description.
|
46
|
+
set_postfix: Set postfix for the progress bar.
|
47
|
+
close: Close the progress bar and clean up.
|
48
|
+
refresh: Refresh the progress bar display.
|
49
|
+
clear: Clear the progress bar from display.
|
50
|
+
write: Write a message without breaking the progress bar.
|
51
|
+
|
52
|
+
Examples:
|
53
|
+
Basic usage with iterator:
|
54
|
+
>>> for i in TQDM(range(100)):
|
55
|
+
... time.sleep(0.01)
|
56
|
+
|
57
|
+
With custom description:
|
58
|
+
>>> pbar = TQDM(range(100), desc="Processing")
|
59
|
+
>>> for i in pbar:
|
60
|
+
... pbar.set_description(f"Processing item {i}")
|
61
|
+
|
62
|
+
Context manager usage:
|
63
|
+
>>> with TQDM(total=100, unit="B", unit_scale=True) as pbar:
|
64
|
+
... for i in range(100):
|
65
|
+
... pbar.update(1)
|
66
|
+
|
67
|
+
Manual updates:
|
68
|
+
>>> pbar = TQDM(total=100, desc="Training")
|
69
|
+
>>> for epoch in range(100):
|
70
|
+
... # Do work
|
71
|
+
... pbar.update(1)
|
72
|
+
>>> pbar.close()
|
73
|
+
"""
|
74
|
+
|
75
|
+
# Constants
|
76
|
+
MIN_RATE_CALC_INTERVAL = 0.01 # Minimum time interval for rate calculation
|
77
|
+
RATE_SMOOTHING_FACTOR = 0.3 # Factor for exponential smoothing of rates
|
78
|
+
MAX_SMOOTHED_RATE = 1000000 # Maximum rate to apply smoothing to
|
79
|
+
NONINTERACTIVE_MIN_INTERVAL = 60.0 # Minimum interval for non-interactive environments
|
80
|
+
|
81
|
+
def __init__(
|
82
|
+
self,
|
83
|
+
iterable: Any = None,
|
84
|
+
desc: str | None = None,
|
85
|
+
total: int | None = None,
|
86
|
+
leave: bool = True,
|
87
|
+
file: IO[str] | None = None,
|
88
|
+
mininterval: float = 0.1,
|
89
|
+
disable: bool | None = None,
|
90
|
+
unit: str = "it",
|
91
|
+
unit_scale: bool = False,
|
92
|
+
unit_divisor: int = 1000,
|
93
|
+
bar_format: str | None = None,
|
94
|
+
initial: int = 0,
|
95
|
+
**kwargs, # Accept unused args for compatibility
|
96
|
+
) -> None:
|
97
|
+
"""
|
98
|
+
Initialize the TQDM progress bar with specified configuration options.
|
99
|
+
|
100
|
+
Args:
|
101
|
+
iterable (object, optional): Iterable to wrap with progress bar.
|
102
|
+
desc (str, optional): Prefix description for the progress bar.
|
103
|
+
total (int, optional): Expected number of iterations.
|
104
|
+
leave (bool, optional): Whether to leave the progress bar after completion.
|
105
|
+
file (object, optional): Output file stream for progress display.
|
106
|
+
mininterval (float, optional): Minimum time interval between updates (default 0.1s, 60s in GitHub Actions).
|
107
|
+
disable (bool, optional): Whether to disable the progress bar. Auto-detected if None.
|
108
|
+
unit (str, optional): String for units of iteration (default "it" for items).
|
109
|
+
unit_scale (bool, optional): Auto-scale units for bytes/data units.
|
110
|
+
unit_divisor (int, optional): Divisor for unit scaling (default 1000).
|
111
|
+
bar_format (str, optional): Custom bar format string.
|
112
|
+
initial (int, optional): Initial counter value.
|
113
|
+
**kwargs (Any): Additional keyword arguments for compatibility (ignored).
|
114
|
+
|
115
|
+
Examples:
|
116
|
+
>>> pbar = TQDM(range(100), desc="Processing")
|
117
|
+
>>> with TQDM(total=1000, unit="B", unit_scale=True) as pbar:
|
118
|
+
... pbar.update(1024) # Updates by 1KB
|
119
|
+
"""
|
120
|
+
# Disable if not verbose
|
121
|
+
if disable is None:
|
122
|
+
try:
|
123
|
+
from ultralytics.utils import LOGGER, VERBOSE
|
124
|
+
|
125
|
+
disable = not VERBOSE or LOGGER.getEffectiveLevel() > 20
|
126
|
+
except ImportError:
|
127
|
+
disable = False
|
128
|
+
|
129
|
+
self.iterable = iterable
|
130
|
+
self.desc = desc or ""
|
131
|
+
self.total = total if total is not None else (len(iterable) if hasattr(iterable, "__len__") else None)
|
132
|
+
self.disable = disable
|
133
|
+
self.unit = unit
|
134
|
+
self.unit_scale = unit_scale
|
135
|
+
self.unit_divisor = unit_divisor
|
136
|
+
self.leave = leave
|
137
|
+
self.noninteractive = is_noninteractive_console()
|
138
|
+
self.mininterval = max(mininterval, self.NONINTERACTIVE_MIN_INTERVAL) if self.noninteractive else mininterval
|
139
|
+
self.initial = initial
|
140
|
+
|
141
|
+
# Set bar format based on whether we have a total
|
142
|
+
if self.total is not None:
|
143
|
+
self.bar_format = bar_format or "{desc}: {percentage:3.0f}% {bar} {n_fmt}/{total_fmt} {rate_fmt} {elapsed}"
|
144
|
+
else:
|
145
|
+
self.bar_format = bar_format or "{desc}: {bar} {n_fmt} {rate_fmt} {elapsed}"
|
146
|
+
|
147
|
+
self.file = file or sys.stdout
|
148
|
+
|
149
|
+
# Internal state
|
150
|
+
self.n = self.initial
|
151
|
+
self.last_print_n = self.initial
|
152
|
+
self.last_print_t = time.time()
|
153
|
+
self.start_t = time.time()
|
154
|
+
self.last_rate = 0
|
155
|
+
self.closed = False
|
156
|
+
|
157
|
+
# Display initial bar if we have total and not disabled
|
158
|
+
if not self.disable and self.total is not None and not self.noninteractive:
|
159
|
+
self._display()
|
160
|
+
|
161
|
+
def _format_rate(self, rate):
|
162
|
+
"""Format rate with proper units and reasonable precision."""
|
163
|
+
if rate <= 0:
|
164
|
+
return ""
|
165
|
+
|
166
|
+
# For bytes with scaling, use binary units
|
167
|
+
if self.unit in ("B", "bytes") and self.unit_scale:
|
168
|
+
for threshold, unit in [(1024**3, "GB/s"), (1024**2, "MB/s"), (1024, "KB/s")]:
|
169
|
+
if rate >= threshold:
|
170
|
+
return f"{rate / threshold:.1f}{unit}"
|
171
|
+
return f"{rate:.1f}B/s"
|
172
|
+
|
173
|
+
# For other scalable units, use decimal units
|
174
|
+
if self.unit_scale and self.unit in ("it", "items", ""):
|
175
|
+
for threshold, prefix in [(1000000, "M"), (1000, "K")]:
|
176
|
+
if rate >= threshold:
|
177
|
+
return f"{rate / threshold:.1f}{prefix}{self.unit}/s"
|
178
|
+
|
179
|
+
# Default formatting
|
180
|
+
precision = ".1f" if rate >= 1 else ".2f"
|
181
|
+
return f"{rate:{precision}}{self.unit}/s"
|
182
|
+
|
183
|
+
def _format_num(self, num):
|
184
|
+
"""Format number with optional unit scaling."""
|
185
|
+
if not self.unit_scale or self.unit not in ("B", "bytes"):
|
186
|
+
return str(num)
|
187
|
+
|
188
|
+
for unit in ["", "K", "M", "G", "T"]:
|
189
|
+
if abs(num) < self.unit_divisor:
|
190
|
+
return f"{num:3.1f}{unit}B" if unit else f"{num:.0f}B"
|
191
|
+
num /= self.unit_divisor
|
192
|
+
return f"{num:.1f}PB"
|
193
|
+
|
194
|
+
def _format_time(self, seconds):
|
195
|
+
"""Format time duration."""
|
196
|
+
if seconds < 60:
|
197
|
+
return f"{seconds:.1f}s"
|
198
|
+
elif seconds < 3600:
|
199
|
+
return f"{int(seconds // 60)}:{seconds % 60:02.0f}"
|
200
|
+
else:
|
201
|
+
h, m = int(seconds // 3600), int((seconds % 3600) // 60)
|
202
|
+
return f"{h}:{m:02d}:{seconds % 60:02.0f}"
|
203
|
+
|
204
|
+
def _generate_bar(self, width=12):
|
205
|
+
"""Generate progress bar."""
|
206
|
+
if self.total is None:
|
207
|
+
return "━" * width if self.closed else "─" * width
|
208
|
+
|
209
|
+
frac = min(1.0, self.n / self.total)
|
210
|
+
filled = int(frac * width)
|
211
|
+
bar = "━" * filled + "─" * (width - filled)
|
212
|
+
if filled < width and frac * width - filled > 0.5:
|
213
|
+
bar = bar[:filled] + "╸" + bar[filled + 1 :]
|
214
|
+
return bar
|
215
|
+
|
216
|
+
def _should_update(self, dt, dn):
|
217
|
+
"""Check if display should update."""
|
218
|
+
if self.noninteractive:
|
219
|
+
return False
|
220
|
+
|
221
|
+
if self.total is not None and self.n >= self.total:
|
222
|
+
return True
|
223
|
+
|
224
|
+
return dt >= self.mininterval
|
225
|
+
|
226
|
+
def _display(self, final=False):
|
227
|
+
"""Display progress bar."""
|
228
|
+
if self.disable or (self.closed and not final):
|
229
|
+
return
|
230
|
+
|
231
|
+
current_time = time.time()
|
232
|
+
dt = current_time - self.last_print_t
|
233
|
+
dn = self.n - self.last_print_n
|
234
|
+
|
235
|
+
if not final and not self._should_update(dt, dn):
|
236
|
+
return
|
237
|
+
|
238
|
+
# Calculate rate (avoid crazy numbers)
|
239
|
+
if dt > self.MIN_RATE_CALC_INTERVAL: # Only calculate rate if enough time has passed
|
240
|
+
rate = dn / dt
|
241
|
+
# Smooth rate for reasonable values, use raw rate for very high values
|
242
|
+
if rate < self.MAX_SMOOTHED_RATE:
|
243
|
+
self.last_rate = self.RATE_SMOOTHING_FACTOR * rate + (1 - self.RATE_SMOOTHING_FACTOR) * self.last_rate
|
244
|
+
rate = self.last_rate
|
245
|
+
else:
|
246
|
+
rate = self.last_rate
|
247
|
+
|
248
|
+
# At completion, use the overall rate for more accurate display
|
249
|
+
if self.n >= (self.total or float("inf")) and self.total and self.total > 0:
|
250
|
+
overall_elapsed = current_time - self.start_t
|
251
|
+
if overall_elapsed > 0:
|
252
|
+
rate = self.n / overall_elapsed
|
253
|
+
|
254
|
+
# Update counters
|
255
|
+
self.last_print_n = self.n
|
256
|
+
self.last_print_t = current_time
|
257
|
+
elapsed = current_time - self.start_t
|
258
|
+
|
259
|
+
# Build progress components
|
260
|
+
if self.total is not None:
|
261
|
+
percentage = (self.n / self.total) * 100
|
262
|
+
# For bytes with unit scaling, avoid repeating units: show "5.4/5.4MB" not "5.4MB/5.4MB"
|
263
|
+
n_fmt = self._format_num(self.n)
|
264
|
+
total_fmt = self._format_num(self.total)
|
265
|
+
if self.unit_scale and self.unit in ("B", "bytes"):
|
266
|
+
n_fmt = n_fmt.rstrip("KMGTPB") # Remove unit suffix from current
|
267
|
+
else:
|
268
|
+
percentage = 0
|
269
|
+
n_fmt = self._format_num(self.n)
|
270
|
+
total_fmt = "?"
|
271
|
+
|
272
|
+
elapsed_str = self._format_time(elapsed)
|
273
|
+
rate_fmt = self._format_rate(rate) or (self._format_rate(self.n / elapsed) if elapsed > 0 else "")
|
274
|
+
|
275
|
+
# Format progress string
|
276
|
+
progress_str = self.bar_format.format(
|
277
|
+
desc=self.desc,
|
278
|
+
percentage=percentage,
|
279
|
+
bar=self._generate_bar(),
|
280
|
+
n_fmt=n_fmt,
|
281
|
+
total_fmt=total_fmt,
|
282
|
+
rate_fmt=rate_fmt,
|
283
|
+
elapsed=elapsed_str,
|
284
|
+
unit=self.unit,
|
285
|
+
)
|
286
|
+
|
287
|
+
# Write to output
|
288
|
+
try:
|
289
|
+
if self.noninteractive:
|
290
|
+
# In non-interactive environments, avoid carriage return which creates empty lines
|
291
|
+
self.file.write(progress_str)
|
292
|
+
else:
|
293
|
+
# In interactive terminals, use carriage return and clear line for updating display
|
294
|
+
self.file.write(f"\r\033[K{progress_str}")
|
295
|
+
self.file.flush()
|
296
|
+
except Exception:
|
297
|
+
pass
|
298
|
+
|
299
|
+
def update(self, n=1):
|
300
|
+
"""Update progress by n steps."""
|
301
|
+
if not self.disable and not self.closed:
|
302
|
+
self.n += n
|
303
|
+
self._display()
|
304
|
+
|
305
|
+
def set_description(self, desc):
|
306
|
+
"""Set description."""
|
307
|
+
self.desc = desc or ""
|
308
|
+
if not self.disable:
|
309
|
+
self._display()
|
310
|
+
|
311
|
+
def set_postfix(self, **kwargs):
|
312
|
+
"""Set postfix (appends to description)."""
|
313
|
+
if kwargs:
|
314
|
+
postfix = ", ".join(f"{k}={v}" for k, v in kwargs.items())
|
315
|
+
base_desc = self.desc.split(" | ")[0] if " | " in self.desc else self.desc
|
316
|
+
self.set_description(f"{base_desc} | {postfix}")
|
317
|
+
|
318
|
+
def close(self):
|
319
|
+
"""Close progress bar."""
|
320
|
+
if self.closed:
|
321
|
+
return
|
322
|
+
|
323
|
+
self.closed = True # Set before final display
|
324
|
+
|
325
|
+
if not self.disable:
|
326
|
+
# Final display
|
327
|
+
if self.total and self.n >= self.total:
|
328
|
+
self.n = self.total
|
329
|
+
self._display(final=True)
|
330
|
+
|
331
|
+
# Cleanup
|
332
|
+
if self.leave:
|
333
|
+
self.file.write("\n")
|
334
|
+
else:
|
335
|
+
self.file.write("\r\033[K")
|
336
|
+
|
337
|
+
try:
|
338
|
+
self.file.flush()
|
339
|
+
except Exception:
|
340
|
+
pass
|
341
|
+
|
342
|
+
def __enter__(self):
|
343
|
+
"""Enter context manager."""
|
344
|
+
return self
|
345
|
+
|
346
|
+
def __exit__(self, *args):
|
347
|
+
"""Exit context manager and close progress bar."""
|
348
|
+
self.close()
|
349
|
+
|
350
|
+
def __iter__(self):
|
351
|
+
"""Iterate over the wrapped iterable with progress updates."""
|
352
|
+
if self.iterable is None:
|
353
|
+
raise TypeError("'NoneType' object is not iterable")
|
354
|
+
|
355
|
+
try:
|
356
|
+
for item in self.iterable:
|
357
|
+
yield item
|
358
|
+
self.update(1)
|
359
|
+
finally:
|
360
|
+
self.close()
|
361
|
+
|
362
|
+
def __del__(self):
|
363
|
+
"""Destructor to ensure cleanup."""
|
364
|
+
try:
|
365
|
+
self.close()
|
366
|
+
except Exception:
|
367
|
+
pass
|
368
|
+
|
369
|
+
def refresh(self):
|
370
|
+
"""Refresh display."""
|
371
|
+
if not self.disable:
|
372
|
+
self._display()
|
373
|
+
|
374
|
+
def clear(self):
|
375
|
+
"""Clear progress bar."""
|
376
|
+
if not self.disable:
|
377
|
+
try:
|
378
|
+
self.file.write("\r\033[K")
|
379
|
+
self.file.flush()
|
380
|
+
except Exception:
|
381
|
+
pass
|
382
|
+
|
383
|
+
@staticmethod
|
384
|
+
def write(s, file=None, end="\n"):
|
385
|
+
"""Static method to write without breaking progress bar."""
|
386
|
+
file = file or sys.stdout
|
387
|
+
try:
|
388
|
+
file.write(s + end)
|
389
|
+
file.flush()
|
390
|
+
except Exception:
|
391
|
+
pass
|
392
|
+
|
393
|
+
|
394
|
+
if __name__ == "__main__":
|
395
|
+
import time
|
396
|
+
|
397
|
+
# Example 1: Basic usage with known total
|
398
|
+
print("1. Basic progress bar with known total:")
|
399
|
+
for i in TQDM(range(20), desc="Known total"):
|
400
|
+
time.sleep(0.05)
|
401
|
+
print()
|
402
|
+
|
403
|
+
# Example 2: Manual updates with known total
|
404
|
+
print("2. Manual updates with known total:")
|
405
|
+
pbar = TQDM(total=30, desc="Manual updates", unit="files")
|
406
|
+
for i in range(30):
|
407
|
+
time.sleep(0.03)
|
408
|
+
pbar.update(1)
|
409
|
+
if i % 10 == 9:
|
410
|
+
pbar.set_description(f"Processing batch {i // 10 + 1}")
|
411
|
+
pbar.close()
|
412
|
+
print()
|
413
|
+
|
414
|
+
# Example 3: Unknown total - this was the problematic case
|
415
|
+
print("3. Progress bar with unknown total:")
|
416
|
+
pbar = TQDM(desc="Unknown total", unit="items")
|
417
|
+
for i in range(25):
|
418
|
+
time.sleep(0.08)
|
419
|
+
pbar.update(1)
|
420
|
+
if i % 5 == 4:
|
421
|
+
pbar.set_postfix(processed=i + 1, status="OK")
|
422
|
+
pbar.close()
|
423
|
+
print()
|
424
|
+
|
425
|
+
# Example 4: Context manager with unknown total
|
426
|
+
print("4. Context manager with unknown total:")
|
427
|
+
with TQDM(desc="Processing stream", unit="B", unit_scale=True, unit_divisor=1024) as pbar:
|
428
|
+
for i in range(30):
|
429
|
+
time.sleep(0.1)
|
430
|
+
pbar.update(1024 * 1024 * i) # Simulate processing MB of data
|
431
|
+
print()
|
432
|
+
|
433
|
+
# Example 5: Generator with unknown length
|
434
|
+
print("5. Iterator with unknown length:")
|
435
|
+
|
436
|
+
def data_stream():
|
437
|
+
"""Simulate a data stream of unknown length."""
|
438
|
+
import random
|
439
|
+
|
440
|
+
for i in range(random.randint(10, 20)):
|
441
|
+
yield f"data_chunk_{i}"
|
442
|
+
|
443
|
+
for chunk in TQDM(data_stream(), desc="Stream processing", unit="chunks"):
|
444
|
+
time.sleep(0.1)
|
445
|
+
print()
|
446
|
+
|
447
|
+
# Example 6: File-like processing simulation
|
448
|
+
print("6. File processing simulation (unknown size):")
|
449
|
+
|
450
|
+
def process_files():
|
451
|
+
"""Simulate processing files of unknown count."""
|
452
|
+
files = [f"file_{i}.txt" for i in range(18)]
|
453
|
+
return files
|
454
|
+
|
455
|
+
pbar = TQDM(desc="Scanning files", unit="files")
|
456
|
+
files = process_files()
|
457
|
+
for i, filename in enumerate(files):
|
458
|
+
time.sleep(0.06)
|
459
|
+
pbar.update(1)
|
460
|
+
pbar.set_description(f"Processing {filename}")
|
461
|
+
pbar.close()
|
462
|
+
print()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|