同時タイムラプスのGUIをpythonでつくってみた

# 作ってみたもの
　大学の研究で、複数の同じカメラが沢山ありました。先輩の研究で、複数のカメラで、同時にタイムラプスを行う必要があり(昆虫を定点観測)、その手伝いでソフトをつくることとなりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001770/8633ebd8-ef23-71d9-843f-a31608bdcc3f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001770/0b6492b0-b257-1431-ae2c-981859003534.png)
# PySimpleGUIとは。
　PySimpleGUIは、PythonでGUIアプリケーションを簡単に作成できるライブラリです。PySimpleGUIを使用すると、テキストボックス、ボタン、ラジオボタン、チェックボックスなどのGUIコンポーネントを簡単に作成し、簡単に配置できます。PySimpleGUIは、Windows、macOS、Linuxなどの複数のプラットフォームで動作します。また、PySimpleGUIはTkinter、Qt、wxPython、GTKなどのGUIライブラリのラッパーとして機能するため、これらのライブラリと連携して使用することもできます。PySimpleGUIは、初心者にとっても扱いやすく、また、高度な機能を必要とするプロジェクトにも適しています。


# 同じカメラでのタイムラプスって、意外と出来ない
 色々なソフトを落としてやってみましたが、これが意外と出来ないんです。USBカメラを指定して、opencvでやってみましたが、これもまた、失敗に終わりました。カメラが同じだと、パソコンが区別できないのでしょうか。。。
# 構成
Malti_CAM.py:GUIを起動させるファイル
Malti_func.py:カメラを起動させるファイル

# どう解決したか。

capを開放することで解決しました。タイムラプスのたびに、カメラをリセットしているような感覚です。


こちらのコードは、capのインスタンスの生成後、画像を撮る設定(露出度etc)をしています。そして、ファイル名をタイムスタンプで保存してます。

```python:Malti_func.py
def cam_2(create_directory1,create_directory2):
    #capのインスタンスを生成
    cap1 = cv2.VideoCapture(1,cv2.CAP_DSHOW)
    time.sleep(1)
    #カメラの露出度を設定
    cap1.set(cv2.CAP_PROP_EXPOSURE,-6)
    ret1, frame1 = cap1.read()
    print(ret1)
    if ret1 == False:
        print('画像の読み込みに失敗しました。Anacondaを再起動してください。')
        os.exit()
    recording = True   
    
    #ファイル名は撮影日時で保存
    t_delta = datetime.timedelta(hours=9)
    JST = datetime.timezone(t_delta, 'JST')
    now = datetime.datetime.now(JST)
    filename1 = create_directory1 + '/{:%m-%d_%H%M_%S}.jpg'.format(now)
    #画像を保存
    cv2.imwrite(filename1, frame1)
    print('CAMERA1: ' + filename1 + ' => saved!')

    #cap1を開放することで解決。
    cap1.release()
    time.sleep(1)
```
# GUIのコード解説
肝の部分を紹介していこうと思います。
```python:Malti_CAM.py
layout = [
        [sg.Text('Check Camera', size=(40, 1), justification='center', font='Helvetica 20',key='-status-')],
        [sg.Text('Camera numbers: ', size=(15, 1)), sg.Drop(values=('1', '2','3','4'),key='-camera_num-')],
        [sg.Image(filename='', key='image')],
        [sg.Button('Start', size=(10, 1), font='Helvetica 14',key ='-start-'),
            sg.Button('Stop', size=(10, 1), font='Helvetica 14',key = '-stop-'),
            sg.Button('Setting', size=(10, 1), font='Helvetica 14', key='-exit-'), ]
        ]

window = sg.Window('Check',layout, location=(25, 75))
```
layoutにリスト形式で、格納すると簡単にボタンを作成することが出来ます。

```python:Malti_CAM.py
while True:
    event, values = window.read(timeout=20)
    if event in (None, '-exit-'):
        window.close()

    # Green & tan color scheme      
        sg.theme('GreenTan')      

        sg.set_options(text_justification='middle')      
```
while文とif文を組み合わせて、関数を実行します。

# 全コード
```python:Malti_func.py
import PySimpleGUI as sg
import datetime
import time
import os
import numpy as np
import cv2
import glob
from time import sleep
mport schedule
import mojimoji
import sys

# class 
def cam_2(create_directory1,create_directory2):

    # global create_directory1
    cap1 = cv2.VideoCapture(1,cv2.CAP_DSHOW)
    time.sleep(1)
    cap1.set(cv2.CAP_PROP_EXPOSURE,-6)
    ret1, frame1 = cap1.read()
    print(ret1)
    if ret1 == False:
        print('画像の読み込みに失敗しました。Anacondaを再起動してください。')
        os.exit()
    recording = True        
    # cf.camera_frame(frame1)


    t_delta = datetime.timedelta(hours=9)
    JST = datetime.timezone(t_delta, 'JST')
    now = datetime.datetime.now(JST)
    filename1 = create_directory1 + '/{:%m-%d_%H%M_%S}.jpg'.format(now)
    # Capture frame-by-frame
    cv2.imwrite(filename1, frame1)
    # frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    print('CAMERA1: ' + filename1 + ' => saved!')
    cap1.release()
    time.sleep(1)


    cap2 = cv2.VideoCapture(2,cv2.CAP_DSHOW)
    time.sleep(1)
    cap2.set(cv2.CAP_PROP_EXPOSURE,-6)
    ret2, frame2 = cap2.read()
    print(ret2)
    if ret2 == False:
        print('画像の読み込みに失敗しました。Anacondaを再起動してください。')
        sys.exit()

    # imgbytes = cv2.imencode('.png', frame2)[1].tobytes() 
    # window_camframe['image2'].update(data=imgbytes)

    t_delta = datetime.timedelta(hours=9)
    JST = datetime.timezone(t_delta, 'JST')
    now = datetime.datetime.now(JST)
    filename2 = create_directory2 + '/{:%m-%d_%H%M_%S}.jpg'.format(now)
    # Capture frame-by-frame
    cv2.imwrite(filename2, frame2)
    # frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    print('CAMERA2: ' + filename2 + ' => saved!')
    cap2.release()
    # window_camframe.Close()
    cv2.destroyAllWindows()


def cam_3(create_directory1,create_directory2,create_directory3):
    cap1 = cv2.VideoCapture(1)
    time.sleep(1)
    cap1.set(cv2.CAP_PROP_EXPOSURE,-6)
    ret1, frame1 = cap1.read()
    print(ret1)
    if ret1 == False:
        print('画像の読み込みに失敗しました。Anacondaを再起動してください。')
        sys.exit()
    t_delta = datetime.timedelta(hours=9)
    JST = datetime.timezone(t_delta, 'JST')
    now = datetime.datetime.now(JST)
    filename1 = create_directory1 + '/{:%m-%d_%H%M_%S}.jpg'.format(now)
    # Capture frame-by-frame
    cv2.imwrite(filename1, frame1)
    # frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    print('CAMERA1: ' + filename1 + ' => saved!')
    cap1.release()
    time.sleep(1)

    cap2 = cv2.VideoCapture(2)
    time.sleep(1)
    cap2.set(cv2.CAP_PROP_EXPOSURE,-6)
    ret2, frame2 = cap2.read()
    print(ret2)
    if ret2 == False:
        print('画像の読み込みに失敗しました。Anacondaを再起動してください。')
        sys.exit()
    t_delta = datetime.timedelta(hours=9)
    JST = datetime.timezone(t_delta, 'JST')
    now = datetime.datetime.now(JST)
    filename2 = create_directory2 + '/{:%m-%d_%H%M_%S}.jpg'.format(now)
    # Capture frame-by-frame
    cv2.imwrite(filename2, frame2)
    # frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    print('CAMERA2: ' + filename2 + ' => saved!')
    cap2.release()
    cv2.destroyAllWindows()

    cap3 = cv2.VideoCapture(3)
    time.sleep(1)
    cap3.set(cv2.CAP_PROP_EXPOSURE,-6)
    ret3, frame3 = cap3.read()
    print(ret3)
    if ret3 == False:
        print('画像の読み込みに失敗しました。Anacondaを再起動してください。')
        sys.exit()
    t_delta = datetime.timedelta(hours=9)
    JST = datetime.timezone(t_delta, 'JST')
    now = datetime.datetime.now(JST)
    filename3 = create_directory3 + '/{:%m-%d_%H%M_%S}.jpg'.format(now)
    # Capture frame-by-frame
    cv2.imwrite(filename3, frame3)
    # frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    print('CAMERA3: ' + filename3 + ' => saved!')
    cap3.release()
    cv2.destroyAllWindows()


def cam_4(create_directory1,create_directory2,create_directory3,create_directory4):
    cap1 = cv2.VideoCapture(1)
    time.sleep(1)
    cap1.set(cv2.CAP_PROP_EXPOSURE,-6)
    ret1, frame1 = cap1.read()
    print(ret1)
    if ret1 == False:
        print('画像の読み込みに失敗しました。Anacondaを再起動してください。')
        sys.exit()
    t_delta = datetime.timedelta(hours=9)
    JST = datetime.timezone(t_delta, 'JST')
    now = datetime.datetime.now(JST)
    filename1 = create_directory1 + '/{:%m-%d_%H%M_%S}.jpg'.format(now)
    # Capture frame-by-frame
    cv2.imwrite(filename1, frame1)
    # frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    print('CAMERA1: ' + filename1 + ' => saved!')
    cap1.release()
    time.sleep(1)

    cap2 = cv2.VideoCapture(2)
    time.sleep(1)
    cap2.set(cv2.CAP_PROP_EXPOSURE,-6)
    ret2, frame2 = cap2.read()
    print(ret2)
    if ret2 == False:
        print('画像の読み込みに失敗しました。Anacondaを再起動してください。')
        sys.exit()
    t_delta = datetime.timedelta(hours=9)
    JST = datetime.timezone(t_delta, 'JST')
    now = datetime.datetime.now(JST)
    filename2 = create_directory2 + '/{:%m-%d_%H%M_%S}.jpg'.format(now)
    # Capture frame-by-frame
    cv2.imwrite(filename2, frame2)
    # frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    print('CAMERA2: ' + filename2 + ' => saved!')
    cap2.release()
    cv2.destroyAllWindows()

    cap3 = cv2.VideoCapture(3)
    time.sleep(1)
    cap3.set(cv2.CAP_PROP_EXPOSURE,-6)
    ret3, frame3 = cap3.read()
    print(ret3)
    if ret3 == False:
        print('画像の読み込みに失敗しました。Anacondaを再起動してください。')
        sys.exit()
    t_delta = datetime.timedelta(hours=9)
    JST = datetime.timezone(t_delta, 'JST')
    now = datetime.datetime.now(JST)
    filename3 = create_directory3 + '/{:%m-%d_%H%M_%S}.jpg'.format(now)
    # Capture frame-by-frame
    cv2.imwrite(filename3, frame3)
    # frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    print('CAMERA3: ' + filename3 + ' => saved!')
    cap3.release()

    cap4 = cv2.VideoCapture(4)
    time.sleep(1)
    cap4.set(cv2.CAP_PROP_EXPOSURE,-6)
    ret4, frame4 = cap4.read()
    print(ret4)
    if ret4 == False:
        print('画像の読み込みに失敗しました。Anacondaを再起動してください。')
        sys.exit()
    t_delta = datetime.timedelta(hours=9)
    JST = datetime.timezone(t_delta, 'JST')
    now = datetime.datetime.now(JST)
    filename4 = create_directory4 + '/{:%m-%d_%H%M_%S}.jpg'.format(now)
    # Capture frame-by-frame
    cv2.imwrite(filename4, frame4)
    # frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    print('CAMERA4: ' + filename4 + ' => saved!')
    cap4.release()
    cv2.destroyAllWindows()
```

Malti_CAMがGUIを起動させるpythonファイルです。
```python:Malti_CAM.py
import cv2
import numpy as np
import PySimpleGUI as sg
import datetime
import time
import os
import numpy as np
import glob
from time import sleep
import schedule
import mojimoji
import Malti_func as Mf

folder = './CAM1'
if os.path.exists(folder) == False:
    os.mkdir('./CAM1')
    os.mkdir('./CAM2')
    os.mkdir('./CAM3')
    os.mkdir('./CAM4')
else:
    pass

sg.theme('GreenTan')


layout = [
        [sg.Text('Check Camera', size=(40, 1), justification='center', font='Helvetica 20',key='-status-')],
        [sg.Text('Camera numbers: ', size=(15, 1)), sg.Drop(values=('1', '2','3','4'),key='-camera_num-')],
        [sg.Image(filename='', key='image')],
        [sg.Button('Start', size=(10, 1), font='Helvetica 14',key ='-start-'),
            sg.Button('Stop', size=(10, 1), font='Helvetica 14',key = '-stop-'),
            sg.Button('Setting', size=(10, 1), font='Helvetica 14', key='-exit-'), ]
        ]


window = sg.Window('Check',layout, location=(25, 75))


recording = False

while True:
    event, values = window.read(timeout=20)
    if event in (None, '-exit-'):
        window.close()

    # Green & tan color scheme      
        sg.theme('GreenTan')      

        sg.set_options(text_justification='middle')      

        layout = [[sg.Text('TimeLaps Settings', font=('Helvetica', 16))],      
                  [sg.Text('Number of cameras', size=(15, 1),key='-camera_num2-'), sg.In(default_text='4', size=(4,1))],      
                  [sg.Text('Timalapse(hours)', size=(15, 1),key='-timelapse2-'), sg.In(default_text='408', size=(4, 1))],      
                  [sg.Text('interval(minutes)', size=(15, 1),key='-interval2-'), sg.In(default_text='10', size=(4, 1))],
                  [sg.Text('_'  * 100, size=(25, 1))],
                  [sg.Text('TimeLapse!', size=(10,1)),sg.Submit(key='-timelapse-')]]
                 ]

        
        if event in (None,'-cancel-'):
            break
    
        window = sg.Window('TimeLaps', layout, font=("Helvetica", 12))      

        event, values = window.read()   
        
        camera_num = str(values[0])
        timelaps_hour = int(values[1])
        minutesinterval = int(values[2])
        
        
        if event in '-timelapse-': 
            
#                 # timelapse_hourの単位は時間、minutesintervalの単位は分である。
#                 # そのため、フレーム数の計算では、timelapse_hourに60を掛け、インターバルで割った。
            numphotos = int((timelaps_hour*60)/minutesinterval)
            print("撮影する画像は ", numphotos, '枚です')
            print('')

            directory = os.path.abspath(os.curdir)
            
            sg.popup('コマンドプロンプトで保存するフォルダーを設定してください')
            window.close()

            if camera_num == '2':
                try:
                    print('カメラ1のフォルダー名を入力してください。')
                    new_dir_name1 = input()
                    create_directory1 = directory + '/CAM1/' + new_dir_name1
                    print('CAERAM1=> ' + create_directory1 + ' フォルダーを作成しました。 ')
                    print('')
                    print('カメラ2のフォルダー名を入力してください。')
                    new_dir_name2 = input()
                    create_directory2 = directory + '/CAM2/' + new_dir_name2
               
                    print('CAMERA2=> ' + create_directory2 + ' フォルダーを作成しました。')
                    print('')
                    print('タイムラプスは　' + str(timelaps_hour), '時間後に完了します。 Happy Timelapsing:D')
                    print('--------------------------------------------------------------------------------------------')

                    while True:  # Event Loop
                        for i in range(numphotos):
                            print('撮影した枚数: ',i+1)
                            Mf.cam_2(create_directory1,create_directory2)
                            print('-------------------------------------------------------------------------------------------')
                            time.sleep(minutesinterval*60)
                        if event is None :
                            break
                except OSError as ex:
                    print(ex)
                    print('違うフォルダー名を設定して下さいよ。')

            elif camera_num == '3':
                try:
                    # print('保存するフォルダー名を入力してください。')
                    print('カメラ1のフォルダー名を入力してください。')
                    new_dir_name1 = input()
                    create_directory1 = directory + '/CAM1/' + new_dir_name1
                    os.mkdir(create_directory1)
                    print('CAERAM1=> ' + create_directory1 + ' フォルダーを作成しました。 ')
                    print('')
                    print('カメラ2のフォルダー名を入力してください。')
                    new_dir_name2 = input()
                    create_directory2 = directory + '/CAM2/' + new_dir_name2
                    # ！保存先のフォルダ(日本語を指定してください！
                    os.mkdir(create_directory2)
                    print('CAMERA2=> ' + create_directory2 + ' フォルダーを作成しました。 ')
                    print('')
                    print('カメラ3のフォルダー名を入力してください。')
                    new_dir_name3 = input()
                    create_directory3 = directory + '/CAM3/' + new_dir_name3
                    # ！保存先のフォルダ(日本語を指定してください！
                    os.mkdir(create_directory3)
                    print('CAMERA3=> ' + create_directory3 + ' フォルダーを作成しました。 ')
                    print('')
                    print('タイムラプスは　' + str(timelaps_hour), '時間後に完了します。 Happy Timelapsing:D')

                    for i in range(numphotos):
                        # schedule.run_pending()
                        print('撮影した枚数: ',i+1)
                        Mf.cam_3(create_directory1,create_directory2,create_directory3)
                        print('-------------------------------------------------------------------------------------------')
                        time.sleep(minutesinterval*60)

                except OSError as ex:
                    print(ex)
                    print('違うフォルダー名を設定して下さい。')

            elif camera_num == '4':
                try:
                    print('カメラ1のフォルダー名を入力してください。')
                    new_dir_name1 = input()
                    create_directory1 = directory + '/CAM1/' + new_dir_name1
                    os.mkdir(create_directory1)
                    print('CAERAM1=> ' + create_directory1 + 'フォルダーを作成しました。')
                    print('')
                    print('カメラ2のフォルダー名を入力してください。')
                    new_dir_name2 = input()
                    create_directory2 = directory + '/CAM2/' + new_dir_name2
                    # ！保存先のフォルダ(日本語を指定してください！
                    os.mkdir(create_directory2)
                    print('CAMERA2=> ' + create_directory2 + 'フォルダーを作成しました。')
                    print('')
                    print('カメラ3のフォルダー名を入力してください。')
                    new_dir_name3 = input()
                    create_directory3 = directory + '/CAM3/' + new_dir_name3
                    # ！保存先のフォルダ(日本語を指定してください！
                    os.mkdir(create_directory3)
                    print('CAMERA3=> ' + create_directory3 + 'フォルダーを作成しました。')
                    print('')
                    print('カメラ4のフォルダー名を入力してください。')
                    new_dir_name4 = input()
                    create_directory4 = directory + '/CAM4/' + new_dir_name4
                    # ！保存先のフォルダ(日本語を指定してください！
                    os.mkdir(create_directory4)
                    print('CAMERA4=> ' + create_directory4 + 'フォルダーを作成しました。')
                    print('')
                    print('タイムラプスは' + str(timelaps_hour), '時間後に完了します。 Happy Timelapsing:D')

                    # schedule.every(minutesinterval * 60).seconds.do(cam_4)

                    for i in range(numphotos):
                        print('撮影した枚数: ',i+1)
                        # schedule.run_pending()
                        Mf.cam_4(create_directory1,create_directory2,create_directory3,create_directory4)
                        print('-------------------------------------------------------------------------------------------')
#                         Mf.main(numphotos)
                        time.sleep(minutesinterval*60)
                #         time.sleep(minutesinterval)

                except OSError as ex:
                    print(ex)
                    print('違うフォルダー名を設定して下さい。')

            else:
                print('対応してないっす。')
                
    elif event == '-start-':
        time.sleep(0.2)
        window['-status-'].update('Live')
        camera_number = int(values['-camera_num-'])
        cap = cv2.VideoCapture(camera_number, cv2.CAP_DSHOW)
        recording = True
        
        if event == '-camera_num-':
#             cap.release()
            time.sleep(0.2)
            window['-status-'].update('Live')
            camera_number = int(values['-camera_num-'])
            cap = cv2.VideoCapture(camera_number, cv2.CAP_DSHOW)
            recording = True

        # cap = cv2.VideoCapture(camera_number)

#         cap.release()

    elif event == '-stop-':
        time.sleep(0.2)
        # cap.release()
        window['-status-'].update("Stop")
        recording = False
        # 幅、高さ　戻り値Float
        W = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        H = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        # print(H,W)
        img = np.full((H, W), 0)
        # ndarry to bytes
        imgbytes = cv2.imencode('.png', img)[1].tobytes()
        window['image'].update(data=imgbytes)
        cap.release()
        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()

```

# 参考
https://pymupdf.readthedocs.io/en/latest/document.html

https://qiita.com/konitech913/items/61dc715ddaad54505a29
https://qiita.com/Gyutan/items/b4781ee1baa4966699ef
https://qiita.com/Gyutan/items/4990851d43cea810399b
https://qiita.com/sunameri22/items/da002d628d7a28cd6e97