在 Python 里用 OpenCV 看一张图,看上去调用一个 cv2.imshow 就完事,但图片能不能稳定显示、按键后能否干净关闭,取决于 imshow、waitKey、destroyAllWindows 是否配套使用。下面以“读入 素材/card.png,在 card 窗口中显示,按任意键后关窗并继续执行”这个最小需求为例,把三步拆开讲清楚,并整理常见错误的排查思路。
【需求与目录约定】
项目目录下要有 素材 子目录,图片 card.png 放在其中。程序的目标行为是:读图 → 弹出标题为 card 的窗口显示 → 等待任意键 → 关闭窗口 → 打印一行提示,程序继续往下走。
【第一步:读入图片】
- import cv2
- import numpy as np
- def imread(path, flags=cv2.IMREAD_COLOR):
- try:
- data = np.fromfile(path, dtype=np.uint8)
- except FileNotFoundError:
- return None
- return cv2.imdecode(data, flags)
- img = imread("素材/card.png")
复制代码
这里没有直接调用 cv2.imread,而是把“读取”和“解码”拆成两步:np.fromfile 以 uint8 字节流的方式把整个文件读进内存,cv2.imdecode 再按 flags 指定的模式把字节流解码成图像矩阵。函数签名 imread(path, flags=cv2.IMREAD_COLOR) 中,path 是图片路径,flags 决定解码方式,默认的 IMREAD_COLOR 表示按彩色三通道读入。函数内部用 try/except 捕获 FileNotFoundError,路径不存在时返回 None,而不是让程序直接抛异常退出。
需要特别注意的是:这个封装把“文件不存在”转化成了 None 返回值,所以调用之后应先判断 img 是否为 None,再交给后面的窗口函数,否则错误会推迟到显示阶段才暴露。
【第二步:imshow 创建窗口】
- cv2.imshow("card", img) # 在名为 card 的窗口显示图片
复制代码
第一个参数是窗口名,会直接显示在窗口标题栏上;第二个参数是上一步读进来的图像矩阵。同一个窗口名会被复用——再次对 card 调用 imshow 不会再开一个新窗口,而是刷新已有窗口的内容。连续查看多张图时这点很方便,但也意味着不能靠同名窗口同时显示两张不同的图。
【第三步:waitKey 与 destroyAllWindows 写完整】
- cv2.waitKey(0) # 一直等到按下任意键
- cv2.destroyAllWindows() # 关闭所有窗口,交还内存
- print("窗口已全部关闭")
复制代码
waitKey 的参数是毫秒数:waitKey(0) 表示无限等待,直到有按键事件才继续;waitKey(3000) 则表示最多等 3 秒,即使没人按键,3 秒后程序也会接着往下执行。它的作用是让程序停在窗口处等待,给了窗口绘制和刷新的时间。
destroyAllWindows 负责收尾,把所有窗口关闭并释放对应的窗口资源。它和 waitKey 是配套的:一个负责停住,一个负责回收。
【完整代码】
- import cv2
- import numpy as np
- def imread(path, flags=cv2.IMREAD_COLOR):
- try:
- data = np.fromfile(path, dtype=np.uint8)
- except FileNotFoundError:
- return None
- return cv2.imdecode(data, flags)
- img = imread("素材/card.png")
- cv2.imshow("card", img) # 在名为 card 的窗口显示图片
- cv2.waitKey(0) # 一直等到按下任意键
- cv2.destroyAllWindows() # 关闭所有窗口,交还内存
- print("窗口已全部关闭")
复制代码
运行后先弹出标题为 card 的窗口显示图片,按下任意键后窗口关闭,控制台打印出“窗口已全部关闭”。
【常见问题与排查顺序】
1. 窗口一闪而过,甚至看不清内容:优先检查是否漏了 cv2.waitKey。没有它,程序不会在窗口处停留,窗口刚创建就结束。
2. 连续打开多张图片后程序越来越卡:检查是否漏了 cv2.destroyAllWindows。窗口资源不回收,累积下来就会拖慢程序。
3. 显示阶段报错或画面异常:回到第一步确认 img 是否拿到了有效数据。封装的 imread 在文件不存在时返回 None,这个 None 会一直传下去,直到 imshow 才失败。
4. 提示找不到文件:确认相对路径“素材/card.png”是相对当前工作目录解析的,运行位置变了,同样的相对路径就会失效;同时确认 素材 目录与 card.png 的文件名拼写一致。
5. 第二张图覆盖了第一张:这是窗口名复用机制的正常表现,同名窗口只有一份,需要同时看多图就给每次 imshow 传不同的窗口名。
【适用场景小结】
这套三步写法适合图像处理脚本在调试阶段确认中间结果:读入某张图、看一眼是否读对、按键继续执行后续流程。把读取部分封装成带 try/except 的函数,能让路径错误以 None 的形式返回,配合显示前的判断,脚本就不至于因为一张图缺失而整体中断;把 waitKey 的毫秒数从 0 改成有限值,还能做成定时展示、无需人工介入的自动播放式预览。 |