Windows下C++ OpenCV cv::imshow()冻结无响应,Ubuntu正常运行
问题分析与解决方案
问题现象
- Ubuntu 20 LTS上基于OpenCV的图像流测试工具可正常显示图像;但在Windows(MSYS2 MinGW-64)环境中,
cv::imshow()窗口弹出后无图像显示,标题栏显示“未响应”并冻结。
最小复现示例(MVE)
#include <iostream> #include <thread> #include <cstdint> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> void stream_callback(uint8_t* img_buffer){ cv::Mat frame(1200, 1920, CV_8UC1, img_buffer); cv::imshow("Video Stream", frame); cv::waitKey(1); return; } void m_dummy_stream_recv_thread(){ uint8_t* img_dat = new uint8_t[1920 * 1200]; int width = 1920; int height = 1200; int shift = 0; while (true){ for (int i = 0; i < width * height; i++){ img_dat[i] = shift + i % 255; } shift += 10; stream_callback(img_dat); } } int main(int argc, char const *argv[]) { cv::namedWindow("Video Stream", cv::WINDOW_AUTOSIZE); std::thread stream_thread(m_dummy_stream_recv_thread); stream_thread.detach(); while(1){} return 0; }
编译环境与命令
Ubuntu环境
通过apt安装OpenCV,编译命令:
g++ ./openCV/imshow_freeze/main.cpp -o ocvtest -I/usr/include/opencv4/ -lopencv_highgui -lopencv_core -pthread
Windows环境
使用MSYS2的MinGW-64工具链,通过pacman安装OpenCV,编译命令:
g++ ./openCV/imshow_freeze/main.cpp -o ocvtest -IC:/msys64/mingw64/include/opencv4 -lopencv_highgui -lopencv_core
调试信息
- 数据可正常传递到
stream_callback()函数,但单步执行cv::imshow()时,线程在basic_string.h的如下代码段终止(推测未捕获异常导致线程退出,主线程持续运行):
basic_string.h:623 (for me) { // NB: Not required, but considered best practice. if (__s == 0 && __n > 0) std::__throw_logic_error(__N("basic_string: " "construction from null is not valid")); _M_construct(__s, __s + __n, std::forward_iterator_tag()); }
- 已确认Windows/MSYS2上OpenCV本身功能正常,单线程实现可正常显示图像:
#include <iostream> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> void show_image(uint8_t* img_buffer){ cv::Mat frame(1200, 1920, CV_8UC1, img_buffer); cv::imshow("Video Stream", frame); cv::waitKey(1); return; } bool read_next_frame(uint8_t* image_data, int* width, int* height){ int w = *width = 1920; int h = *height = 1200; static int shift = 0; for (int i = 0; i < w * h; ++i){ image_data[i] = shift + i % 255; } shift += 10; return true; } void displayVideoStream(){ // Create a window to display the video stream cv::namedWindow("Video Stream", cv::WINDOW_AUTOSIZE); // Assume you have a function 'read_next_frame()' to get the next frame as uint8_t* image_data, and its width and height. // Replace this loop with your video stream loop: uint8_t* image_data = new uint8_t[1920 * 1200]; int width; int height; while (read_next_frame(image_data, &width, &height)){ // Create a cv::Mat object from the image data show_image(image_data); } } int main(int argc, char* argv[]){ displayVideoStream(); return 0; }
问题根源
Windows平台下,OpenCV的HighGUI模块依赖的GUI框架(Win32或Qt)要求窗口的创建、更新和事件处理必须在同一个线程中。你的多线程实现中,cv::namedWindow()在主线程调用,而cv::imshow()和cv::waitKey()在子线程执行,违反了线程亲和性要求,导致窗口消息循环无法正常处理,最终出现冻结。
另外,主线程的while(1){}是忙等待,会占用100%CPU,同时无法处理任何窗口事件,进一步加剧了冻结问题。
解决方案
调整线程架构,让窗口操作和事件循环都在主线程执行,子线程仅负责生成/获取图像数据,通过线程安全的方式传递给主线程显示。
修改后的跨平台代码示例
#include <iostream> #include <thread> #include <cstdint> #include <mutex> #include <condition_variable> #include <chrono> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> // 线程安全的图像数据缓冲区 struct ImageBuffer { std::mutex mutex; std::condition_variable cv; uint8_t* data = nullptr; bool new_data = false; bool running = true; }; void stream_producer(ImageBuffer& buffer) { uint8_t* img_dat = new uint8_t[1920 * 1200]; int width = 1920; int height = 1200; int shift = 0; while (buffer.running) { // 生成图像数据 for (int i = 0; i < width * height; i++) { img_dat[i] = shift + i % 255; } shift += 10; // 线程安全地更新缓冲区 std::lock_guard<std::mutex> lock(buffer.mutex); buffer.data = img_dat; buffer.new_data = true; buffer.cv.notify_one(); // 控制帧率,避免过度占用CPU std::this_thread::sleep_for(std::chrono::milliseconds(33)); } delete[] img_dat; } int main(int argc, char const *argv[]) { cv::namedWindow("Video Stream", cv::WINDOW_AUTOSIZE); ImageBuffer buffer; // 启动生产者线程 std::thread producer_thread(stream_producer, std::ref(buffer)); while (true) { // 等待新图像数据 std::unique_lock<std::mutex> lock(buffer.mutex); buffer.cv.wait(lock, [&buffer](){ return buffer.new_data || !buffer.running; }); if (!buffer.running) break; // 在主线程显示图像 cv::Mat frame(1200, 1920, CV_8UC1, buffer.data); cv::imshow("Video Stream", frame); buffer.new_data = false; lock.unlock(); // 处理窗口事件,同时控制帧率 int key = cv::waitKey(1); if (key == 27) { // ESC键退出 buffer.running = false; break; } } // 等待线程结束 producer_thread.join(); cv::destroyAllWindows(); return 0; }
关键修改点
- 线程职责分离:子线程仅负责生成图像数据,主线程负责窗口创建、图像显示和事件处理。
- 线程安全的数据传递:使用互斥锁和条件变量确保图像数据在主线程和子线程间安全传递。
- 移除忙等待:主线程通过
cv::waitKey()处理窗口事件,避免CPU占用过高;子线程添加睡眠控制帧率。 - 优雅退出:通过
running标志控制线程退出,避免资源泄漏。
编译命令
- Ubuntu:保持原命令不变,确保链接
-pthread。 - Windows/MSYS2:编译命令不变,无需额外修改。
内容的提问来源于stack exchange,提问作者Douglas B
相关产品推荐
相关产品推荐

