使用 C++ 和 OpenCV 构建智能停车场视觉管理系统

本文将详细介绍如何利用 C++ 和 OpenCV 库,从零开始创建一个智能停车场管理系统。该系统通过摄像头捕捉的画面,能自动完成两项核心任务:

  1. 车位识别:通过检测地面上的黄色停车线,自动识别并标定出所有停车位的位置。
  2. 状态监控:实时监控每个车位,判断其是**“空闲”还是“被占用”**,并进行可视化展示。

🧠 核心逻辑与方法

我们将整个系统分为两个主要阶段:校准阶段监控阶段

1. 校准阶段 (Calibration Phase)

这个阶段的目标是自动定义停车位。我们只需要对一张空停车场的图片进行一次性处理。

  • 颜色分割: 将图像从 BGR 转换到 HSV 颜色空间。HSV 对光照变化不敏感,能更准确地提取出黄色。我们使用 cv::inRange 函数来创建一个只包含黄色标线的二值化“蒙版”。
  • 轮廓检测: 在蒙版上,我们使用 cv::findContours 来寻找所有黄色区域的轮廓。
  • 车位标定: 通过对轮廓的面积形状进行筛选,我们可以过滤掉噪声,只保留代表停车位的矩形区域。我们使用 cv::minAreaRect 来获取这些区域的精确位置(包括旋转角度),并将这些位置信息保存到一个文件中(例如 parking_spots.xml)。

2. 监控阶段 (Monitoring Phase)

这个阶段是系统的核心,它在实时的视频流上运行。

  • 加载车位数据: 程序首先从 parking_spots.xml 文件中加载所有预先标定好的停车位位置。
  • 占用检测: 对每一个停车位区域(ROI),我们使用一种简单而有效的方法来判断是否有车:边缘密度分析
    • 一个空车位(通常是沥青或水泥地面)的纹理较少,因此边缘也较少。
    • 一辆汽车具有复杂的轮廓、窗户、轮胎和车身线条,会产生大量的边缘
  • 状态判断: 我们在每个车位 ROI 上运行 Canny 边缘检测,然后计算边缘像素的数量。如果数量超过一个预设的阈值,我们就判定该车位**“被占用”,否则为“空闲”**。
  • 可视化: 根据判断结果,在视频画面上用不同颜色的矩形(例如红色代表占用,绿色代表空闲)框出每个车位,并显示可用的车位总数。

🛠️ 环境与准备

  • C++ 编译器: G++, Clang, 或 MSVC。
  • OpenCV 库: 确保已正确安装并配置。
  • 校准图像: 一张停车场空无一车时的图像,例如 empty_lot.jpg
  • 测试视频/图像: 一段停车场有车辆进出的视频或图像,例如 parking_video.mp4

💻 代码实现

我们将代码分为两个独立的文件:一个用于校准,一个用于监控。

第1部分: 校准程序 (calibrate.cpp)

这个程序只运行一次,用于生成车位坐标文件。

#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. 转换为 HSVcv::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;
}

第2部分: 监控程序 (monitor.cpp)

这个程序加载校准数据,并对视频进行实时分析。

#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>// 定义占用阈值
const int EDGE_PIXEL_THRESHOLD = 300; // 需要根据分辨率和场景微调int main(int argc, char** argv) {if (argc != 2) {std::cout << "Usage: ./monitor_app <video_file>" << 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. 提取每个车位的 ROIcv::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;
}

🚀 编译与运行

  1. 编译: 打开终端,使用 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 替换为你的版本。

  2. 运行:

    • 第一步:运行校准程序,传入空停车场的图片。
      ./calibrate_app empty_lot.jpg
      
      这会生成一个 parking_spots.xml 文件。
    • 第二步:运行监控程序,传入要分析的视频。
      ./monitor_app parking_video.mp4
      
      程序会加载 parking_spots.xml 并开始实时分析和显示结果。

总结与改进

这个项目展示了如何用标准 OpenCV 功能构建一个实用的计算机视觉应用。它的优点是逻辑清晰、实现简单。当然,它也有可以改进的地方:

  • 鲁棒性: 在光照剧烈变化或有阴影的情况下,基于边缘的检测可能会不稳定。可以引入更高级的特征(如 HOG)和机器学习分类器(如 SVM)来提高准确性。
  • 灵活性: 对于非黄色标线或不规则车位,需要修改颜色分割和轮廓筛选逻辑。
  • 用户界面: 可以创建一个简单的 GUI,让用户通过鼠标点击来手动调整或标定车位,而不是完全依赖自动检测。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/83972.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/83972.shtml
英文地址,请注明出处:http://en.pswp.cn/web/83972.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

服务器静态ip,网关不能占用*.*.*.1

网关不能占用*.*.*.1.1 通常用于运行关键服务&#xff08;如DHCP、NAT、DNS代理&#xff09;&#xff0c;.1 是网络世界的"VIP包厢"&#xff0c;普通用户强闯只会被"请出"。

自然语言处理【NLP】—— CBOW模型

文章目录 引言一、CBOW模型概述1.1 什么是CBOW模型1.2 CBOW vs Skip-gram 二、CBOW模型原理详解2.1 模型架构2.2 数学原理2.3 训练过程 三、CBOW的PyTorch实现四、CBOW模型的应用与优化4.1 典型应用场景4.2 性能优化技巧 五、CBOW的局限性六、结语 引言 在自然语言处理(NLP)领…

为MTK 9300开发板移植Linux系统(以Debian为例)的详细技术指南

以下是为MTK 9300开发板移植Linux系统(以Debian为例)的详细技术指南,涵盖环境搭建、内核移植、驱动适配(摄像头/显示器/WiFi)、系统集成与优化。 MTK 9300开发板Linux系统移植全流程指南 1 项目概述 1.1 硬件平台 SoC:MediaTek MTK9300 (ARMv8-A架构,4Cortex-A78 + 4C…

Java Lambda 表达式与 Stream API 全解析:从基础到进阶

以下是对您博客内容的优化版本&#xff0c;在保留原有核心内容的基础上&#xff0c;补充了Lambda表达式及Stream API的完整方法体系&#xff0c;并通过结构化排版和扩展说明提升可读性。 Java Lambda表达式与Stream API全解析&#xff1a;从基础到进阶 一、Lambda表达式与Str…

Let’s Encrypt(乐此加密) 免费SSL证书申请

一、前言 腾讯云、阿里云等平台都支持免费的SSL证书申请&#xff0c;但只支持单域名SSL证书申请&#xff0c;不支持泛域名证书申请&#xff0c;而且每年只有20张免费证书额度&#xff0c;自2024年4月25日之起免费申请的证书只有3个月有效期。域名比较多的情况下&#xff0c;更新…

SQLite3 性能优化

在嵌入式开发和轻量级应用场景中&#xff0c;SQLite3 作为轻量级数据库引擎&#xff0c;凭借其无需独立服务器、部署便捷等特点被广泛应用。然而&#xff0c;当面对大量数据的高速读写需求时&#xff0c;默认配置下的 SQLite3 性能往往难以满足要求。本文将从数据库配置调整、W…

零基础设计模式——行为型模式 - 状态模式

第四部分&#xff1a;行为型模式 - 状态模式 (State Pattern) 我们继续学习行为型模式&#xff0c;接下来是状态模式。这个模式允许一个对象在其内部状态改变时改变它的行为&#xff0c;对象看起来就像是改变了它的类。 核心思想&#xff1a;允许一个对象在其内部状态改变时改…

面向对象面试题集合

前言 记录面向对象面试题相关内容&#xff0c;方便复习及查漏补缺 题1.简述面向对象&#xff1f;主要特征是什么&#xff1f; 面向对象编程&#xff08;Object-Oriented Programming&#xff0c;简称OOP&#xff09;是一种以“对象”为核心的编程范式&#xff0c;通过将现实…

二十一、【用户管理与权限 - 篇三】角色管理:前端角色列表与 CRUD 实现

【用户管理与权限 - 篇三】角色管理:前端角色列表与 CRUD 实现 前言准备工作第一部分:更新 API 服务以包含角色管理第二部分:添加角色管理页面的路由和侧边栏入口第三部分:实现角色列表页面第四部分:实现角色表单对话框组件第五部分:全面测试总结前言 一个完善的权限系统…

Objective-c protocol 练习

题目描述&#xff1a; 请使用 Objective-C 中的 protocol 协议机制&#xff0c;实现一个简易的门禁控制系统。 系统包含两个类&#xff1a; AccessControlSystem —— 门禁系统&#xff0c;用于执行开门操作&#xff1b;Admin —— 实现权限判断逻辑的管理员。 要求如下&am…

科技创新赋能产业创新,双轮驱动助力新疆高质量发展!

在新疆维吾尔自治区成立70周年之际&#xff0c;中国产学研合作促进会于6月14日在乌鲁木齐举办“天山对话&#xff1a;推动新疆科技创新与产业创新”盛会。多位院士、专家、学者及企业代表齐聚一堂&#xff0c;探寻推动新疆科技创新和产业创新的新路径、新动能。活动现场&#x…

C#最佳实践:推荐使用 nameof 而非硬编码名称

C#最佳实践:推荐使用 nameof 而非硬编码名称 在 C# 编程领域,代码的可维护性、健壮性和可读性是衡量程序质量的重要指标。在日常开发中,我们常常会遇到需要引用类型、成员或变量名称的场景,比如在抛出异常时指定错误相关的变量名、在日志记录中标记关键元素名称等。传统的…

vue3 iframe 跨域-通讯

一、基础嵌套方法 直接在 HTML 中使用 <iframe> 标签指定 src 属性&#xff1a; <iframe src"https://目标网址.com" width"800" height"600"></iframe>‌限制‌&#xff1a;若目标网站设置了 X-Frame-Options 响应头&#x…

Iceberg与Hive集成深度

一、Iceberg在Hive中的ACID事务实现与实战 1.1 传统Hive的事务局限性 Hive原生仅支持非事务表&#xff08;Non-ACID&#xff09;&#xff0c;存在以下痛点&#xff1a; 不支持行级更新/删除并发写入时数据一致性无法保证无事务回滚机制历史版本查询需手动实现 1.2 Iceberg为…

深入剖析 Celery:分布式异步任务处理的利器

本文在创作过程中借助 AI 工具辅助资料整理与内容优化。图片来源网络。 文章目录 引言一、Celery 概述1.1 Celery 的定义和作用1.2 Celery 的应用场景 二、Celery 架构分析2.1 Celery 的整体架构2.2 消息中间件&#xff08;Broker&#xff09;2.3 任务队列&#xff08;Task Que…

Flask应用中处理异步事件(后台线程+事件循环)的方法(2)

在上一节&#xff0c;我们讲述了最简单最基础的后线程的建立&#xff0c;现在我们将进行拓展 Flask应用中处理异步事件&#xff08;后台线程事件循环&#xff09;的方法&#xff08;1&#xff09; 在我们的实际应用当中&#xff0c;我们需要定义三个东西 一个多线程的信号旗&am…

C++(面向对象编程)

思维导图 面向对象 1.面向对象思想 概念&#xff1a;面向对象编程&#xff08;OOP&#xff09;是一种以对象为基础的编程范式&#xff0c;强调将数据和操作数据的方法封装在一起。这就是上篇文章讲过的。面向过程是以“怎么解决问题”为核心&#xff0c;而面向对象思想在于“谁…

驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接,

驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接,Error: “The server selected protocol version TLS10 is not accepted by client preferences [TLS13&#xff0c;TLS12]”. ClientConnectionId:d5fd8d69-ae88-4055-9f6d-6e8515224ce2】。 基本上就是…

【三大前端语言之一】交互:JavaScript详解

【三大前端语言之一】交互&#xff1a;JavaScript详解 在学习完HTML和CSS之后&#xff0c;最后一门前端语言——JavaScript&#xff0c;是重中之重。HTML负责页面结构&#xff0c;CSS负责页面样式&#xff0c;而JavaScript则赋予网页“生命”&#xff0c;让网页可以动起来、响…

LangChain面试内容整理-知识点12:检索器(Retriever)接口与实现

在LangChain中,检索器(Retriever)是一个抽象接口,负责根据用户查询从数据源中检索相关文档。可以把Retriever理解为“搜索工具”:给它一个未经结构化的查询文本(如用户问题),它返回一组与之相关的 Document 对象。内部可以基于向量相似度、数据库查询、甚至网络搜索。 …