需求
使用pysimplegui和opencv实现一个播放器,播放 摄像头的画面。
代码实现
import cv2
import time
from typing import Iterable, NamedTuple, Optionalimport PySimpleGUI as sgclass CameraSpec(NamedTuple):name: strindex: intwidth: intheight: intfps: intdef init_window(theme_name: str = "DarkBlack", window_name: str = "UVC capture"):print(f"init theme with name {theme_name!r}")sg.theme(theme_name)layout = [[sg.Text('UVC Demo', size=(60, 1), justification='center')],[sg.Image(filename='', key='-IMAGE-')],[sg.Button('退出', size=(10, 1), key='-Exit-')]]print(f"init window with name {window_name!r}")window = sg.Window(window_name, layout, location=(10, 10), resizable=True)return windowdef main(camera_spec: CameraSpec):print(f"init {camera_spec.index}th camera with name {camera_spec.name}")capture = cv2.VideoCapture(camera_spec.index)if capture == None:print(f"No matching camera with CameraSpec {camera_spec} found")returnsize = (int(capture.get(cv2.CAP_PROP_FRAME_WIDTH)), int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT)))print(f"get size:{size}")wret = capture.set(cv2.CAP_PROP_FRAME_WIDTH, camera_spec.width)hret = capture.set(cv2.CAP_PROP_FRAME_HEIGHT, camera_spec.height)print(f"wret:{wret} hret:{hret}")size = (int(capture.get(cv2.CAP_PROP_FRAME_WIDTH)), int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT)))print(f"get size:{size}")window = init_window(window_name=camera_spec.name)#last_update = time.perf_counter()try:keep_running = Truei = 0while keep_running:before = time.perf_counter()event, values = window.read(timeout=5)if event == '-Exit-' or event == sg.WIN_CLOSED:breakafter_event = time.perf_counter()print(f"====after_event:{after_event-before}====")try:_, frame = capture.read()except TimeoutError:passelse:after_frame = time.perf_counter()print(f"after_frame:{after_frame-after_event}")#将每一帧编码成png播放imgbytes = cv2.imencode('.png', frame)[1].tobytes()after_show = time.perf_counter()print(f"after_show:{after_show-after_frame}")print(f"sum:{after_show-before}")window['-IMAGE-'].update(data=imgbytes)#cv2.imshow(camera_spec.name, bgr) # if cv2.waitKey(1) & 0xFF == 27:# break # with open(f"bgr{i}.bgr",'wb') as f:# f.write(bgr)# i += 1except KeyboardInterrupt:passcapture.close()print(f"close camera:{camera_spec}")if __name__ == "__main__":main(CameraSpec(name="播放摄像头测试",index=0, #摄像头编号width=1280,height=720,fps=10,),)
效果:
代码说明
打开摄像头:
capture = cv2.VideoCapture(camera_spec.index)
从摄像头取帧:
_, frame = capture.read()
将帧送到窗口播放:
#将每一帧编码成png图片
imgbytes = cv2.imencode('.png', frame)[1].tobytes()
window['-IMAGE-'].update(data=imgbytes) #这里播放
由于使用PySimpleGUI的Image作为播放控件,所以每一帧都要转换成图片。除了png, 好像tif也可以,我没试。
从这里也可以看出来,pysimplegui播放的效率还是有点低的,要先编码成图片。但是作为一些小工具来讲,可以接受。