PysimpleGUIを用いたOpenCVのカメラ画像表示


はじめに

PysimpleGUIのデモコードをそのままコピーペーストしても動かなかったので、修正したコードを覚えとして載せておきます。

参考 PysimpleGUIのデモコード

Demo program that displays a webcam using OpenCV

動作環境

Windows10 64bit
Anaconda
python 3.7
OpenCV 4.2.0 (conda-forge からインストール)

修正したコード


import PySimpleGUI as sg
import cv2
import numpy as np

"""
Demo program that displays a webcam using OpenCV
"""

def main():

    sg.theme('Black')

    # define the window layout
    layout = [
            [sg.Text('Realtime movie', size=(40, 1), justification='center', font='Helvetica 20',key='-status-')],
            [sg.Image(filename='', key='image')],
            [sg.Button('Record', size=(10, 1), font='Helvetica 14'),
             sg.Button('Stop', size=(10, 1), font='Helvetica 14'),
             sg.Button('Exit', size=(10, 1), font='Helvetica 14'), ]
            ]

    # create the window and show it without the plot
    window = sg.Window('Realtime movie',
                       layout, location=(800, 400))

    # ---===--- Event LOOP Read and display frames, operate the GUI --- #

    recording = False

    while True:
        event, values = window.read(timeout=20)
        if event in (None, 'Exit'):
            break

        elif event == 'Record':
            window['-status-'].update("Record")
            cap = cv2.VideoCapture(0,cv2.CAP_DSHOW) #1
            # cap = cv2.VideoCapture(0)
            recording = True

        elif event == 'Stop':
            window['-status-'].update("Stop")
            recording = False
            img = np.full((480, 640), 0)
            # this is faster, shorter and needs less includes
            imgbytes = cv2.imencode('.png', img)[1].tobytes()
            window['image'].update(data=imgbytes)
            cap.release() #2
            cv2.destroyAllWindows()

        if recording:
            ret, frame = cap.read()
            if ret is True:
                imgbytes = cv2.imencode('.png', frame)[1].tobytes() 
                window['image'].update(data=imgbytes)

    window.close()

if __name__ == '__main__':
    main()

修正点#1

cap = cv2.VideoCapture(0,cv2.CAP_DSHOW) #1
[ WARN:1] global ..\modules\videoio\src\cap_msmf.cpp (674) SourceReaderCB::~SourceReaderCB terminating async callback
Direct Showの設定をするとterminating async callbackのエラーは出なくなりますが、設定スピード(フレームレート)が遅くなることがあるようです。(私もフレームレートを測ってみたら30fpsから5fpsに落ちました。)
参考:
WARN:0 terminating async callback error in cv2
【openCV】遅いのはCAP_DSHOWだった?【Webカメラ】

修正点#2

不要かもしれませんが、
cap.release()
cv2.destroyAllWindows() #2
を入れてリリースしています。

その他の修正点

・'Record'が押されたらVideoCaptureが呼ばれるように変更。
・NoneとExitが押されたらループを抜けるように変更。