feat: initial HSAP platform

Huaxu Sentinel Active Safety Platform with embedded algorithm code,
Docker Compose setup, and vendored dataset scaffolds for clone-and-run.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-25 16:59:59 +08:00
commit 7c43b44c57
1619 changed files with 373355 additions and 0 deletions

View File

@@ -0,0 +1,134 @@
# Regions Counting Using Ultralytics YOLO11 (Inference on Video)
> **Note:** Region Counter is now part of **[Ultralytics Solutions](https://docs.ultralytics.com/solutions/)**, offering enhanced features and ongoing updates.
>
> 🔗 **Explore the official [Region Counting Guide](https://docs.ultralytics.com/guides/region-counting/) for the latest implementation.**
> 🔔 **Notice:**
>
> This GitHub example (`ultralytics/examples/YOLOv8-Region-Counter/`) will remain available but **is no longer actively maintained**. For the most current features, updates, and support, please refer to the official [Region Counting guide](https://docs.ultralytics.com/guides/region-counting/) within the Ultralytics documentation. Thank you!
Region counting is a technique used to count objects within predefined areas or zones in a video feed. This allows for more detailed analysis, especially when monitoring multiple distinct areas simultaneously. Users can interactively adjust these regions by clicking and dragging with the left mouse button, enabling real-time counting tailored to specific needs and layouts. This method is valuable in various [computer vision](https://www.ultralytics.com/glossary/computer-vision-cv) applications, from traffic analysis to retail analytics.
<div>
<p align="center">
<img src="https://github.com/RizwanMunawar/ultralytics/assets/62513924/5ab3bbd7-fd12-4849-928e-5f294d6c3fcf" width="45%" alt="YOLOv8 region counting visual 1">
<img src="https://github.com/RizwanMunawar/ultralytics/assets/62513924/e7c1aea7-474d-4d78-8d48-b50854ffe1ca" width="45%" alt="YOLOv8 region counting visual 2">
</p>
</div>
## 📚 Table of Contents
- [Step 1: Install the Required Libraries](#-step-1-install-the-required-libraries)
- [Step 2: Run Region Counting with Ultralytics YOLO11](#-step-2-run-region-counting-with-ultralytics-yolo11)
- [Usage Options](#-usage-options)
- [Frequently Asked Questions (FAQ)](#-frequently-asked-questions-faq)
- [Contributing](#-contributing)
## ⚙️ Step 1: Install the Required Libraries
First, clone the Ultralytics repository and navigate to the example directory. Ensure you have Python installed along with necessary dependencies like [PyTorch](https://pytorch.org/) and [OpenCV](https://opencv.org/).
```bash
# Clone the ultralytics repository
git clone https://github.com/ultralytics/ultralytics
# Navigate to the example directory
cd ultralytics/examples/YOLOv8-Region-Counter
# Install required packages (if not already installed)
pip install ultralytics shapely
```
## ▶️ Step 2: Run Region Counting with Ultralytics YOLO11
Execute the script using the following commands. You can customize the source, model, device, and other parameters.
### Note
Once the video starts playing, you can dynamically reposition the counting regions within the video frame by clicking and dragging them with your left mouse button.
```bash
# Run inference on a video source, saving results and viewing output
python yolov8_region_counter.py --source "path/to/video.mp4" --save-img --view-img
# Run inference using the CPU
python yolov8_region_counter.py --source "path/to/video.mp4" --save-img --view-img --device cpu
# Use a specific Ultralytics YOLO11 model file
python yolov8_region_counter.py --source "path/to/video.mp4" --save-img --weights "path/to/yolo11n.pt"
# Detect only specific classes (e.g., class 0 and class 2)
python yolov8_region_counter.py --source "path/to/video.mp4" --classes 0 2 --weights "path/to/yolo11m.pt"
# Run inference without saving the output video/images
python yolov8_region_counter.py --source "path/to/video.mp4" --view-img
```
Learn more about inference arguments in the Ultralytics [Predict Mode documentation](https://docs.ultralytics.com/modes/predict/).
## 🛠️ Usage Options
The script accepts several command-line arguments for customization:
- `--source`: Path to the input video file.
- `--device`: Computation device (`cpu` or GPU ID like `0`).
- `--save-img`: Boolean flag to save output frames with detections.
- `--view-img`: Boolean flag to display the output video stream in real-time.
- `--weights`: Path to the [Ultralytics YOLO11](https://docs.ultralytics.com/models/yolo11/) model file (`.pt`). Defaults typically use a standard model like `yolo11n.pt`.
- `--classes`: Filter detections by specific class IDs (e.g., `--classes 0 2 3` to detect classes 0, 2, and 3).
- `--line-thickness`: Thickness of the [bounding box](https://www.ultralytics.com/glossary/bounding-box) lines.
- `--region-thickness`: Thickness of the lines defining the counting regions.
- `--track-thickness`: Thickness of the object tracking lines.
Explore different models and training options in the [Ultralytics documentation](https://docs.ultralytics.com/).
## ❓ Frequently Asked Questions (FAQ)
### What is Region Counting?
Region counting is a process in [computer vision](https://www.ultralytics.com/glossary/computer-vision-cv) used to determine the number of objects within predefined areas of an image or video frame. It's commonly applied in fields like [image processing](https://en.wikipedia.org/wiki/Image_processing) and [pattern recognition](https://en.wikipedia.org/wiki/Pattern_recognition) for analyzing spatial distributions and segmenting objects based on location.
### Does the Region Counter Support Custom Region Shapes?
Yes, the Region Counter allows defining regions using polygons (including rectangles). You can customize region properties like coordinates, names, and colors directly in the script. The `shapely` library is used for polygon definitions. See the [Shapely User Manual](https://shapely.readthedocs.io/en/stable/manual.html#polygons) for more details on polygon creation.
```python
from shapely.geometry import Polygon
# Example definition of counting regions
counting_regions = [
{
"name": "Region 1 (Pentagon)",
"polygon": Polygon([(50, 80), (250, 20), (450, 80), (400, 350), (100, 350)]), # 5-point polygon
"counts": 0,
"dragging": False,
"region_color": (255, 42, 4), # BGR color for region
"text_color": (255, 255, 255), # BGR color for text
},
{
"name": "Region 2 (Rectangle)",
"polygon": Polygon([(200, 250), (440, 250), (440, 550), (200, 550)]), # 4-point polygon (rectangle)
"counts": 0,
"dragging": False,
"region_color": (37, 255, 225), # BGR color for region
"text_color": (0, 0, 0), # BGR color for text
},
]
```
### Why Combine Region Counting with YOLO11?
[Ultralytics YOLO11](https://docs.ultralytics.com/models/yolo11/) excels at [object detection](https://www.ultralytics.com/glossary/object-detection) and [tracking](https://www.ultralytics.com/glossary/object-tracking) in video streams. Integrating region counting enhances its capabilities by enabling object quantification within specific zones, making it useful for applications like crowd monitoring, traffic flow analysis, and retail footfall counting. Check out our blog post on [Object Detection and Tracking with Ultralytics YOLOv8](https://www.ultralytics.com/blog/object-detection-and-tracking-with-ultralytics-yolov8).
### How Can I Troubleshoot Issues?
For debugging, you can enable more verbose output. While this specific script doesn't have a dedicated `--debug` flag, you can add print statements within the code to inspect variables or use standard Python debugging tools. Ensure your video path and model weights path are correct. For common issues, refer to the [Ultralytics FAQ](https://docs.ultralytics.com/help/FAQ/).
### Can I Use Other YOLO Versions or Custom Models?
Yes, you can use different Ultralytics YOLO model versions (like YOLOv5, YOLOv9, YOLOv10, YOLO11) or your own custom-trained models by specifying the path to the `.pt` file using the `--weights` argument. Ensure the model is compatible with the Ultralytics framework. Find more about training custom models in the [Model Training guide](https://docs.ultralytics.com/modes/train/).
## 🤝 Contributing
Contributions to improve this example or add new features are welcome! Please feel free to submit Pull Requests or open Issues on the main [Ultralytics repository](https://github.com/ultralytics/ultralytics). Remember to check the official [Region Counting guide](https://docs.ultralytics.com/guides/region-counting/) for the latest maintained version.

View File

@@ -0,0 +1,253 @@
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
from __future__ import annotations
import argparse
from collections import defaultdict
from pathlib import Path
from typing import Any
import cv2
import numpy as np
from shapely.geometry import Polygon
from shapely.geometry.point import Point
from ultralytics import YOLO
from ultralytics.utils.files import increment_path
from ultralytics.utils.plotting import Annotator, colors
track_history = defaultdict(list)
current_region = None
counting_regions = [
{
"name": "Ultralytics YOLO Polygon Region",
"polygon": Polygon([(50, 80), (250, 20), (450, 80), (400, 350), (100, 350)]), # Polygon points
"counts": 0,
"dragging": False,
"region_color": (255, 42, 4), # BGR Value
"text_color": (255, 255, 255), # Region Text Color
},
{
"name": "Ultralytics YOLO Rectangle Region",
"polygon": Polygon([(200, 250), (440, 250), (440, 550), (200, 550)]), # Polygon points
"counts": 0,
"dragging": False,
"region_color": (37, 255, 225), # BGR Value
"text_color": (0, 0, 0), # Region Text Color
},
]
def mouse_callback(event: int, x: int, y: int, flags: int, param: Any) -> None:
"""Handle mouse events for region manipulation in the video frame.
This function enables interactive region selection and dragging functionality for counting regions. It responds to
mouse button down, move, and up events to allow users to select and reposition counting regions in real-time.
Args:
event (int): The mouse event type (e.g., cv2.EVENT_LBUTTONDOWN, cv2.EVENT_MOUSEMOVE).
x (int): The x-coordinate of the mouse pointer.
y (int): The y-coordinate of the mouse pointer.
flags (int): Additional flags passed by OpenCV.
param (Any): Additional parameters passed to the callback.
Examples:
Set up mouse callback for interactive region manipulation
>>> cv2.setMouseCallback("window_name", mouse_callback)
"""
global current_region
# Mouse left button down event
if event == cv2.EVENT_LBUTTONDOWN:
for region in counting_regions:
if region["polygon"].contains(Point((x, y))):
current_region = region
current_region["dragging"] = True
current_region["offset_x"] = x
current_region["offset_y"] = y
# Mouse move event
elif event == cv2.EVENT_MOUSEMOVE:
if current_region is not None and current_region["dragging"]:
dx = x - current_region["offset_x"]
dy = y - current_region["offset_y"]
current_region["polygon"] = Polygon(
[(p[0] + dx, p[1] + dy) for p in current_region["polygon"].exterior.coords]
)
current_region["offset_x"] = x
current_region["offset_y"] = y
# Mouse left button up event
elif event == cv2.EVENT_LBUTTONUP:
if current_region is not None and current_region["dragging"]:
current_region["dragging"] = False
def run(
weights: str = "yolo11n.pt",
source: str | None = None,
device: str = "cpu",
view_img: bool = False,
save_img: bool = False,
exist_ok: bool = False,
classes: list[int] | None = None,
line_thickness: int = 2,
track_thickness: int = 2,
region_thickness: int = 2,
) -> None:
"""Run object detection and counting within specified regions using YOLO and ByteTrack.
This function performs real-time object detection, tracking, and counting within user-defined polygonal or
rectangular regions. It supports interactive region manipulation, multiple counting areas, and both live viewing and
video saving capabilities.
Args:
weights (str): Path to the YOLO model weights file.
source (str): Path to the input video file.
device (str): Processing device specification ('cpu', '0', '1', etc.).
view_img (bool): Display results in a live window.
save_img (bool): Save processed video to file.
exist_ok (bool): Overwrite existing output files without incrementing.
classes (list[int], optional): Specific class IDs to detect and track.
line_thickness (int): Thickness of bounding box lines.
track_thickness (int): Thickness of object tracking lines.
region_thickness (int): Thickness of counting region boundaries.
Examples:
Run region counting with default settings
>>> run(source="video.mp4", view_img=True)
Run with custom model and specific classes
>>> run(weights="yolo11s.pt", source="traffic.mp4", classes=[0, 2, 3], device="0")
"""
vid_frame_count = 0
# Check source path
if not Path(source).exists():
raise FileNotFoundError(f"Source path '{source}' does not exist.")
# Setup Model
model = YOLO(f"{weights}")
model.to("cuda") if device == "0" else model.to("cpu")
# Extract classes names
names = model.names
# Video setup
videocapture = cv2.VideoCapture(source)
frame_width = int(videocapture.get(3))
frame_height = int(videocapture.get(4))
fps = int(videocapture.get(5))
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
# Output setup
save_dir = increment_path(Path("ultralytics_rc_output") / "exp", exist_ok)
save_dir.mkdir(parents=True, exist_ok=True)
video_writer = cv2.VideoWriter(str(save_dir / f"{Path(source).stem}.avi"), fourcc, fps, (frame_width, frame_height))
# Iterate over video frames
while videocapture.isOpened():
success, frame = videocapture.read()
if not success:
break
vid_frame_count += 1
# Extract the results
results = model.track(frame, persist=True, classes=classes)
if results[0].boxes.is_track:
boxes = results[0].boxes.xyxy.cpu()
track_ids = results[0].boxes.id.int().cpu().tolist()
clss = results[0].boxes.cls.cpu().tolist()
annotator = Annotator(frame, line_width=line_thickness, example=str(names))
for box, track_id, cls in zip(boxes, track_ids, clss):
annotator.box_label(box, str(names[cls]), color=colors(cls, True))
bbox_center = (box[0] + box[2]) / 2, (box[1] + box[3]) / 2 # Bbox center
track = track_history[track_id] # Tracking Lines plot
track.append((float(bbox_center[0]), float(bbox_center[1])))
if len(track) > 30:
track.pop(0)
points = np.hstack(track).astype(np.int32).reshape((-1, 1, 2))
cv2.polylines(frame, [points], isClosed=False, color=colors(cls, True), thickness=track_thickness)
# Check if detection inside region
for region in counting_regions:
if region["polygon"].contains(Point((bbox_center[0], bbox_center[1]))):
region["counts"] += 1
# Draw regions (Polygons/Rectangles)
for region in counting_regions:
region_label = str(region["counts"])
region_color = region["region_color"]
region_text_color = region["text_color"]
polygon_coordinates = np.array(region["polygon"].exterior.coords, dtype=np.int32)
centroid_x, centroid_y = int(region["polygon"].centroid.x), int(region["polygon"].centroid.y)
text_size, _ = cv2.getTextSize(
region_label, cv2.FONT_HERSHEY_SIMPLEX, fontScale=0.7, thickness=line_thickness
)
text_x = centroid_x - text_size[0] // 2
text_y = centroid_y + text_size[1] // 2
cv2.rectangle(
frame,
(text_x - 5, text_y - text_size[1] - 5),
(text_x + text_size[0] + 5, text_y + 5),
region_color,
-1,
)
cv2.putText(
frame, region_label, (text_x, text_y), cv2.FONT_HERSHEY_SIMPLEX, 0.7, region_text_color, line_thickness
)
cv2.polylines(frame, [polygon_coordinates], isClosed=True, color=region_color, thickness=region_thickness)
if view_img:
if vid_frame_count == 1:
cv2.namedWindow("Ultralytics YOLO Region Counter Movable")
cv2.setMouseCallback("Ultralytics YOLO Region Counter Movable", mouse_callback)
cv2.imshow("Ultralytics YOLO Region Counter Movable", frame)
if save_img:
video_writer.write(frame)
for region in counting_regions: # Reinitialize count for each region
region["counts"] = 0
if cv2.waitKey(1) & 0xFF == ord("q"):
break
del vid_frame_count
video_writer.release()
videocapture.release()
cv2.destroyAllWindows()
def parse_opt() -> argparse.Namespace:
"""Parse command line arguments for the region counting application."""
parser = argparse.ArgumentParser()
parser.add_argument("--weights", type=str, default="yolo11n.pt", help="initial weights path")
parser.add_argument("--device", default="", help="cuda device, i.e. 0 or 0,1,2,3 or cpu")
parser.add_argument("--source", type=str, required=True, help="video file path")
parser.add_argument("--view-img", action="store_true", help="show results")
parser.add_argument("--save-img", action="store_true", help="save results")
parser.add_argument("--exist-ok", action="store_true", help="existing project/name ok, do not increment")
parser.add_argument("--classes", nargs="+", type=int, help="filter by class: --classes 0, or --classes 0 2 3")
parser.add_argument("--line-thickness", type=int, default=2, help="bounding box thickness")
parser.add_argument("--track-thickness", type=int, default=2, help="Tracking line thickness")
parser.add_argument("--region-thickness", type=int, default=4, help="Region thickness")
return parser.parse_args()
def main(options: argparse.Namespace) -> None:
"""Execute the main region counting functionality with the provided options."""
run(**vars(options))
if __name__ == "__main__":
opt = parse_opt()
main(opt)