You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

关键修改点

  1. 线程职责分离:子线程仅负责生成图像数据,主线程负责窗口创建、图像显示和事件处理。
  2. 线程安全的数据传递:使用互斥锁和条件变量确保图像数据在主线程和子线程间安全传递。
  3. 移除忙等待:主线程通过cv::waitKey()处理窗口事件,避免CPU占用过高;子线程添加睡眠控制帧率。
  4. 优雅退出:通过running标志控制线程退出,避免资源泄漏。

编译命令

  • Ubuntu:保持原命令不变,确保链接-pthread。
  • Windows/MSYS2:编译命令不变,无需额外修改。

内容的提问来源于stack exchange,提问作者Douglas B

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 02:37:04