本文将详细介绍如何利用 C++ 和 OpenCV 库,从零开始创建一个智能停车场管理系统。该系统通过摄像头捕捉的画面,能自动完成两项核心任务:
我们将整个系统分为两个主要阶段:校准阶段和监控阶段。
这个阶段的目标是自动定义停车位。我们只需要对一张空停车场的图片进行一次性处理。
cv::inRange
函数来创建一个只包含黄色标线的二值化“蒙版”。cv::findContours
来寻找所有黄色区域的轮廓。cv::minAreaRect
来获取这些区域的精确位置(包括旋转角度),并将这些位置信息保存到一个文件中(例如 parking_spots.xml
)。这个阶段是系统的核心,它在实时的视频流上运行。
parking_spots.xml
文件中加载所有预先标定好的停车位位置。empty_lot.jpg
。parking_video.mp4
。我们将代码分为两个独立的文件:一个用于校准,一个用于监控。
calibrate.cpp
)这个程序只运行一次,用于生成车位坐标文件。
#include
#include
#include
int main(int argc, char** argv) {
if (argc != 2) {
std::cout << "Usage: ./calibrate_app " << 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. 转换为 HSV
cv::Mat hsv;
cv::cvtColor(frame, hsv, cv::COLOR_BGR2HSV);
// 2. 颜色阈值分割 (针对黄色)
// 注意: 这个范围可能需要根据你的实际光照进行微调
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. 形态学操作去噪
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. 过滤轮廓并获取最小外接旋转矩形
double area = cv::contourArea(contour);
if (area > 2000) { // 根据车位实际像素大小调整此阈值
cv::RotatedRect rotatedRect = cv::minAreaRect(contour);
parkingSpots.push_back(rotatedRect);
}
}
// 6. 保存车位坐标到文件
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;
// 可选: 可视化检测到的车位
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;
}
monitor.cpp
)这个程序加载校准数据,并对视频进行实时分析。
#include
#include
#include
// 定义占用阈值
const int EDGE_PIXEL_THRESHOLD = 300; // 需要根据分辨率和场景微调
int main(int argc, char** argv) {
if (argc != 2) {
std::cout << "Usage: ./monitor_app " << std::endl;
return -1;
}
// 1. 加载车位数据
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. 提取每个车位的 ROI
cv::Rect br = spot.boundingRect();
// 保证 ROI 在图像边界内
br &= cv::Rect(0, 0, frame.cols, frame.rows);
if (br.width == 0 || br.height == 0) continue;
roi = gray(br);
// 3. 计算 ROI 内的边缘密度
cv::Canny(roi, edges, 100, 200);
int edge_pixels = cv::countNonZero(edges);
// 4. 判断车位状态并可视化
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. 显示状态信息
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;
}
编译: 打开终端,使用 g++
和 pkg-config
编译两个程序。
# 编译校准程序
g++ -o calibrate_app calibrate.cpp $(pkg-config --cflags --libs opencv4)
# 编译监控程序
g++ -o monitor_app monitor.cpp $(pkg-config --cflags --libs opencv4)
注意: 如果你的 OpenCV 版本不是 4,请将 opencv4
替换为你的版本。
运行:
./calibrate_app empty_lot.jpg
这会生成一个 parking_spots.xml
文件。./monitor_app parking_video.mp4
程序会加载 parking_spots.xml
并开始实时分析和显示结果。这个项目展示了如何用标准 OpenCV 功能构建一个实用的计算机视觉应用。它的优点是逻辑清晰、实现简单。当然,它也有可以改进的地方: