dgenerate-ultralytics-headless 8.3.253__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 (299) hide show
  1. dgenerate_ultralytics_headless-8.3.253.dist-info/METADATA +405 -0
  2. dgenerate_ultralytics_headless-8.3.253.dist-info/RECORD +299 -0
  3. dgenerate_ultralytics_headless-8.3.253.dist-info/WHEEL +5 -0
  4. dgenerate_ultralytics_headless-8.3.253.dist-info/entry_points.txt +3 -0
  5. dgenerate_ultralytics_headless-8.3.253.dist-info/licenses/LICENSE +661 -0
  6. dgenerate_ultralytics_headless-8.3.253.dist-info/top_level.txt +1 -0
  7. tests/__init__.py +23 -0
  8. tests/conftest.py +59 -0
  9. tests/test_cli.py +131 -0
  10. tests/test_cuda.py +216 -0
  11. tests/test_engine.py +157 -0
  12. tests/test_exports.py +309 -0
  13. tests/test_integrations.py +151 -0
  14. tests/test_python.py +777 -0
  15. tests/test_solutions.py +371 -0
  16. ultralytics/__init__.py +48 -0
  17. ultralytics/assets/bus.jpg +0 -0
  18. ultralytics/assets/zidane.jpg +0 -0
  19. ultralytics/cfg/__init__.py +1028 -0
  20. ultralytics/cfg/datasets/Argoverse.yaml +78 -0
  21. ultralytics/cfg/datasets/DOTAv1.5.yaml +37 -0
  22. ultralytics/cfg/datasets/DOTAv1.yaml +36 -0
  23. ultralytics/cfg/datasets/GlobalWheat2020.yaml +68 -0
  24. ultralytics/cfg/datasets/HomeObjects-3K.yaml +32 -0
  25. ultralytics/cfg/datasets/ImageNet.yaml +2025 -0
  26. ultralytics/cfg/datasets/Objects365.yaml +447 -0
  27. ultralytics/cfg/datasets/SKU-110K.yaml +58 -0
  28. ultralytics/cfg/datasets/TT100K.yaml +346 -0
  29. ultralytics/cfg/datasets/VOC.yaml +102 -0
  30. ultralytics/cfg/datasets/VisDrone.yaml +87 -0
  31. ultralytics/cfg/datasets/african-wildlife.yaml +25 -0
  32. ultralytics/cfg/datasets/brain-tumor.yaml +22 -0
  33. ultralytics/cfg/datasets/carparts-seg.yaml +44 -0
  34. ultralytics/cfg/datasets/coco-pose.yaml +64 -0
  35. ultralytics/cfg/datasets/coco.yaml +118 -0
  36. ultralytics/cfg/datasets/coco128-seg.yaml +101 -0
  37. ultralytics/cfg/datasets/coco128.yaml +101 -0
  38. ultralytics/cfg/datasets/coco8-grayscale.yaml +103 -0
  39. ultralytics/cfg/datasets/coco8-multispectral.yaml +104 -0
  40. ultralytics/cfg/datasets/coco8-pose.yaml +47 -0
  41. ultralytics/cfg/datasets/coco8-seg.yaml +101 -0
  42. ultralytics/cfg/datasets/coco8.yaml +101 -0
  43. ultralytics/cfg/datasets/construction-ppe.yaml +32 -0
  44. ultralytics/cfg/datasets/crack-seg.yaml +22 -0
  45. ultralytics/cfg/datasets/dog-pose.yaml +52 -0
  46. ultralytics/cfg/datasets/dota8-multispectral.yaml +38 -0
  47. ultralytics/cfg/datasets/dota8.yaml +35 -0
  48. ultralytics/cfg/datasets/hand-keypoints.yaml +50 -0
  49. ultralytics/cfg/datasets/kitti.yaml +27 -0
  50. ultralytics/cfg/datasets/lvis.yaml +1240 -0
  51. ultralytics/cfg/datasets/medical-pills.yaml +21 -0
  52. ultralytics/cfg/datasets/open-images-v7.yaml +663 -0
  53. ultralytics/cfg/datasets/package-seg.yaml +22 -0
  54. ultralytics/cfg/datasets/signature.yaml +21 -0
  55. ultralytics/cfg/datasets/tiger-pose.yaml +41 -0
  56. ultralytics/cfg/datasets/xView.yaml +155 -0
  57. ultralytics/cfg/default.yaml +130 -0
  58. ultralytics/cfg/models/11/yolo11-cls-resnet18.yaml +17 -0
  59. ultralytics/cfg/models/11/yolo11-cls.yaml +33 -0
  60. ultralytics/cfg/models/11/yolo11-obb.yaml +50 -0
  61. ultralytics/cfg/models/11/yolo11-pose.yaml +51 -0
  62. ultralytics/cfg/models/11/yolo11-seg.yaml +50 -0
  63. ultralytics/cfg/models/11/yolo11.yaml +50 -0
  64. ultralytics/cfg/models/11/yoloe-11-seg.yaml +48 -0
  65. ultralytics/cfg/models/11/yoloe-11.yaml +48 -0
  66. ultralytics/cfg/models/12/yolo12-cls.yaml +32 -0
  67. ultralytics/cfg/models/12/yolo12-obb.yaml +48 -0
  68. ultralytics/cfg/models/12/yolo12-pose.yaml +49 -0
  69. ultralytics/cfg/models/12/yolo12-seg.yaml +48 -0
  70. ultralytics/cfg/models/12/yolo12.yaml +48 -0
  71. ultralytics/cfg/models/rt-detr/rtdetr-l.yaml +53 -0
  72. ultralytics/cfg/models/rt-detr/rtdetr-resnet101.yaml +45 -0
  73. ultralytics/cfg/models/rt-detr/rtdetr-resnet50.yaml +45 -0
  74. ultralytics/cfg/models/rt-detr/rtdetr-x.yaml +57 -0
  75. ultralytics/cfg/models/v10/yolov10b.yaml +45 -0
  76. ultralytics/cfg/models/v10/yolov10l.yaml +45 -0
  77. ultralytics/cfg/models/v10/yolov10m.yaml +45 -0
  78. ultralytics/cfg/models/v10/yolov10n.yaml +45 -0
  79. ultralytics/cfg/models/v10/yolov10s.yaml +45 -0
  80. ultralytics/cfg/models/v10/yolov10x.yaml +45 -0
  81. ultralytics/cfg/models/v3/yolov3-spp.yaml +49 -0
  82. ultralytics/cfg/models/v3/yolov3-tiny.yaml +40 -0
  83. ultralytics/cfg/models/v3/yolov3.yaml +49 -0
  84. ultralytics/cfg/models/v5/yolov5-p6.yaml +62 -0
  85. ultralytics/cfg/models/v5/yolov5.yaml +51 -0
  86. ultralytics/cfg/models/v6/yolov6.yaml +56 -0
  87. ultralytics/cfg/models/v8/yoloe-v8-seg.yaml +48 -0
  88. ultralytics/cfg/models/v8/yoloe-v8.yaml +48 -0
  89. ultralytics/cfg/models/v8/yolov8-cls-resnet101.yaml +28 -0
  90. ultralytics/cfg/models/v8/yolov8-cls-resnet50.yaml +28 -0
  91. ultralytics/cfg/models/v8/yolov8-cls.yaml +32 -0
  92. ultralytics/cfg/models/v8/yolov8-ghost-p2.yaml +58 -0
  93. ultralytics/cfg/models/v8/yolov8-ghost-p6.yaml +60 -0
  94. ultralytics/cfg/models/v8/yolov8-ghost.yaml +50 -0
  95. ultralytics/cfg/models/v8/yolov8-obb.yaml +49 -0
  96. ultralytics/cfg/models/v8/yolov8-p2.yaml +57 -0
  97. ultralytics/cfg/models/v8/yolov8-p6.yaml +59 -0
  98. ultralytics/cfg/models/v8/yolov8-pose-p6.yaml +60 -0
  99. ultralytics/cfg/models/v8/yolov8-pose.yaml +50 -0
  100. ultralytics/cfg/models/v8/yolov8-rtdetr.yaml +49 -0
  101. ultralytics/cfg/models/v8/yolov8-seg-p6.yaml +59 -0
  102. ultralytics/cfg/models/v8/yolov8-seg.yaml +49 -0
  103. ultralytics/cfg/models/v8/yolov8-world.yaml +51 -0
  104. ultralytics/cfg/models/v8/yolov8-worldv2.yaml +49 -0
  105. ultralytics/cfg/models/v8/yolov8.yaml +49 -0
  106. ultralytics/cfg/models/v9/yolov9c-seg.yaml +41 -0
  107. ultralytics/cfg/models/v9/yolov9c.yaml +41 -0
  108. ultralytics/cfg/models/v9/yolov9e-seg.yaml +64 -0
  109. ultralytics/cfg/models/v9/yolov9e.yaml +64 -0
  110. ultralytics/cfg/models/v9/yolov9m.yaml +41 -0
  111. ultralytics/cfg/models/v9/yolov9s.yaml +41 -0
  112. ultralytics/cfg/models/v9/yolov9t.yaml +41 -0
  113. ultralytics/cfg/trackers/botsort.yaml +21 -0
  114. ultralytics/cfg/trackers/bytetrack.yaml +12 -0
  115. ultralytics/data/__init__.py +26 -0
  116. ultralytics/data/annotator.py +66 -0
  117. ultralytics/data/augment.py +2801 -0
  118. ultralytics/data/base.py +435 -0
  119. ultralytics/data/build.py +437 -0
  120. ultralytics/data/converter.py +855 -0
  121. ultralytics/data/dataset.py +834 -0
  122. ultralytics/data/loaders.py +704 -0
  123. ultralytics/data/scripts/download_weights.sh +18 -0
  124. ultralytics/data/scripts/get_coco.sh +61 -0
  125. ultralytics/data/scripts/get_coco128.sh +18 -0
  126. ultralytics/data/scripts/get_imagenet.sh +52 -0
  127. ultralytics/data/split.py +138 -0
  128. ultralytics/data/split_dota.py +344 -0
  129. ultralytics/data/utils.py +798 -0
  130. ultralytics/engine/__init__.py +1 -0
  131. ultralytics/engine/exporter.py +1580 -0
  132. ultralytics/engine/model.py +1125 -0
  133. ultralytics/engine/predictor.py +508 -0
  134. ultralytics/engine/results.py +1522 -0
  135. ultralytics/engine/trainer.py +977 -0
  136. ultralytics/engine/tuner.py +449 -0
  137. ultralytics/engine/validator.py +387 -0
  138. ultralytics/hub/__init__.py +166 -0
  139. ultralytics/hub/auth.py +151 -0
  140. ultralytics/hub/google/__init__.py +174 -0
  141. ultralytics/hub/session.py +422 -0
  142. ultralytics/hub/utils.py +162 -0
  143. ultralytics/models/__init__.py +9 -0
  144. ultralytics/models/fastsam/__init__.py +7 -0
  145. ultralytics/models/fastsam/model.py +79 -0
  146. ultralytics/models/fastsam/predict.py +169 -0
  147. ultralytics/models/fastsam/utils.py +23 -0
  148. ultralytics/models/fastsam/val.py +38 -0
  149. ultralytics/models/nas/__init__.py +7 -0
  150. ultralytics/models/nas/model.py +98 -0
  151. ultralytics/models/nas/predict.py +56 -0
  152. ultralytics/models/nas/val.py +38 -0
  153. ultralytics/models/rtdetr/__init__.py +7 -0
  154. ultralytics/models/rtdetr/model.py +63 -0
  155. ultralytics/models/rtdetr/predict.py +88 -0
  156. ultralytics/models/rtdetr/train.py +89 -0
  157. ultralytics/models/rtdetr/val.py +216 -0
  158. ultralytics/models/sam/__init__.py +25 -0
  159. ultralytics/models/sam/amg.py +275 -0
  160. ultralytics/models/sam/build.py +365 -0
  161. ultralytics/models/sam/build_sam3.py +377 -0
  162. ultralytics/models/sam/model.py +169 -0
  163. ultralytics/models/sam/modules/__init__.py +1 -0
  164. ultralytics/models/sam/modules/blocks.py +1067 -0
  165. ultralytics/models/sam/modules/decoders.py +495 -0
  166. ultralytics/models/sam/modules/encoders.py +794 -0
  167. ultralytics/models/sam/modules/memory_attention.py +298 -0
  168. ultralytics/models/sam/modules/sam.py +1160 -0
  169. ultralytics/models/sam/modules/tiny_encoder.py +979 -0
  170. ultralytics/models/sam/modules/transformer.py +344 -0
  171. ultralytics/models/sam/modules/utils.py +512 -0
  172. ultralytics/models/sam/predict.py +3940 -0
  173. ultralytics/models/sam/sam3/__init__.py +3 -0
  174. ultralytics/models/sam/sam3/decoder.py +546 -0
  175. ultralytics/models/sam/sam3/encoder.py +529 -0
  176. ultralytics/models/sam/sam3/geometry_encoders.py +415 -0
  177. ultralytics/models/sam/sam3/maskformer_segmentation.py +286 -0
  178. ultralytics/models/sam/sam3/model_misc.py +199 -0
  179. ultralytics/models/sam/sam3/necks.py +129 -0
  180. ultralytics/models/sam/sam3/sam3_image.py +339 -0
  181. ultralytics/models/sam/sam3/text_encoder_ve.py +307 -0
  182. ultralytics/models/sam/sam3/vitdet.py +547 -0
  183. ultralytics/models/sam/sam3/vl_combiner.py +160 -0
  184. ultralytics/models/utils/__init__.py +1 -0
  185. ultralytics/models/utils/loss.py +466 -0
  186. ultralytics/models/utils/ops.py +315 -0
  187. ultralytics/models/yolo/__init__.py +7 -0
  188. ultralytics/models/yolo/classify/__init__.py +7 -0
  189. ultralytics/models/yolo/classify/predict.py +90 -0
  190. ultralytics/models/yolo/classify/train.py +202 -0
  191. ultralytics/models/yolo/classify/val.py +216 -0
  192. ultralytics/models/yolo/detect/__init__.py +7 -0
  193. ultralytics/models/yolo/detect/predict.py +122 -0
  194. ultralytics/models/yolo/detect/train.py +227 -0
  195. ultralytics/models/yolo/detect/val.py +507 -0
  196. ultralytics/models/yolo/model.py +430 -0
  197. ultralytics/models/yolo/obb/__init__.py +7 -0
  198. ultralytics/models/yolo/obb/predict.py +56 -0
  199. ultralytics/models/yolo/obb/train.py +79 -0
  200. ultralytics/models/yolo/obb/val.py +302 -0
  201. ultralytics/models/yolo/pose/__init__.py +7 -0
  202. ultralytics/models/yolo/pose/predict.py +65 -0
  203. ultralytics/models/yolo/pose/train.py +110 -0
  204. ultralytics/models/yolo/pose/val.py +248 -0
  205. ultralytics/models/yolo/segment/__init__.py +7 -0
  206. ultralytics/models/yolo/segment/predict.py +109 -0
  207. ultralytics/models/yolo/segment/train.py +69 -0
  208. ultralytics/models/yolo/segment/val.py +307 -0
  209. ultralytics/models/yolo/world/__init__.py +5 -0
  210. ultralytics/models/yolo/world/train.py +173 -0
  211. ultralytics/models/yolo/world/train_world.py +178 -0
  212. ultralytics/models/yolo/yoloe/__init__.py +22 -0
  213. ultralytics/models/yolo/yoloe/predict.py +162 -0
  214. ultralytics/models/yolo/yoloe/train.py +287 -0
  215. ultralytics/models/yolo/yoloe/train_seg.py +122 -0
  216. ultralytics/models/yolo/yoloe/val.py +206 -0
  217. ultralytics/nn/__init__.py +27 -0
  218. ultralytics/nn/autobackend.py +964 -0
  219. ultralytics/nn/modules/__init__.py +182 -0
  220. ultralytics/nn/modules/activation.py +54 -0
  221. ultralytics/nn/modules/block.py +1947 -0
  222. ultralytics/nn/modules/conv.py +669 -0
  223. ultralytics/nn/modules/head.py +1183 -0
  224. ultralytics/nn/modules/transformer.py +793 -0
  225. ultralytics/nn/modules/utils.py +159 -0
  226. ultralytics/nn/tasks.py +1768 -0
  227. ultralytics/nn/text_model.py +356 -0
  228. ultralytics/py.typed +1 -0
  229. ultralytics/solutions/__init__.py +41 -0
  230. ultralytics/solutions/ai_gym.py +108 -0
  231. ultralytics/solutions/analytics.py +264 -0
  232. ultralytics/solutions/config.py +107 -0
  233. ultralytics/solutions/distance_calculation.py +123 -0
  234. ultralytics/solutions/heatmap.py +125 -0
  235. ultralytics/solutions/instance_segmentation.py +86 -0
  236. ultralytics/solutions/object_blurrer.py +89 -0
  237. ultralytics/solutions/object_counter.py +190 -0
  238. ultralytics/solutions/object_cropper.py +87 -0
  239. ultralytics/solutions/parking_management.py +280 -0
  240. ultralytics/solutions/queue_management.py +93 -0
  241. ultralytics/solutions/region_counter.py +133 -0
  242. ultralytics/solutions/security_alarm.py +151 -0
  243. ultralytics/solutions/similarity_search.py +219 -0
  244. ultralytics/solutions/solutions.py +828 -0
  245. ultralytics/solutions/speed_estimation.py +114 -0
  246. ultralytics/solutions/streamlit_inference.py +260 -0
  247. ultralytics/solutions/templates/similarity-search.html +156 -0
  248. ultralytics/solutions/trackzone.py +88 -0
  249. ultralytics/solutions/vision_eye.py +67 -0
  250. ultralytics/trackers/__init__.py +7 -0
  251. ultralytics/trackers/basetrack.py +115 -0
  252. ultralytics/trackers/bot_sort.py +257 -0
  253. ultralytics/trackers/byte_tracker.py +469 -0
  254. ultralytics/trackers/track.py +116 -0
  255. ultralytics/trackers/utils/__init__.py +1 -0
  256. ultralytics/trackers/utils/gmc.py +339 -0
  257. ultralytics/trackers/utils/kalman_filter.py +482 -0
  258. ultralytics/trackers/utils/matching.py +154 -0
  259. ultralytics/utils/__init__.py +1450 -0
  260. ultralytics/utils/autobatch.py +118 -0
  261. ultralytics/utils/autodevice.py +205 -0
  262. ultralytics/utils/benchmarks.py +728 -0
  263. ultralytics/utils/callbacks/__init__.py +5 -0
  264. ultralytics/utils/callbacks/base.py +233 -0
  265. ultralytics/utils/callbacks/clearml.py +146 -0
  266. ultralytics/utils/callbacks/comet.py +625 -0
  267. ultralytics/utils/callbacks/dvc.py +197 -0
  268. ultralytics/utils/callbacks/hub.py +110 -0
  269. ultralytics/utils/callbacks/mlflow.py +134 -0
  270. ultralytics/utils/callbacks/neptune.py +126 -0
  271. ultralytics/utils/callbacks/platform.py +453 -0
  272. ultralytics/utils/callbacks/raytune.py +42 -0
  273. ultralytics/utils/callbacks/tensorboard.py +123 -0
  274. ultralytics/utils/callbacks/wb.py +188 -0
  275. ultralytics/utils/checks.py +1020 -0
  276. ultralytics/utils/cpu.py +85 -0
  277. ultralytics/utils/dist.py +123 -0
  278. ultralytics/utils/downloads.py +529 -0
  279. ultralytics/utils/errors.py +35 -0
  280. ultralytics/utils/events.py +113 -0
  281. ultralytics/utils/export/__init__.py +7 -0
  282. ultralytics/utils/export/engine.py +237 -0
  283. ultralytics/utils/export/imx.py +325 -0
  284. ultralytics/utils/export/tensorflow.py +231 -0
  285. ultralytics/utils/files.py +219 -0
  286. ultralytics/utils/git.py +137 -0
  287. ultralytics/utils/instance.py +484 -0
  288. ultralytics/utils/logger.py +506 -0
  289. ultralytics/utils/loss.py +849 -0
  290. ultralytics/utils/metrics.py +1563 -0
  291. ultralytics/utils/nms.py +337 -0
  292. ultralytics/utils/ops.py +664 -0
  293. ultralytics/utils/patches.py +201 -0
  294. ultralytics/utils/plotting.py +1047 -0
  295. ultralytics/utils/tal.py +404 -0
  296. ultralytics/utils/torch_utils.py +984 -0
  297. ultralytics/utils/tqdm.py +443 -0
  298. ultralytics/utils/triton.py +112 -0
  299. ultralytics/utils/tuner.py +168 -0
@@ -0,0 +1,506 @@
1
+ # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
+
3
+ import logging
4
+ import shutil
5
+ import sys
6
+ import threading
7
+ import time
8
+ from datetime import datetime
9
+ from pathlib import Path
10
+
11
+ from ultralytics.utils import LOGGER, MACOS, RANK
12
+ from ultralytics.utils.checks import check_requirements
13
+
14
+
15
+ class ConsoleLogger:
16
+ """Console output capture with batched streaming to file, API, or custom callback.
17
+
18
+ Captures stdout/stderr output and streams it with intelligent deduplication and configurable batching.
19
+
20
+ Attributes:
21
+ destination (str | Path | None): Target destination for streaming (URL, Path, or None for callback-only).
22
+ batch_size (int): Number of lines to batch before flushing (default: 1 for immediate).
23
+ flush_interval (float): Seconds between automatic flushes (default: 5.0).
24
+ on_flush (callable | None): Optional callback function called with batched content on flush.
25
+ active (bool): Whether console capture is currently active.
26
+
27
+ Examples:
28
+ File logging (immediate):
29
+ >>> logger = ConsoleLogger("training.log")
30
+ >>> logger.start_capture()
31
+ >>> print("This will be logged")
32
+ >>> logger.stop_capture()
33
+
34
+ API streaming with batching:
35
+ >>> logger = ConsoleLogger("https://api.example.com/logs", batch_size=10)
36
+ >>> logger.start_capture()
37
+
38
+ Custom callback with batching:
39
+ >>> def my_handler(content, line_count, chunk_id):
40
+ ... print(f"Received {line_count} lines")
41
+ >>> logger = ConsoleLogger(on_flush=my_handler, batch_size=5)
42
+ >>> logger.start_capture()
43
+ """
44
+
45
+ def __init__(self, destination=None, batch_size=1, flush_interval=5.0, on_flush=None):
46
+ """Initialize console logger with optional batching.
47
+
48
+ Args:
49
+ destination (str | Path | None): API endpoint URL (http/https), local file path, or None.
50
+ batch_size (int): Lines to accumulate before flush (1 = immediate, higher = batched).
51
+ flush_interval (float): Max seconds between flushes when batching.
52
+ on_flush (callable | None): Callback(content: str, line_count: int, chunk_id: int) for custom handling.
53
+ """
54
+ self.destination = destination
55
+ self.is_api = isinstance(destination, str) and destination.startswith(("http://", "https://"))
56
+ if destination is not None and not self.is_api:
57
+ self.destination = Path(destination)
58
+
59
+ # Batching configuration
60
+ self.batch_size = max(1, batch_size)
61
+ self.flush_interval = flush_interval
62
+ self.on_flush = on_flush
63
+
64
+ # Console capture state
65
+ self.original_stdout = sys.stdout
66
+ self.original_stderr = sys.stderr
67
+ self.active = False
68
+ self._log_handler = None # Track handler for cleanup
69
+
70
+ # Buffer for batching
71
+ self.buffer = []
72
+ self.buffer_lock = threading.Lock()
73
+ self.flush_thread = None
74
+ self.chunk_id = 0
75
+
76
+ # Deduplication state
77
+ self.last_line = ""
78
+ self.last_time = 0.0
79
+ self.last_progress_line = "" # Track progress sequence key for deduplication
80
+ self.last_was_progress = False # Track if last line was a progress bar
81
+
82
+ def start_capture(self):
83
+ """Start capturing console output and redirect stdout/stderr.
84
+
85
+ Notes:
86
+ In DDP training, only activates on rank 0/-1 to prevent duplicate logging.
87
+ """
88
+ if self.active or RANK not in {-1, 0}:
89
+ return
90
+
91
+ self.active = True
92
+ sys.stdout = self._ConsoleCapture(self.original_stdout, self._queue_log)
93
+ sys.stderr = self._ConsoleCapture(self.original_stderr, self._queue_log)
94
+
95
+ # Hook Ultralytics logger
96
+ try:
97
+ self._log_handler = self._LogHandler(self._queue_log)
98
+ logging.getLogger("ultralytics").addHandler(self._log_handler)
99
+ except Exception:
100
+ pass
101
+
102
+ # Start background flush thread for batched mode
103
+ if self.batch_size > 1:
104
+ self.flush_thread = threading.Thread(target=self._flush_worker, daemon=True)
105
+ self.flush_thread.start()
106
+
107
+ def stop_capture(self):
108
+ """Stop capturing console output and flush remaining buffer."""
109
+ if not self.active:
110
+ return
111
+
112
+ self.active = False
113
+ sys.stdout = self.original_stdout
114
+ sys.stderr = self.original_stderr
115
+
116
+ # Remove logging handler to prevent memory leak
117
+ if self._log_handler:
118
+ try:
119
+ logging.getLogger("ultralytics").removeHandler(self._log_handler)
120
+ except Exception:
121
+ pass
122
+ self._log_handler = None
123
+
124
+ # Final flush
125
+ self._flush_buffer()
126
+
127
+ def _queue_log(self, text):
128
+ """Queue console text with deduplication and timestamp processing."""
129
+ if not self.active:
130
+ return
131
+
132
+ current_time = time.time()
133
+
134
+ # Handle carriage returns and process lines
135
+ if "\r" in text:
136
+ text = text.split("\r")[-1]
137
+
138
+ lines = text.split("\n")
139
+ if lines and lines[-1] == "":
140
+ lines.pop()
141
+
142
+ for line in lines:
143
+ line = line.rstrip()
144
+
145
+ # Skip lines with only thin progress bars (partial progress)
146
+ if "─" in line: # Has thin lines but no thick lines
147
+ continue
148
+
149
+ # Only show 100% completion lines for progress bars
150
+ if " ━━" in line:
151
+ is_complete = "100%" in line
152
+
153
+ # Skip ALL non-complete progress lines
154
+ if not is_complete:
155
+ continue
156
+
157
+ # Extract sequence key to deduplicate multiple 100% lines for same sequence
158
+ parts = line.split()
159
+ seq_key = ""
160
+ if parts:
161
+ # Check for epoch pattern (X/Y at start)
162
+ if "/" in parts[0] and parts[0].replace("/", "").isdigit():
163
+ seq_key = parts[0] # e.g., "1/3"
164
+ elif parts[0] == "Class" and len(parts) > 1:
165
+ seq_key = f"{parts[0]}_{parts[1]}" # e.g., "Class_train:" or "Class_val:"
166
+ elif parts[0] in ("train:", "val:"):
167
+ seq_key = parts[0] # Phase identifier
168
+
169
+ # Skip if we already showed 100% for this sequence
170
+ if seq_key and self.last_progress_line == f"{seq_key}:done":
171
+ continue
172
+
173
+ # Mark this sequence as done
174
+ if seq_key:
175
+ self.last_progress_line = f"{seq_key}:done"
176
+
177
+ self.last_was_progress = True
178
+ else:
179
+ # Skip empty line after progress bar
180
+ if not line and self.last_was_progress:
181
+ self.last_was_progress = False
182
+ continue
183
+ self.last_was_progress = False
184
+
185
+ # General deduplication
186
+ if line == self.last_line and current_time - self.last_time < 0.1:
187
+ continue
188
+
189
+ self.last_line = line
190
+ self.last_time = current_time
191
+
192
+ # Add timestamp if needed
193
+ if not line.startswith("[20"):
194
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
195
+ line = f"[{timestamp}] {line}"
196
+
197
+ # Add to buffer and check if flush needed
198
+ should_flush = False
199
+ with self.buffer_lock:
200
+ self.buffer.append(line)
201
+ if len(self.buffer) >= self.batch_size:
202
+ should_flush = True
203
+
204
+ # Flush outside lock to avoid deadlock
205
+ if should_flush:
206
+ self._flush_buffer()
207
+
208
+ def _flush_worker(self):
209
+ """Background worker that flushes buffer periodically."""
210
+ while self.active:
211
+ time.sleep(self.flush_interval)
212
+ if self.active:
213
+ self._flush_buffer()
214
+
215
+ def _flush_buffer(self):
216
+ """Flush buffered lines to destination and/or callback."""
217
+ with self.buffer_lock:
218
+ if not self.buffer:
219
+ return
220
+ lines = self.buffer.copy()
221
+ self.buffer.clear()
222
+ self.chunk_id += 1
223
+ chunk_id = self.chunk_id # Capture under lock to avoid race
224
+
225
+ content = "\n".join(lines)
226
+ line_count = len(lines)
227
+
228
+ # Call custom callback if provided
229
+ if self.on_flush:
230
+ try:
231
+ self.on_flush(content, line_count, chunk_id)
232
+ except Exception:
233
+ pass # Silently ignore callback errors to avoid flooding stderr
234
+
235
+ # Write to destination (file or API)
236
+ if self.destination is not None:
237
+ self._write_destination(content)
238
+
239
+ def _write_destination(self, content):
240
+ """Write content to file or API destination."""
241
+ try:
242
+ if self.is_api:
243
+ import requests
244
+
245
+ payload = {"timestamp": datetime.now().isoformat(), "message": content}
246
+ requests.post(str(self.destination), json=payload, timeout=5)
247
+ else:
248
+ self.destination.parent.mkdir(parents=True, exist_ok=True)
249
+ with self.destination.open("a", encoding="utf-8") as f:
250
+ f.write(content + "\n")
251
+ except Exception as e:
252
+ print(f"Console logger write error: {e}", file=self.original_stderr)
253
+
254
+ class _ConsoleCapture:
255
+ """Lightweight stdout/stderr capture."""
256
+
257
+ __slots__ = ("callback", "original")
258
+
259
+ def __init__(self, original, callback):
260
+ """Initialize a stream wrapper that redirects writes to a callback while preserving the original."""
261
+ self.original = original
262
+ self.callback = callback
263
+
264
+ def write(self, text):
265
+ """Forward text to the wrapped original stream, preserving default stdout/stderr semantics."""
266
+ self.original.write(text)
267
+ self.callback(text)
268
+
269
+ def flush(self):
270
+ """Flush the wrapped stream to propagate buffered output promptly during console capture."""
271
+ self.original.flush()
272
+
273
+ class _LogHandler(logging.Handler):
274
+ """Lightweight logging handler."""
275
+
276
+ __slots__ = ("callback",)
277
+
278
+ def __init__(self, callback):
279
+ """Initialize a lightweight logging.Handler that forwards log records to the provided callback."""
280
+ super().__init__()
281
+ self.callback = callback
282
+
283
+ def emit(self, record):
284
+ """Format and forward LogRecord messages to the capture callback for unified log streaming."""
285
+ self.callback(self.format(record) + "\n")
286
+
287
+
288
+ class SystemLogger:
289
+ """Log dynamic system metrics for training monitoring.
290
+
291
+ Captures real-time system metrics including CPU, RAM, disk I/O, network I/O, and NVIDIA GPU statistics for training
292
+ performance monitoring and analysis.
293
+
294
+ Attributes:
295
+ pynvml: NVIDIA pynvml module instance if successfully imported, None otherwise.
296
+ nvidia_initialized (bool): Whether NVIDIA GPU monitoring is available and initialized.
297
+ net_start: Initial network I/O counters for calculating cumulative usage.
298
+ disk_start: Initial disk I/O counters for calculating cumulative usage.
299
+
300
+ Examples:
301
+ Basic usage:
302
+ >>> logger = SystemLogger()
303
+ >>> metrics = logger.get_metrics()
304
+ >>> print(f"CPU: {metrics['cpu']}%, RAM: {metrics['ram']}%")
305
+ >>> if metrics["gpus"]:
306
+ ... gpu0 = metrics["gpus"]["0"]
307
+ ... print(f"GPU0: {gpu0['usage']}% usage, {gpu0['temp']}°C")
308
+
309
+ Training loop integration:
310
+ >>> system_logger = SystemLogger()
311
+ >>> for epoch in range(epochs):
312
+ ... # Training code here
313
+ ... metrics = system_logger.get_metrics()
314
+ ... # Log to database/file
315
+ """
316
+
317
+ def __init__(self):
318
+ """Initialize the system logger."""
319
+ import psutil # scoped as slow import
320
+
321
+ self.pynvml = None
322
+ self.nvidia_initialized = self._init_nvidia()
323
+ self.net_start = psutil.net_io_counters()
324
+ self.disk_start = psutil.disk_io_counters()
325
+
326
+ # For rate calculation
327
+ self._prev_net = self.net_start
328
+ self._prev_disk = self.disk_start
329
+ self._prev_time = time.time()
330
+
331
+ def _init_nvidia(self):
332
+ """Initialize NVIDIA GPU monitoring with pynvml."""
333
+ if MACOS:
334
+ return False
335
+
336
+ try:
337
+ check_requirements("nvidia-ml-py>=12.0.0")
338
+ self.pynvml = __import__("pynvml")
339
+ self.pynvml.nvmlInit()
340
+ return True
341
+ except Exception as e:
342
+ import torch
343
+
344
+ if torch.cuda.is_available():
345
+ LOGGER.warning(f"SystemLogger NVML init failed: {e}")
346
+ return False
347
+
348
+ def get_metrics(self, rates=False):
349
+ """Get current system metrics including CPU, RAM, disk, network, and GPU usage.
350
+
351
+ Collects comprehensive system metrics including CPU usage, RAM usage, disk I/O statistics, network I/O
352
+ statistics, and GPU metrics (if available).
353
+
354
+ Example output (rates=False, default):
355
+ ```python
356
+ {
357
+ "cpu": 45.2,
358
+ "ram": 78.9,
359
+ "disk": {"read_mb": 156.7, "write_mb": 89.3, "used_gb": 256.8},
360
+ "network": {"recv_mb": 157.2, "sent_mb": 89.1},
361
+ "gpus": {
362
+ "0": {"usage": 95.6, "memory": 85.4, "temp": 72, "power": 285},
363
+ "1": {"usage": 94.1, "memory": 82.7, "temp": 70, "power": 278},
364
+ },
365
+ }
366
+ ```
367
+
368
+ Example output (rates=True):
369
+ ```python
370
+ {
371
+ "cpu": 45.2,
372
+ "ram": 78.9,
373
+ "disk": {"read_mbs": 12.5, "write_mbs": 8.3, "used_gb": 256.8},
374
+ "network": {"recv_mbs": 5.2, "sent_mbs": 1.1},
375
+ "gpus": {
376
+ "0": {"usage": 95.6, "memory": 85.4, "temp": 72, "power": 285},
377
+ },
378
+ }
379
+ ```
380
+
381
+ Args:
382
+ rates (bool): If True, return disk/network as MB/s rates instead of cumulative MB.
383
+
384
+ Returns:
385
+ (dict): Metrics dictionary with cpu, ram, disk, network, and gpus keys.
386
+
387
+ Examples:
388
+ >>> logger = SystemLogger()
389
+ >>> logger.get_metrics()["cpu"] # CPU percentage
390
+ >>> logger.get_metrics(rates=True)["network"]["recv_mbs"] # MB/s download rate
391
+ """
392
+ import psutil # scoped as slow import
393
+
394
+ net = psutil.net_io_counters()
395
+ disk = psutil.disk_io_counters()
396
+ memory = psutil.virtual_memory()
397
+ disk_usage = shutil.disk_usage("/")
398
+ now = time.time()
399
+
400
+ metrics = {
401
+ "cpu": round(psutil.cpu_percent(), 3),
402
+ "ram": round(memory.percent, 3),
403
+ "gpus": {},
404
+ }
405
+
406
+ # Calculate elapsed time since last call
407
+ elapsed = max(0.1, now - self._prev_time) # Avoid division by zero
408
+
409
+ if rates:
410
+ # Calculate MB/s rates from delta since last call
411
+ metrics["disk"] = {
412
+ "read_mbs": round(max(0, (disk.read_bytes - self._prev_disk.read_bytes) / (1 << 20) / elapsed), 3),
413
+ "write_mbs": round(max(0, (disk.write_bytes - self._prev_disk.write_bytes) / (1 << 20) / elapsed), 3),
414
+ "used_gb": round(disk_usage.used / (1 << 30), 3),
415
+ }
416
+ metrics["network"] = {
417
+ "recv_mbs": round(max(0, (net.bytes_recv - self._prev_net.bytes_recv) / (1 << 20) / elapsed), 3),
418
+ "sent_mbs": round(max(0, (net.bytes_sent - self._prev_net.bytes_sent) / (1 << 20) / elapsed), 3),
419
+ }
420
+ else:
421
+ # Cumulative MB since initialization (original behavior)
422
+ metrics["disk"] = {
423
+ "read_mb": round((disk.read_bytes - self.disk_start.read_bytes) / (1 << 20), 3),
424
+ "write_mb": round((disk.write_bytes - self.disk_start.write_bytes) / (1 << 20), 3),
425
+ "used_gb": round(disk_usage.used / (1 << 30), 3),
426
+ }
427
+ metrics["network"] = {
428
+ "recv_mb": round((net.bytes_recv - self.net_start.bytes_recv) / (1 << 20), 3),
429
+ "sent_mb": round((net.bytes_sent - self.net_start.bytes_sent) / (1 << 20), 3),
430
+ }
431
+
432
+ # Always update previous values for accurate rate calculation on next call
433
+ self._prev_net = net
434
+ self._prev_disk = disk
435
+ self._prev_time = now
436
+
437
+ # Add GPU metrics (NVIDIA only)
438
+ if self.nvidia_initialized:
439
+ metrics["gpus"].update(self._get_nvidia_metrics())
440
+
441
+ return metrics
442
+
443
+ def _get_nvidia_metrics(self):
444
+ """Get NVIDIA GPU metrics including utilization, memory, temperature, and power."""
445
+ gpus = {}
446
+ if not self.nvidia_initialized or not self.pynvml:
447
+ return gpus
448
+ try:
449
+ device_count = self.pynvml.nvmlDeviceGetCount()
450
+ for i in range(device_count):
451
+ handle = self.pynvml.nvmlDeviceGetHandleByIndex(i)
452
+ util = self.pynvml.nvmlDeviceGetUtilizationRates(handle)
453
+ memory = self.pynvml.nvmlDeviceGetMemoryInfo(handle)
454
+ temp = self.pynvml.nvmlDeviceGetTemperature(handle, self.pynvml.NVML_TEMPERATURE_GPU)
455
+ power = self.pynvml.nvmlDeviceGetPowerUsage(handle) // 1000
456
+
457
+ gpus[str(i)] = {
458
+ "usage": round(util.gpu, 3),
459
+ "memory": round((memory.used / memory.total) * 100, 3),
460
+ "temp": temp,
461
+ "power": power,
462
+ }
463
+ except Exception:
464
+ pass
465
+ return gpus
466
+
467
+
468
+ if __name__ == "__main__":
469
+ print("SystemLogger Real-time Metrics Monitor")
470
+ print("Press Ctrl+C to stop\n")
471
+
472
+ logger = SystemLogger()
473
+
474
+ try:
475
+ while True:
476
+ metrics = logger.get_metrics()
477
+
478
+ # Clear screen (works on most terminals)
479
+ print("\033[H\033[J", end="")
480
+
481
+ # Display system metrics
482
+ print(f"CPU: {metrics['cpu']:5.1f}%")
483
+ print(f"RAM: {metrics['ram']:5.1f}%")
484
+ print(f"Disk Read: {metrics['disk']['read_mb']:8.1f} MB")
485
+ print(f"Disk Write: {metrics['disk']['write_mb']:7.1f} MB")
486
+ print(f"Disk Used: {metrics['disk']['used_gb']:8.1f} GB")
487
+ print(f"Net Recv: {metrics['network']['recv_mb']:9.1f} MB")
488
+ print(f"Net Sent: {metrics['network']['sent_mb']:9.1f} MB")
489
+
490
+ # Display GPU metrics if available
491
+ if metrics["gpus"]:
492
+ print("\nGPU Metrics:")
493
+ for gpu_id, gpu_data in metrics["gpus"].items():
494
+ print(
495
+ f" GPU {gpu_id}: {gpu_data['usage']:3}% | "
496
+ f"Mem: {gpu_data['memory']:5.1f}% | "
497
+ f"Temp: {gpu_data['temp']:2}°C | "
498
+ f"Power: {gpu_data['power']:3}W"
499
+ )
500
+ else:
501
+ print("\nGPU: No NVIDIA GPUs detected")
502
+
503
+ time.sleep(1)
504
+
505
+ except KeyboardInterrupt:
506
+ print("\n\nStopped monitoring.")