OpenCV多线程编程:从单线程到多线程的视频处理
一、最简单的摄像头显示程序让我们从最基础的版本开始一个单线程程序直接从摄像头读取并显示画面。基础版本代码#include iostream #include opencv2/opencv.hpp using namespace std; int main() { // 打开摄像头默认摄像头编号0 cv::VideoCapture cap(0); if (!cap.isOpened()) { cerr Error: Could not open camera! endl; return -1; } cv::Mat frame; while (true) { cap frame; // 读取一帧 if (frame.empty()) { cerr Error: Empty frame! endl; break; } cv::imshow(Camera, frame); // 显示画面 char key cv::waitKey(1); if (key q || key Q) { break; // 按q键退出 } } cap.release(); cv::destroyAllWindows(); return 0; }基础版本的特点优点简单直接易于理解缺点所有操作都在一个线程中执行如果添加复杂的图像处理会导致画面卡顿二、尝试使用线程初学者可能会尝试将摄像头读取放入单独的线程#include iostream #include opencv2/opencv.hpp #include thread using namespace std; void captureThread() { cv::VideoCapture cap(0); if (!cap.isOpened()) { std::cerr Error: Could not open camera! std::endl; return; } cv::Mat frame; while (true) { cap frame; if (frame.empty()) break; cv::imshow(Camera, frame); // 错误在子线程中显示 char key cv::waitKey(1); if (key q || key Q) break; } cap.release(); cv::destroyAllWindows(); } int main() { thread captureVideo(captureThread); captureVideo.join(); return 0; }三、再进一步使用双线程实现#include iostream #include opencv2/opencv.hpp #include thread #include mutex #include atomic using namespace std; // 共享数据 cv::Mat sharedFrame; mutex mtx; atomicbool running(true); // 线程1负责捕获视频帧 void captureThread() { cv::VideoCapture cap(0); if (!cap.isOpened()) { cerr Error: Could not open camera! endl; running false; return; } cv::Mat frame; while (running) { cap frame; if (frame.empty()) { cerr Error: Empty frame! endl; break; } // 使用互斥锁保护共享数据 lock_guardmutex lock(mtx); frame.copyTo(sharedFrame); } cap.release(); } // 线程2负责处理和显示 void displayAndProcessThread() { cv::namedWindow(Camera, cv::WINDOW_AUTOSIZE); while (running) { cv::Mat frame; // 获取最新的帧 { lock_guardmutex lock(mtx); if (!sharedFrame.empty()) { sharedFrame.copyTo(frame); } } if (!frame.empty()) { // 在这里添加你的图像处理代码 // 示例1添加文字 string text Hello OpenCV!; cv::putText(frame, text, cv::Point(50, 50), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 255, 0), 2); // 示例2添加时间戳信息 cv::putText(frame, Press q to quit, cv::Point(10, frame.rows - 10), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 1); // 显示处理后的画面 cv::imshow(Camera, frame); } // 检查退出条件 char key cv::waitKey(30); if (key q || key Q) { running false; break; } } cv::destroyAllWindows(); } int main() { cout Program started. Press q to quit. endl; // 创建两个线程 thread capture(captureThread); thread display(displayAndProcessThread); // 等待线程结束 display.join(); capture.join(); cout Program terminated. endl; return 0; }四、代码解析1. 线程同步机制mutex mtx; // 互斥锁防止数据竞争 atomicbool running; // 原子变量控制线程结束互斥锁确保同一时刻只有一个线程访问共享数据原子变量安全地在多线程间传递状态信息2. 线程分工线程职责说明captureThread捕获视频帧持续从摄像头读取存入共享变量displayAndProcessThread处理和显示获取帧添加特效显示画面3. 关键代码说明// 保护共享数据的访问 { lock_guardmutex lock(mtx); // 自动加锁解锁 frame.copyTo(sharedFrame); // 安全的拷贝 }五、进阶添加更多图像处理效果你可以在显示线程中添加各种OpenCV特效// 在显示线程的处理部分添加 // 1. 转为灰度图 cv::Mat gray; cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY); // 2. 边缘检测 cv::Mat edges; cv::Canny(gray, edges, 50, 150); // 3. 人脸检测需要haar cascade文件 // cv::CascadeClassifier faceCascade; // faceCascade.load(haarcascade_frontalface_default.xml); // vectorcv::Rect faces; // faceCascade.detectMultiScale(gray, faces); // 4. 添加帧率显示 static int frameCount 0; static auto startTime chrono::steady_clock::now(); frameCount; auto currentTime chrono::steady_clock::now(); auto elapsed chrono::duration_castchrono::seconds(currentTime - startTime).count(); if (elapsed 1) { double fps frameCount / elapsed; cout FPS: fps endl; frameCount 0; startTime currentTime; }六、总结单线程 vs 双线程对比特性单线程双线程实现复杂度简单中等响应性好极好处理复杂任务会卡顿流畅CPU利用率一般更好代码可维护性简单良好多线程编程要点正确使用互斥锁保护共享数据避免死锁注意加锁顺序使用原子变量控制线程状态确保主线程等待子线程结束OpenCV的显示操作必须在主线程改进点有朋友说我这个会导致使用的那个线程空转然后上面那个如果挂了底下一直阻塞。的确是有这个问题我的出发点是最简单的实现既然这么说了那就优化一下#include iostream #include opencv2/opencv.hpp #include thread using namespace std; cv::Mat shareFrame; atomicbool running(true); mutex mtx; condition_variable condin_v; bool frameReady false; void captureThread() { cv::VideoCapture cap(0); if (!cap.isOpened()) { std::cerr Error: Could not open camera! std::endl; running false; condin_v.notify_all(); return; } cv::Mat frame; cap.set(cv::CAP_PROP_FPS, 30); while (running) { cap frame; if (frame.empty()) { std::cerr Error: Empty frame! std::endl; break; } { lock_guardmutex lock(mtx); frame.copyTo(shareFrame); frameReady true; } condin_v.notify_one(); this_thread::sleep_for(chrono::microseconds(33)); } cap.release(); condin_v.notify_all(); } void displayAndProcessThread() { cv::namedWindow(Camera, cv::WINDOW_AUTOSIZE); cv::Mat frame; while (running) { { unique_lockmutex lock(mtx); condin_v.wait(lock, [] {return frameReady || !running; }); if (!running) break; if (!shareFrame.empty()) { shareFrame.copyTo(frame); frameReady false; } } if (!frame.empty()) { string text Hello Opencv!; cv::putText(frame, text, cv::Point(50, 50), cv::FONT_HERSHEY_DUPLEX, 1.0, cv::Scalar(0, 255, 0), 2); cv::imshow(Camera, frame); } char key cv::waitKey(30); if (key Q || key q) { running false; condin_v.notify_all(); break; } } cv::destroyAllWindows(); } int main() { thread captureVideo(captureThread); thread display(displayAndProcessThread); captureVideo.join(); display.join(); return 0; }时间 → 生产者线程 | 消费者线程 --------------------|-------------------- 获取锁 | 生产帧 | (可能正在等待) frameReady true | 释放锁 | notify_one() ------→ 被唤醒 | 尝试获取锁 | 获取锁成功 | 检查 frameReady true | 消费帧 | frameReady false | 释放锁 | 处理并显示帧
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2489556.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!