Merge branch 'main' into quan

mct-2.1.1
Francesco Mattioli 2 months ago committed by GitHub
commit cc80f984c9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      .github/workflows/ci.yaml
  2. 3
      docker/Dockerfile
  3. 59
      docs/en/guides/queue-management.md
  4. 60
      docs/en/guides/speed-estimation.md
  5. 4
      docs/en/integrations/kaggle.md
  6. 2
      docs/en/integrations/openvino.md
  7. 19
      docs/en/integrations/weights-biases.md
  8. 4
      docs/en/reference/data/converter.md
  9. 2
      docs/en/yolov5/environments/docker_image_quickstart_tutorial.md
  10. 2
      pyproject.toml
  11. 11
      tests/test_solutions.py
  12. 7
      ultralytics/__init__.py
  13. 5
      ultralytics/cfg/__init__.py
  14. 18
      ultralytics/cfg/solutions/default.yaml
  15. 73
      ultralytics/data/converter.py
  16. 46
      ultralytics/data/dataset.py
  17. 21
      ultralytics/data/explorer/gui/dash.py
  18. 5
      ultralytics/data/utils.py
  19. 2
      ultralytics/engine/model.py
  20. 2
      ultralytics/models/yolo/detect/val.py
  21. 17
      ultralytics/nn/autobackend.py
  22. 30
      ultralytics/nn/tasks.py
  23. 2
      ultralytics/solutions/object_counter.py
  24. 149
      ultralytics/solutions/queue_management.py
  25. 8
      ultralytics/solutions/solutions.py
  26. 118
      ultralytics/solutions/speed_estimation.py
  27. 38
      ultralytics/utils/__init__.py
  28. 9
      ultralytics/utils/autobatch.py
  29. 4
      ultralytics/utils/callbacks/clearml.py
  30. 8
      ultralytics/utils/callbacks/comet.py
  31. 2
      ultralytics/utils/callbacks/mlflow.py
  32. 6
      ultralytics/utils/callbacks/neptune.py
  33. 32
      ultralytics/utils/callbacks/tensorboard.py
  34. 2
      ultralytics/utils/callbacks/wb.py
  35. 22
      ultralytics/utils/checks.py
  36. 6
      ultralytics/utils/downloads.py
  37. 17
      ultralytics/utils/plotting.py
  38. 13
      ultralytics/utils/torch_utils.py

@ -56,7 +56,7 @@ jobs:
shell: bash # for Windows compatibility shell: bash # for Windows compatibility
run: | run: |
python -m pip install --upgrade pip wheel python -m pip install --upgrade pip wheel
pip install -e . --extra-index-url https://download.pytorch.org/whl/cpu pip install . --extra-index-url https://download.pytorch.org/whl/cpu
- name: Check environment - name: Check environment
run: | run: |
yolo checks yolo checks
@ -213,7 +213,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install requirements - name: Install requirements
run: pip install -e . pytest-cov run: pip install . pytest-cov
- name: Check environment - name: Check environment
run: | run: |
yolo checks yolo checks

@ -11,7 +11,8 @@ ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \ PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1 \ PIP_NO_CACHE_DIR=1 \
PIP_BREAK_SYSTEM_PACKAGES=1 \ PIP_BREAK_SYSTEM_PACKAGES=1 \
MKL_THREADING_LAYER=GNU MKL_THREADING_LAYER=GNU \
OMP_NUM_THREADS=1
# Downloads to user config dir # Downloads to user config dir
ADD https://github.com/ultralytics/assets/releases/download/v0.0.0/Arial.ttf \ ADD https://github.com/ultralytics/assets/releases/download/v0.0.0/Arial.ttf \

@ -40,10 +40,9 @@ Queue management using [Ultralytics YOLO11](https://github.com/ultralytics/ultra
```python ```python
import cv2 import cv2
from ultralytics import YOLO, solutions from ultralytics import solutions
model = YOLO("yolo11n.pt") cap = cv2.VideoCapture("Path/to/video/file.mp4")
cap = cv2.VideoCapture("path/to/video/file.mp4")
assert cap.isOpened(), "Error reading video file" assert cap.isOpened(), "Error reading video file"
w, h, fps = (int(cap.get(x)) for x in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS)) w, h, fps = (int(cap.get(x)) for x in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS))
@ -53,18 +52,15 @@ Queue management using [Ultralytics YOLO11](https://github.com/ultralytics/ultra
queue_region = [(20, 400), (1080, 404), (1080, 360), (20, 360)] queue_region = [(20, 400), (1080, 404), (1080, 360), (20, 360)]
queue = solutions.QueueManager( queue = solutions.QueueManager(
names=model.names, model="yolo11n.pt",
reg_pts=queue_region, region=queue_region,
line_thickness=3,
) )
while cap.isOpened(): while cap.isOpened():
success, im0 = cap.read() success, im0 = cap.read()
if success: if success:
tracks = model.track(im0, persist=True) out = queue.process_queue(im0)
out = queue.process_queue(im0, tracks)
video_writer.write(im0) video_writer.write(im0)
if cv2.waitKey(1) & 0xFF == ord("q"): if cv2.waitKey(1) & 0xFF == ord("q"):
break break
@ -82,10 +78,9 @@ Queue management using [Ultralytics YOLO11](https://github.com/ultralytics/ultra
```python ```python
import cv2 import cv2
from ultralytics import YOLO, solutions from ultralytics import solutions
model = YOLO("yolo11n.pt") cap = cv2.VideoCapture("Path/to/video/file.mp4")
cap = cv2.VideoCapture("path/to/video/file.mp4")
assert cap.isOpened(), "Error reading video file" assert cap.isOpened(), "Error reading video file"
w, h, fps = (int(cap.get(x)) for x in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS)) w, h, fps = (int(cap.get(x)) for x in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS))
@ -95,18 +90,15 @@ Queue management using [Ultralytics YOLO11](https://github.com/ultralytics/ultra
queue_region = [(20, 400), (1080, 404), (1080, 360), (20, 360)] queue_region = [(20, 400), (1080, 404), (1080, 360), (20, 360)]
queue = solutions.QueueManager( queue = solutions.QueueManager(
names=model.names, model="yolo11n.pt",
reg_pts=queue_region, classes=3,
line_thickness=3,
) )
while cap.isOpened(): while cap.isOpened():
success, im0 = cap.read() success, im0 = cap.read()
if success: if success:
tracks = model.track(im0, persist=True, classes=0) # Only person class out = queue.process_queue(im0)
out = queue.process_queue(im0, tracks)
video_writer.write(im0) video_writer.write(im0)
if cv2.waitKey(1) & 0xFF == ord("q"): if cv2.waitKey(1) & 0xFF == ord("q"):
break break
@ -121,13 +113,12 @@ Queue management using [Ultralytics YOLO11](https://github.com/ultralytics/ultra
### Arguments `QueueManager` ### Arguments `QueueManager`
| Name | Type | Default | Description | | Name | Type | Default | Description |
| ---------------- | ---------------- | -------------------------- | -------------------------------------------------------------------------------- | | ------------ | ------ | -------------------------- | ---------------------------------------------------- |
| `names` | `dict` | `model.names` | A dictionary mapping class IDs to class names. | | `model` | `str` | `None` | Path to Ultralytics YOLO Model File |
| `reg_pts` | `list of tuples` | `[(20, 400), (1260, 400)]` | Points defining the counting region polygon. Defaults to a predefined rectangle. | | `region` | `list` | `[(20, 400), (1260, 400)]` | List of points defining the queue region. |
| `line_thickness` | `int` | `2` | Thickness of the annotation lines. | | `line_width` | `int` | `2` | Line thickness for bounding boxes. |
| `view_img` | `bool` | `False` | Whether to display the image frames. | | `show` | `bool` | `False` | Flag to control whether to display the video stream. |
| `draw_tracks` | `bool` | `False` | Whether to draw tracks of the objects. |
### Arguments `model.track` ### Arguments `model.track`
@ -149,23 +140,21 @@ Here's a minimal example:
```python ```python
import cv2 import cv2
from ultralytics import YOLO, solutions from ultralytics import solutions
model = YOLO("yolo11n.pt")
cap = cv2.VideoCapture("path/to/video.mp4") cap = cv2.VideoCapture("path/to/video.mp4")
queue_region = [(20, 400), (1080, 404), (1080, 360), (20, 360)] queue_region = [(20, 400), (1080, 404), (1080, 360), (20, 360)]
queue = solutions.QueueManager( queue = solutions.QueueManager(
names=model.names, model="yolo11n.pt",
reg_pts=queue_region, region=queue_region,
line_thickness=3, line_width=3,
) )
while cap.isOpened(): while cap.isOpened():
success, im0 = cap.read() success, im0 = cap.read()
if success: if success:
tracks = model.track(im0, show=False, persist=True, verbose=False) out = queue.process_queue(im0)
out = queue.process_queue(im0, tracks)
cv2.imshow("Queue Management", im0) cv2.imshow("Queue Management", im0)
if cv2.waitKey(1) & 0xFF == ord("q"): if cv2.waitKey(1) & 0xFF == ord("q"):
break break
@ -207,9 +196,9 @@ Example for airports:
```python ```python
queue_region_airport = [(50, 600), (1200, 600), (1200, 550), (50, 550)] queue_region_airport = [(50, 600), (1200, 600), (1200, 550), (50, 550)]
queue_airport = solutions.QueueManager( queue_airport = solutions.QueueManager(
names=model.names, model="yolo11n.pt",
reg_pts=queue_region_airport, region=queue_region_airport,
line_thickness=3, line_width=3,
) )
``` ```

@ -45,40 +45,33 @@ keywords: Ultralytics YOLO11, speed estimation, object tracking, computer vision
```python ```python
import cv2 import cv2
from ultralytics import YOLO, solutions from ultralytics import solutions
model = YOLO("yolo11n.pt") cap = cv2.VideoCapture("Path/to/video/file.mp4")
names = model.model.names
cap = cv2.VideoCapture("path/to/video/file.mp4")
assert cap.isOpened(), "Error reading video file" assert cap.isOpened(), "Error reading video file"
w, h, fps = (int(cap.get(x)) for x in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS)) w, h, fps = (int(cap.get(x)) for x in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS))
# Video writer video_writer = cv2.VideoWriter("speed_management.avi", cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h))
video_writer = cv2.VideoWriter("speed_estimation.avi", cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h))
line_pts = [(0, 360), (1280, 360)] speed_region = [(20, 400), (1080, 404), (1080, 360), (20, 360)]
# Init speed-estimation obj speed = solutions.SpeedEstimator(model="yolo11n.pt", region=speed_region, show=True)
speed_obj = solutions.SpeedEstimator(
reg_pts=line_pts,
names=names,
view_img=True,
)
while cap.isOpened(): while cap.isOpened():
success, im0 = cap.read() success, im0 = cap.read()
if not success:
print("Video frame is empty or video processing has been successfully completed.")
break
tracks = model.track(im0, persist=True) if success:
out = speed.estimate_speed(im0)
video_writer.write(im0)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
continue
im0 = speed_obj.estimate_speed(im0, tracks) print("Video frame is empty or video processing has been successfully completed.")
video_writer.write(im0) break
cap.release() cap.release()
video_writer.release()
cv2.destroyAllWindows() cv2.destroyAllWindows()
``` ```
@ -88,13 +81,12 @@ keywords: Ultralytics YOLO11, speed estimation, object tracking, computer vision
### Arguments `SpeedEstimator` ### Arguments `SpeedEstimator`
| Name | Type | Default | Description | | Name | Type | Default | Description |
| ------------------ | ------ | -------------------------- | ---------------------------------------------------- | | ------------ | ------ | -------------------------- | ---------------------------------------------------- |
| `names` | `dict` | `None` | Dictionary of class names. | | `model` | `str` | `None` | Path to Ultralytics YOLO Model File |
| `reg_pts` | `list` | `[(20, 400), (1260, 400)]` | List of region points for speed estimation. | | `region` | `list` | `[(20, 400), (1260, 400)]` | List of points defining the counting region. |
| `view_img` | `bool` | `False` | Whether to display the image with annotations. | | `line_width` | `int` | `2` | Line thickness for bounding boxes. |
| `line_thickness` | `int` | `2` | Thickness of the lines for drawing boxes and tracks. | | `show` | `bool` | `False` | Flag to control whether to display the video stream. |
| `spdl_dist_thresh` | `int` | `10` | Distance threshold for speed calculation. |
### Arguments `model.track` ### Arguments `model.track`
@ -111,10 +103,7 @@ Estimating object speed with Ultralytics YOLO11 involves combining [object detec
```python ```python
import cv2 import cv2
from ultralytics import YOLO, solutions from ultralytics import solutions
model = YOLO("yolo11n.pt")
names = model.model.names
cap = cv2.VideoCapture("path/to/video/file.mp4") cap = cv2.VideoCapture("path/to/video/file.mp4")
w, h, fps = (int(cap.get(x)) for x in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS)) w, h, fps = (int(cap.get(x)) for x in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS))
@ -122,17 +111,16 @@ video_writer = cv2.VideoWriter("speed_estimation.avi", cv2.VideoWriter_fourcc(*"
# Initialize SpeedEstimator # Initialize SpeedEstimator
speed_obj = solutions.SpeedEstimator( speed_obj = solutions.SpeedEstimator(
reg_pts=[(0, 360), (1280, 360)], region=[(0, 360), (1280, 360)],
names=names, model="yolo11n.pt",
view_img=True, show=True,
) )
while cap.isOpened(): while cap.isOpened():
success, im0 = cap.read() success, im0 = cap.read()
if not success: if not success:
break break
tracks = model.track(im0, persist=True, show=False) im0 = speed_obj.estimate_speed(im0)
im0 = speed_obj.estimate_speed(im0, tracks)
video_writer.write(im0) video_writer.write(im0)
cap.release() cap.release()

@ -48,7 +48,7 @@ These options include:
When working with Kaggle, you might come across some common issues. Here are some points to help you navigate the platform smoothly: When working with Kaggle, you might come across some common issues. Here are some points to help you navigate the platform smoothly:
- **Access to GPUs**: In your Kaggle notebooks, you can activate a GPU at any time, with usage allowed for up to 30 hours per week. Kaggle provides the Nvidia Tesla P100 GPU with 16GB of memory and also offers the option of using a Nvidia GPU T4 x2. Powerful hardware accelerates your machine-learning tasks, making model training and inference much faster. - **Access to GPUs**: In your Kaggle notebooks, you can activate a GPU at any time, with usage allowed for up to 30 hours per week. Kaggle provides the NVIDIA Tesla P100 GPU with 16GB of memory and also offers the option of using a NVIDIA GPU T4 x2. Powerful hardware accelerates your machine-learning tasks, making model training and inference much faster.
- **Kaggle Kernels**: Kaggle Kernels are free Jupyter notebook servers that can integrate GPUs, allowing you to perform machine learning operations on cloud computers. You don't have to rely on your own computer's CPU, avoiding overload and freeing up your local resources. - **Kaggle Kernels**: Kaggle Kernels are free Jupyter notebook servers that can integrate GPUs, allowing you to perform machine learning operations on cloud computers. You don't have to rely on your own computer's CPU, avoiding overload and freeing up your local resources.
- **Kaggle Datasets**: Kaggle datasets are free to download. However, it's important to check the license for each dataset to understand any usage restrictions. Some datasets may have limitations on academic publications or commercial use. You can download datasets directly to your Kaggle notebook or anywhere else via the Kaggle API. - **Kaggle Datasets**: Kaggle datasets are free to download. However, it's important to check the license for each dataset to understand any usage restrictions. Some datasets may have limitations on academic publications or commercial use. You can download datasets directly to your Kaggle notebook or anywhere else via the Kaggle API.
- **Saving and Committing Notebooks**: To save and commit a notebook on Kaggle, click "Save Version." This saves the current state of your notebook. Once the background kernel finishes generating the output files, you can access them from the Output tab on the main notebook page. - **Saving and Committing Notebooks**: To save and commit a notebook on Kaggle, click "Save Version." This saves the current state of your notebook. Once the background kernel finishes generating the output files, you can access them from the Output tab on the main notebook page.
@ -101,7 +101,7 @@ Training a YOLO11 model on Kaggle is straightforward. First, access the [Kaggle
Kaggle offers several advantages for training YOLO11 models: Kaggle offers several advantages for training YOLO11 models:
- **Free GPU Access**: Utilize powerful GPUs like Nvidia Tesla P100 or T4 x2 for up to 30 hours per week. - **Free GPU Access**: Utilize powerful GPUs like NVIDIA Tesla P100 or T4 x2 for up to 30 hours per week.
- **Pre-installed Libraries**: Libraries like TensorFlow and PyTorch are pre-installed, simplifying the setup. - **Pre-installed Libraries**: Libraries like TensorFlow and PyTorch are pre-installed, simplifying the setup.
- **Community Collaboration**: Engage with a vast community of data scientists and machine learning enthusiasts. - **Community Collaboration**: Engage with a vast community of data scientists and machine learning enthusiasts.
- **Version Control**: Easily manage different versions of your notebooks and revert to previous versions if needed. - **Version Control**: Easily manage different versions of your notebooks and revert to previous versions if needed.

@ -148,7 +148,7 @@ This table represents the benchmark results for five different models (YOLOv8n,
### Intel Arc GPU ### Intel Arc GPU
Intel® Arc™ represents Intel's foray into the dedicated GPU market. The Arc™ series, designed to compete with leading GPU manufacturers like AMD and Nvidia, caters to both the laptop and desktop markets. The series includes mobile versions for compact devices like laptops, and larger, more powerful versions for desktop computers. Intel® Arc™ represents Intel's foray into the dedicated GPU market. The Arc™ series, designed to compete with leading GPU manufacturers like AMD and NVIDIA, caters to both the laptop and desktop markets. The series includes mobile versions for compact devices like laptops, and larger, more powerful versions for desktop computers.
The Arc™ series is divided into three categories: Arc™ 3, Arc™ 5, and Arc™ 7, with each number indicating the performance level. Each category includes several models, and the 'M' in the GPU model name signifies a mobile, integrated variant. The Arc™ series is divided into three categories: Arc™ 3, Arc™ 5, and Arc™ 7, with each number indicating the performance level. Each category includes several models, and the 'M' in the GPU model name signifies a mobile, integrated variant.

@ -106,16 +106,19 @@ Before diving into the usage instructions for YOLO11 model training with Weights
| project | `None` | Specifies the name of the project logged locally and in W&B. This way you can group multiple runs together. | | project | `None` | Specifies the name of the project logged locally and in W&B. This way you can group multiple runs together. |
| name | `None` | The name of the training run. This determines the name used to create subfolders and the name used for W&B logging | | name | `None` | The name of the training run. This determines the name used to create subfolders and the name used for W&B logging |
!!! Tip "Enable or Disable Weights & Biases" !!! tip "Enable or Disable Weights & Biases"
If you want to enable or disable Weights & Biases logging, you can use the `wandb` command. By default, Weights & Biases logging is enabled.
```bash If you want to enable or disable Weights & Biases logging, you can use the `wandb` command. By default, Weights & Biases logging is enabled.
# Enable Weights & Biases logging
wandb enabled
# Disable Weights & Biases logging === "CLI"
wandb disabled
``` ```bash
# Enable Weights & Biases logging
wandb enabled
# Disable Weights & Biases logging
wandb disabled
```
### Understanding the Output ### Understanding the Output

@ -41,4 +41,8 @@ keywords: Ultralytics, data conversion, YOLO models, COCO, DOTA, YOLO bbox2segme
## ::: ultralytics.data.converter.yolo_bbox2segment ## ::: ultralytics.data.converter.yolo_bbox2segment
<br><br><hr><br>
## ::: ultralytics.data.converter.create_synthetic_coco_dataset
<br><br> <br><br>

@ -12,7 +12,7 @@ You can also explore other quickstart options for YOLOv5, such as our [Colab Not
## Prerequisites ## Prerequisites
1. **NVIDIA Driver**: Version 455.23 or higher. Download from [Nvidia's website](https://www.nvidia.com/Download/index.aspx). 1. **NVIDIA Driver**: Version 455.23 or higher. Download from [NVIDIA's website](https://www.nvidia.com/Download/index.aspx).
2. **NVIDIA-Docker**: Allows Docker to interact with your local GPU. Installation instructions are available on the [NVIDIA-Docker GitHub repository](https://github.com/NVIDIA/nvidia-docker). 2. **NVIDIA-Docker**: Allows Docker to interact with your local GPU. Installation instructions are available on the [NVIDIA-Docker GitHub repository](https://github.com/NVIDIA/nvidia-docker).
3. **Docker Engine - CE**: Version 19.03 or higher. Download and installation instructions can be found on the [Docker website](https://docs.docker.com/get-started/get-docker/). 3. **Docker Engine - CE**: Version 19.03 or higher. Download and installation instructions can be found on the [Docker website](https://docs.docker.com/get-started/get-docker/).

@ -19,7 +19,7 @@
# For comprehensive documentation and usage instructions, visit: https://docs.ultralytics.com # For comprehensive documentation and usage instructions, visit: https://docs.ultralytics.com
[build-system] [build-system]
requires = ["setuptools>=57.0.0", "wheel"] requires = ["setuptools>=70.0.0", "wheel"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
# Project settings ----------------------------------------------------------------------------------------------------- # Project settings -----------------------------------------------------------------------------------------------------

@ -14,25 +14,22 @@ WORKOUTS_SOLUTION_DEMO = "https://github.com/ultralytics/assets/releases/downloa
def test_major_solutions(): def test_major_solutions():
"""Test the object counting, heatmap, speed estimation and queue management solution.""" """Test the object counting, heatmap, speed estimation and queue management solution."""
safe_download(url=MAJOR_SOLUTIONS_DEMO) safe_download(url=MAJOR_SOLUTIONS_DEMO)
model = YOLO("yolo11n.pt")
names = model.names
cap = cv2.VideoCapture("solutions_ci_demo.mp4") cap = cv2.VideoCapture("solutions_ci_demo.mp4")
assert cap.isOpened(), "Error reading video file" assert cap.isOpened(), "Error reading video file"
region_points = [(20, 400), (1080, 404), (1080, 360), (20, 360)] region_points = [(20, 400), (1080, 404), (1080, 360), (20, 360)]
counter = solutions.ObjectCounter(region=region_points, model="yolo11n.pt", show=False) counter = solutions.ObjectCounter(region=region_points, model="yolo11n.pt", show=False)
heatmap = solutions.Heatmap(colormap=cv2.COLORMAP_PARULA, model="yolo11n.pt", show=False) heatmap = solutions.Heatmap(colormap=cv2.COLORMAP_PARULA, model="yolo11n.pt", show=False)
speed = solutions.SpeedEstimator(reg_pts=region_points, names=names, view_img=False) speed = solutions.SpeedEstimator(region=region_points, model="yolo11n.pt", show=False)
queue = solutions.QueueManager(names=names, reg_pts=region_points, view_img=False) queue = solutions.QueueManager(region=region_points, model="yolo11n.pt", show=False)
while cap.isOpened(): while cap.isOpened():
success, im0 = cap.read() success, im0 = cap.read()
if not success: if not success:
break break
original_im0 = im0.copy() original_im0 = im0.copy()
tracks = model.track(im0, persist=True, show=False)
_ = counter.count(original_im0.copy()) _ = counter.count(original_im0.copy())
_ = heatmap.generate_heatmap(original_im0.copy()) _ = heatmap.generate_heatmap(original_im0.copy())
_ = speed.estimate_speed(original_im0.copy(), tracks) _ = speed.estimate_speed(original_im0.copy())
_ = queue.process_queue(original_im0.copy(), tracks) _ = queue.process_queue(original_im0.copy())
cap.release() cap.release()
cv2.destroyAllWindows() cv2.destroyAllWindows()

@ -1,11 +1,12 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license # Ultralytics YOLO 🚀, AGPL-3.0 license
__version__ = "8.3.6" __version__ = "8.3.8"
import os import os
# Set ENV Variables (place before imports) # Set ENV variables (place before imports)
os.environ["OMP_NUM_THREADS"] = "1" # reduce CPU utilization during training if not os.environ.get("OMP_NUM_THREADS"):
os.environ["OMP_NUM_THREADS"] = "1" # default for reduced CPU utilization during training
from ultralytics.data.explorer.explorer import Explorer from ultralytics.data.explorer.explorer import Explorer
from ultralytics.models import NAS, RTDETR, SAM, YOLO, FastSAM, YOLOWorld from ultralytics.models import NAS, RTDETR, SAM, YOLO, FastSAM, YOLOWorld

@ -669,9 +669,10 @@ def smart_value(v):
elif v_lower == "false": elif v_lower == "false":
return False return False
else: else:
with contextlib.suppress(Exception): try:
return eval(v) return eval(v)
return v except: # noqa E722
return v
def entrypoint(debug=""): def entrypoint(debug=""):

@ -2,15 +2,15 @@
# Configuration for Ultralytics Solutions # Configuration for Ultralytics Solutions
model: "yolo11n.pt" # The Ultralytics YOLO11 model to be used (e.g., yolo11n.pt for YOLO11 nano version) model: "yolo11n.pt" # The Ultralytics YOLO11 model to be used (e.g., yolo11n.pt for YOLO11 nano version and yolov8n.pt for YOLOv8 nano version)
region: # Object counting, queue or speed estimation region points region: # Object counting, queue or speed estimation region points. Default region points are [(20, 400), (1080, 404), (1080, 360), (20, 360)]
line_width: 2 # Thickness of the lines used to draw regions on the image/video frames line_width: 2 # Width of the annotator used to draw regions on the image/video frames + bounding boxes and tracks drawing. Default value is 2.
show: True # Flag to control whether to display output image or not show: True # Flag to control whether to display output image or not, you can set this as False i.e. when deploying it on some embedded devices.
show_in: True # Flag to display objects moving *into* the defined region show_in: True # Flag to display objects moving *into* the defined region
show_out: True # Flag to display objects moving *out of* the defined region show_out: True # Flag to display objects moving *out of* the defined region
classes: # To count specific classes classes: # To count specific classes. i.e, if you want to detect, track and count the person with COCO model, you can use classes=0, Default its None
up_angle: 145.0 # Workouts up_angle for counts, 145.0 is default value up_angle: 145.0 # Workouts up_angle for counts, 145.0 is default value. You can adjust it for different workouts, based on position of keypoints.
down_angle: 90 # Workouts down_angle for counts, 90 is default value down_angle: 90 # Workouts down_angle for counts, 90 is default value. You can change it for different workouts, based on position of keypoints.
kpts: [6, 8, 10] # Keypoints for workouts monitoring kpts: [6, 8, 10] # Keypoints for workouts monitoring, i.e. If you want to consider keypoints for pushups that have mostly values of [6, 8, 10].
colormap: # Colormap for heatmap colormap: # Colormap for heatmap, Only OPENCV supported colormaps can be used. By default COLORMAP_PARULA will be used for visualization.

@ -1,13 +1,18 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license # Ultralytics YOLO 🚀, AGPL-3.0 license
import json import json
import random
import shutil
from collections import defaultdict from collections import defaultdict
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path from pathlib import Path
import cv2 import cv2
import numpy as np import numpy as np
from PIL import Image
from ultralytics.utils import LOGGER, TQDM from ultralytics.utils import DATASETS_DIR, LOGGER, NUM_THREADS, TQDM
from ultralytics.utils.downloads import download
from ultralytics.utils.files import increment_path from ultralytics.utils.files import increment_path
@ -588,15 +593,13 @@ def yolo_bbox2segment(im_dir, save_dir=None, sam_model="sam_b.pt"):
- im_dir - im_dir
001.jpg 001.jpg
.. ...
NNN.jpg NNN.jpg
- labels - labels
001.txt 001.txt
.. ...
NNN.txt NNN.txt
""" """
from tqdm import tqdm
from ultralytics import SAM from ultralytics import SAM
from ultralytics.data import YOLODataset from ultralytics.data import YOLODataset
from ultralytics.utils import LOGGER from ultralytics.utils import LOGGER
@ -610,7 +613,7 @@ def yolo_bbox2segment(im_dir, save_dir=None, sam_model="sam_b.pt"):
LOGGER.info("Detection labels detected, generating segment labels by SAM model!") LOGGER.info("Detection labels detected, generating segment labels by SAM model!")
sam_model = SAM(sam_model) sam_model = SAM(sam_model)
for label in tqdm(dataset.labels, total=len(dataset.labels), desc="Generating segment labels"): for label in TQDM(dataset.labels, total=len(dataset.labels), desc="Generating segment labels"):
h, w = label["shape"] h, w = label["shape"]
boxes = label["bboxes"] boxes = label["bboxes"]
if len(boxes) == 0: # skip empty labels if len(boxes) == 0: # skip empty labels
@ -635,3 +638,61 @@ def yolo_bbox2segment(im_dir, save_dir=None, sam_model="sam_b.pt"):
with open(txt_file, "a") as f: with open(txt_file, "a") as f:
f.writelines(text + "\n" for text in texts) f.writelines(text + "\n" for text in texts)
LOGGER.info(f"Generated segment labels saved in {save_dir}") LOGGER.info(f"Generated segment labels saved in {save_dir}")
def create_synthetic_coco_dataset():
"""
Creates a synthetic COCO dataset with random images based on filenames from label lists.
This function downloads COCO labels, reads image filenames from label list files,
creates synthetic images for train2017 and val2017 subsets, and organizes
them in the COCO dataset structure. It uses multithreading to generate images efficiently.
Examples:
>>> from ultralytics.data.converter import create_synthetic_coco_dataset
>>> create_synthetic_coco_dataset()
Notes:
- Requires internet connection to download label files.
- Generates random RGB images of varying sizes (480x480 to 640x640 pixels).
- Existing test2017 directory is removed as it's not needed.
- Reads image filenames from train2017.txt and val2017.txt files.
"""
def create_synthetic_image(image_file):
"""Generates synthetic images with random sizes and colors for dataset augmentation or testing purposes."""
if not image_file.exists():
size = (random.randint(480, 640), random.randint(480, 640))
Image.new(
"RGB",
size=size,
color=(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)),
).save(image_file)
# Download labels
dir = DATASETS_DIR / "coco"
url = "https://github.com/ultralytics/assets/releases/download/v0.0.0/"
label_zip = "coco2017labels-segments.zip"
download([url + label_zip], dir=dir.parent)
# Create synthetic images
shutil.rmtree(dir / "labels" / "test2017", ignore_errors=True) # Remove test2017 directory as not needed
with ThreadPoolExecutor(max_workers=NUM_THREADS) as executor:
for subset in ["train2017", "val2017"]:
subset_dir = dir / "images" / subset
subset_dir.mkdir(parents=True, exist_ok=True)
# Read image filenames from label list file
label_list_file = dir / f"{subset}.txt"
if label_list_file.exists():
with open(label_list_file) as f:
image_files = [dir / line.strip() for line in f]
# Submit all tasks
futures = [executor.submit(create_synthetic_image, image_file) for image_file in image_files]
for _ in TQDM(as_completed(futures), total=len(futures), desc=f"Generating images for {subset}"):
pass # The actual work is done in the background
else:
print(f"Warning: Labels file {label_list_file} does not exist. Skipping image creation for {subset}.")
print("Synthetic COCO dataset created successfully.")

@ -1,6 +1,5 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license # Ultralytics YOLO 🚀, AGPL-3.0 license
import contextlib
import json import json
from collections import defaultdict from collections import defaultdict
from itertools import repeat from itertools import repeat
@ -483,7 +482,7 @@ class ClassificationDataset:
desc = f"{self.prefix}Scanning {self.root}..." desc = f"{self.prefix}Scanning {self.root}..."
path = Path(self.root).with_suffix(".cache") # *.cache file path path = Path(self.root).with_suffix(".cache") # *.cache file path
with contextlib.suppress(FileNotFoundError, AssertionError, AttributeError): try:
cache = load_dataset_cache_file(path) # attempt to load a *.cache file cache = load_dataset_cache_file(path) # attempt to load a *.cache file
assert cache["version"] == DATASET_CACHE_VERSION # matches current version assert cache["version"] == DATASET_CACHE_VERSION # matches current version
assert cache["hash"] == get_hash([x[0] for x in self.samples]) # identical hash assert cache["hash"] == get_hash([x[0] for x in self.samples]) # identical hash
@ -495,24 +494,25 @@ class ClassificationDataset:
LOGGER.info("\n".join(cache["msgs"])) # display warnings LOGGER.info("\n".join(cache["msgs"])) # display warnings
return samples return samples
# Run scan if *.cache retrieval failed except (FileNotFoundError, AssertionError, AttributeError):
nf, nc, msgs, samples, x = 0, 0, [], [], {} # Run scan if *.cache retrieval failed
with ThreadPool(NUM_THREADS) as pool: nf, nc, msgs, samples, x = 0, 0, [], [], {}
results = pool.imap(func=verify_image, iterable=zip(self.samples, repeat(self.prefix))) with ThreadPool(NUM_THREADS) as pool:
pbar = TQDM(results, desc=desc, total=len(self.samples)) results = pool.imap(func=verify_image, iterable=zip(self.samples, repeat(self.prefix)))
for sample, nf_f, nc_f, msg in pbar: pbar = TQDM(results, desc=desc, total=len(self.samples))
if nf_f: for sample, nf_f, nc_f, msg in pbar:
samples.append(sample) if nf_f:
if msg: samples.append(sample)
msgs.append(msg) if msg:
nf += nf_f msgs.append(msg)
nc += nc_f nf += nf_f
pbar.desc = f"{desc} {nf} images, {nc} corrupt" nc += nc_f
pbar.close() pbar.desc = f"{desc} {nf} images, {nc} corrupt"
if msgs: pbar.close()
LOGGER.info("\n".join(msgs)) if msgs:
x["hash"] = get_hash([x[0] for x in self.samples]) LOGGER.info("\n".join(msgs))
x["results"] = nf, nc, len(samples), samples x["hash"] = get_hash([x[0] for x in self.samples])
x["msgs"] = msgs # warnings x["results"] = nf, nc, len(samples), samples
save_dataset_cache_file(self.prefix, path, x, DATASET_CACHE_VERSION) x["msgs"] = msgs # warnings
return samples save_dataset_cache_file(self.prefix, path, x, DATASET_CACHE_VERSION)
return samples

@ -39,24 +39,11 @@ def init_explorer_form(data=None, model=None):
else: else:
ds = [data] ds = [data]
prefixes = ["yolov8", "yolo11"]
sizes = ["n", "s", "m", "l", "x"]
tasks = ["", "-seg", "-pose"]
if model is None: if model is None:
models = [ models = [f"{p}{s}{t}" for p in prefixes for s in sizes for t in tasks]
"yolov8n.pt",
"yolov8s.pt",
"yolov8m.pt",
"yolov8l.pt",
"yolov8x.pt",
"yolov8n-seg.pt",
"yolov8s-seg.pt",
"yolov8m-seg.pt",
"yolov8l-seg.pt",
"yolov8x-seg.pt",
"yolov8n-pose.pt",
"yolov8s-pose.pt",
"yolov8m-pose.pt",
"yolov8l-pose.pt",
"yolov8x-pose.pt",
]
else: else:
models = [model] models = [model]

@ -1,6 +1,5 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license # Ultralytics YOLO 🚀, AGPL-3.0 license
import contextlib
import hashlib import hashlib
import json import json
import os import os
@ -60,12 +59,14 @@ def exif_size(img: Image.Image):
"""Returns exif-corrected PIL size.""" """Returns exif-corrected PIL size."""
s = img.size # (width, height) s = img.size # (width, height)
if img.format == "JPEG": # only support JPEG images if img.format == "JPEG": # only support JPEG images
with contextlib.suppress(Exception): try:
exif = img.getexif() exif = img.getexif()
if exif: if exif:
rotation = exif.get(274, None) # the EXIF key for the orientation tag is 274 rotation = exif.get(274, None) # the EXIF key for the orientation tag is 274
if rotation in {6, 8}: # rotation 270 or 90 if rotation in {6, 8}: # rotation 270 or 90
s = s[1], s[0] s = s[1], s[0]
except: # noqa E722
pass
return s return s

@ -543,7 +543,7 @@ class Model(nn.Module):
prompts = args.pop("prompts", None) # for SAM-type models prompts = args.pop("prompts", None) # for SAM-type models
if not self.predictor: if not self.predictor:
self.predictor = predictor or self._smart_load("predictor")(overrides=args, _callbacks=self.callbacks) self.predictor = (predictor or self._smart_load("predictor"))(overrides=args, _callbacks=self.callbacks)
self.predictor.setup_model(model=self.model, verbose=is_cli) self.predictor.setup_model(model=self.model, verbose=is_cli)
else: # only update args if predictor is already setup else: # only update args if predictor is already setup
self.predictor.args = get_cfg(self.predictor.args, args) self.predictor.args = get_cfg(self.predictor.args, args)

@ -75,7 +75,7 @@ class DetectionValidator(BaseValidator):
) # is COCO ) # is COCO
self.is_lvis = isinstance(val, str) and "lvis" in val and not self.is_coco # is LVIS self.is_lvis = isinstance(val, str) and "lvis" in val and not self.is_coco # is LVIS
self.class_map = converter.coco80_to_coco91_class() if self.is_coco else list(range(len(model.names))) self.class_map = converter.coco80_to_coco91_class() if self.is_coco else list(range(len(model.names)))
self.args.save_json |= (self.is_coco or self.is_lvis) and not self.training # run on final val if training COCO self.args.save_json |= self.args.val and (self.is_coco or self.is_lvis) and not self.training # run final val
self.names = model.names self.names = model.names
self.nc = len(model.names) self.nc = len(model.names)
self.metrics.names = self.names self.metrics.names = self.names

@ -1,7 +1,6 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license # Ultralytics YOLO 🚀, AGPL-3.0 license
import ast import ast
import contextlib
import json import json
import platform import platform
import zipfile import zipfile
@ -45,8 +44,10 @@ def check_class_names(names):
def default_class_names(data=None): def default_class_names(data=None):
"""Applies default class names to an input YAML file or returns numerical class names.""" """Applies default class names to an input YAML file or returns numerical class names."""
if data: if data:
with contextlib.suppress(Exception): try:
return yaml_load(check_yaml(data))["names"] return yaml_load(check_yaml(data))["names"]
except: # noqa E722
pass
return {i: f"class{i}" for i in range(999)} # return default if above errors return {i: f"class{i}" for i in range(999)} # return default if above errors
@ -261,8 +262,8 @@ class AutoBackend(nn.Module):
if -1 in tuple(model.get_tensor_shape(name)): if -1 in tuple(model.get_tensor_shape(name)):
dynamic = True dynamic = True
context.set_input_shape(name, tuple(model.get_tensor_profile_shape(name, 0)[1])) context.set_input_shape(name, tuple(model.get_tensor_profile_shape(name, 0)[1]))
if dtype == np.float16: if dtype == np.float16:
fp16 = True fp16 = True
else: else:
output_names.append(name) output_names.append(name)
shape = tuple(context.get_tensor_shape(name)) shape = tuple(context.get_tensor_shape(name))
@ -318,8 +319,10 @@ class AutoBackend(nn.Module):
with open(w, "rb") as f: with open(w, "rb") as f:
gd.ParseFromString(f.read()) gd.ParseFromString(f.read())
frozen_func = wrap_frozen_graph(gd, inputs="x:0", outputs=gd_outputs(gd)) frozen_func = wrap_frozen_graph(gd, inputs="x:0", outputs=gd_outputs(gd))
with contextlib.suppress(StopIteration): # find metadata in SavedModel alongside GraphDef try: # find metadata in SavedModel alongside GraphDef
metadata = next(Path(w).resolve().parent.rglob(f"{Path(w).stem}_saved_model*/metadata.yaml")) metadata = next(Path(w).resolve().parent.rglob(f"{Path(w).stem}_saved_model*/metadata.yaml"))
except StopIteration:
pass
# TFLite or TFLite Edge TPU # TFLite or TFLite Edge TPU
elif tflite or edgetpu: # https://www.tensorflow.org/lite/guide/python#install_tensorflow_lite_for_python elif tflite or edgetpu: # https://www.tensorflow.org/lite/guide/python#install_tensorflow_lite_for_python
@ -342,10 +345,12 @@ class AutoBackend(nn.Module):
input_details = interpreter.get_input_details() # inputs input_details = interpreter.get_input_details() # inputs
output_details = interpreter.get_output_details() # outputs output_details = interpreter.get_output_details() # outputs
# Load metadata # Load metadata
with contextlib.suppress(zipfile.BadZipFile): try:
with zipfile.ZipFile(w, "r") as model: with zipfile.ZipFile(w, "r") as model:
meta_file = model.namelist()[0] meta_file = model.namelist()[0]
metadata = ast.literal_eval(model.read(meta_file).decode("utf-8")) metadata = ast.literal_eval(model.read(meta_file).decode("utf-8"))
except zipfile.BadZipFile:
pass
# TF.js # TF.js
elif tfjs: elif tfjs:

@ -2,6 +2,7 @@
import contextlib import contextlib
import pickle import pickle
import re
import types import types
from copy import deepcopy from copy import deepcopy
from pathlib import Path from pathlib import Path
@ -981,8 +982,10 @@ def parse_model(d, ch, verbose=True): # model_dict, input_channels(3)
m = getattr(torch.nn, m[3:]) if "nn." in m else globals()[m] # get module m = getattr(torch.nn, m[3:]) if "nn." in m else globals()[m] # get module
for j, a in enumerate(args): for j, a in enumerate(args):
if isinstance(a, str): if isinstance(a, str):
with contextlib.suppress(ValueError): try:
args[j] = locals()[a] if a in locals() else ast.literal_eval(a) args[j] = locals()[a] if a in locals() else ast.literal_eval(a)
except ValueError:
pass
n = n_ = max(round(n * depth), 1) if n > 1 else n # depth gain n = n_ = max(round(n * depth), 1) if n > 1 else n # depth gain
if m in { if m in {
@ -1081,10 +1084,10 @@ def parse_model(d, ch, verbose=True): # model_dict, input_channels(3)
m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args) # module m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args) # module
t = str(m)[8:-2].replace("__main__.", "") # module type t = str(m)[8:-2].replace("__main__.", "") # module type
m.np = sum(x.numel() for x in m_.parameters()) # number params m_.np = sum(x.numel() for x in m_.parameters()) # number params
m_.i, m_.f, m_.type = i, f, t # attach index, 'from' index, type m_.i, m_.f, m_.type = i, f, t # attach index, 'from' index, type
if verbose: if verbose:
LOGGER.info(f"{i:>3}{str(f):>20}{n_:>3}{m.np:10.0f} {t:<45}{str(args):<30}") # print LOGGER.info(f"{i:>3}{str(f):>20}{n_:>3}{m_.np:10.0f} {t:<45}{str(args):<30}") # print
save.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1) # append to savelist save.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1) # append to savelist
layers.append(m_) layers.append(m_)
if i == 0: if i == 0:
@ -1095,8 +1098,6 @@ def parse_model(d, ch, verbose=True): # model_dict, input_channels(3)
def yaml_model_load(path): def yaml_model_load(path):
"""Load a YOLOv8 model from a YAML file.""" """Load a YOLOv8 model from a YAML file."""
import re
path = Path(path) path = Path(path)
if path.stem in (f"yolov{d}{x}6" for x in "nsmlx" for d in (5, 8)): if path.stem in (f"yolov{d}{x}6" for x in "nsmlx" for d in (5, 8)):
new_stem = re.sub(r"(\d+)([nslmx])6(.+)?$", r"\1\2-p6\3", path.stem) new_stem = re.sub(r"(\d+)([nslmx])6(.+)?$", r"\1\2-p6\3", path.stem)
@ -1123,11 +1124,10 @@ def guess_model_scale(model_path):
Returns: Returns:
(str): The size character of the model's scale, which can be n, s, m, l, or x. (str): The size character of the model's scale, which can be n, s, m, l, or x.
""" """
with contextlib.suppress(AttributeError): try:
import re
return re.search(r"yolo[v]?\d+([nslmx])", Path(model_path).stem).group(1) # n, s, m, l, or x return re.search(r"yolo[v]?\d+([nslmx])", Path(model_path).stem).group(1) # n, s, m, l, or x
return "" except AttributeError:
return ""
def guess_model_task(model): def guess_model_task(model):
@ -1160,17 +1160,23 @@ def guess_model_task(model):
# Guess from model cfg # Guess from model cfg
if isinstance(model, dict): if isinstance(model, dict):
with contextlib.suppress(Exception): try:
return cfg2task(model) return cfg2task(model)
except: # noqa E722
pass
# Guess from PyTorch model # Guess from PyTorch model
if isinstance(model, nn.Module): # PyTorch model if isinstance(model, nn.Module): # PyTorch model
for x in "model.args", "model.model.args", "model.model.model.args": for x in "model.args", "model.model.args", "model.model.model.args":
with contextlib.suppress(Exception): try:
return eval(x)["task"] return eval(x)["task"]
except: # noqa E722
pass
for x in "model.yaml", "model.model.yaml", "model.model.model.yaml": for x in "model.yaml", "model.model.yaml", "model.model.model.yaml":
with contextlib.suppress(Exception): try:
return cfg2task(eval(x)) return cfg2task(eval(x))
except: # noqa E722
pass
for m in model.modules(): for m in model.modules():
if isinstance(m, Segment): if isinstance(m, Segment):

@ -116,7 +116,7 @@ class ObjectCounter(BaseSolution):
self.store_tracking_history(track_id, box) # Store track history self.store_tracking_history(track_id, box) # Store track history
self.store_classwise_counts(cls) # store classwise counts in dict self.store_classwise_counts(cls) # store classwise counts in dict
# Draw centroid of objects # Draw tracks of objects
self.annotator.draw_centroid_and_tracks( self.annotator.draw_centroid_and_tracks(
self.track_line, color=colors(int(track_id), True), track_thickness=self.line_width self.track_line, color=colors(int(track_id), True), track_thickness=self.line_width
) )

@ -1,127 +1,64 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license # Ultralytics YOLO 🚀, AGPL-3.0 license
from collections import defaultdict from shapely.geometry import Point
import cv2 from ultralytics.solutions.solutions import BaseSolution # Import a parent class
from ultralytics.utils.checks import check_imshow, check_requirements
from ultralytics.utils.plotting import Annotator, colors from ultralytics.utils.plotting import Annotator, colors
check_requirements("shapely>=2.0.0")
from shapely.geometry import Point, Polygon
class QueueManager: class QueueManager(BaseSolution):
"""A class to manage the queue in a real-time video stream based on object tracks.""" """A class to manage the queue in a real-time video stream based on object tracks."""
def __init__( def __init__(self, **kwargs):
self, """Initializes the QueueManager with specified parameters for tracking and counting objects."""
names, super().__init__(**kwargs)
reg_pts=None, self.initialize_region()
line_thickness=2, self.counts = 0 # Queue counts Information
view_img=False, self.rect_color = (255, 255, 255) # Rectangle color
draw_tracks=False, self.region_length = len(self.region) # Store region length for further usage
):
def process_queue(self, im0):
""" """
Initializes the QueueManager with specified parameters for tracking and counting objects. Main function to start the queue management process.
Args: Args:
names (dict): A dictionary mapping class IDs to class names. im0 (ndarray): The input image that will be used for processing
reg_pts (list of tuples, optional): Points defining the counting region polygon. Defaults to a predefined Returns
rectangle. im0 (ndarray): The processed image for more usage
line_thickness (int, optional): Thickness of the annotation lines. Defaults to 2.
view_img (bool, optional): Whether to display the image frames. Defaults to False.
draw_tracks (bool, optional): Whether to draw tracks of the objects. Defaults to False.
""" """
# Region & Line Information
self.reg_pts = reg_pts if reg_pts is not None else [(20, 60), (20, 680), (1120, 680), (1120, 60)]
self.counting_region = (
Polygon(self.reg_pts) if len(self.reg_pts) >= 3 else Polygon([(20, 60), (20, 680), (1120, 680), (1120, 60)])
)
# annotation Information
self.tf = line_thickness
self.view_img = view_img
self.names = names # Class names
# Object counting Information
self.counts = 0
# Tracks info
self.track_history = defaultdict(list)
self.draw_tracks = draw_tracks
# Check if environment supports imshow
self.env_check = check_imshow(warn=True)
def extract_and_process_tracks(self, tracks, im0):
"""Extracts and processes tracks for queue management in a video stream."""
# Initialize annotator and draw the queue region
annotator = Annotator(im0, self.tf, self.names)
self.counts = 0 # Reset counts every frame self.counts = 0 # Reset counts every frame
if tracks[0].boxes.id is not None: self.annotator = Annotator(im0, line_width=self.line_width) # Initialize annotator
boxes = tracks[0].boxes.xyxy.cpu() self.extract_tracks(im0) # Extract tracks
clss = tracks[0].boxes.cls.cpu().tolist()
track_ids = tracks[0].boxes.id.int().cpu().tolist()
# Extract tracks self.annotator.draw_region(
for box, track_id, cls in zip(boxes, track_ids, clss): reg_pts=self.region, color=self.rect_color, thickness=self.line_width * 2
# Draw bounding box ) # Draw region
annotator.box_label(box, label=self.names[cls], color=colors(int(track_id), True))
# Update track history for box, track_id, cls in zip(self.boxes, self.track_ids, self.clss):
track_line = self.track_history[track_id] # Draw bounding box and counting region
track_line.append((float((box[0] + box[2]) / 2), float((box[1] + box[3]) / 2))) self.annotator.box_label(box, label=self.names[cls], color=colors(track_id, True))
if len(track_line) > 30: self.store_tracking_history(track_id, box) # Store track history
track_line.pop(0)
# Draw track trails if enabled # Draw tracks of objects
if self.draw_tracks: self.annotator.draw_centroid_and_tracks(
annotator.draw_centroid_and_tracks( self.track_line, color=colors(int(track_id), True), track_thickness=self.line_width
track_line,
color=colors(int(track_id), True),
track_thickness=self.line_thickness,
)
prev_position = self.track_history[track_id][-2] if len(self.track_history[track_id]) > 1 else None
# Check if the object is inside the counting region
if len(self.reg_pts) >= 3:
is_inside = self.counting_region.contains(Point(track_line[-1]))
if prev_position is not None and is_inside:
self.counts += 1
# Display queue counts
label = f"Queue Counts : {str(self.counts)}"
if label is not None:
annotator.queue_counts_display(
label,
points=self.reg_pts,
region_color=(255, 0, 255),
txt_color=(104, 31, 17),
) )
if self.env_check and self.view_img: # Cache frequently accessed attributes
annotator.draw_region(reg_pts=self.reg_pts, thickness=self.tf * 2, color=(255, 0, 255)) track_history = self.track_history.get(track_id, [])
cv2.imshow("Ultralytics YOLOv8 Queue Manager", im0)
# Close window on 'q' key press
if cv2.waitKey(1) & 0xFF == ord("q"):
return
def process_queue(self, im0, tracks): # store previous position of track and check if the object is inside the counting region
""" prev_position = track_history[-2] if len(track_history) > 1 else None
Main function to start the queue management process. if self.region_length >= 3 and prev_position and self.r_s.contains(Point(self.track_line[-1])):
self.counts += 1
Args:
im0 (ndarray): Current frame from the video stream.
tracks (list): List of tracks obtained from the object tracking process.
"""
self.extract_and_process_tracks(tracks, im0) # Extract and process tracks
return im0
# Display queue counts
self.annotator.queue_counts_display(
f"Queue Counts : {str(self.counts)}",
points=self.region,
region_color=self.rect_color,
txt_color=(104, 31, 17),
)
self.display_output(im0) # display output with base class function
if __name__ == "__main__": return im0 # return output image for more usage
classes_names = {0: "person", 1: "car"} # example class names
queue_manager = QueueManager(classes_names)

@ -76,9 +76,11 @@ class BaseSolution:
def initialize_region(self): def initialize_region(self):
"""Initialize the counting region and line segment based on config.""" """Initialize the counting region and line segment based on config."""
self.region = [(20, 400), (1260, 400)] if self.region is None else self.region self.region = [(20, 400), (1080, 404), (1080, 360), (20, 360)] if self.region is None else self.region
self.r_s = Polygon(self.region) if len(self.region) >= 3 else LineString(self.region) self.r_s = Polygon(self.region) if len(self.region) >= 3 else LineString(self.region) # region segment
self.l_s = LineString([(self.region[0][0], self.region[0][1]), (self.region[1][0], self.region[1][1])]) self.l_s = LineString(
[(self.region[0][0], self.region[0][1]), (self.region[1][0], self.region[1][1])]
) # line segment
def display_output(self, im0): def display_output(self, im0):
""" """

@ -1,116 +1,76 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license # Ultralytics YOLO 🚀, AGPL-3.0 license
from collections import defaultdict
from time import time from time import time
import cv2
import numpy as np import numpy as np
from ultralytics.utils.checks import check_imshow from ultralytics.solutions.solutions import BaseSolution, LineString
from ultralytics.utils.plotting import Annotator, colors from ultralytics.utils.plotting import Annotator, colors
class SpeedEstimator: class SpeedEstimator(BaseSolution):
"""A class to estimate the speed of objects in a real-time video stream based on their tracks.""" """A class to estimate the speed of objects in a real-time video stream based on their tracks."""
def __init__(self, names, reg_pts=None, view_img=False, line_thickness=2, spdl_dist_thresh=10): def __init__(self, **kwargs):
""" """Initializes the SpeedEstimator with the given parameters."""
Initializes the SpeedEstimator with the given parameters. super().__init__(**kwargs)
Args:
names (dict): Dictionary of class names.
reg_pts (list, optional): List of region points for speed estimation. Defaults to [(20, 400), (1260, 400)].
view_img (bool, optional): Whether to display the image with annotations. Defaults to False.
line_thickness (int, optional): Thickness of the lines for drawing boxes and tracks. Defaults to 2.
spdl_dist_thresh (int, optional): Distance threshold for speed calculation. Defaults to 10.
"""
# Region information
self.reg_pts = reg_pts if reg_pts is not None else [(20, 400), (1260, 400)]
self.names = names # Classes names self.initialize_region() # Initialize speed region
# Tracking information
self.trk_history = defaultdict(list)
self.view_img = view_img # bool for displaying inference
self.tf = line_thickness # line thickness for annotator
self.spd = {} # set for speed data self.spd = {} # set for speed data
self.trkd_ids = [] # list for already speed_estimated and tracked ID's self.trkd_ids = [] # list for already speed_estimated and tracked ID's
self.spdl = spdl_dist_thresh # Speed line distance threshold
self.trk_pt = {} # set for tracks previous time self.trk_pt = {} # set for tracks previous time
self.trk_pp = {} # set for tracks previous point self.trk_pp = {} # set for tracks previous point
# Check if the environment supports imshow def estimate_speed(self, im0):
self.env_check = check_imshow(warn=True)
def estimate_speed(self, im0, tracks):
""" """
Estimates the speed of objects based on tracking data. Estimates the speed of objects based on tracking data.
Args: Args:
im0 (ndarray): Image. im0 (ndarray): The input image that will be used for processing
tracks (list): List of tracks obtained from the object tracking process. Returns
im0 (ndarray): The processed image for more usage
Returns:
(ndarray): The image with annotated boxes and tracks.
""" """
if tracks[0].boxes.id is None: self.annotator = Annotator(im0, line_width=self.line_width) # Initialize annotator
return im0 self.extract_tracks(im0) # Extract tracks
boxes = tracks[0].boxes.xyxy.cpu() self.annotator.draw_region(
clss = tracks[0].boxes.cls.cpu().tolist() reg_pts=self.region, color=(104, 0, 123), thickness=self.line_width * 2
t_ids = tracks[0].boxes.id.int().cpu().tolist() ) # Draw region
annotator = Annotator(im0, line_width=self.tf)
annotator.draw_region(reg_pts=self.reg_pts, color=(255, 0, 255), thickness=self.tf * 2)
for box, t_id, cls in zip(boxes, t_ids, clss): for box, track_id, cls in zip(self.boxes, self.track_ids, self.clss):
track = self.trk_history[t_id] self.store_tracking_history(track_id, box) # Store track history
bbox_center = (float((box[0] + box[2]) / 2), float((box[1] + box[3]) / 2))
track.append(bbox_center)
if len(track) > 30: # Check if track_id is already in self.trk_pp or trk_pt initialize if not
track.pop(0) if track_id not in self.trk_pt:
self.trk_pt[track_id] = 0
if track_id not in self.trk_pp:
self.trk_pp[track_id] = self.track_line[-1]
trk_pts = np.hstack(track).astype(np.int32).reshape((-1, 1, 2)) speed_label = f"{int(self.spd[track_id])} km/h" if track_id in self.spd else self.names[int(cls)]
self.annotator.box_label(box, label=speed_label, color=colors(track_id, True)) # Draw bounding box
if t_id not in self.trk_pt: # Draw tracks of objects
self.trk_pt[t_id] = 0 self.annotator.draw_centroid_and_tracks(
self.track_line, color=colors(int(track_id), True), track_thickness=self.line_width
)
speed_label = f"{int(self.spd[t_id])} km/h" if t_id in self.spd else self.names[int(cls)] # Calculate object speed and direction based on region intersection
bbox_color = colors(int(t_id), True) if LineString([self.trk_pp[track_id], self.track_line[-1]]).intersects(self.l_s):
annotator.box_label(box, speed_label, bbox_color)
cv2.polylines(im0, [trk_pts], isClosed=False, color=bbox_color, thickness=self.tf)
cv2.circle(im0, (int(track[-1][0]), int(track[-1][1])), self.tf * 2, bbox_color, -1)
# Calculation of object speed
if not self.reg_pts[0][0] < track[-1][0] < self.reg_pts[1][0]:
return
if self.reg_pts[1][1] - self.spdl < track[-1][1] < self.reg_pts[1][1] + self.spdl:
direction = "known"
elif self.reg_pts[0][1] - self.spdl < track[-1][1] < self.reg_pts[0][1] + self.spdl:
direction = "known" direction = "known"
else: else:
direction = "unknown" direction = "unknown"
if self.trk_pt.get(t_id) != 0 and direction != "unknown" and t_id not in self.trkd_ids: # Perform speed calculation and tracking updates if direction is valid
self.trkd_ids.append(t_id) if direction == "known" and track_id not in self.trkd_ids:
self.trkd_ids.append(track_id)
time_difference = time() - self.trk_pt[t_id] time_difference = time() - self.trk_pt[track_id]
if time_difference > 0: if time_difference > 0:
self.spd[t_id] = np.abs(track[-1][1] - self.trk_pp[t_id][1]) / time_difference self.spd[track_id] = np.abs(self.track_line[-1][1] - self.trk_pp[track_id][1]) / time_difference
self.trk_pt[t_id] = time()
self.trk_pp[t_id] = track[-1]
if self.view_img and self.env_check:
cv2.imshow("Ultralytics Speed Estimation", im0)
if cv2.waitKey(1) & 0xFF == ord("q"):
return
return im0 self.trk_pt[track_id] = time()
self.trk_pp[track_id] = self.track_line[-1]
self.display_output(im0) # display output with base class function
if __name__ == "__main__": return im0 # return output image for more usage
names = {0: "person", 1: "car"} # example class names
speed_estimator = SpeedEstimator(names)

@ -523,10 +523,11 @@ def read_device_model() -> str:
Returns: Returns:
(str): Model file contents if read successfully or empty string otherwise. (str): Model file contents if read successfully or empty string otherwise.
""" """
with contextlib.suppress(Exception): try:
with open("/proc/device-tree/model") as f: with open("/proc/device-tree/model") as f:
return f.read() return f.read()
return "" except: # noqa E722
return ""
def is_ubuntu() -> bool: def is_ubuntu() -> bool:
@ -536,10 +537,11 @@ def is_ubuntu() -> bool:
Returns: Returns:
(bool): True if OS is Ubuntu, False otherwise. (bool): True if OS is Ubuntu, False otherwise.
""" """
with contextlib.suppress(FileNotFoundError): try:
with open("/etc/os-release") as f: with open("/etc/os-release") as f:
return "ID=ubuntu" in f.read() return "ID=ubuntu" in f.read()
return False except FileNotFoundError:
return False
def is_colab(): def is_colab():
@ -569,11 +571,7 @@ def is_jupyter():
Returns: Returns:
(bool): True if running inside a Jupyter Notebook, False otherwise. (bool): True if running inside a Jupyter Notebook, False otherwise.
""" """
with contextlib.suppress(Exception): return "get_ipython" in locals()
from IPython import get_ipython
return get_ipython() is not None
return False
def is_docker() -> bool: def is_docker() -> bool:
@ -583,10 +581,11 @@ def is_docker() -> bool:
Returns: Returns:
(bool): True if the script is running inside a Docker container, False otherwise. (bool): True if the script is running inside a Docker container, False otherwise.
""" """
with contextlib.suppress(Exception): try:
with open("/proc/self/cgroup") as f: with open("/proc/self/cgroup") as f:
return "docker" in f.read() return "docker" in f.read()
return False except: # noqa E722
return False
def is_raspberrypi() -> bool: def is_raspberrypi() -> bool:
@ -617,14 +616,15 @@ def is_online() -> bool:
Returns: Returns:
(bool): True if connection is successful, False otherwise. (bool): True if connection is successful, False otherwise.
""" """
with contextlib.suppress(Exception): try:
assert str(os.getenv("YOLO_OFFLINE", "")).lower() != "true" # check if ENV var YOLO_OFFLINE="True" assert str(os.getenv("YOLO_OFFLINE", "")).lower() != "true" # check if ENV var YOLO_OFFLINE="True"
import socket import socket
for dns in ("1.1.1.1", "8.8.8.8"): # check Cloudflare and Google DNS for dns in ("1.1.1.1", "8.8.8.8"): # check Cloudflare and Google DNS
socket.create_connection(address=(dns, 80), timeout=2.0).close() socket.create_connection(address=(dns, 80), timeout=2.0).close()
return True return True
return False except: # noqa E722
return False
def is_pip_package(filepath: str = __name__) -> bool: def is_pip_package(filepath: str = __name__) -> bool:
@ -711,9 +711,11 @@ def get_git_origin_url():
(str | None): The origin URL of the git repository or None if not git directory. (str | None): The origin URL of the git repository or None if not git directory.
""" """
if IS_GIT_DIR: if IS_GIT_DIR:
with contextlib.suppress(subprocess.CalledProcessError): try:
origin = subprocess.check_output(["git", "config", "--get", "remote.origin.url"]) origin = subprocess.check_output(["git", "config", "--get", "remote.origin.url"])
return origin.decode().strip() return origin.decode().strip()
except subprocess.CalledProcessError:
return None
def get_git_branch(): def get_git_branch():
@ -724,9 +726,11 @@ def get_git_branch():
(str | None): The current git branch name or None if not a git directory. (str | None): The current git branch name or None if not a git directory.
""" """
if IS_GIT_DIR: if IS_GIT_DIR:
with contextlib.suppress(subprocess.CalledProcessError): try:
origin = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"]) origin = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])
return origin.decode().strip() return origin.decode().strip()
except subprocess.CalledProcessError:
return None
def get_default_args(func): def get_default_args(func):
@ -751,9 +755,11 @@ def get_ubuntu_version():
(str): Ubuntu version or None if not an Ubuntu OS. (str): Ubuntu version or None if not an Ubuntu OS.
""" """
if is_ubuntu(): if is_ubuntu():
with contextlib.suppress(FileNotFoundError, AttributeError): try:
with open("/etc/os-release") as f: with open("/etc/os-release") as f:
return re.search(r'VERSION_ID="(\d+\.\d+)"', f.read())[1] return re.search(r'VERSION_ID="(\d+\.\d+)"', f.read())[1]
except (FileNotFoundError, AttributeError):
return None
def get_user_config_dir(sub_dir="Ultralytics"): def get_user_config_dir(sub_dir="Ultralytics"):

@ -1,6 +1,7 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license # Ultralytics YOLO 🚀, AGPL-3.0 license
"""Functions for estimating the best YOLO batch size to use a fraction of the available CUDA memory in PyTorch.""" """Functions for estimating the best YOLO batch size to use a fraction of the available CUDA memory in PyTorch."""
import os
from copy import deepcopy from copy import deepcopy
import numpy as np import numpy as np
@ -57,7 +58,7 @@ def autobatch(model, imgsz=640, fraction=0.60, batch_size=DEFAULT_CFG.batch):
# Inspect CUDA memory # Inspect CUDA memory
gb = 1 << 30 # bytes to GiB (1024 ** 3) gb = 1 << 30 # bytes to GiB (1024 ** 3)
d = str(device).upper() # 'CUDA:0' d = f"CUDA:{os.getenv('CUDA_VISIBLE_DEVICES', '0').strip()[0]}" # 'CUDA:0'
properties = torch.cuda.get_device_properties(device) # device properties properties = torch.cuda.get_device_properties(device) # device properties
t = properties.total_memory / gb # GiB total t = properties.total_memory / gb # GiB total
r = torch.cuda.memory_reserved(device) / gb # GiB reserved r = torch.cuda.memory_reserved(device) / gb # GiB reserved
@ -66,10 +67,10 @@ def autobatch(model, imgsz=640, fraction=0.60, batch_size=DEFAULT_CFG.batch):
LOGGER.info(f"{prefix}{d} ({properties.name}) {t:.2f}G total, {r:.2f}G reserved, {a:.2f}G allocated, {f:.2f}G free") LOGGER.info(f"{prefix}{d} ({properties.name}) {t:.2f}G total, {r:.2f}G reserved, {a:.2f}G allocated, {f:.2f}G free")
# Profile batch sizes # Profile batch sizes
batch_sizes = [1, 2, 4, 8, 16] batch_sizes = [1, 2, 4, 8, 16] if t < 16 else [1, 2, 4, 8, 16, 32, 64]
try: try:
img = [torch.empty(b, 3, imgsz, imgsz) for b in batch_sizes] img = [torch.empty(b, 3, imgsz, imgsz) for b in batch_sizes]
results = profile(img, model, n=3, device=device) results = profile(img, model, n=1, device=device)
# Fit a solution # Fit a solution
y = [x[2] for x in results if x] # memory [2] y = [x[2] for x in results if x] # memory [2]
@ -89,3 +90,5 @@ def autobatch(model, imgsz=640, fraction=0.60, batch_size=DEFAULT_CFG.batch):
except Exception as e: except Exception as e:
LOGGER.warning(f"{prefix}WARNING ⚠ error detected: {e}, using default batch-size {batch_size}.") LOGGER.warning(f"{prefix}WARNING ⚠ error detected: {e}, using default batch-size {batch_size}.")
return batch_size return batch_size
finally:
torch.cuda.empty_cache()

@ -68,9 +68,9 @@ def on_pretrain_routine_start(trainer):
PatchedMatplotlib.update_current_task(None) PatchedMatplotlib.update_current_task(None)
else: else:
task = Task.init( task = Task.init(
project_name=trainer.args.project or "YOLOv8", project_name=trainer.args.project or "Ultralytics",
task_name=trainer.args.name, task_name=trainer.args.name,
tags=["YOLOv8"], tags=["Ultralytics"],
output_uri=True, output_uri=True,
reuse_last_task_id=False, reuse_last_task_id=False,
auto_connect_frameworks={"pytorch": False, "matplotlib": False}, auto_connect_frameworks={"pytorch": False, "matplotlib": False},

@ -15,7 +15,7 @@ try:
# Ensures certain logging functions only run for supported tasks # Ensures certain logging functions only run for supported tasks
COMET_SUPPORTED_TASKS = ["detect"] COMET_SUPPORTED_TASKS = ["detect"]
# Names of plots created by YOLOv8 that are logged to Comet # Names of plots created by Ultralytics that are logged to Comet
EVALUATION_PLOT_NAMES = "F1_curve", "P_curve", "R_curve", "PR_curve", "confusion_matrix" EVALUATION_PLOT_NAMES = "F1_curve", "P_curve", "R_curve", "PR_curve", "confusion_matrix"
LABEL_PLOT_NAMES = "labels", "labels_correlogram" LABEL_PLOT_NAMES = "labels", "labels_correlogram"
@ -31,8 +31,8 @@ def _get_comet_mode():
def _get_comet_model_name(): def _get_comet_model_name():
"""Returns the model name for Comet from the environment variable 'COMET_MODEL_NAME' or defaults to 'YOLOv8'.""" """Returns the model name for Comet from the environment variable COMET_MODEL_NAME or defaults to 'Ultralytics'."""
return os.getenv("COMET_MODEL_NAME", "YOLOv8") return os.getenv("COMET_MODEL_NAME", "Ultralytics")
def _get_eval_batch_logging_interval(): def _get_eval_batch_logging_interval():
@ -110,7 +110,7 @@ def _fetch_trainer_metadata(trainer):
def _scale_bounding_box_to_original_image_shape(box, resized_image_shape, original_image_shape, ratio_pad): def _scale_bounding_box_to_original_image_shape(box, resized_image_shape, original_image_shape, ratio_pad):
""" """
YOLOv8 resizes images during training and the label values are normalized based on this resized shape. YOLO resizes images during training and the label values are normalized based on this resized shape.
This function rescales the bounding box labels to the original image shape. This function rescales the bounding box labels to the original image shape.
""" """

@ -71,7 +71,7 @@ def on_pretrain_routine_end(trainer):
mlflow.set_tracking_uri(uri) mlflow.set_tracking_uri(uri)
# Set experiment and run names # Set experiment and run names
experiment_name = os.environ.get("MLFLOW_EXPERIMENT_NAME") or trainer.args.project or "/Shared/YOLOv8" experiment_name = os.environ.get("MLFLOW_EXPERIMENT_NAME") or trainer.args.project or "/Shared/Ultralytics"
run_name = os.environ.get("MLFLOW_RUN") or trainer.args.name run_name = os.environ.get("MLFLOW_RUN") or trainer.args.name
mlflow.set_experiment(experiment_name) mlflow.set_experiment(experiment_name)

@ -52,7 +52,11 @@ def on_pretrain_routine_start(trainer):
"""Callback function called before the training routine starts.""" """Callback function called before the training routine starts."""
try: try:
global run global run
run = neptune.init_run(project=trainer.args.project or "YOLOv8", name=trainer.args.name, tags=["YOLOv8"]) run = neptune.init_run(
project=trainer.args.project or "Ultralytics",
name=trainer.args.name,
tags=["Ultralytics"],
)
run["Configuration/Hyperparameters"] = {k: "" if v is None else v for k, v in vars(trainer.args).items()} run["Configuration/Hyperparameters"] = {k: "" if v is None else v for k, v in vars(trainer.args).items()}
except Exception as e: except Exception as e:
LOGGER.warning(f"WARNING ⚠ NeptuneAI installed but not initialized correctly, not logging this run. {e}") LOGGER.warning(f"WARNING ⚠ NeptuneAI installed but not initialized correctly, not logging this run. {e}")

@ -1,6 +1,5 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license # Ultralytics YOLO 🚀, AGPL-3.0 license
import contextlib
from ultralytics.utils import LOGGER, SETTINGS, TESTS_RUNNING, colorstr from ultralytics.utils import LOGGER, SETTINGS, TESTS_RUNNING, colorstr
@ -45,26 +44,27 @@ def _log_tensorboard_graph(trainer):
warnings.simplefilter("ignore", category=torch.jit.TracerWarning) # suppress jit trace warning warnings.simplefilter("ignore", category=torch.jit.TracerWarning) # suppress jit trace warning
# Try simple method first (YOLO) # Try simple method first (YOLO)
with contextlib.suppress(Exception): try:
trainer.model.eval() # place in .eval() mode to avoid BatchNorm statistics changes trainer.model.eval() # place in .eval() mode to avoid BatchNorm statistics changes
WRITER.add_graph(torch.jit.trace(de_parallel(trainer.model), im, strict=False), []) WRITER.add_graph(torch.jit.trace(de_parallel(trainer.model), im, strict=False), [])
LOGGER.info(f"{PREFIX}model graph visualization added ✅") LOGGER.info(f"{PREFIX}model graph visualization added ✅")
return return
# Fallback to TorchScript export steps (RTDETR) except: # noqa E722
try: # Fallback to TorchScript export steps (RTDETR)
model = deepcopy(de_parallel(trainer.model)) try:
model.eval() model = deepcopy(de_parallel(trainer.model))
model = model.fuse(verbose=False) model.eval()
for m in model.modules(): model = model.fuse(verbose=False)
if hasattr(m, "export"): # Detect, RTDETRDecoder (Segment and Pose use Detect base class) for m in model.modules():
m.export = True if hasattr(m, "export"): # Detect, RTDETRDecoder (Segment and Pose use Detect base class)
m.format = "torchscript" m.export = True
model(im) # dry run m.format = "torchscript"
WRITER.add_graph(torch.jit.trace(model, im, strict=False), []) model(im) # dry run
LOGGER.info(f"{PREFIX}model graph visualization added ✅") WRITER.add_graph(torch.jit.trace(model, im, strict=False), [])
except Exception as e: LOGGER.info(f"{PREFIX}model graph visualization added ✅")
LOGGER.warning(f"{PREFIX}WARNING ⚠ TensorBoard graph visualization failure {e}") except Exception as e:
LOGGER.warning(f"{PREFIX}WARNING ⚠ TensorBoard graph visualization failure {e}")
def on_pretrain_routine_start(trainer): def on_pretrain_routine_start(trainer):

@ -109,7 +109,7 @@ def _log_plots(plots, step):
def on_pretrain_routine_start(trainer): def on_pretrain_routine_start(trainer):
"""Initiate and start project if module is present.""" """Initiate and start project if module is present."""
wb.run or wb.init(project=trainer.args.project or "YOLOv8", name=trainer.args.name, config=vars(trainer.args)) wb.run or wb.init(project=trainer.args.project or "Ultralytics", name=trainer.args.name, config=vars(trainer.args))
def on_fit_epoch_end(trainer): def on_fit_epoch_end(trainer):

@ -1,6 +1,5 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license # Ultralytics YOLO 🚀, AGPL-3.0 license
import contextlib
import glob import glob
import inspect import inspect
import math import math
@ -271,11 +270,13 @@ def check_latest_pypi_version(package_name="ultralytics"):
Returns: Returns:
(str): The latest version of the package. (str): The latest version of the package.
""" """
with contextlib.suppress(Exception): try:
requests.packages.urllib3.disable_warnings() # Disable the InsecureRequestWarning requests.packages.urllib3.disable_warnings() # Disable the InsecureRequestWarning
response = requests.get(f"https://pypi.org/pypi/{package_name}/json", timeout=3) response = requests.get(f"https://pypi.org/pypi/{package_name}/json", timeout=3)
if response.status_code == 200: if response.status_code == 200:
return response.json()["info"]["version"] return response.json()["info"]["version"]
except: # noqa E722
return None
def check_pip_update_available(): def check_pip_update_available():
@ -286,7 +287,7 @@ def check_pip_update_available():
(bool): True if an update is available, False otherwise. (bool): True if an update is available, False otherwise.
""" """
if ONLINE and IS_PIP_PACKAGE: if ONLINE and IS_PIP_PACKAGE:
with contextlib.suppress(Exception): try:
from ultralytics import __version__ from ultralytics import __version__
latest = check_latest_pypi_version() latest = check_latest_pypi_version()
@ -296,6 +297,8 @@ def check_pip_update_available():
f"Update with 'pip install -U ultralytics'" f"Update with 'pip install -U ultralytics'"
) )
return True return True
except: # noqa E722
pass
return False return False
@ -577,10 +580,12 @@ def check_yolo(verbose=True, device=""):
ram = psutil.virtual_memory().total ram = psutil.virtual_memory().total
total, used, free = shutil.disk_usage("/") total, used, free = shutil.disk_usage("/")
s = f"({os.cpu_count()} CPUs, {ram / gib:.1f} GB RAM, {(total - free) / gib:.1f}/{total / gib:.1f} GB disk)" s = f"({os.cpu_count()} CPUs, {ram / gib:.1f} GB RAM, {(total - free) / gib:.1f}/{total / gib:.1f} GB disk)"
with contextlib.suppress(Exception): # clear display if ipython is installed try:
from IPython import display from IPython import display
display.clear_output() display.clear_output() # clear display if notebook
except ImportError:
pass
else: else:
s = "" s = ""
@ -619,7 +624,7 @@ def collect_system_info():
for r in parse_requirements(package="ultralytics"): for r in parse_requirements(package="ultralytics"):
try: try:
current = metadata.version(r.name) current = metadata.version(r.name)
is_met = "" if check_version(current, str(r.specifier), hard=True) else "" is_met = "" if check_version(current, str(r.specifier), name=r.name, hard=True) else ""
except metadata.PackageNotFoundError: except metadata.PackageNotFoundError:
current = "(not installed)" current = "(not installed)"
is_met = "" is_met = ""
@ -707,9 +712,10 @@ def check_amp(model):
def git_describe(path=ROOT): # path must be a directory def git_describe(path=ROOT): # path must be a directory
"""Return human-readable git description, i.e. v5.0-5-g3e25f1e https://git-scm.com/docs/git-describe.""" """Return human-readable git description, i.e. v5.0-5-g3e25f1e https://git-scm.com/docs/git-describe."""
with contextlib.suppress(Exception): try:
return subprocess.check_output(f"git -C {path} describe --tags --long --always", shell=True).decode()[:-1] return subprocess.check_output(f"git -C {path} describe --tags --long --always", shell=True).decode()[:-1]
return "" except: # noqa E722
return ""
def print_args(args: Optional[dict] = None, show_file=True, show_func=False): def print_args(args: Optional[dict] = None, show_file=True, show_func=False):

@ -1,6 +1,5 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license # Ultralytics YOLO 🚀, AGPL-3.0 license
import contextlib
import re import re
import shutil import shutil
import subprocess import subprocess
@ -53,7 +52,7 @@ def is_url(url, check=False):
valid = is_url("https://www.example.com") valid = is_url("https://www.example.com")
``` ```
""" """
with contextlib.suppress(Exception): try:
url = str(url) url = str(url)
result = parse.urlparse(url) result = parse.urlparse(url)
assert all([result.scheme, result.netloc]) # check if is url assert all([result.scheme, result.netloc]) # check if is url
@ -61,7 +60,8 @@ def is_url(url, check=False):
with request.urlopen(url) as response: with request.urlopen(url) as response:
return response.getcode() == 200 # check if exists online return response.getcode() == 200 # check if exists online
return True return True
return False except: # noqa E722
return False
def delete_dsstore(path, files_to_delete=(".DS_Store", "__MACOSX")): def delete_dsstore(path, files_to_delete=(".DS_Store", "__MACOSX")):

@ -1,6 +1,5 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license # Ultralytics YOLO 🚀, AGPL-3.0 license
import contextlib
import math import math
import warnings import warnings
from pathlib import Path from pathlib import Path
@ -13,8 +12,8 @@ import torch
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw, ImageFont
from PIL import __version__ as pil_version from PIL import __version__ as pil_version
from ultralytics.utils import IS_JUPYTER, LOGGER, TryExcept, ops, plt_settings, threaded from ultralytics.utils import IS_COLAB, IS_KAGGLE, LOGGER, TryExcept, ops, plt_settings, threaded
from ultralytics.utils.checks import check_font, check_requirements, check_version, is_ascii from ultralytics.utils.checks import check_font, check_version, is_ascii
from ultralytics.utils.files import increment_path from ultralytics.utils.files import increment_path
@ -525,16 +524,12 @@ class Annotator:
def show(self, title=None): def show(self, title=None):
"""Show the annotated image.""" """Show the annotated image."""
im = Image.fromarray(np.asarray(self.im)[..., ::-1]) # Convert numpy array to PIL Image with RGB to BGR im = Image.fromarray(np.asarray(self.im)[..., ::-1]) # Convert numpy array to PIL Image with RGB to BGR
if IS_JUPYTER: if IS_COLAB or IS_KAGGLE: # can not use IS_JUPYTER as will run for all ipython environments
check_requirements("ipython")
try: try:
from IPython.display import display display(im) # noqa - display() function only available in ipython environments
display(im)
except ImportError as e: except ImportError as e:
LOGGER.warning(f"Unable to display image in Jupyter notebooks: {e}") LOGGER.warning(f"Unable to display image in Jupyter notebooks: {e}")
else: else:
# Convert numpy array to PIL Image and show
im.show(title=title) im.show(title=title)
def save(self, filename="image.jpg"): def save(self, filename="image.jpg"):
@ -1119,10 +1114,12 @@ def plot_images(
mask = mask.astype(bool) mask = mask.astype(bool)
else: else:
mask = image_masks[j].astype(bool) mask = image_masks[j].astype(bool)
with contextlib.suppress(Exception): try:
im[y : y + h, x : x + w, :][mask] = ( im[y : y + h, x : x + w, :][mask] = (
im[y : y + h, x : x + w, :][mask] * 0.4 + np.array(color) * 0.6 im[y : y + h, x : x + w, :][mask] * 0.4 + np.array(color) * 0.6
) )
except: # noqa E722
pass
annotator.fromarray(im) annotator.fromarray(im)
if not save: if not save:
return np.asarray(annotator.im) return np.asarray(annotator.im)

@ -1,6 +1,5 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license # Ultralytics YOLO 🚀, AGPL-3.0 license
import contextlib
import gc import gc
import math import math
import os import os
@ -113,13 +112,15 @@ def get_cpu_info():
from ultralytics.utils import PERSISTENT_CACHE # avoid circular import error from ultralytics.utils import PERSISTENT_CACHE # avoid circular import error
if "cpu_info" not in PERSISTENT_CACHE: if "cpu_info" not in PERSISTENT_CACHE:
with contextlib.suppress(Exception): try:
import cpuinfo # pip install py-cpuinfo import cpuinfo # pip install py-cpuinfo
k = "brand_raw", "hardware_raw", "arch_string_raw" # keys sorted by preference k = "brand_raw", "hardware_raw", "arch_string_raw" # keys sorted by preference
info = cpuinfo.get_cpu_info() # info dict info = cpuinfo.get_cpu_info() # info dict
string = info.get(k[0] if k[0] in info else k[1] if k[1] in info else k[2], "unknown") string = info.get(k[0] if k[0] in info else k[1] if k[1] in info else k[2], "unknown")
PERSISTENT_CACHE["cpu_info"] = string.replace("(R)", "").replace("CPU ", "").replace("@ ", "") PERSISTENT_CACHE["cpu_info"] = string.replace("(R)", "").replace("CPU ", "").replace("@ ", "")
except: # noqa E722
pass
return PERSISTENT_CACHE.get("cpu_info", "unknown") return PERSISTENT_CACHE.get("cpu_info", "unknown")
@ -643,7 +644,8 @@ def profile(input, ops, n=10, device=None):
f"{'Params':>12s}{'GFLOPs':>12s}{'GPU_mem (GB)':>14s}{'forward (ms)':>14s}{'backward (ms)':>14s}" f"{'Params':>12s}{'GFLOPs':>12s}{'GPU_mem (GB)':>14s}{'forward (ms)':>14s}{'backward (ms)':>14s}"
f"{'input':>24s}{'output':>24s}" f"{'input':>24s}{'output':>24s}"
) )
gc.collect() # attempt to free unused memory
torch.cuda.empty_cache()
for x in input if isinstance(input, list) else [input]: for x in input if isinstance(input, list) else [input]:
x = x.to(device) x = x.to(device)
x.requires_grad = True x.requires_grad = True
@ -677,8 +679,9 @@ def profile(input, ops, n=10, device=None):
except Exception as e: except Exception as e:
LOGGER.info(e) LOGGER.info(e)
results.append(None) results.append(None)
gc.collect() # attempt to free unused memory finally:
torch.cuda.empty_cache() gc.collect() # attempt to free unused memory
torch.cuda.empty_cache()
return results return results

Loading…
Cancel
Save