-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathcamera_inference.cpp
More file actions
171 lines (146 loc) · 6.01 KB
/
Copy pathcamera_inference.cpp
File metadata and controls
171 lines (146 loc) · 6.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/**
* @file camera_inference.cpp
* @brief Real-time object detection using YOLOv8 and YOLOv11 (OBB format) with camera input.
*
* This file serves as the main entry point for a real-time object detection
* application that utilizes the YOLO (You Only Look Once) models, specifically
* versions 8 and 11, with Oriented Bounding Box (OBB) support. The application
* captures video frames from a specified camera device, processes those frames
* to detect objects, and displays the results with oriented bounding boxes around
* detected objects.
*
* The program operates in a multi-threaded environment, featuring the following
* threads:
* 1. **Producer Thread**: Captures frames from the video source and enqueues them
* into a thread-safe bounded queue for subsequent processing.
* 2. **Consumer Thread**: Dequeues frames from the producer's queue, executes
* object detection using the specified YOLO model, and enqueues the processed
* frames along with detection results into another thread-safe bounded queue.
* 3. **Display Thread**: Dequeues processed frames from the consumer's queue,
* draws oriented bounding boxes around detected objects, and displays the frames
* to the user.
*
* Configuration parameters can be adjusted to suit specific requirements:
* - `isGPU`: Set to true to enable GPU processing for improved performance;
* set to false for CPU processing.
* - `labelsPath`: Path to the class labels file (e.g., Dota dataset).
* - `modelPath`: Path to the YOLO model file (e.g., ONNX format).
* - `videoSource`: Path to the video capture device (e.g., camera).
*
* The application employs a double buffering technique by maintaining two bounded
* queues to efficiently manage the flow of frames between the producer and
* consumer threads. This setup helps prevent processing delays due to slow frame
* capture or detection times.
*
* Debugging messages can be enabled by defining the `DEBUG_MODE` macro, allowing
* developers to trace the execution flow and internal state of the application
* during runtime.
*
* Usage Instructions:
* 1. Compile the application with the necessary OpenCV and YOLO dependencies.
* 2. Run the executable to initiate the object detection process.
* 3. Press 'q' to quit the application at any time.
*
* @note Ensure that the required model files and labels are present in the
* specified paths before running the application.
*
* Author: Mohamed Samir, https://www.linkedin.com/in/mohamed-samir-7a730b237/
* Date: 05.03.2025
*/
#include <iostream>
#include <vector>
#include <thread>
#include <atomic>
#include <opencv2/highgui/highgui.hpp>
#include "YOLO11-OBB.hpp"
// Include the bounded queue
#include "tools/BoundedThreadSafeQueue.hpp"
int main(){
// Configuration parameters
const bool isGPU = true;
const std::string labelsPath = "../models/Dota.names";
const std::string modelPath = "../models/yolo11n-obb.onnx"; //V11
// const std::string modelPath = "../models/yolov8n-obb.onnx"; //V8
const std::string videoSource = "/dev/video0"; // your usb cam device
// Initialize YOLO detector
YOLO11OBBDetector detector(modelPath, labelsPath, isGPU);
// Open video capture
cv::VideoCapture cap;
cap.open(videoSource, cv::CAP_V4L2); // Specify V4L2 backend for better performance
if (!cap.isOpened()){
std::cerr << "Error: Could not open the camera!\n";
return -1;
}
// Set camera properties
cap.set(cv::CAP_PROP_FRAME_WIDTH, 1280);
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 720);
cap.set(cv::CAP_PROP_FPS, 30);
// Initialize queues with bounded capacity
const size_t max_queue_size = 2; // Double buffering
BoundedThreadSafeQueue<cv::Mat> frameQueue(max_queue_size);
BoundedThreadSafeQueue<std::pair<cv::Mat, std::vector<Detection>>> processedQueue(max_queue_size);
std::atomic<bool> stopFlag(false);
// Producer thread: Capture frames
std::thread producer([&]() {
cv::Mat frame;
while (!stopFlag.load() && cap.read(frame)){
if (!frameQueue.enqueue(frame))
break; // Queue is finished
}
frameQueue.set_finished();
});
// Consumer thread: Process frames
std::thread consumer([&]() {
cv::Mat frame;
while (!stopFlag.load() && frameQueue.dequeue(frame)){
// Perform detection
std::vector<Detection> detections = detector.detect(frame);
// Enqueue processed frame
if (!processedQueue.enqueue(std::make_pair(frame, detections)))
break;
}
processedQueue.set_finished();
});
std::pair<cv::Mat, std::vector<Detection>> item;
#ifdef __APPLE__
// For macOS, ensure UI runs on the main thread
while (!stopFlag.load() && processedQueue.dequeue(item)){
cv::Mat displayFrame = item.first;
detector.drawBoundingBox(displayFrame, item.second);
cv::imshow("Detections", displayFrame);
if (cv::waitKey(1) == 'q')
{
stopFlag.store(true);
frameQueue.set_finished();
processedQueue.set_finished();
break;
}
}
#else
// Display thread: Show processed frames
std::thread displayThread([&]() {
while (!stopFlag.load() && processedQueue.dequeue(item)){
cv::Mat displayFrame = item.first;
// detector.drawBoundingBox(displayFrame, item.second);
detector.drawBoundingBox(displayFrame, item.second);
// Display the frame
cv::imshow("Detections", displayFrame);
// Use a small delay and check for 'q' key press to quit
if (cv::waitKey(1) == 'q') {
stopFlag.store(true);
frameQueue.set_finished();
processedQueue.set_finished();
break;
}
}
});
displayThread.join();
#endif
// Join all threads
producer.join();
consumer.join();
// Release resources
cap.release();
cv::destroyAllWindows();
return 0;
}