OpenCV跨线程捕获的cv::Mat帧无法执行写入绘制操作的问题
问题诱因
- cv::Mat浅拷贝导致内存共享与数据覆写
cv::Mat的赋值、拷贝构造默认都是浅拷贝,仅复制头信息,指向同一块像素数据内存。你代码中Camera类的captureVideo方法返回的是类成员frame,子线程采集时每次调用cap >> frame都会覆写同一块内存,而你往frameStack里push的frame本质都指向这块内存。主线程刚拿到contour准备绘制时,子线程已经将该内存块的内容替换成了新的采集帧,绘制操作相当于画在了马上要被丢弃的内存上,自然看不到效果。 - 多线程无锁访问导致数据竞争
全局的frameStack和停止信号stopSig都没有加互斥保护,子线程增删帧、主线程读取帧的操作会同时发生,可能出现你读取到的帧是不完整的、或者容器结构被破坏的情况,进一步导致绘制失效,严重时会触发程序崩溃。 - 缓冲区逻辑混乱
你现有逻辑中缓冲区超过2帧就pop末尾、满20帧就清空全量缓冲的逻辑非常不合理,会频繁出现缓冲被清空、主线程拿不到稳定帧的情况,也会加剧上述两个问题的出现概率。
修复方案
- 强制帧数据深拷贝:子线程往缓冲区存帧时,调用
clone()方法生成独立的帧副本,避免共享同一块采集内存:
// 修改grabFrame中的push逻辑 ::frameStack.push_back(frame.clone());
- 加互斥锁保护共享资源:定义全局互斥锁,所有操作
frameStack、stopSig的位置都要加锁:
// 全局添加互斥锁定义 std::mutex frameMutex;
子线程操作缓冲时加锁:
void grabFrame(void) { Mat frame; std::lock_guard<std::mutex> lock(frameMutex); ::frameStack.clear(); lock.unlock(); while(!::stopSig) { frame = ::cam1.captureVideo(); std::lock_guard<std::mutex> opLock(frameMutex); // 操作frameStack的代码都放在锁区间内 if(::frameStack.size() >= ::frameBuffer) { ::frameStack.erase(::frameStack.begin()); // 弹出最旧的帧,不要全清 } ::frameStack.push_back(frame.clone()); } return; }
主线程读取帧时加锁:
while(1) { Mat contour; { std::lock_guard<std::mutex> lock(frameMutex); if(::frameStack.size() >= 2) { contour = ::frameStack.back().clone(); // 再拷贝一份,避免锁释放后数据被修改 } } // 锁释放后再执行绘制、显示操作,避免锁占用时间过长影响采集 if(!contour.empty()) { circle(contour, Point(150,150),50, Scalar(0,255,255),cv::FILLED, 8); putText(contour, "some text", Point(100,100), FONT_HERSHEY_DUPLEX, 1, Scalar(0,143,143), 2); imshow("Contour Video", contour); } // 退出逻辑加锁 if (waitKey(1) == 27) { std::lock_guard<std::mutex> lock(frameMutex); ::stopSig = 1; frameStack.clear(); break; } }
- 优化缓冲区逻辑:将缓冲区改为先进先出的环形缓冲,满了就弹出最旧的首帧,不要直接清空整个缓冲区,保证主线程始终能拿到最新的几帧数据。
内容的提问来源于stack exchange,提问作者fisakhan
相关产品推荐
相关产品推荐

