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

在Python3.6中结合OpenCV与asyncio显示图像的实现疑问

解决异步程序中OpenCV GUI的主线程警告问题

首先得明确这个警告的核心原因:OpenCV的HighGUI模块(负责图像显示、按键监听的功能)在macOS上依赖Cocoa框架,要求所有GUI操作必须在主线程执行,而你用loop.run_in_executor把load_img放到了线程池的子线程里,直接违反了这个限制,所以触发了警告,后续Python版本甚至会直接抛出异常。

下面给你两个可行的解决方案,适配不同场景需求:

方案1:用多进程分离OpenCV GUI与异步事件循环

每个进程都有独立的主线程,我们可以把OpenCV的图像显示和按键监听放到单独的子进程中,让它的GUI操作在自己的主线程运行,完全避开主进程的asyncio事件循环冲突。

修改后的代码示例:

import asyncio
import cv2
import multiprocessing

def load_img(image):
    print(image)
    im = cv2.imread(image, cv2.IMREAD_GRAYSCALE)
    _, inv = cv2.threshold(im, 150, 255, cv2.THRESH_BINARY_INV)
    cv2.GaussianBlur(inv, (3, 3), 0)
    cv2.imshow('Async test', inv)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    return

async def test():
    tiff_img = 'Im1.tiff'
    # 创建子进程执行OpenCV GUI操作
    p = multiprocessing.Process(target=load_img, args=(tiff_img,))
    p.start()
    p.join()  # 等待子进程完成(即用户按下按键后)
    return

async def numbers():
    for number in range(200):
        await asyncio.sleep(0.5)
        print(number)
    return

if __name__ == '__main__':
    # Windows下多进程必须加这个判断
    multiprocessing.freeze_support()
    loop = asyncio.get_event_loop()
    single = asyncio.gather(test(), numbers())
    loop.run_until_complete(single)

这个方案的优势是跨平台兼容性拉满,不管是macOS、Windows还是Linux都能正常运行,不会再出现主线程相关警告。

方案2:将OpenCV GUI操作移回主进程主线程

如果不想用多进程,也可以把OpenCV的GUI操作放回主线程,同时把阻塞的按键监听改成异步形式,避免卡死整个事件循环:

import asyncio
import cv2

async def load_img(image):
    print(image)
    im = cv2.imread(image, cv2.IMREAD_GRAYSCALE)
    _, inv = cv2.threshold(im, 150, 255, cv2.THRESH_BINARY_INV)
    cv2.GaussianBlur(inv, (3, 3), 0)
    cv2.imshow('Async test', inv)
    
    # 把阻塞的waitKey(0)改成异步循环检查,不占用事件循环主线程
    while True:
        await asyncio.sleep(0.01)  # 让出控制权,让其他异步任务运行
        key = cv2.waitKey(1)  # 非阻塞检查按键
        if key != -1:
            break
    cv2.destroyAllWindows()
    return

async def numbers():
    for number in range(200):
        await asyncio.sleep(0.5)
        print(number)
    return

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    single = asyncio.gather(load_img('Im1.tiff'), numbers())
    loop.run_until_complete(single)

这个方案的核心是用非阻塞的waitKey(1)配合异步循环监听按键,既实现了等待用户输入的需求,又不会阻塞asyncio的事件循环,同时所有GUI操作都在主线程执行,完美规避了警告。

补充小提示

  • 如果你用的是Python 3.9+,可以用asyncio.to_thread单独执行非GUI的OpenCV操作(比如图像读取、阈值处理),把GUI操作留在主线程,能进一步优化性能。
  • 尽量不要在子线程中调用任何OpenCV的GUI函数,这是HighGUI模块的设计限制,尤其是macOS平台,没有绕开的办法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:27:18