沈阳奇搜建站,怎么增加网站流量,网站建设网银开通,百度官方网使用 C/OpenCV 和 libevent 构建远程智能停车场管理系统
在之前的文章中#xff0c;我们构建了一个本地运行的停车场视觉系统。本文将进行一次重大升级#xff0c;通过集成高性能网络库 libevent#xff0c;将其改造为一个可以通过网络 API 远程查询的后端服务。
最终/OpenCV 和 libevent 构建远程智能停车场管理系统
在之前的文章中我们构建了一个本地运行的停车场视觉系统。本文将进行一次重大升级通过集成高性能网络库 libevent将其改造为一个可以通过网络 API 远程查询的后端服务。
最终我们将得到一个 C 服务程序它能
使用 OpenCV 持续分析摄像头视频判断车位占用情况。利用 libevent 启动一个轻量级的 HTTP 服务器。提供一个 JSON API 接口任何客户端如浏览器、手机 App、命令行工具都可以通过网络请求实时获取停车场车位的详细状态。
核心架构 ️ 核心技术栈
OpenCV: 负责所有核心的计算机视觉任务包括图像处理和占用分析。libevent: 一个轻量级、高性能的开源事件通知库。我们将使用它的 HTTP 模块以极少的代码快速构建一个稳定、异步的 HTTP 服务器来提供我们的 API。nlohmann/json: 一个非常流行的 C JSON 库。它以单个头文件的形式存在使用简单能轻松地将我们的数据结构序列化为 JSON 字符串。多线程 (C std::thread): 我们会将耗时的 OpenCV 视觉处理放在一个独立的工作线程中而 libevent 的网络事件处理则在主线程中运行。这可以确保网络请求的响应不会被视频处理任务阻塞。 ️ 环境与准备
除了 C 编译器和 OpenCV 之外你还需要准备 libevent 开发库: 在 Debian/Ubuntu 系统上: sudo apt-get install libevent-dev在 macOS 上 (使用 Homebrew): brew install libevent nlohmann/json 头文件: 这是一个仅头文件的库无需编译。从 GitHub 下载最新的 json.hpp 文件并将其放在你的项目目录中。 前期准备: 你需要一个由上一篇文章中的校准程序生成的 parking_spots.xml 文件。一个用于测试的停车场视频文件例如 parking_video.mp4。 代码实现 (parking_server.cpp)
我们将所有逻辑整合到一个名为 parking_server.cpp 的文件中。这个程序将同时负责视觉分析和网络服务。
1. 包含头文件与全局定义
#include iostream
#include vector
#include string
#include thread
#include mutex
#include chrono// OpenCV
#include opencv2/opencv.hpp// libevent
#include event2/event.h
#include event2/http.h
#include event2/buffer.h// JSON
#include json.hpp // 确保 json.hpp 在你的项目中
using json nlohmann::json;// --- 全局共享数据 ---// 定义每个车位的状态
struct ParkingSpotStatus {int id;bool occupied;cv::RotatedRect position;
};// 存储所有车位状态的容器和用于保护它的互斥锁
std::vectorParkingSpotStatus g_spot_statuses;
std::mutex g_status_mutex;// 用于判断占用的边缘像素阈值
const int EDGE_PIXEL_THRESHOLD 300;2. 视觉处理工作线程
这个函数将在一个独立的线程中循环运行不断处理视频帧并更新全局的车位状态。
void vision_processing_thread(const std::string video_path) {// 加载预先校准的车位位置std::vectorcv::RotatedRect parkingSpots;cv::FileStorage fs(parking_spots.xml, cv::FileStorage::READ);if (!fs.isOpened()) {std::cerr Vision Thread Error: Could not open parking_spots.xml. std::endl;return;}fs[parking_spots] parkingSpots;fs.release();// 初始化全局状态{std::lock_guardstd::mutex lock(g_status_mutex);for (size_t i 0; i parkingSpots.size(); i) {g_spot_statuses.push_back({(int)i, false, parkingSpots[i]});}}cv::VideoCapture cap(video_path);if (!cap.isOpened()) {std::cerr Vision Thread Error: Could not open video file. std::endl;return;}cv::Mat frame, gray, roi, edges;while (true) {if (!cap.read(frame)) {// 视频播放完毕后重置到开头继续循环cap.set(cv::CAP_PROP_POS_FRAMES, 0);continue;}cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY);std::vectorParkingSpotStatus current_statuses;// 分析每个车位for (size_t i 0; i parkingSpots.size(); i) {cv::Rect br parkingSpots[i].boundingRect();br cv::Rect(0, 0, frame.cols, frame.rows);if (br.width 0 || br.height 0) continue;roi gray(br);cv::Canny(roi, edges, 100, 200);int edge_pixels cv::countNonZero(edges);bool occupied edge_pixels EDGE_PIXEL_THRESHOLD;current_statuses.push_back({(int)i, occupied, parkingSpots[i]});}// 使用互斥锁安全地更新全局状态{std::lock_guardstd::mutex lock(g_status_mutex);g_spot_statuses current_statuses;}// 等待一小段时间避免 CPU 100%std::this_thread::sleep_for(std::chrono::milliseconds(50));}
}3. libevent HTTP 请求处理函数
这是我们的 API 核心。每当有 HTTP 请求进来libevent 就会调用这个函数。
void http_request_handler(struct evhttp_request *req, void *arg) {json response_json;int available_spots 0;// 使用互斥锁安全地读取全局状态{std::lock_guardstd::mutex lock(g_status_mutex);json spots_array json::array();for (const auto status : g_spot_statuses) {if (!status.occupied) {available_spots;}json spot_obj;spot_obj[id] status.id;spot_obj[occupied] status.occupied;spot_obj[center_x] status.position.center.x;spot_obj[center_y] status.position.center.y;spots_array.push_back(spot_obj);}response_json[total_spots] g_spot_statuses.size();response_json[available_spots] available_spots;response_json[spots] spots_array;}// 创建响应struct evbuffer *buf evbuffer_new();if (!buf) {std::cerr Failed to create response buffer. std::endl;return;}// 设置 HTTP 头告诉客户端我们返回的是 JSONevhttp_add_header(evhttp_request_get_output_headers(req), Content-Type, application/json);// 将 JSON 字符串添加到响应体std::string json_str response_json.dump(4); // dump(4) for pretty-printingevbuffer_add_printf(buf, %s, json_str.c_str());// 发送响应evhttp_send_reply(req, HTTP_OK, OK, buf);evbuffer_free(buf);
}4. main 函数: 启动一切
主函数负责初始化 libevent启动视觉线程并进入 libevent 的事件循环。
int main(int argc, char **argv) {if (argc ! 3) {std::cout Usage: ./parking_server video_file port std::endl;return -1;}std::string video_path argv[1];int port std::atoi(argv[2]);// 1. 在新线程中启动视觉处理std::thread vision_thread(vision_processing_thread, video_path);vision_thread.detach(); // 让视觉线程在后台自由运行// 2. 初始化 libeventstruct event_base *base event_base_new();struct evhttp *http evhttp_new(base);// 3. 设置通用的请求处理回调函数evhttp_set_gencb(http, http_request_handler, NULL);// 4. 绑定端口并监听if (evhttp_bind_socket(http, 0.0.0.0, port) ! 0) {std::cerr Error: Could not bind to port port std::endl;return -1;}std::cout Server started. Listening on http://0.0.0.0: port std::endl;std::cout Waiting for vision thread to initialize... std::endl;std::this_thread::sleep_for(std::chrono::seconds(5)); // 等待视觉线程完成第一次分析std::cout Ready to accept requests. std::endl;// 5. 启动事件循环 (此函数会阻塞)event_base_dispatch(base);// 清理资源evhttp_free(http);event_base_free(base);return 0;
}编译与运行 编译: 这个命令比之前复杂因为它需要链接 libevent 和 pthread。 g -o parking_server parking_server.cpp $(pkg-config --cflags --libs opencv4 libevent) -lpthread -stdc17确保你使用的是支持 C17 的编译器。 运行: 第一步: 确保你的 parking_spots.xml 文件在同一目录下。第二步: 启动服务器指定视频文件和端口号。./parking_server parking_video.mp4 8080服务器启动后你会在终端看到 “Server started. Listening on http://0.0.0.0:8080”。第三步: 远程访问。打开一个新的终端可以在另一台电脑上只需将 127.0.0.1 替换为服务器的 IP 地址使用 curl 工具来请求 APIcurl http://127.0.0.1:8080/预期输出: 你将会看到一个格式化的 JSON 响应实时反映了停车场的状况{available_spots: 18,total_spots: 25,spots: [{center_x: 150.5,center_y: 230.0,id: 0,occupied: true},{center_x: 250.8,center_y: 232.1,id: 1,occupied: false},// ... more spots]
}总结与展望
我们成功地将一个本地的 OpenCV 应用改造成了一个功能强大的、基于网络的远程服务。通过 libevent我们以非常高效和简洁的方式实现了网络通信。
下一步可以做什么
Web 前端: 创建一个简单的 HTML 和 JavaScript 前端页面使用 fetch API 定期调用这个后端接口并在网页上以图形化方式动态展示停车场地图和状态。数据持久化: 将车位状态的变化记录到数据库如 SQLite 或 PostgreSQL中用于历史数据分析。功能扩展: 增加更多的 API 端点例如 /api/spot/{id} 来获取单个车位的详细信息或者 /api/history 来查询历史占用率。部署: 使用 Docker 将此服务容器化以便在任何服务器上轻松部署。