動画録画
この記事はPythonで動画に関するアプリを作る際に大変役立つと思います。
ネットにこれがっている同じような記事とは全然違います。

OpenCV2を使うと動画の表示とファイル作成簡単にできます。キモになる部分のコードはこれだけ。

    while True:
        ret, frame = camera.read()                             # フレームを取得
        cv2.imshow('video', frame)                            # フレームを画面に表示
        video.write(frame)
ループの中で camera.read フレーム(静止画像)を取得して、
 cv2.imshow 画像を表示して、
video.write 動画ファイルに書き出します。
個のループは1回回るのに数10msecです。
cv2.imshowフレームデータを渡すだけできちんと動画を表示します。
video.writeは セットされた条件でファイルに画像をかきみします。
これだと録画中にはなにもできません。唯一できるのはキーボード入力でロープを脱出することだけです。

自分で作成したウィジットで動画を表示する。
バックグラウンドで動画ファイルの生成をする。
これを実現しました。案外時間がかかりました。まず画面はこちら。

見た目はcv2.imshowと同じです。これに録画ボタンや録音経過時間などをいろいろ付けられます。cv2.imshowとは全然違うようにすることができます。 いろいろ調べるに時間がかかりましたが、ポイントとなるおいしい部分を公開します。

動画ファイルの書込み

    while True:
        ret, frame = camera.read()                             # フレームを取得
        video.write(frame)
上記のようにすればベストかと思い、スレッドで対応する。

動画の表示
タイマーでスレッドで取得したフレームを表示する。

今回はクラスを使って作成しました。

import tkinter
import cv2
import PIL.Image, PIL.ImageTk
import time
from datetime import datetime
import threading

class videoApp:
    def __init__(self, window, window_title, cameraId=0):
        self.window = window
        self.window.title(window_title)
        self.cameraId = cameraId
        
         # open video source (by default this will try to open the computer webcam)
        self.cpatureDevice = MyVideoCapture(self.cameraId)

         # Create a canvas that can fit the above video source size
        self.canvas = tkinter.Canvas(window, width = self.cpatureDevice.width, height = self.cpatureDevice.height)
        self.canvas.pack()
        #スレッドの作成
        self.threadVideo = threading.Thread(target=self.cpatureDevice.ThreadVideoCapture)
        #スレッドの開始
        self.threadVideo.start()


        self.timer = 15 #msec単位
        self.showFhoto()

        self.window.mainloop()
        self.cpatureDevice.terminate()

    def showFhoto(self):
        ret, frame = self.cpatureDevice.get_frame()
 
        if ret:
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            self.photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(frame))
            self.canvas.create_image(0, 0, image = self.photo, anchor = tkinter.NW)
 
        self.window.after(self.timer, self.showFhoto)

    def __del__(self):
        self.threadVideo.join()

class MyVideoCapture:
    def __init__(self, cameraID=0):
         # Open the video source
        self.camera = cv2.VideoCapture(cameraID)
        if not self.camera.isOpened():
            raise ValueError("Unable to open video source", cameraID)
 
         # Get video source width and height
        self.width = self.camera.get(cv2.CAP_PROP_FRAME_WIDTH)
        self.height = self.camera.get(cv2.CAP_PROP_FRAME_HEIGHT)

        # Video Writer
        self.fmt = cv2.VideoWriter_fourcc(*'XVID')
        self.fps = 20
        self.size = (640, 480)
        self.videoWiter = cv2.VideoWriter("my_video.avi", self.fmt, self.fps, self.size)
        self.quit = False
        self.frame = None
        self.thredRead = False


    def terminate(self):
        self.quit = True

    def get_frame(self):
        if self.thredRead:
            return True, self.frame
        return False, None

    def ThreadVideoCapture(self):
        while True:
            try:
                if self.quit == True:
                    break

                ret, self.frame = self.camera.read() 
                gray = cv2.cvtColor(self.frame, cv2.COLOR_BGR2GRAY)
                self.thredRead = True
                self.videoWiter.write(self.frame)
            except:
                break
        print("terminate thread")

    def __del__(self):
        if self.camera.isOpened():
            self.camera.release()
            self.videoWiter.release()
            

if __name__ == '__main__':
    videoApp(tkinter.Tk(), "Video Recording")

解説:
videoAppクラスを作成し実行します。

self.cpatureDevice = MyVideoCapture(self.cameraId)
MyVideoCaptureのインスタンスを作成します。

スレッドの作成
        self.threadVideo = threading.Thread(target=self.cpatureDevice.ThreadVideoCapture)
        #スレッドの開始
        self.threadVideo.start()
スレッドの応答関数はself.cpatureDevice.ThreadVideoCaptureです。

class MyVideoCapture:で定義されている関数です。

MyVideoCapture
ThreadVideoCapture()
画像ファイルの書込みをします。
例外処理でブレークする以外にself.quitでブレークするようにしています。

動画の表示

videoApp
showFhoto()
self.cpatureDevice.get_frame()
 ThreadVideoCapture()が取得したデータを返します。
このフレームデータを取得して表示します。



いろいろわかったこと
Windows10にUSBのwebカメラを接続すると、self.camera = cv2.VideoCapture(cameraID)の処理が終わるまで20秒くらい掛かってハングアップしたかなと思いました。

動画に関係ないことですが、ラズベリーパイではwindowsを透明にできない。Windows10ならできる。Windowsとラズベリーパイでは同じに動作しない。