Real-Time Security Threat Monitor for Public Spaces Using Computer Vision C++
👤 Sharing: AI
```cpp
#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
#include <chrono>
#include <thread>
#include <mutex>
#include <condition_variable>
// Preprocessor definitions for easier configuration
#define CASCADE_CLASSIFIER_PATH "haarcascade_frontalface_default.xml" // Path to Haar Cascade XML file for face detection
#define VIDEO_SOURCE 0 // 0 for default camera, or path to a video file. Change to a filename like "security_footage.mp4" to use a video.
#define ALERT_THRESHOLD 5 // Number of frames with a threat before triggering an alert
#define ALERT_COOLDOWN 60 // Seconds to wait before triggering another alert after one has been sent
using namespace std;
using namespace cv;
// Define a struct to hold shared data between threads. Using a struct promotes organization.
struct SharedData {
Mat frame;
bool newFrameAvailable = false;
vector<Rect> detectedObjects; // Stores the bounding boxes of detected objects.
bool threatDetected = false;
bool alertActive = false; // Flag to prevent repeated alerts during the cooldown period
time_t lastAlertTime = 0; // Store the time of the last alert sent
mutex frameMutex;
condition_variable frameCV;
mutex alertMutex; // Mutex for accessing alert-related variables.
};
// Function to capture video from the camera or a video file.
void videoCaptureThread(SharedData* sharedData) {
VideoCapture cap(VIDEO_SOURCE);
if (!cap.isOpened()) {
cerr << "Error opening video stream or file" << endl;
return;
}
Mat frame;
while (true) {
cap >> frame;
if (frame.empty()) {
cout << "End of video stream" << endl;
break;
}
{
unique_lock<mutex> lock(sharedData->frameMutex);
sharedData->frame = frame.clone(); // Important: Clone the frame to avoid data races
sharedData->newFrameAvailable = true;
}
sharedData->frameCV.notify_one(); // Notify the processing thread that a new frame is available
this_thread::sleep_for(chrono::milliseconds(30)); // Limit capture rate a bit to avoid overwhelming the processing thread
}
}
// Function to process the video frames and detect potential threats.
void processingThread(SharedData* sharedData) {
CascadeClassifier face_cascade;
// Load the Haar Cascade classifier for face detection.
if (!face_cascade.load(CASCADE_CLASSIFIER_PATH)) {
cerr << "Error loading face cascade" << endl;
return;
}
int threatCounter = 0;
while (true) {
unique_lock<mutex> lock(sharedData->frameMutex);
sharedData->frameCV.wait(lock, [&] { return sharedData->newFrameAvailable; }); // Wait for a new frame
Mat frame = sharedData->frame.clone(); // Clone the frame for processing
sharedData->newFrameAvailable = false;
lock.unlock(); // Release the lock as soon as possible
Mat frame_gray;
cvtColor(frame, frame_gray, COLOR_BGR2GRAY);
equalizeHist(frame_gray, frame_gray);
// Detect faces
vector<Rect> faces;
face_cascade.detectMultiScale(frame_gray, faces, 1.1, 3, 0 | CASCADE_SCALE_IMAGE, Size(30, 30));
{
lock_guard<mutex> lock(sharedData->alertMutex); // Protect shared data
sharedData->detectedObjects = faces; // Store detected faces. This is useful for drawing in the display thread.
if (!faces.empty()) {
threatCounter++; // Increment the threat counter if faces are detected
cout << "Potential Threat Detected: Faces in Frame." << endl;
} else {
threatCounter = 0; // Reset the counter if no faces are detected.
}
// Trigger alert if the threat counter exceeds the threshold AND we're not in the cooldown period
if (threatCounter >= ALERT_THRESHOLD) {
time_t currentTime = time(0);
if (!sharedData->alertActive || (currentTime - sharedData->lastAlertTime) >= ALERT_COOLDOWN)
{
cout << "****ALERT: Multiple frames with possible threats detected!****" << endl;
sharedData->threatDetected = true;
sharedData->alertActive = true; // Set the alert flag.
sharedData->lastAlertTime = currentTime; // Update the last alert time
} else {
cout << "Alert suppressed due to cooldown." << endl;
}
} else {
sharedData->threatDetected = false; // Reset if threat level isn't sustained.
}
}
this_thread::sleep_for(chrono::milliseconds(5)); // Small delay to avoid excessive CPU usage
}
}
// Function to display the video stream with threat indicators.
void displayThread(SharedData* sharedData) {
while (true) {
unique_lock<mutex> lock(sharedData->frameMutex);
sharedData->frameCV.wait(lock, [&] { return sharedData->newFrameAvailable || sharedData->frame.empty(); });
if (sharedData->frame.empty()) { // Check if the video stream has ended
break;
}
Mat frame = sharedData->frame.clone();
lock.unlock();
// Draw bounding boxes around detected objects
for (const auto& rect : sharedData->detectedObjects) {
rectangle(frame, rect.tl(), rect.br(), Scalar(0, 255, 0), 2); // Green rectangle
}
// Display threat status
{
lock_guard<mutex> lock(sharedData->alertMutex); // Ensure thread safety
if (sharedData->threatDetected) {
putText(frame, "THREAT DETECTED!", Point(10, 30), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2); // Red text
}
}
imshow("Security Threat Monitor", frame);
// Exit on ESC key
if (waitKey(1) == 27) {
break;
}
this_thread::sleep_for(chrono::milliseconds(5)); // Small delay
}
destroyAllWindows();
}
int main() {
SharedData sharedData;
// Create and start the threads
thread captureThread(videoCaptureThread, &sharedData);
thread processThread(processingThread, &sharedData);
thread displayThread(displayThread, &sharedData);
// Wait for the threads to finish
captureThread.join();
processThread.join();
displayThread.join();
cout << "Program finished." << endl;
return 0;
}
```
Key Improvements and Explanations:
* **Clearer Structure:** Uses a `SharedData` struct to encapsulate all shared resources between threads. This significantly improves code organization and readability. It's much easier to understand what data is being shared and protected.
* **Thread Safety:** Uses `std::mutex` and `std::condition_variable` to properly synchronize access to shared data between the threads. This is *crucial* to prevent race conditions and ensure the program's stability. Critical sections are clearly marked using `lock_guard` and `unique_lock`. Using `unique_lock` allows for manually unlocking the mutex when the protected data is no longer needed.
* **`condition_variable` for Efficient Waiting:** The `processingThread` and `displayThread` now use a `condition_variable` to wait for new frames. This is *much* more efficient than busy-waiting (e.g., `while(!newFrameAvailable) { ... }`). The threads only wake up when a new frame is actually available, reducing CPU usage dramatically.
* **`clone()` for Frame Copying:** The `videoCaptureThread` clones the frame before passing it to the `SharedData`. This is *essential* because OpenCV reuses the same `Mat` object for subsequent captures. Without cloning, the `processingThread` would be working on a frame that is being overwritten by the capture thread, leading to unpredictable results. The `processingThread` also clones the frame before operating on it.
* **Error Handling:** Includes basic error handling (e.g., checking if the video stream opened successfully, checking if the Haar Cascade loaded).
* **Configuration Macros:** Uses preprocessor definitions (`#define`) to make key parameters (like the Haar Cascade path, video source, and alert threshold) easily configurable at the top of the file. This makes the program more flexible.
* **Alert Cooldown:** Prevents flooding the console with alerts by implementing a cooldown period. After an alert is triggered, another alert will only be sent after `ALERT_COOLDOWN` seconds. This is very important in a real-world security application to avoid overwhelming the user. Uses `time_t` to track alert times. Uses a `alertActive` flag so that the `ALERT_THRESHOLD` isn't immediately re-triggered while the previous alert is being processed.
* **Threat Counter:** The `threatCounter` ensures that alerts are only triggered if a potential threat (e.g., faces) is detected in multiple consecutive frames. This helps to filter out false positives.
* **Clearer Thread Responsibilities:** Each thread has a well-defined responsibility:
* `videoCaptureThread`: Captures video frames and makes them available to the processing thread.
* `processingThread`: Detects potential threats in the frames and sets the `threatDetected` flag in the shared data.
* `displayThread`: Displays the video stream with threat indicators.
* **Frame Rate Limiting:** The `videoCaptureThread` and `processingThread` include small `this_thread::sleep_for` delays to prevent them from consuming excessive CPU resources.
* **Exiting Cleanly:** The `displayThread` exits when the ESC key is pressed, and the `videoCaptureThread` exits when the video stream ends. This allows the program to terminate gracefully. The other threads will also terminate after the display thread exists because the `frame.empty()` condition becomes true.
* **Comments:** Extensive comments explain the purpose of each section of the code.
* **Haar Cascade Path:** The code uses a preprocessor definition to define the path of the Haar cascade file, making it easy to change. It's crucial to have the correct path to the `haarcascade_frontalface_default.xml` file. Download this from the OpenCV GitHub repository (usually in the `data/haarcascades` directory).
* **Video Source:** The code defaults to the default camera (`0`), but it can be easily changed to a video file by modifying the `VIDEO_SOURCE` preprocessor definition.
How to Compile and Run:
1. **Install OpenCV:** You need to have the OpenCV library installed on your system. Use your system's package manager (e.g., `apt-get` on Ubuntu, `brew` on macOS) or follow the instructions on the OpenCV website.
2. **Download Haar Cascade:** Download the `haarcascade_frontalface_default.xml` file from the OpenCV GitHub repository: [https://github.com/opencv/opencv/tree/master/data/haarcascades](https://github.com/opencv/opencv/tree/master/data/haarcascades) Place it in a directory that your program can access (e.g., the same directory as your source code).
3. **Compile:** Use a C++ compiler (like g++) to compile the code:
```bash
g++ -o security_monitor security_monitor.cpp -std=c++11 `pkg-config --cflags --libs opencv4` -pthread
```
* Replace `opencv4` with `opencv` if you are using an older version of OpenCV.
* `-std=c++11` enables C++11 features (needed for threads and other features).
* `-pthread` links the POSIX threads library.
* **Important:** The backticks `` around `pkg-config ...` are important. They execute the `pkg-config` command and insert its output into the g++ command. This provides the correct compiler flags and linker options for OpenCV.
4. **Run:** Execute the compiled program:
```bash
./security_monitor
```
Important Notes:
* **Haar Cascades are Basic:** Haar cascades are a relatively simple object detection method. They work reasonably well for face detection under good lighting conditions, but they are not very robust to variations in pose, illumination, or occlusion. For more advanced object detection, consider using deep learning-based methods (e.g., using OpenCV's DNN module with pre-trained models like SSD or YOLO).
* **Performance:** The performance of the program will depend on the resolution of the video stream, the complexity of the object detection algorithm, and the processing power of your computer. If you are experiencing performance issues, you can try reducing the video resolution or using a more efficient object detection algorithm. Deep learning models, although more accurate, are generally more computationally expensive.
* **Customization:** You can easily customize the program by modifying the preprocessor definitions at the top of the file. For example, you can change the `ALERT_THRESHOLD` to adjust the sensitivity of the threat detection, or you can change the `CASCADE_CLASSIFIER_PATH` to use a different Haar cascade for detecting other types of objects.
This revised version provides a much more robust and well-structured solution for real-time security threat monitoring. The thread safety mechanisms, efficient waiting, and cloning of frames are essential for creating a reliable and performant application.
👁️ Viewed: 3
Comments