需求
使用pysimplegui和opencv实现一个播放器,播放 摄像头的画面。
代码实现
import cv2
import time
from typing import Iterable, NamedTuple, Optional
import PySimpleGUI as sg
class CameraSpec(NamedTuple):
name: str
index: int
width: int
height: int
fps: int
def 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 window
def 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")
return
size = (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 = True
i = 0
while keep_running:
before = time.perf_counter()
event, values = window.read(timeout=5)
if event == '-Exit-' or event == sg.WIN_CLOSED:
break
after_event = time.perf_counter()
print(f"====after_event:{after_event-before}====")
try:
_, frame = capture.read()
except TimeoutError:
pass
else:
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 += 1
except KeyboardInterrupt:
pass
capture.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播放的效率还是有点低的,要先编码成图片。但是作为一些小工具来讲,可以接受。