diff --git a/docs/en/guides/index.md b/docs/en/guides/index.md index a01e08a78a..b7c071e14c 100644 --- a/docs/en/guides/index.md +++ b/docs/en/guides/index.md @@ -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. diff --git a/docs/en/guides/isolating-segmentation-objects.md b/docs/en/guides/isolating-segmentation-objects.md index 334367f2c5..aac16941c7 100644 --- a/docs/en/guides/isolating-segmentation-objects.md +++ b/docs/en/guides/isolating-segmentation-objects.md @@ -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.
Expand to understand what is happening when defining the contour variable. @@ -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) diff --git a/docs/en/guides/object-counting.md b/docs/en/guides/object-counting.md new file mode 100644 index 0000000000..e15f243419 --- /dev/null +++ b/docs/en/guides/object-counting.md @@ -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 | diff --git a/docs/en/guides/region-counting.md b/docs/en/guides/region-counting.md new file mode 100644 index 0000000000..7dfc8112db --- /dev/null +++ b/docs/en/guides/region-counting.md @@ -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. + +

+
+ +
+ Watch: Ultralytics YOLOv8 Object Counting in Multiple & Moveable Regions +

+ +## 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 | diff --git a/docs/en/guides/security-alarm-system.md b/docs/en/guides/security-alarm-system.md new file mode 100644 index 0000000000..ee349600e2 --- /dev/null +++ b/docs/en/guides/security-alarm-system.md @@ -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 + +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. + +

+
+ +
+ Watch: Security Alarm System Project with Ultralytics YOLOv8 Object Detection +

+ +### 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 + +Email Received Sample diff --git a/docs/en/guides/workouts-monitoring.md b/docs/en/guides/workouts-monitoring.md new file mode 100644 index 0000000000..c368b52222 --- /dev/null +++ b/docs/en/guides/workouts-monitoring.md @@ -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 | diff --git a/docs/en/reference/solutions/ai_gym.md b/docs/en/reference/solutions/ai_gym.md new file mode 100644 index 0000000000..eb3e634a97 --- /dev/null +++ b/docs/en/reference/solutions/ai_gym.md @@ -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 🙏! + +

+ +## ::: ultralytics.solutions.ai_gym.AIGym + +

diff --git a/docs/en/reference/solutions/object_counter.md b/docs/en/reference/solutions/object_counter.md new file mode 100644 index 0000000000..6cd3e00ed1 --- /dev/null +++ b/docs/en/reference/solutions/object_counter.md @@ -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 🙏! + +

+ +## ::: ultralytics.solutions.object_counter.ObjectCounter + +

diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 765b9d7790..bf4af06efb 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -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 diff --git a/ultralytics/solutions/ai_gym.py b/ultralytics/solutions/ai_gym.py new file mode 100644 index 0000000000..08df550fe0 --- /dev/null +++ b/ultralytics/solutions/ai_gym.py @@ -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() diff --git a/ultralytics/solutions/object_counter.py b/ultralytics/solutions/object_counter.py new file mode 100644 index 0000000000..75fca11777 --- /dev/null +++ b/ultralytics/solutions/object_counter.py @@ -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() diff --git a/ultralytics/utils/downloads.py b/ultralytics/utils/downloads.py index e26474498b..3d8ad6737e 100644 --- a/ultralytics/utils/downloads.py +++ b/ultralytics/utils/downloads.py @@ -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 diff --git a/ultralytics/utils/plotting.py b/ultralytics/utils/plotting.py index 9a41a94988..aebc4003d3 100644 --- a/ultralytics/utils/plotting.py +++ b/ultralytics/utils/plotting.py @@ -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()