使用OpenCV保存FLIR相机图像时,Matplotlib弹出保存图表弹窗问题
问题原因与解决方案
环境信息
- OpenCV 4.7
- Matplotlib 3.7.1
- Spinnaker-Python 3.0.0.118
- Python 3.10
- Windows 10 x64
核心问题
同时用OpenCV显示热成像图、Matplotlib同步绘制直方图时,按s键调用cv2.imwrite()会触发Matplotlib的保存弹窗,最终保存的是直方图而非目标热成像图;移除Matplotlib后功能恢复正常。
原因分析
Matplotlib默认将s键绑定为“保存当前Figure”的快捷键,且当Matplotlib窗口处于焦点时,按键事件会被Matplotlib的事件循环捕获,而非OpenCV的cv2.waitKey()。这就导致按s时触发的是Matplotlib的保存逻辑,而非你编写的OpenCV保存代码。
解决方案
方案1:禁用Matplotlib的s快捷键
在创建Matplotlib Figure后,断开默认的按键处理逻辑,避免s被Matplotlib拦截:
fig = plt.figure(1) fig.canvas.mpl_connect('close_event', handle_close) # 禁用默认按键处理器,阻止's'触发保存弹窗 fig.canvas.mpl_disconnect(fig.canvas.manager.key_press_handler_id)
方案2:让OpenCV窗口始终置顶
强制OpenCV窗口保持在最上层,确保按键事件优先被OpenCV捕获:
cv2.imshow("FLIR", image_data) # 设置窗口置顶 cv2.setWindowProperty("FLIR", cv2.WND_PROP_TOPMOST, 1)
方案3:优化Matplotlib绘图逻辑(推荐)
避免每次循环重绘整个Figure,改为更新已有直方图数据,既提升性能又减少事件冲突:
# 初始化阶段创建直方图对象 plt.ion() fig, ax = plt.subplots(num=1) fig.canvas.mpl_connect('close_event', handle_close) fig.canvas.mpl_disconnect(fig.canvas.manager.key_press_handler_id) # 提前获取第一帧数据初始化直方图 image_result = cam.GetNextImage(1000) if not image_result.IsIncomplete(): image_data = image_result.GetNDArray() image_data = cv2.rotate(image_data, cv2.ROTATE_90_COUNTERCLOCKWISE) image_data = cv2.flip(image_data, 1) count, bins = np.histogram(image_data.flatten()) line, = ax.stairs(count, bins) image_result.Release() # 循环内仅更新数据 count, bins = np.histogram(image_data.flatten()) line.set_data(bins[:-1], count) ax.relim() ax.autoscale_view() plt.draw() plt.pause(0.001)
完整修改示例
import PySpin import cv2 import numpy as np import matplotlib.pyplot as plt import keyboard def handle_close(event): global continue_recording continue_recording = False continue_recording = True # 假设已完成相机初始化(如枚举、选择、初始化相机) # cam = ... cam.BeginAcquisition() print('Acquiring images...') print('Press enter to close the program..') # 初始化Matplotlib并禁用默认's'快捷键 plt.ion() fig, ax = plt.subplots(num=1) fig.canvas.mpl_connect('close_event', handle_close) fig.canvas.mpl_disconnect(fig.canvas.manager.key_press_handler_id) acq_count = 0 # 初始化直方图 image_result = cam.GetNextImage(1000) if not image_result.IsIncomplete(): image_data = image_result.GetNDArray() image_data = cv2.rotate(image_data, cv2.ROTATE_90_COUNTERCLOCKWISE) image_data = cv2.flip(image_data, 1) count, bins = np.histogram(image_data.flatten()) line, = ax.stairs(count, bins) image_result.Release() while(continue_recording): try: image_result = cam.GetNextImage(1000) if image_result.IsIncomplete(): print('Image incomplete with image status %d ...' % image_result.GetImageStatus()) else: image_data = image_result.GetNDArray() image_data = cv2.rotate(image_data, cv2.ROTATE_90_COUNTERCLOCKWISE) image_data = cv2.flip(image_data, 1) count, bins = np.histogram(image_data.flatten()) cv2.imshow("FLIR", image_data) cv2.setWindowProperty("FLIR", cv2.WND_PROP_TOPMOST, 1) # 更新直方图 line.set_data(bins[:-1], count) ax.relim() ax.autoscale_view() plt.draw() plt.pause(0.001) key = cv2.waitKey(100) & 0xFF if key == ord('s'): f_name = f"test_{acq_count}.png" cv2.imwrite(f_name, image_data) acq_count += 1 print(f"Saved image: {f_name}") if keyboard.is_pressed('ENTER'): print('Program is closing...') cv2.destroyAllWindows() plt.close('all') input('Done! Press Enter to exit...') continue_recording=False image_result.Release() except PySpin.SpinnakerException as ex: print('Error: %s' % ex) continue_recording = False cam.EndAcquisition() # cam.DeInit() # 按需添加相机销毁逻辑
内容的提问来源于stack exchange,提问作者DrBwts
相关产品推荐
相关产品推荐

