Add real-world projects in Ultralytics + `guides` in Docs (#6695)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Glenn Jocher <glenn.jocher@ultralytics.com>
pull/6524/head
Muhammad Rizwan Munawar 1 year ago committed by GitHub
parent 9618025416
commit 8c4094e7d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      docs/en/guides/index.md
  2. 38
      docs/en/guides/isolating-segmentation-objects.md
  3. 62
      docs/en/guides/object-counting.md
  4. 86
      docs/en/guides/region-counting.md
  5. 166
      docs/en/guides/security-alarm-system.md
  6. 65
      docs/en/guides/workouts-monitoring.md
  7. 16
      docs/en/reference/solutions/ai_gym.md
  8. 16
      docs/en/reference/solutions/object_counter.md
  9. 8
      docs/mkdocs.yml
  10. 130
      ultralytics/solutions/ai_gym.py
  11. 165
      ultralytics/solutions/object_counter.py
  12. 8
      ultralytics/utils/downloads.py
  13. 125
      ultralytics/utils/plotting.py

@ -28,6 +28,13 @@ Here's a compilation of in-depth guides to help you master different aspects of
* [YOLO Thread-Safe Inference](yolo-thread-safe-inference.md) 🚀 NEW: Guidelines for performing inference with YOLO models in a thread-safe manner. Learn the importance of thread safety and best practices to prevent race conditions and ensure consistent predictions.
* [Isolating Segmentation Objects](isolating-segmentation-objects.md) 🚀 NEW: Step-by-step recipe and explanation on how to extract and/or isolate objects from images using Ultralytics Segmentation.
## Real-World Projects
* [Object Counting](object-counting.md) 🚀 NEW: Explore the process of real-time object counting with Ultralytics YOLOv8 and acquire the knowledge to effectively count objects in a live video stream.
* [Workouts Monitoring](workouts-monitoring.md) 🚀 NEW: Discover the comprehensive approach to monitoring workouts with Ultralytics YOLOv8. Acquire the skills and insights necessary to effectively use YOLOv8 for tracking and analyzing various aspects of fitness routines in real time.
* [Objects Counting in Regions](region-counting.md) 🚀 NEW: Explore counting objects in specific regions with Ultralytics YOLOv8 for precise and efficient object detection in varied areas.
* [Security Alarm System](security-alarm-system.md) 🚀 NEW: Discover the process of creating a security alarm system with Ultralytics YOLOv8. This system triggers alerts upon detecting new objects in the frame. Subsequently, you can customize the code to align with your specific use case.
## Contribute to Our Guides
We welcome contributions from the community! If you've mastered a particular aspect of Ultralytics YOLO that's not yet covered in our guides, we encourage you to share your expertise. Writing a guide is a great way to give back to the community and help us make our documentation more comprehensive and user-friendly.

@ -24,7 +24,7 @@ After performing the [Segment Task](../tasks/segment.md), it's sometimes desirab
from ultralytics import YOLO
```
???+ tip "Ultralytics Install"
???+ tip "Ultralytics Install"
See the Ultralytics [Quickstart](../quickstart.md/#install-ultralytics) Installation section for a quick walkthrough on installing the required libraries.
@ -37,7 +37,7 @@ After performing the [Segment Task](../tasks/segment.md), it's sometimes desirab
res = m.predict()
```
??? question "No Prediction Arguments?"
??? question "No Prediction Arguments?"
Without specifying a source, the example images from the library will be used:
@ -48,7 +48,7 @@ After performing the [Segment Task](../tasks/segment.md), it's sometimes desirab
This is helpful for rapid testing with the `predict()` method.
For additional information about Segmentation Models, visit the [Segment Task](../tasks/segment.md/#models) page. To learn more about `predict()` method, see [Predict Mode](../modes/predict.md) section of the Documentation.
For additional information about Segmentation Models, visit the [Segment Task](../tasks/segment.md/#models) page. To learn more about `predict()` method, see [Predict Mode](../modes/predict.md) section of the Documentation.
---
@ -67,10 +67,10 @@ After performing the [Segment Task](../tasks/segment.md), it's sometimes desirab
```
1. To learn more about working with detection results, see [Boxes Section for Predict Mode](../modes/predict.md/#boxes).
2. To learn more about `predict()` results see [Working with Results for Predict Mode](../modes/predict.md/#working-with-results)
1. To learn more about working with detection results, see [Boxes Section for Predict Mode](../modes/predict.md/#boxes).
2. To learn more about `predict()` results see [Working with Results for Predict Mode](../modes/predict.md/#working-with-results)
??? info "For-Loop"
??? info "For-Loop"
A single image will only iterate the first loop once. A single image with only a single detection will iterate each loop _only_ once.
@ -78,7 +78,7 @@ After performing the [Segment Task](../tasks/segment.md), it's sometimes desirab
1. Start with generating a binary mask from the source image and then draw a filled contour onto the mask. This will allow the object to be isolated from the other parts of the image. An example from `bus.jpg` for one of the detected `person` class objects is shown on the right.
![Binary Mask Image](https://github.com/ultralytics/ultralytics/assets/62214284/59bce684-fdda-4b17-8104-0b4b51149aca){ width="240", align="right" }
![Binary Mask Image](https://github.com/ultralytics/ultralytics/assets/62214284/59bce684-fdda-4b17-8104-0b4b51149aca){ width="240", align="right" }
``` { .py .annotate }
# Create binary mask
b_mask = np.zeros(img.shape[:2], np.uint8)
@ -100,11 +100,11 @@ After performing the [Segment Task](../tasks/segment.md), it's sometimes desirab
```
1. For more info on `c.masks.xy` see [Masks Section from Predict Mode](../modes/predict.md/#masks).
1. For more info on `c.masks.xy` see [Masks Section from Predict Mode](../modes/predict.md/#masks).
2. Here, the values are cast into `np.int32` for compatibility with `drawContours()` function from OpenCV.
2. Here, the values are cast into `np.int32` for compatibility with `drawContours()` function from OpenCV.
3. The OpenCV `drawContours()` function expects contours to have a shape of `[N, 1, 2]` expand section below for more details.
3. The OpenCV `drawContours()` function expects contours to have a shape of `[N, 1, 2]` expand section below for more details.
<details>
<summary> Expand to understand what is happening when defining the <code>contour</code> variable.</summary>
@ -141,7 +141,7 @@ After performing the [Segment Task](../tasks/segment.md), it's sometimes desirab
1. Next the there are 2 options for how to move forward with the image from this point and a subsequent option for each.
### Object Isolation Options
### Object Isolation Options
!!! Example ""
@ -246,7 +246,7 @@ After performing the [Segment Task](../tasks/segment.md), it's sometimes desirab
- Finally the image region for the bounding box is cropped using index slicing, where the bounds are set using the `[ymin:ymax, xmin:xmax]` coordinates of the detection bounding box.
??? question "What if I want the cropped object **including** the background?"
??? question "What if I want the cropped object **including** the background?"
This is a built in feature for the Ultralytics library. See the `save_crop` argument for [Predict Mode Inference Arguments](../modes/predict.md/#inference-arguments) for details.
@ -256,7 +256,7 @@ After performing the [Segment Task](../tasks/segment.md), it's sometimes desirab
- **NOTE:** this step is optional and can be skipped if not required for your specific use case.
??? example "Example Final Step"
??? example "Example Final Step"
```py
# Save isolated object to file
@ -311,9 +311,9 @@ for r in res:
```
1. The line populating `contour` is combined into a single line here, where it was split to multiple above.
2. {==What goes here is up to you!==}
3. See [Predict Mode](../modes/predict.md) for additional information.
4. See [Segment Task](../tasks/segment.md/#models) for more information.
5. Learn more about [Working with Results](../modes/predict.md/#working-with-results)
6. Learn more about [Segmentation Mask Results](../modes/predict.md/#masks)
1. The line populating `contour` is combined into a single line here, where it was split to multiple above.
2. {==What goes here is up to you!==}
3. See [Predict Mode](../modes/predict.md) for additional information.
4. See [Segment Task](../tasks/segment.md/#models) for more information.
5. Learn more about [Working with Results](../modes/predict.md/#working-with-results)
6. Learn more about [Segmentation Mask Results](../modes/predict.md/#masks)

@ -0,0 +1,62 @@
---
comments: true
description: Object Counting Using Ultralytics YOLOv8
keywords: Ultralytics, YOLOv8, Object Detection, Object Counting, Object Tracking, Notebook, IPython Kernel, CLI, Python SDK
---
# Object Counting using Ultralytics YOLOv8 🚀
## What is Object Counting?
Object counting with [Ultralytics YOLOv8](https://github.com/ultralytics/ultralytics/) involves accurate identification and counting of specific objects in videos and camera streams. YOLOv8 excels in real-time applications, providing efficient and precise object counting for various scenarios like crowd analysis and surveillance, thanks to its state-of-the-art algorithms and deep learning capabilities.
## Advantages of Object Counting?
- **Resource Optimization:** Object counting facilitates efficient resource management by providing accurate counts, and optimizing resource allocation in applications like inventory management.
- **Enhanced Security:** Object counting enhances security and surveillance by accurately tracking and counting entities, aiding in proactive threat detection.
- **Informed Decision-Making:** Object counting offers valuable insights for decision-making, optimizing processes in retail, traffic management, and various other domains.
## Real World Applications
| Logistics | Aquaculture |
|:-------------------------------------------------------------------------------------------------------------------------------------------------------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------:|
| ![Conveyor Belt Packets Counting Using Ultralytics YOLOv8](https://github.com/RizwanMunawar/ultralytics/assets/62513924/70e2d106-510c-4c6c-a57a-d34a765aa757) | ![Fish Counting in Sea using Ultralytics YOLOv8](https://github.com/RizwanMunawar/ultralytics/assets/62513924/c60d047b-3837-435f-8d29-bb9fc95d2191) |
| Conveyor Belt Packets Counting Using Ultralytics YOLOv8 | Fish Counting in Sea using Ultralytics YOLOv8 |
## Example
```python
from ultralytics import YOLO
from ultralytics.solutions import object_counter
import cv2
model = YOLO("yolov8n.pt")
cap = cv2.VideoCapture("path/to/video/file.mp4")
counter = object_counter.ObjectCounter() # Init Object Counter
region_points = [(20, 400), (1080, 404), (1080, 360), (20, 360)]
counter.set_args(view_img=True, reg_pts=region_points,
classes_names=model.model.names, draw_tracks=True)
while cap.isOpened():
success, frame = cap.read()
if not success:
exit(0)
tracks = model.track(frame, persist=True, show=False)
counter.start_counting(frame, tracks)
```
???+ tip "Region is Moveable"
You can move the region anywhere in the frame by clicking on its edges
### Optional Arguments `set_args`
| Name | Type | Default | Description |
|-----------------|---------|--------------------------------------------------|---------------------------------------|
| view_img | `bool` | `False` | Display the frame with counts |
| line_thickness | `int` | `2` | Increase the thickness of count value |
| reg_pts | `list` | `(20, 400), (1080, 404), (1080, 360), (20, 360)` | Region Area Points |
| classes_names | `dict` | `model.model.names` | Classes Names Dict |
| region_color | `tuple` | `(0, 255, 0)` | Region Area Color |
| track_thickness | `int` | `2` | Tracking line thickness |

@ -0,0 +1,86 @@
---
comments: true
description: Object Counting in Different Region using Ultralytics YOLOv8
keywords: Ultralytics, YOLOv8, Object Detection, Object Counting, Object Tracking, Notebook, IPython Kernel, CLI, Python SDK
---
# Object Counting in Different Regions using Ultralytics YOLOv8 🚀
## What is Object Counting in Regions?
Object counting in regions with [Ultralytics YOLOv8](https://github.com/ultralytics/ultralytics/) involves precisely determining the number of objects within specified areas using advanced computer vision. This approach is valuable for optimizing processes, enhancing security, and improving efficiency in various applications.
<p align="center">
<br>
<iframe width="720" height="405" src="https://www.youtube.com/embed/okItf1iHlV8"
title="YouTube video player" frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen>
</iframe>
<br>
<strong>Watch:</strong> Ultralytics YOLOv8 Object Counting in Multiple & Moveable Regions
</p>
## Advantages of Object Counting in Regions?
- **Precision and Accuracy:** Object counting in regions with advanced computer vision ensures precise and accurate counts, minimizing errors often associated with manual counting.
- **Efficiency Improvement:** Automated object counting enhances operational efficiency, providing real-time results and streamlining processes across different applications.
- **Versatility and Application:** The versatility of object counting in regions makes it applicable across various domains, from manufacturing and surveillance to traffic monitoring, contributing to its widespread utility and effectiveness.
## Real World Applications
| Retail | Market Streets |
|:------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------:|
| ![People Counting in Different Region using Ultralytics YOLOv8](https://github.com/RizwanMunawar/ultralytics/assets/62513924/5ab3bbd7-fd12-4849-928e-5f294d6c3fcf) | ![Crowd Counting in Different Region using Ultralytics YOLOv8](https://github.com/RizwanMunawar/ultralytics/assets/62513924/e7c1aea7-474d-4d78-8d48-b50854ffe1ca) |
| People Counting in Different Region using Ultralytics YOLOv8 | Crowd Counting in Different Region using Ultralytics YOLOv8 |
## Steps to Run
### Step 1: Install Required Libraries
Begin by cloning the Ultralytics repository, installing dependencies, and navigating to the local directory using the provided commands in Step 2.
```bash
# Clone Ultralytics repo
git clone https://github.com/ultralytics/ultralytics
# Navigate to the local directory
cd ultralytics/examples/YOLOv8-Region-Counter
```
### Step 2: Run Region Counting Using Ultralytics YOLOv8
Execute the following basic commands for inference.
???+ tip "Region is Moveable"
During video playback, you can interactively move the region within the video by clicking and dragging using the left mouse button.
```bash
# Save results
python yolov8_region_counter.py --source "path/to/video.mp4" --save-img
# Run model on CPU
python yolov8_region_counter.py --source "path/to/video.mp4" --device cpu
# Change model file
python yolov8_region_counter.py --source "path/to/video.mp4" --weights "path/to/model.pt"
# Detect specific classes (e.g., first and third classes)
python yolov8_region_counter.py --source "path/to/video.mp4" --classes 0 2
# View results without saving
python yolov8_region_counter.py --source "path/to/video.mp4" --view-img
```
### Optional Arguments
| Name | Type | Default | Description |
|----------------------|--------|--------------|-------------------------------------------|
| `--source` | `str` | `None` | Path to video file, for webcam 0 |
| `--line_thickness` | `int` | `2` | Bounding Box thickness |
| `--save-img` | `bool` | `False` | Save the predicted video/image |
| `--weights` | `str` | `yolov8n.pt` | Weights file path |
| `--classes` | `list` | `None` | Detect specific classes i.e --classes 0 2 |
| `--region-thickness` | `int` | `2` | Region Box thickness |
| `--track-thickness` | `int` | `2` | Tracking line thickness |

@ -0,0 +1,166 @@
---
comments: true
description: Security Alarm System Project Using Ultralytics YOLOv8. Learn How to implement a Security Alarm System Using ultralytics YOLOv8
keywords: Object Detection, Security Alarm, Object Tracking, YOLOv8, Computer Vision Projects
---
# Security Alarm System Project Using Ultralytics YOLOv8
<img src="https://github.com/RizwanMunawar/ultralytics/assets/62513924/f4e4a613-fb25-4bd0-9ec5-78352ddb62bd" alt="Security Alarm System">
The Security Alarm System Project utilizing Ultralytics YOLOv8 integrates advanced computer vision capabilities to enhance security measures. YOLOv8, developed by Ultralytics, provides real-time object detection, allowing the system to identify and respond to potential security threats promptly. This project offers several advantages:
- **Real-time Detection:** YOLOv8's efficiency enables the Security Alarm System to detect and respond to security incidents in real-time, minimizing response time.
- **Accuracy:** YOLOv8 is known for its accuracy in object detection, reducing false positives and enhancing the reliability of the security alarm system.
- **Integration Capabilities:** The project can be seamlessly integrated with existing security infrastructure, providing an upgraded layer of intelligent surveillance.
<p align="center">
<br>
<iframe width="720" height="405" src="https://www.youtube.com/embed/_1CmwUzoxY4"
title="YouTube video player" frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen>
</iframe>
<br>
<strong>Watch:</strong> Security Alarm System Project with Ultralytics YOLOv8 Object Detection
</p>
### Code
#### Import Libraries
```python
import torch
import numpy as np
import cv2
from time import time
from ultralytics import YOLO
from ultralytics.utils.plotting import Annotator, colors
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
```
#### Set up the parameters of the message
???+ tip "Note"
App Password Generation is necessary
- Navigate to [App Password Generator](https://myaccount.google.com/apppasswords), designate an app name such as "security project," and obtain a 16-digit password. Copy this password and paste it into the designated password field as instructed.
```python
password = ""
from_email = "" # must match the email used to generate the password
to_email = "" # receiver email
```
#### Server creation and authentication
```python
server = smtplib.SMTP('smtp.gmail.com: 587')
server.starttls()
server.login(from_email, password)
```
#### Email Send Function
```python
def send_email(to_email, from_email, object_detected=1):
message = MIMEMultipart()
message['From'] = from_email
message['To'] = to_email
message['Subject'] = "Security Alert"
# Add in the message body
message_body = f'ALERT - {object_detected} objects has been detected!!'
message.attach(MIMEText(message_body, 'plain'))
server.sendmail(from_email, to_email, message.as_string())
```
#### Object Detection and Alert Sender
```python
class ObjectDetection:
def __init__(self, capture_index):
# default parameters
self.capture_index = capture_index
self.email_sent = False
# model information
self.model = YOLO("yolov8n.pt")
# visual information
self.annotator = None
self.start_time = 0
self.end_time = 0
# device information
self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
def predict(self, im0):
results = self.model(im0)
return results
def display_fps(self, im0):
self.end_time = time()
fps = 1 / np.round(self.end_time - self.start_time, 2)
text = f'FPS: {int(fps)}'
text_size = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 1.0, 2)[0]
gap = 10
cv2.rectangle(im0, (20 - gap, 70 - text_size[1] - gap), (20 + text_size[0] + gap, 70 + gap), (255, 255, 255),-1)
cv2.putText(im0, text, (20, 70), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 0), 2)
def plot_bboxes(self, results, im0):
class_ids = []
self.annotator = Annotator(im0, 3, results[0].names)
boxes = results[0].boxes.xyxy.cpu()
clss = results[0].boxes.cls.cpu().tolist()
names = results[0].names
for box, cls in zip(boxes, clss):
class_ids.append(cls)
self.annotator.box_label(box, label=names[int(cls)], color=colors(int(cls), True))
return im0, class_ids
def __call__(self):
cap = cv2.VideoCapture(self.capture_index)
assert cap.isOpened()
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
frame_count = 0
while True:
self.start_time = time()
ret, im0 = cap.read()
assert ret
results = self.predict(im0)
im0, class_ids = self.plot_bboxes(results, im0)
if len(class_ids) > 0: # Only send email If not sent before
if not self.email_sent:
send_email(to_email, from_email, len(class_ids))
self.email_sent = True
else:
self.email_sent = False
self.display_fps(im0)
cv2.imshow('YOLOv8 Detection', im0)
frame_count += 1
if cv2.waitKey(5) & 0xFF == 27:
break
cap.release()
cv2.destroyAllWindows()
server.quit()
```
#### Call the Object Detection class and Run the Inference
```python
detector = ObjectDetection(capture_index=0)
detector()
```
That's it! When you execute the code, you'll receive a single notification on your email if any object is detected. The notification is sent immediately, not repeatedly. However, feel free to customize the code to suit your project requirements.
#### Email Received Sample
<img width="256" src="https://github.com/RizwanMunawar/ultralytics/assets/62513924/db79ccc6-aabd-4566-a825-b34e679c90f9" alt="Email Received Sample">

@ -0,0 +1,65 @@
---
comments: true
description: Workouts Monitoring Using Ultralytics YOLOv8
keywords: Ultralytics, YOLOv8, Object Detection, Pose Estimation, PushUps, PullUps, Ab workouts, Notebook, IPython Kernel, CLI, Python SDK
---
# Workouts Monitoring using Ultralytics YOLOv8 🚀
Monitoring workouts through pose estimation with [Ultralytics YOLOv8](https://github.com/ultralytics/ultralytics/) enhances exercise assessment by accurately tracking key body landmarks and joints in real-time. This technology provides instant feedback on exercise form, tracks workout routines, and measures performance metrics, optimizing training sessions for users and trainers alike.
## Advantages of Workouts Monitoring?
- **Optimized Performance:** Tailoring workouts based on monitoring data for better results.
- **Goal Achievement:** Track and adjust fitness goals for measurable progress.
- **Personalization:** Customized workout plans based on individual data for effectiveness.
- **Health Awareness:** Early detection of patterns indicating health issues or overtraining.
- **Informed Decisions:** Data-driven decisions for adjusting routines and setting realistic goals.
## Real World Applications
| Workouts Monitoring | Workouts Monitoring |
|:----------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------:|
| ![PushUps Counting](https://github.com/RizwanMunawar/ultralytics/assets/62513924/cf016a41-589f-420f-8a8c-2cc8174a16de) | ![PullUps Counting](https://github.com/RizwanMunawar/ultralytics/assets/62513924/cb20f316-fac2-4330-8445-dcf5ffebe329) |
| PushUps Counting | PullUps Counting |
## Example
```python
from ultralytics import YOLO
from ultralytics.solutions import ai_gym
import cv2
model = YOLO("yolov8n-pose.pt")
cap = cv2.VideoCapture("path/to/video.mp4")
gym_object = ai_gym.AIGym() # init AI GYM module
gym_object.set_args(line_thickness=2, view_img=True, pose_type="pushup", kpts_to_check=[6, 8, 10])
frame_count = 0
while cap.isOpened():
success, frame = cap.read()
if not success: exit(0)
frame_count += 1
results = model.predict(frame, verbose=False)
gym_object.start_counting(frame, results, frame_count)
```
???+ tip "Support"
"pushup", "pullup" and "abworkout" supported
### KeyPoints Map
![keyPoints Order Ultralytics YOLOv8 Pose](https://github.com/RizwanMunawar/ultralytics/assets/62513924/520059af-f961-433b-b2fb-7fe8c4336ee5)
### Arguments `set_args`
| Name | Type | Default | Description |
|-----------------|--------|----------|----------------------------------------------------------------------------------------|
| kpts_to_check | `list` | `None` | List of three keypoints index, for counting specific workout, followed by keypoint Map |
| view_img | `bool` | `False` | Display the frame with counts |
| line_thickness | `int` | `2` | Increase the thickness of count value |
| pose_type | `str` | `pushup` | Pose that need to be monitored, "pullup" and "abworkout" also supported |
| pose_up_angle | `int` | `145` | Pose Up Angle value |
| pose_down_angle | `int` | `90` | Pose Down Angle value |

@ -0,0 +1,16 @@
---
description: Explore Ultralytics YOLO's advanced AI Gym feature for real-time pose estimation and gym exercise tracking using cutting-edge machine learning technology.
keywords: Ultralytics, YOLO, AI Gym, pose estimation, real-time tracking, machine learning, exercise counting, AI fitness, computer vision, gym workout analysis, YOLOv8, artificial intelligence, fitness technology
---
# Reference for `ultralytics/solutions/ai_gym.py`
!!! Note
This file is available at [https://github.com/ultralytics/ultralytics/blob/main/ultralytics/solutions/ai_gym.py](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/solutions/ai_gym.py). If you spot a problem please help fix it by [contributing](https://docs.ultralytics.com/help/contributing/) a [Pull Request](https://github.com/ultralytics/ultralytics/edit/main/ultralytics/solutions/ai_gym.py) 🛠. Thank you 🙏!
<br><br>
## ::: ultralytics.solutions.ai_gym.AIGym
<br><br>

@ -0,0 +1,16 @@
---
description: Transform object tracking with Ultralytics YOLO Object Counter featuring cutting-edge technology for precise real-time counting in video streams.
keywords: Ultralytics YOLO, object tracking software, real-time counting solutions, video stream analysis, YOLOv8 object detection, AI surveillance, smart counting technology, computer vision, AI-powered tracking, object counting accuracy, video analytics tools, automated monitoring.
---
# Reference for `ultralytics/solutions/object_counter.py`
!!! Note
This file is available at [https://github.com/ultralytics/ultralytics/blob/main/ultralytics/solutions/object_counter.py](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/solutions/object_counter.py). If you spot a problem please help fix it by [contributing](https://docs.ultralytics.com/help/contributing/) a [Pull Request](https://github.com/ultralytics/ultralytics/edit/main/ultralytics/solutions/object_counter.py) 🛠. Thank you 🙏!
<br><br>
## ::: ultralytics.solutions.object_counter.ObjectCounter
<br><br>

@ -265,6 +265,11 @@ nav:
- Raspberry Pi: guides/raspberry-pi.md
- Triton Inference Server: guides/triton-inference-server.md
- Isolating Segmentation Objects: guides/isolating-segmentation-objects.md
- Real-World Projects:
- Object Counting: guides/object-counting.md
- Workouts Monitoring: guides/workouts-monitoring.md
- Objects Counting in Regions: guides/region-counting.md
- Security Alarm System: guides/security-alarm-system.md
- Integrations:
- integrations/index.md
- Comet ML: integrations/comet.md
@ -399,6 +404,9 @@ nav:
- transformer: reference/nn/modules/transformer.md
- utils: reference/nn/modules/utils.md
- tasks: reference/nn/tasks.md
- solutions:
- ai_gym: reference/solutions/ai_gym.md
- object_counter: reference/solutions/object_counter.md
- trackers:
- basetrack: reference/trackers/basetrack.md
- bot_sort: reference/trackers/bot_sort.md

@ -0,0 +1,130 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license
import cv2
from ultralytics.utils.plotting import Annotator
class AIGym:
"""A class to manage the gym steps of people in a real-time video stream based on their poses."""
def __init__(self):
"""Initializes the AIGym with default values for Visual and Image parameters."""
# Image and line thickness
self.im0 = None
self.tf = None
# Keypoints and count information
self.keypoints = None
self.poseup_angle = None
self.posedown_angle = None
self.threshold = 0.001
# Store stage, count and angle information
self.angle = None
self.count = None
self.stage = None
self.pose_type = 'pushup'
self.kpts_to_check = None
# Visual Information
self.view_img = False
self.annotator = None
def set_args(self,
kpts_to_check,
line_thickness=2,
view_img=False,
pose_up_angle=145.0,
pose_down_angle=90.0,
pose_type='pullup'):
"""
Configures the AIGym line_thickness, save image and view image parameters
Args:
kpts_to_check (list): 3 keypoints for counting
line_thickness (int): Line thickness for bounding boxes.
view_img (bool): display the im0
pose_up_angle (float): Angle to set pose position up
pose_down_angle (float): Angle to set pose position down
pose_type: "pushup", "pullup" or "abworkout"
"""
self.kpts_to_check = kpts_to_check
self.tf = line_thickness
self.view_img = view_img
self.poseup_angle = pose_up_angle
self.posedown_angle = pose_down_angle
self.pose_type = pose_type
def start_counting(self, im0, results, frame_count):
"""
function used to count the gym steps
Args:
im0 (ndarray): Current frame from the video stream.
results: Pose estimation data
frame_count: store current frame count
"""
self.im0 = im0
if frame_count == 1:
self.count = [0] * len(results[0])
self.angle = [0] * len(results[0])
self.stage = ['-' for _ in results[0]]
self.keypoints = results[0].keypoints.data
self.annotator = Annotator(im0, line_width=2)
for ind, k in enumerate(reversed(self.keypoints)):
if self.pose_type == 'pushup' or self.pose_type == 'pullup':
self.angle[ind] = self.annotator.estimate_pose_angle(k[int(self.kpts_to_check[0])].cpu(),
k[int(self.kpts_to_check[1])].cpu(),
k[int(self.kpts_to_check[2])].cpu())
self.im0 = self.annotator.draw_specific_points(k, self.kpts_to_check, shape=(640, 640), radius=10)
if self.pose_type == 'abworkout':
self.angle[ind] = self.annotator.estimate_pose_angle(k[int(self.kpts_to_check[0])].cpu(),
k[int(self.kpts_to_check[1])].cpu(),
k[int(self.kpts_to_check[2])].cpu())
self.im0 = self.annotator.draw_specific_points(k, self.kpts_to_check, shape=(640, 640), radius=10)
if self.angle[ind] > self.poseup_angle:
self.stage[ind] = 'down'
if self.angle[ind] < self.posedown_angle and self.stage[ind] == 'down':
self.stage[ind] = 'up'
self.count[ind] += 1
self.annotator.plot_angle_and_count_and_stage(angle_text=self.angle[ind],
count_text=self.count[ind],
stage_text=self.stage[ind],
center_kpt=k[int(self.kpts_to_check[1])],
line_thickness=self.tf)
if self.pose_type == 'pushup':
if self.angle[ind] > self.poseup_angle:
self.stage[ind] = 'up'
if self.angle[ind] < self.posedown_angle and self.stage[ind] == 'up':
self.stage[ind] = 'down'
self.count[ind] += 1
self.annotator.plot_angle_and_count_and_stage(angle_text=self.angle[ind],
count_text=self.count[ind],
stage_text=self.stage[ind],
center_kpt=k[int(self.kpts_to_check[1])],
line_thickness=self.tf)
if self.pose_type == 'pullup':
if self.angle[ind] > self.poseup_angle:
self.stage[ind] = 'down'
if self.angle[ind] < self.posedown_angle and self.stage[ind] == 'down':
self.stage[ind] = 'up'
self.count[ind] += 1
self.annotator.plot_angle_and_count_and_stage(angle_text=self.angle[ind],
count_text=self.count[ind],
stage_text=self.stage[ind],
center_kpt=k[int(self.kpts_to_check[1])],
line_thickness=self.tf)
self.annotator.kpts(k, shape=(640, 640), radius=1, kpt_line=True)
if self.view_img:
cv2.imshow('Ultralytics YOLOv8 AI GYM', self.im0)
if cv2.waitKey(1) & 0xFF == ord('q'):
return
if __name__ == '__main__':
AIGym()

@ -0,0 +1,165 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license
from collections import defaultdict
import cv2
from ultralytics.utils.checks import check_requirements
from ultralytics.utils.plotting import Annotator, colors
check_requirements('shapely>=2.0.0')
from shapely.geometry import Polygon
from shapely.geometry.point import Point
class ObjectCounter:
"""A class to manage the counting of objects in a real-time video stream based on their tracks."""
def __init__(self):
"""Initializes the Counter with default values for various tracking and counting parameters."""
# Mouse events
self.is_drawing = False
self.selected_point = None
# Region Information
self.reg_pts = None
self.counting_region = None
self.region_color = (255, 255, 255)
# Image and annotation Information
self.im0 = None
self.tf = None
self.view_img = False
self.names = None # Classes names
self.annotator = None # Annotator
# Object counting Information
self.in_counts = 0
self.out_counts = 0
self.counting_list = []
# Tracks info
self.track_history = defaultdict(list)
self.track_thickness = 2
self.draw_tracks = False
def set_args(self,
classes_names,
reg_pts,
region_color=None,
line_thickness=2,
track_thickness=2,
view_img=False,
draw_tracks=False):
"""
Configures the Counter's image, bounding box line thickness, and counting region points.
Args:
line_thickness (int): Line thickness for bounding boxes.
view_img (bool): Flag to control whether to display the video stream.
reg_pts (list): Initial list of points defining the counting region.
classes_names (dict): Classes names
region_color (tuple): color for region line
track_thickness (int): Track thickness
draw_tracks (Bool): draw tracks
"""
self.tf = line_thickness
self.view_img = view_img
self.track_thickness = track_thickness
self.draw_tracks = draw_tracks
self.reg_pts = reg_pts
self.counting_region = Polygon(self.reg_pts)
self.names = classes_names
self.region_color = region_color if region_color else self.region_color
def mouse_event_for_region(self, event, x, y, flags, params):
"""
This function is designed to move region with mouse events in a real-time video stream.
Args:
event (int): The type of mouse event (e.g., cv2.EVENT_MOUSEMOVE, cv2.EVENT_LBUTTONDOWN, etc.).
x (int): The x-coordinate of the mouse pointer.
y (int): The y-coordinate of the mouse pointer.
flags (int): Any flags associated with the event (e.g., cv2.EVENT_FLAG_CTRLKEY,
cv2.EVENT_FLAG_SHIFTKEY, etc.).
params (dict): Additional parameters you may want to pass to the function.
"""
# global is_drawing, selected_point
if event == cv2.EVENT_LBUTTONDOWN:
for i, point in enumerate(self.reg_pts):
if isinstance(point, (tuple, list)) and len(point) >= 2:
if abs(x - point[0]) < 10 and abs(y - point[1]) < 10:
self.selected_point = i
self.is_drawing = True
break
elif event == cv2.EVENT_MOUSEMOVE:
if self.is_drawing and self.selected_point is not None:
self.reg_pts[self.selected_point] = (x, y)
self.counting_region = Polygon(self.reg_pts)
elif event == cv2.EVENT_LBUTTONUP:
self.is_drawing = False
self.selected_point = None
def extract_and_process_tracks(self, tracks):
boxes = tracks[0].boxes.xyxy.cpu()
clss = tracks[0].boxes.cls.cpu().tolist()
track_ids = tracks[0].boxes.id.int().cpu().tolist()
self.annotator = Annotator(self.im0, self.tf, self.names)
self.annotator.draw_region(reg_pts=self.reg_pts, color=(0, 255, 0))
for box, track_id, cls in zip(boxes, track_ids, clss):
self.annotator.box_label(box, label=self.names[cls], color=colors(int(cls), True)) # Draw bounding box
# Draw Tracks
track_line = self.track_history[track_id]
track_line.append((float((box[0] + box[2]) / 2), float((box[1] + box[3]) / 2)))
track_line.pop(0) if len(track_line) > 30 else None
if self.draw_tracks:
self.annotator.draw_centroid_and_tracks(track_line,
color=(0, 255, 0),
track_thickness=self.track_thickness)
# Count objects
if self.counting_region.contains(Point(track_line[-1])):
if track_id not in self.counting_list:
self.counting_list.append(track_id)
if box[0] < self.counting_region.centroid.x:
self.out_counts += 1
else:
self.in_counts += 1
if self.view_img:
incount_label = 'InCount : ' + f'{self.in_counts}'
outcount_label = 'OutCount : ' + f'{self.out_counts}'
self.annotator.count_labels(in_count=incount_label, out_count=outcount_label)
cv2.namedWindow('Ultralytics YOLOv8 Object Counter')
cv2.setMouseCallback('Ultralytics YOLOv8 Object Counter', self.mouse_event_for_region,
{'region_points': self.reg_pts})
cv2.imshow('Ultralytics YOLOv8 Object Counter', self.im0)
# Break Window
if cv2.waitKey(1) & 0xFF == ord('q'):
return
def start_counting(self, im0, tracks):
"""
Main function to start the object counting process.
Args:
im0 (ndarray): Current frame from the video stream.
tracks (list): List of tracks obtained from the object tracking process.
"""
self.im0 = im0 # store image
if tracks[0].boxes.id is None:
return
self.extract_and_process_tracks(tracks)
if __name__ == '__main__':
ObjectCounter()

@ -37,7 +37,7 @@ def is_url(url, check=True):
Defaults to True.
Returns:
bool: Returns True if the string is a valid URL. If 'check' is True, also returns True if the URL exists online.
(bool): Returns True if the string is a valid URL. If 'check' is True, also returns True if the URL exists online.
Returns False otherwise.
Example:
@ -362,7 +362,7 @@ def get_github_assets(repo='ultralytics/assets', version='latest', retry=False):
retry (bool, optional): Flag to retry the request in case of a failure. Defaults to False.
Returns:
tuple: A tuple containing the release tag and a list of asset names.
(tuple): A tuple containing the release tag and a list of asset names.
Example:
```python
@ -392,10 +392,10 @@ def attempt_download_asset(file, repo='ultralytics/assets', release='v0.0.0', **
file (str | Path): The filename or file path to be downloaded.
repo (str, optional): The GitHub repository in the format 'owner/repo'. Defaults to 'ultralytics/assets'.
release (str, optional): The specific release version to be downloaded. Defaults to 'v0.0.0'.
**kwargs: Additional keyword arguments for the download process.
**kwargs (dict): Additional keyword arguments for the download process.
Returns:
str: The path to the downloaded file.
(str): The path to the downloaded file.
Example:
```python

@ -258,6 +258,131 @@ class Annotator:
"""Return annotated image as array."""
return np.asarray(self.im)
# Object Counting Annotator
def draw_region(self, reg_pts=None, color=(0, 255, 0)):
# Draw region line
cv2.polylines(self.im, [np.array(reg_pts, dtype=np.int32)], isClosed=True, color=color, thickness=self.tf + 2)
def draw_centroid_and_tracks(self, track, color=(255, 0, 255), track_thickness=2):
# Draw region line
points = np.hstack(track).astype(np.int32).reshape((-1, 1, 2))
cv2.polylines(self.im, [points], isClosed=False, color=color, thickness=track_thickness)
cv2.circle(self.im, (int(track[-1][0]), int(track[-1][1])), track_thickness * 2, color, -1)
def count_labels(self, in_count=0, out_count=0, color=(255, 255, 255), txt_color=(0, 0, 0)):
tl = self.tf or round(0.002 * (self.im.shape[0] + self.im.shape[1]) / 2) + 1
tf = max(tl - 1, 1)
gap = int(24 * tl) # Calculate the gap between in_count and out_count based on line_thickness
# Get text size for in_count and out_count
t_size_in = cv2.getTextSize(str(in_count), 0, fontScale=tl / 2, thickness=tf)[0]
t_size_out = cv2.getTextSize(str(out_count), 0, fontScale=tl / 2, thickness=tf)[0]
# Calculate positions for in_count and out_count labels
text_width = max(t_size_in[0], t_size_out[0])
text_x1 = (self.im.shape[1] - text_width - 120 * self.tf) // 2 - gap
text_x2 = (self.im.shape[1] - text_width + 120 * self.tf) // 2 + gap
text_y = max(t_size_in[1], t_size_out[1])
# Create a rounded rectangle for in_count
cv2.rectangle(self.im, (text_x1 - 5, text_y - 5), (text_x1 + text_width + 7, text_y + t_size_in[1] + 7), color,
-1)
cv2.putText(self.im,
str(in_count), (text_x1, text_y + t_size_in[1]),
0,
tl / 2,
txt_color,
self.tf,
lineType=cv2.LINE_AA)
# Create a rounded rectangle for out_count
cv2.rectangle(self.im, (text_x2 - 5, text_y - 5), (text_x2 + text_width + 7, text_y + t_size_out[1] + 7), color,
-1)
cv2.putText(self.im,
str(out_count), (text_x2, text_y + t_size_out[1]),
0,
tl / 2,
txt_color,
thickness=self.tf,
lineType=cv2.LINE_AA)
# AI GYM Annotator
def estimate_pose_angle(self, a, b, c):
"""Calculate the pose angle for object
Args:
a (float) : The value of pose point a
b (float): The value of pose point b
c (float): The value o pose point c
Returns:
angle (degree): Degree value of angle between three points
"""
a, b, c = np.array(a), np.array(b), np.array(c)
radians = np.arctan2(c[1] - b[1], c[0] - b[0]) - np.arctan2(a[1] - b[1], a[0] - b[0])
angle = np.abs(radians * 180.0 / np.pi)
if angle > 180.0:
angle = 360 - angle
return angle
def draw_specific_points(self, keypoints, indices=[2, 5, 7], shape=(640, 640), radius=2):
"""Draw specific keypoints for gym steps counting."""
nkpts, ndim = keypoints.shape
nkpts == 17 and ndim == 3
for i, k in enumerate(keypoints):
if i in indices:
x_coord, y_coord = k[0], k[1]
if x_coord % shape[1] != 0 and y_coord % shape[0] != 0:
if len(k) == 3:
conf = k[2]
if conf < 0.5:
continue
cv2.circle(self.im, (int(x_coord), int(y_coord)), radius, (0, 255, 0), -1, lineType=cv2.LINE_AA)
return self.im
def plot_angle_and_count_and_stage(self, angle_text, count_text, stage_text, center_kpt, line_thickness=2):
"""Plot the pose angle, count value and step stage."""
angle_text, count_text, stage_text = f' {angle_text:.2f}', 'Steps : ' + f'{count_text}', f' {stage_text}'
font_scale = 0.6 + (line_thickness / 10.0)
# Draw angle
(angle_text_width, angle_text_height), _ = cv2.getTextSize(angle_text, cv2.FONT_HERSHEY_SIMPLEX, font_scale,
line_thickness)
angle_text_position = (int(center_kpt[0]), int(center_kpt[1]))
angle_background_position = (angle_text_position[0], angle_text_position[1] - angle_text_height - 5)
angle_background_size = (angle_text_width + 2 * 5, angle_text_height + 2 * 5 + (line_thickness * 2))
cv2.rectangle(self.im, angle_background_position, (angle_background_position[0] + angle_background_size[0],
angle_background_position[1] + angle_background_size[1]),
(255, 255, 255), -1)
cv2.putText(self.im, angle_text, angle_text_position, cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 0),
line_thickness)
# Draw Counts
(count_text_width, count_text_height), _ = cv2.getTextSize(count_text, cv2.FONT_HERSHEY_SIMPLEX, font_scale,
line_thickness)
count_text_position = (angle_text_position[0], angle_text_position[1] + angle_text_height + 20)
count_background_position = (angle_background_position[0],
angle_background_position[1] + angle_background_size[1] + 5)
count_background_size = (count_text_width + 10, count_text_height + 10 + (line_thickness * 2))
cv2.rectangle(self.im, count_background_position, (count_background_position[0] + count_background_size[0],
count_background_position[1] + count_background_size[1]),
(255, 255, 255), -1)
cv2.putText(self.im, count_text, count_text_position, cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 0),
line_thickness)
# Draw Stage
(stage_text_width, stage_text_height), _ = cv2.getTextSize(stage_text, cv2.FONT_HERSHEY_SIMPLEX, font_scale,
line_thickness)
stage_text_position = (int(center_kpt[0]), int(center_kpt[1]) + angle_text_height + count_text_height + 40)
stage_background_position = (stage_text_position[0], stage_text_position[1] - stage_text_height - 5)
stage_background_size = (stage_text_width + 10, stage_text_height + 10)
cv2.rectangle(self.im, stage_background_position, (stage_background_position[0] + stage_background_size[0],
stage_background_position[1] + stage_background_size[1]),
(255, 255, 255), -1)
cv2.putText(self.im, stage_text, stage_text_position, cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 0),
line_thickness)
@TryExcept() # known issue https://github.com/ultralytics/yolov5/issues/5395
@plt_settings()

Loading…
Cancel
Save