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:
@@ -0,0 +1,51 @@
|
||||
# Ultralytics YOLOv8 Object Detection with OpenCV and ONNX
|
||||
|
||||
This example demonstrates how to implement [Ultralytics YOLOv8](https://docs.ultralytics.com/models/yolov8/) object detection using [OpenCV](https://opencv.org/) in [Python](https://www.python.org/), leveraging the [ONNX (Open Neural Network Exchange)](https://onnx.ai/) model format for efficient inference.
|
||||
|
||||
## 🚀 Getting Started
|
||||
|
||||
Follow these simple steps to get the example running on your local machine.
|
||||
|
||||
1. **Clone the Repository:**
|
||||
If you haven't already, clone the Ultralytics repository to access the example code:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/ultralytics/ultralytics.git
|
||||
cd ultralytics/examples/YOLOv8-OpenCV-ONNX-Python/
|
||||
```
|
||||
|
||||
2. **Install Requirements:**
|
||||
Install the necessary Python packages listed in the `requirements.txt` file. We recommend using a virtual environment.
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
3. **Run the Detection Script:**
|
||||
Execute the main Python script, specifying the ONNX model path and the input image.
|
||||
```bash
|
||||
python main.py --model yolov8n.onnx --img image.jpg
|
||||
```
|
||||
The script will perform object detection on `image.jpg` using the `yolov8n.onnx` model and display the results.
|
||||
|
||||
## 🛠️ Exporting Your Model
|
||||
|
||||
If you want to use a different Ultralytics YOLOv8 model or one you've trained yourself, you need to export it to the ONNX format first.
|
||||
|
||||
1. **Install Ultralytics:**
|
||||
If you don't have it installed, get the latest `ultralytics` package:
|
||||
|
||||
```bash
|
||||
pip install ultralytics
|
||||
```
|
||||
|
||||
2. **Export the Model:**
|
||||
Use the `yolo export` command to convert your desired model (e.g., `yolov8n.pt`) to ONNX. Ensure you specify `opset=12` or higher for compatibility with OpenCV's DNN module. You can find more details in the Ultralytics [Export documentation](https://docs.ultralytics.com/modes/export/).
|
||||
```bash
|
||||
yolo export model=yolov8n.pt imgsz=640 format=onnx opset=12
|
||||
```
|
||||
This command will generate a `yolov8n.onnx` file (or the corresponding name for your model) in your working directory. You can then use this `.onnx` file with the `main.py` script.
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
Contributions are welcome! If you find any issues or have suggestions for improvement, please feel free to open an issue or submit a pull request to the main [Ultralytics repository](https://github.com/ultralytics/ultralytics). Thank you for helping us make Ultralytics YOLO even better!
|
||||
@@ -0,0 +1,134 @@
|
||||
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from typing import Any
|
||||
|
||||
import cv2.dnn
|
||||
import numpy as np
|
||||
|
||||
from ultralytics.utils import ASSETS, YAML
|
||||
from ultralytics.utils.checks import check_yaml
|
||||
|
||||
CLASSES = YAML.load(check_yaml("coco8.yaml"))["names"]
|
||||
colors = np.random.uniform(0, 255, size=(len(CLASSES), 3))
|
||||
|
||||
|
||||
def draw_bounding_box(
|
||||
img: np.ndarray, class_id: int, confidence: float, x: int, y: int, x_plus_w: int, y_plus_h: int
|
||||
) -> None:
|
||||
"""Draw bounding boxes on the input image based on the provided arguments.
|
||||
|
||||
Args:
|
||||
img (np.ndarray): The input image to draw the bounding box on.
|
||||
class_id (int): Class ID of the detected object.
|
||||
confidence (float): Confidence score of the detected object.
|
||||
x (int): X-coordinate of the top-left corner of the bounding box.
|
||||
y (int): Y-coordinate of the top-left corner of the bounding box.
|
||||
x_plus_w (int): X-coordinate of the bottom-right corner of the bounding box.
|
||||
y_plus_h (int): Y-coordinate of the bottom-right corner of the bounding box.
|
||||
"""
|
||||
label = f"{CLASSES[class_id]} ({confidence:.2f})"
|
||||
color = colors[class_id]
|
||||
cv2.rectangle(img, (x, y), (x_plus_w, y_plus_h), color, 2)
|
||||
cv2.putText(img, label, (x - 10, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
|
||||
|
||||
|
||||
def main(onnx_model: str, input_image: str) -> list[dict[str, Any]]:
|
||||
"""Load ONNX model, perform inference, draw bounding boxes, and display the output image.
|
||||
|
||||
Args:
|
||||
onnx_model (str): Path to the ONNX model.
|
||||
input_image (str): Path to the input image.
|
||||
|
||||
Returns:
|
||||
(list[dict[str, Any]]): List of dictionaries containing detection information such as class_id, class_name,
|
||||
confidence, box coordinates, and scale factor.
|
||||
"""
|
||||
# Load the ONNX model
|
||||
model: cv2.dnn.Net = cv2.dnn.readNetFromONNX(onnx_model)
|
||||
|
||||
# Read the input image
|
||||
original_image: np.ndarray = cv2.imread(input_image)
|
||||
[height, width, _] = original_image.shape
|
||||
|
||||
# Prepare a square image for inference
|
||||
length = max((height, width))
|
||||
image = np.zeros((length, length, 3), np.uint8)
|
||||
image[0:height, 0:width] = original_image
|
||||
|
||||
# Calculate scale factor
|
||||
scale = length / 640
|
||||
|
||||
# Preprocess the image and prepare blob for model
|
||||
blob = cv2.dnn.blobFromImage(image, scalefactor=1 / 255, size=(640, 640), swapRB=True)
|
||||
model.setInput(blob)
|
||||
|
||||
# Perform inference
|
||||
outputs = model.forward()
|
||||
|
||||
# Prepare output array
|
||||
outputs = np.array([cv2.transpose(outputs[0])])
|
||||
rows = outputs.shape[1]
|
||||
|
||||
boxes = []
|
||||
scores = []
|
||||
class_ids = []
|
||||
|
||||
# Iterate through output to collect bounding boxes, confidence scores, and class IDs
|
||||
for i in range(rows):
|
||||
classes_scores = outputs[0][i][4:]
|
||||
(_minScore, maxScore, _minClassLoc, (_x, maxClassIndex)) = cv2.minMaxLoc(classes_scores)
|
||||
if maxScore >= 0.25:
|
||||
box = [
|
||||
outputs[0][i][0] - (0.5 * outputs[0][i][2]), # x center - width/2 = left x
|
||||
outputs[0][i][1] - (0.5 * outputs[0][i][3]), # y center - height/2 = top y
|
||||
outputs[0][i][2], # width
|
||||
outputs[0][i][3], # height
|
||||
]
|
||||
boxes.append(box)
|
||||
scores.append(maxScore)
|
||||
class_ids.append(maxClassIndex)
|
||||
|
||||
# Apply NMS (Non-maximum suppression)
|
||||
result_boxes = np.array(cv2.dnn.NMSBoxes(boxes, scores, 0.25, 0.45, 0.5)).flatten()
|
||||
|
||||
detections = []
|
||||
|
||||
# Iterate through NMS results to draw bounding boxes and labels
|
||||
for index in result_boxes:
|
||||
index = int(index)
|
||||
box = boxes[index]
|
||||
detection = {
|
||||
"class_id": class_ids[index],
|
||||
"class_name": CLASSES[class_ids[index]],
|
||||
"confidence": scores[index],
|
||||
"box": box,
|
||||
"scale": scale,
|
||||
}
|
||||
detections.append(detection)
|
||||
draw_bounding_box(
|
||||
original_image,
|
||||
class_ids[index],
|
||||
scores[index],
|
||||
round(box[0] * scale),
|
||||
round(box[1] * scale),
|
||||
round((box[0] + box[2]) * scale),
|
||||
round((box[1] + box[3]) * scale),
|
||||
)
|
||||
|
||||
# Display the image with bounding boxes
|
||||
cv2.imshow("image", original_image)
|
||||
cv2.waitKey(0)
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
return detections
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--model", default="yolov8n.onnx", help="Input your ONNX model.")
|
||||
parser.add_argument("--img", default=str(ASSETS / "bus.jpg"), help="Path to input image.")
|
||||
args = parser.parse_args()
|
||||
main(args.model, args.img)
|
||||
@@ -0,0 +1,3 @@
|
||||
numpy
|
||||
opencv-python
|
||||
ultralytics
|
||||
Reference in New Issue
Block a user