在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
相关产品推荐
相关产品推荐

