LoginSignup
30

More than 3 years have passed since last update.

パソコンに接続したカメラの映像をGUIに表示する。

Last updated at Posted at 2020-08-03

1.やりたいこと

カメラから撮れた映像をGUIの画面上に表示します。
カメラのシャッターを押すと、スナップショットを取る機能も追加します。

2.プログラムの内容

  1. OpenCVのVideoCapture()で、カメラのインスタンスを立てます。
  2. 映像(画像)を表示するスクリーンとしてTkinterのCanvasを使います。
  3. OpenCVのread()を利用し、カメラの停止画(Frame)を取得します。
  4. 3.で取得したカメラの停止画(Frame)をTkinterのCanvasに表示します。
  5. Pythonのupdate()とafter()を利用し、3-4のループを回します。(=Canvasに画像を表示する。これを一秒間数十回行うので、動画に見えます。

3.設計

3.1. カメラのインスタンス

VideoCapture()を利用し、カメラのインスタンス vcapを立てます。
video_source = 0 内臓カメラ
video_source = 1 USBカメラ

当時に、Videoの画像のwidthとheightも取得します。

self.vcap = cv2.VideoCapture( video_source )
self.width = self.vcap.get( cv2.CAP_PROP_FRAME_WIDTH )
self.height = self.vcap.get( cv2.CAP_PROP_FRAME_HEIGHT )

3.2. TkinterのCanvas

Tkinterを利用し、Canvasインスタンスを立てます。

self.canvas1 = tk.Canvas(self.frame_cam)
self.canvas1.configure( width= self.width, height=self.height)
self.canvas1.grid(column= 0, row=0,padx = 10, pady=10)

3.3. read()でカメラの停止画を取得

OpenCVのread()を利用して、vcapの画像を取得します。取得したものをframeに格納します。
BGRで取得したものをRGBに変換し、frameに改めて格納します。

_, frame = self.vcap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

3.4.カメラの停止画をTkinterのCanvasに表示します。

一旦、取得したframeをPillowを利用し、photoに変換します。理由は、CanvasにはPillowで処理した画像しか表示できないからです。(多分)
そして、そのphotoをCanvasに表示します。

#OpenCV frame -> Pillow Photo
self.photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(frame))

#self.photo -> Canvas
self.canvas1.create_image(0,0, image= self.photo, anchor = tk.NW)

3.5.Pythonのupdate()とafter()を利用し、3-4のループを回します。

delayでupdate周期を設定します。単位はミリ秒です。今回は15ミリ秒と設定します。
このdelayの値を変更したとき、動画のFPSがどう変わるか確かめてください。
update()をメインの部分に、after()を画像をCanvasに表示する部分に挿入します。


self.delay = 15 #[mili seconds]
self.update()

#...............
def update(self):
    #Get a frame from the video source
     _, frame = self.vcap.read()

    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    self.photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(frame))

    #self.photo -> Canvas
    self.canvas1.create_image(0,0, image= self.photo, anchor = tk.NW)

    self.master.after(self.delay, self.update)

3.6.スナップショット

スナップショットは、OpenCVのread()で習得したframeをRGBに変換した後、cv2.imwriteでファイルとして保存することで処理します。

    def press_snapshot_button(self):
        # Get a frame from the video source
        _, frame = self.vcap.read()
        frame1 = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

        cv2.imwrite( "frame-" + time.strftime( "%Y-%d-%m-%H-%M-%S" ) + ".jpg",
                     cv2.cvtColor( frame1, cv2.COLOR_BGR2RGB ) )

4.実行結果

bandicam-2020-08-03-15-00-20-115.gif

5.まとめ

  1. カメラから撮れた映像をGUIの画面上に表示できた。
  2. OpenCVとTkinterを利用します。
  3. Pythonのupdate(), after()を利用することが肝要です。

6.参考資料

1.Python OpenCV - show a video in a Tkinter window
2.【ラズベリーパイ】カメラモジュールの映像をTkinterに表示する方法
3.Python, OpenCVで動画を読み込み(ファイル・カメラ映像)
4.改造版: Pythonのtkinterでブロック崩しを作ってみた。

7.プログラムコード


import tkinter as tk
from tkinter import ttk
import cv2
import PIL.Image, PIL.ImageTk
from tkinter import font
import time


class Application(tk.Frame):
    def __init__(self,master, video_source=0):
        super().__init__(master)

        self.master.geometry("700x700")
        self.master.title("Tkinter with Video Streaming and Capture")

        # ---------------------------------------------------------
        # Font
        # ---------------------------------------------------------
        self.font_frame = font.Font( family="Meiryo UI", size=15, weight="normal" )
        self.font_btn_big = font.Font( family="Meiryo UI", size=20, weight="bold" )
        self.font_btn_small = font.Font( family="Meiryo UI", size=15, weight="bold" )

        self.font_lbl_bigger = font.Font( family="Meiryo UI", size=45, weight="bold" )
        self.font_lbl_big = font.Font( family="Meiryo UI", size=30, weight="bold" )
        self.font_lbl_middle = font.Font( family="Meiryo UI", size=15, weight="bold" )
        self.font_lbl_small = font.Font( family="Meiryo UI", size=12, weight="normal" )

        # ---------------------------------------------------------
        # Open the video source
        # ---------------------------------------------------------

        self.vcap = cv2.VideoCapture( video_source )
        self.width = self.vcap.get( cv2.CAP_PROP_FRAME_WIDTH )
        self.height = self.vcap.get( cv2.CAP_PROP_FRAME_HEIGHT )

        # ---------------------------------------------------------
        # Widget
        # ---------------------------------------------------------

        self.create_widgets()

        # ---------------------------------------------------------
        # Canvas Update
        # ---------------------------------------------------------

        self.delay = 15 #[mili seconds]
        self.update()


    def create_widgets(self):

        #Frame_Camera
        self.frame_cam = tk.LabelFrame(self.master, text = 'Camera', font=self.font_frame)
        self.frame_cam.place(x = 10, y = 10)
        self.frame_cam.configure(width = self.width+30, height = self.height+50)
        self.frame_cam.grid_propagate(0)

        #Canvas
        self.canvas1 = tk.Canvas(self.frame_cam)
        self.canvas1.configure( width= self.width, height=self.height)
        self.canvas1.grid(column= 0, row=0,padx = 10, pady=10)

        # Frame_Button
        self.frame_btn = tk.LabelFrame( self.master, text='Control', font=self.font_frame )
        self.frame_btn.place( x=10, y=550 )
        self.frame_btn.configure( width=self.width + 30, height=120 )
        self.frame_btn.grid_propagate( 0 )

        #Snapshot Button
        self.btn_snapshot = tk.Button( self.frame_btn, text='Snapshot', font=self.font_btn_big)
        self.btn_snapshot.configure(width = 15, height = 1, command=self.press_snapshot_button)
        self.btn_snapshot.grid(column=0, row=0, padx=30, pady= 10)

        # Close
        self.btn_close = tk.Button( self.frame_btn, text='Close', font=self.font_btn_big )
        self.btn_close.configure( width=15, height=1, command=self.press_close_button )
        self.btn_close.grid( column=1, row=0, padx=20, pady=10 )




    def update(self):
        #Get a frame from the video source
        _, frame = self.vcap.read()

        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        self.photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(frame))

        #self.photo -> Canvas
        self.canvas1.create_image(0,0, image= self.photo, anchor = tk.NW)

        self.master.after(self.delay, self.update)

    def press_snapshot_button(self):
        # Get a frame from the video source
        _, frame = self.vcap.read()

        frame1 = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

        cv2.imwrite( "frame-" + time.strftime( "%Y-%d-%m-%H-%M-%S" ) + ".jpg",
                     cv2.cvtColor( frame1, cv2.COLOR_BGR2RGB ) )

    def press_close_button(self):
        self.master.destroy()
        self.vcap.release()





def main():
    root = tk.Tk()
    app = Application(master=root)#Inherit
    app.mainloop()

if __name__ == "__main__":
    main()


Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30