網(wǎng)站推廣需要域名遷移嘉興seo外包服務(wù)商
使用 C++ 和 OpenCV 構(gòu)建智能停車場(chǎng)視覺管理系統(tǒng)
本文將詳細(xì)介紹如何利用 C++ 和 OpenCV 庫,從零開始創(chuàng)建一個(gè)智能停車場(chǎng)管理系統(tǒng)。該系統(tǒng)通過攝像頭捕捉的畫面,能自動(dòng)完成兩項(xiàng)核心任務(wù):
- 車位識(shí)別:通過檢測(cè)地面上的黃色停車線,自動(dòng)識(shí)別并標(biāo)定出所有停車位的位置。
- 狀態(tài)監(jiān)控:實(shí)時(shí)監(jiān)控每個(gè)車位,判斷其是**“空閑”還是“被占用”**,并進(jìn)行可視化展示。
🧠 核心邏輯與方法
我們將整個(gè)系統(tǒng)分為兩個(gè)主要階段:校準(zhǔn)階段和監(jiān)控階段。
1. 校準(zhǔn)階段 (Calibration Phase)
這個(gè)階段的目標(biāo)是自動(dòng)定義停車位。我們只需要對(duì)一張空停車場(chǎng)的圖片進(jìn)行一次性處理。
- 顏色分割: 將圖像從 BGR 轉(zhuǎn)換到 HSV 顏色空間。HSV 對(duì)光照變化不敏感,能更準(zhǔn)確地提取出黃色。我們使用
cv::inRange
函數(shù)來創(chuàng)建一個(gè)只包含黃色標(biāo)線的二值化“蒙版”。 - 輪廓檢測(cè): 在蒙版上,我們使用
cv::findContours
來尋找所有黃色區(qū)域的輪廓。 - 車位標(biāo)定: 通過對(duì)輪廓的面積和形狀進(jìn)行篩選,我們可以過濾掉噪聲,只保留代表停車位的矩形區(qū)域。我們使用
cv::minAreaRect
來獲取這些區(qū)域的精確位置(包括旋轉(zhuǎn)角度),并將這些位置信息保存到一個(gè)文件中(例如parking_spots.xml
)。
2. 監(jiān)控階段 (Monitoring Phase)
這個(gè)階段是系統(tǒng)的核心,它在實(shí)時(shí)的視頻流上運(yùn)行。
- 加載車位數(shù)據(jù): 程序首先從
parking_spots.xml
文件中加載所有預(yù)先標(biāo)定好的停車位位置。 - 占用檢測(cè): 對(duì)每一個(gè)停車位區(qū)域(ROI),我們使用一種簡(jiǎn)單而有效的方法來判斷是否有車:邊緣密度分析。
- 一個(gè)空車位(通常是瀝青或水泥地面)的紋理較少,因此邊緣也較少。
- 一輛汽車具有復(fù)雜的輪廓、窗戶、輪胎和車身線條,會(huì)產(chǎn)生大量的邊緣。
- 狀態(tài)判斷: 我們?cè)诿總€(gè)車位 ROI 上運(yùn)行 Canny 邊緣檢測(cè),然后計(jì)算邊緣像素的數(shù)量。如果數(shù)量超過一個(gè)預(yù)設(shè)的閾值,我們就判定該車位**“被占用”,否則為“空閑”**。
- 可視化: 根據(jù)判斷結(jié)果,在視頻畫面上用不同顏色的矩形(例如紅色代表占用,綠色代表空閑)框出每個(gè)車位,并顯示可用的車位總數(shù)。
🛠? 環(huán)境與準(zhǔn)備
- C++ 編譯器: G++, Clang, 或 MSVC。
- OpenCV 庫: 確保已正確安裝并配置。
- 校準(zhǔn)圖像: 一張停車場(chǎng)空無一車時(shí)的圖像,例如
empty_lot.jpg
。 - 測(cè)試視頻/圖像: 一段停車場(chǎng)有車輛進(jìn)出的視頻或圖像,例如
parking_video.mp4
。
💻 代碼實(shí)現(xiàn)
我們將代碼分為兩個(gè)獨(dú)立的文件:一個(gè)用于校準(zhǔn),一個(gè)用于監(jiān)控。
第1部分: 校準(zhǔn)程序 (calibrate.cpp
)
這個(gè)程序只運(yùn)行一次,用于生成車位坐標(biāo)文件。
#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>int main(int argc, char** argv) {if (argc != 2) {std::cout << "Usage: ./calibrate_app <empty_lot_image>" << std::endl;return -1;}cv::Mat frame = cv::imread(argv[1]);if (frame.empty()) {std::cerr << "Error: Could not read the image." << std::endl;return -1;}// 1. 轉(zhuǎn)換為 HSVcv::Mat hsv;cv::cvtColor(frame, hsv, cv::COLOR_BGR2HSV);// 2. 顏色閾值分割 (針對(duì)黃色)// 注意: 這個(gè)范圍可能需要根據(jù)你的實(shí)際光照進(jìn)行微調(diào)cv::Scalar lower_yellow(20, 100, 100);cv::Scalar upper_yellow(30, 255, 255);cv::Mat mask;cv::inRange(hsv, lower_yellow, upper_yellow, mask);// 3. 形態(tài)學(xué)操作去噪cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));cv::morphologyEx(mask, mask, cv::MORPH_CLOSE, kernel, cv::Point(-1,-1), 2);// 4. 尋找輪廓std::vector<std::vector<cv::Point>> contours;cv::findContours(mask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);std::vector<cv::RotatedRect> parkingSpots;for (const auto& contour : contours) {// 5. 過濾輪廓并獲取最小外接旋轉(zhuǎn)矩形double area = cv::contourArea(contour);if (area > 2000) { // 根據(jù)車位實(shí)際像素大小調(diào)整此閾值cv::RotatedRect rotatedRect = cv::minAreaRect(contour);parkingSpots.push_back(rotatedRect);}}// 6. 保存車位坐標(biāo)到文件cv::FileStorage fs("parking_spots.xml", cv::FileStorage::WRITE);fs << "parking_spots" << parkingSpots;fs.release();std::cout << "Successfully detected and saved " << parkingSpots.size() << " parking spots." << std::endl;// 可選: 可視化檢測(cè)到的車位for (const auto& spot : parkingSpots) {cv::Point2f vertices[4];spot.points(vertices);for (int i = 0; i < 4; i++) {cv::line(frame, vertices[i], vertices[(i + 1) % 4], cv::Scalar(0, 255, 0), 2);}}cv::imshow("Detected Parking Spots", frame);cv::waitKey(0);return 0;
}
第2部分: 監(jiān)控程序 (monitor.cpp
)
這個(gè)程序加載校準(zhǔn)數(shù)據(jù),并對(duì)視頻進(jìn)行實(shí)時(shí)分析。
#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>// 定義占用閾值
const int EDGE_PIXEL_THRESHOLD = 300; // 需要根據(jù)分辨率和場(chǎng)景微調(diào)int main(int argc, char** argv) {if (argc != 2) {std::cout << "Usage: ./monitor_app <video_file>" << std::endl;return -1;}// 1. 加載車位數(shù)據(jù)std::vector<cv::RotatedRect> parkingSpots;cv::FileStorage fs("parking_spots.xml", cv::FileStorage::READ);if (!fs.isOpened()) {std::cerr << "Error: Could not open parking_spots.xml. Run calibration first." << std::endl;return -1;}fs["parking_spots"] >> parkingSpots;fs.release();cv::VideoCapture cap(argv[1]);if (!cap.isOpened()) {std::cerr << "Error: Could not open video file." << std::endl;return -1;}cv::Mat frame, gray, roi, edges;while (cap.read(frame)) {int available_spots = 0;cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY);for (const auto& spot : parkingSpots) {// 2. 提取每個(gè)車位的 ROIcv::Rect br = spot.boundingRect();// 保證 ROI 在圖像邊界內(nèi)br &= cv::Rect(0, 0, frame.cols, frame.rows);if (br.width == 0 || br.height == 0) continue;roi = gray(br);// 3. 計(jì)算 ROI 內(nèi)的邊緣密度cv::Canny(roi, edges, 100, 200);int edge_pixels = cv::countNonZero(edges);// 4. 判斷車位狀態(tài)并可視化cv::Point2f vertices[4];spot.points(vertices);bool occupied = edge_pixels > EDGE_PIXEL_THRESHOLD;cv::Scalar color = occupied ? cv::Scalar(0, 0, 255) : cv::Scalar(0, 255, 0);for (int i = 0; i < 4; i++) {cv::line(frame, vertices[i], vertices[(i + 1) % 4], color, 2);}if (!occupied) {available_spots++;}}// 5. 顯示狀態(tài)信息std::string status_text = "Available: " + std::to_string(available_spots) + "/" + std::to_string(parkingSpots.size());cv::putText(frame, status_text, cv::Point(20, 40), cv::FONT_HERSHEY_SIMPLEX, 1.5, cv::Scalar(255, 255, 0), 3);cv::imshow("Parking Lot Monitor", frame);if (cv::waitKey(30) >= 0) break;}return 0;
}
🚀 編譯與運(yùn)行
-
編譯: 打開終端,使用
g++
和pkg-config
編譯兩個(gè)程序。# 編譯校準(zhǔn)程序 g++ -o calibrate_app calibrate.cpp $(pkg-config --cflags --libs opencv4)# 編譯監(jiān)控程序 g++ -o monitor_app monitor.cpp $(pkg-config --cflags --libs opencv4)
注意: 如果你的 OpenCV 版本不是 4,請(qǐng)將
opencv4
替換為你的版本。 -
運(yùn)行:
- 第一步:運(yùn)行校準(zhǔn)程序,傳入空停車場(chǎng)的圖片。
這會(huì)生成一個(gè)./calibrate_app empty_lot.jpg
parking_spots.xml
文件。 - 第二步:運(yùn)行監(jiān)控程序,傳入要分析的視頻。
程序會(huì)加載./monitor_app parking_video.mp4
parking_spots.xml
并開始實(shí)時(shí)分析和顯示結(jié)果。
- 第一步:運(yùn)行校準(zhǔn)程序,傳入空停車場(chǎng)的圖片。
總結(jié)與改進(jìn)
這個(gè)項(xiàng)目展示了如何用標(biāo)準(zhǔn) OpenCV 功能構(gòu)建一個(gè)實(shí)用的計(jì)算機(jī)視覺應(yīng)用。它的優(yōu)點(diǎn)是邏輯清晰、實(shí)現(xiàn)簡(jiǎn)單。當(dāng)然,它也有可以改進(jìn)的地方:
- 魯棒性: 在光照劇烈變化或有陰影的情況下,基于邊緣的檢測(cè)可能會(huì)不穩(wěn)定??梢砸敫呒?jí)的特征(如 HOG)和機(jī)器學(xué)習(xí)分類器(如 SVM)來提高準(zhǔn)確性。
- 靈活性: 對(duì)于非黃色標(biāo)線或不規(guī)則車位,需要修改顏色分割和輪廓篩選邏輯。
- 用戶界面: 可以創(chuàng)建一個(gè)簡(jiǎn)單的 GUI,讓用戶通過鼠標(biāo)點(diǎn)擊來手動(dòng)調(diào)整或標(biāo)定車位,而不是完全依賴自動(dòng)檢測(cè)。