-
Notifications
You must be signed in to change notification settings - Fork 37
/
Copy pathvideo_inference.cpp
191 lines (167 loc) · 6.15 KB
/
video_inference.cpp
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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/**
* @file video_inference.cpp
* @brief Object detection in a video stream using YOLO models (v5, v7, v8, v10).
*
* This file implements an object detection application that utilizes YOLO
* (You Only Look Once) models, specifically versions 5, 7, 8, and 10.
* The application processes a video stream to detect objects and saves
* the results to a new video file with bounding boxes around detected objects.
*
* The application supports the following functionality:
* - Loading a video stream from disk or camera.
* - Initializing the YOLO detector with the desired model and labels.
* - Detecting objects within each frame of the video.
* - Drawing bounding boxes around detected objects and saving the result.
*
* 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., COCO dataset).
* - `videoPath`: Path to the input video file (e.g., input.mp4).
* - `outputPath`: Path for saving the output video file (e.g., output.mp4).
* - `modelPath`: Path to the desired YOLO model file (e.g., yolo.onnx format).
*
* The application can be extended to use different YOLO versions by modifying
* the model path and the corresponding detector class.
*
* Usage Instructions:
* 1. Compile the application with the necessary OpenCV and YOLO dependencies.
* 2. Ensure that the specified video and model files are present in the
* provided paths.
* 3. Run the executable to initiate the object detection process.
*
* @note The code includes commented-out sections to demonstrate how to switch
* between different YOLO models and video inputs.
*
* Author: Abdalrahman M. Amer, www.linkedin.com/in/abdalrahman-m-amer
* Date: 29.09.2024
*/
// Include necessary headers
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/videoio.hpp>
#include <iostream>
#include <string>
#include <thread>
#include <queue>
#include <mutex>
#include <atomic>
#include <condition_variable>
// #include "YOLO5.hpp" // Uncomment for YOLOv5
// #include "YOLO7.hpp" // Uncomment for YOLOv7
// #include "YOLO8.hpp" // Uncomment for YOLOv8
// #include "YOLO10.hpp" // Uncomment for YOLOv10
#include "YOLO11.hpp" // Uncomment for YOLOv10
// Thread-safe queue implementation
template <typename T>
class SafeQueue {
public:
SafeQueue() : q(), m(), c() {}
// Add an element to the queue.
void enqueue(T t) {
std::lock_guard<std::mutex> lock(m);
q.push(t);
c.notify_one();
}
// Get the first element from the queue.
bool dequeue(T& t) {
std::unique_lock<std::mutex> lock(m);
while (q.empty()) {
if (finished) return false;
c.wait(lock);
}
t = q.front();
q.pop();
return true;
}
void setFinished() {
std::lock_guard<std::mutex> lock(m);
finished = true;
c.notify_all();
}
private:
std::queue<T> q;
mutable std::mutex m;
std::condition_variable c;
bool finished = false;
};
int main()
{
// Paths to the model, labels, input video, and output video
const std::string labelsPath = "../models/coco.names";
const std::string videoPath = "../data/SIG_experience_center.mp4"; // Input video path
const std::string outputPath = "../data/SIG_experience_center_processed.mp4"; // Output video path
// Model paths for different YOLO versions
const std::string modelPath = "../models/yolo11n.onnx"; // YOLOv11
// Initialize the YOLO detector
bool isGPU = true; // Set to false for CPU processing
YOLO11Detector detector(modelPath, labelsPath, isGPU); // YOLOv11
// Open the video file
cv::VideoCapture cap(videoPath);
if (!cap.isOpened())
{
std::cerr << "Error: Could not open or find the video file!\n";
return -1;
}
// Get video properties
int frameWidth = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH));
int frameHeight = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_HEIGHT));
int fps = static_cast<int>(cap.get(cv::CAP_PROP_FPS));
int fourcc = static_cast<int>(cap.get(cv::CAP_PROP_FOURCC)); // Get codec of input video
// Create a VideoWriter object to save the output video with the same codec
cv::VideoWriter out(outputPath, fourcc, fps, cv::Size(frameWidth, frameHeight), true);
if (!out.isOpened())
{
std::cerr << "Error: Could not open the output video file for writing!\n";
return -1;
}
// Thread-safe queues and processing...
// Thread-safe queues
SafeQueue<cv::Mat> frameQueue;
SafeQueue<std::pair<int, cv::Mat>> processedQueue;
// Flag to indicate processing completion
std::atomic<bool> processingDone(false);
// Capture thread
std::thread captureThread([&]() {
cv::Mat frame;
int frameCount = 0;
while (cap.read(frame))
{
frameQueue.enqueue(frame.clone()); // Clone to ensure thread safety
frameCount++;
}
frameQueue.setFinished();
});
// Processing thread
std::thread processingThread([&]() {
cv::Mat frame;
int frameIndex = 0;
while (frameQueue.dequeue(frame))
{
// Detect objects in the frame
std::vector<Detection> results = detector.detect(frame);
// Draw bounding boxes on the frame
detector.drawBoundingBoxMask(frame, results); // Uncomment for mask drawing
// Enqueue the processed frame
processedQueue.enqueue(std::make_pair(frameIndex++, frame));
}
processedQueue.setFinished();
});
// Writing thread
std::thread writingThread([&]() {
std::pair<int, cv::Mat> processedFrame;
while (processedQueue.dequeue(processedFrame))
{
out.write(processedFrame.second);
}
});
// Wait for all threads to finish
captureThread.join();
processingThread.join();
writingThread.join();
// Release resources
cap.release();
out.release();
cv::destroyAllWindows();
std::cout << "Video processing completed successfully." << std::endl;
return 0;
}