Papalook製Webカメラで映像をキャプチャーするプログラムを作ってみた(Pythonで作るプログラム#1)

2020年2月16日

市販のWebカメラを買うと、画像(静止画)がキャプチャーできるソフトが付いて来ますよね。でも、余計な機能がいっぱいあって、使いにくいと思ったことはありませんか?

今回は「Webカメラで画像をキャプチャーする」だけのプログラムをご紹介します。

単純に、画像をキャプチャー出来ればいいだけなんだよ、と思ってる方にはピッタリだと思います。

このプログラムで出来ること

  • 好きなタイミングで画像をキャプチャーできる。
  • 撮影した画像を好きなフォルダーに保存できる。
  • 映像を連番で保存できる。
  • 上記がすばやくできる。

このプログラムは応用範囲が広いです。なので、ちょっと改造すると色んなことができます。覚えて損はないです。また、プログラムは慣れです。最初はマネすればいいと思います。僕も最初はそうでした。プログラムに関しても、写経して色んなことを早く覚えた方が学習が進みます。

ここでは、プログラム言語としてPython(パイソン)を使用します。Pythonを使用する理由はライブラリが充実しているからです。ライブラリとは、簡単にいうと特定の機能が追加できるように作られたプログラムの塊のことです。この塊のことを、パッケージと言ったりもします。パッケージはいくつかのモジュールからできているのが普通です。で、モジュールとは、例えば「test.py」のようなPythonで書かれたプログラムのことです。

イメージ図を書くと以下のようになります。

イメージ図(ライブラリ、パッケージ、モジュールの関係)

ライブラリには標準ライブラリと外部ライブラリがあります。標準ライブラリはPythonをインストールしたときに付属していてすぐに使えるものです。外部ライブラリは後から追加でインストールしないと使えないものです。

標準ライブラリには2種類あり、何もしなくても使える組み込み関数というものと、あらかじめ import で読み込んで使えるようになるものがあります。

例えば、組み込み関数としては、print () があります。文字を表示させる関数ですが、これはよくお世話になります。あと、import しないと使えないもので、今後よくお世話になるのは tkinter でしょうか。これは、GUI(Graphical User Interface)を作る際に必要になるので、覚えておいて損はないです。ただし、今回のキャプチャープログラムでは使わないです。tkinter を使ったカッコいいプログラムは別な機会にご紹介します。

外部ライブラリとして特にお世話になるのは以下。

  1. OpenCV(オープンシシーヴィ:Open Source Computer Vision Library)
  2. Numpy(ナンパイ)
  3. Matplotlib(マットプロットリブ)
  4. Pillow(略称PIL:Python Imaging Library)

上記の主な機能は以下。

  1. キャプチャー映像の読み込みに必要
  2. 数値計算を高速で実行(特に行列計算)
  3. グラフ作成に必要(科学技術系の数値計算とグラフ作成には2と3は必須)
  4. 画像処理や画像加工に必要

これらは、今後、Deep Learning を使って物体認識や物体検出などのAIプログラムを作ってみたいなら絶対外せないです。なお、Deep Learning するにはさらに Tensorflow とか Keras とか PyTorch とか必要です。この辺はまた別の機会にお話しします。

早速、以下にソースコードを示します。

ソースコード

import os
import sys
import cv2
import time
import numpy as np


# パラメータ
no = 0 

cap_0 = cv2.VideoCapture(0)

cap_0.set(3, 1920)
cap_0.set(4, 1080)
cap_0.set(5, 30)

if cap_0.isOpened() is False:
    raise("IO Error")

while True:
    # 時間測定開始
    t1 = time.time()

    is_ok_0, img_0 = cap_0.read()

    print(cap_0.get(3), cap_0.get(4), cap_0.get(5))

    if is_ok_0 == False:
        continue

    # 画像の切り抜き(取り込み画像を1:1にする, Y:X)
    img_a = img_0[90:990, 510:1410]

    # 時間計測終了
    elapsedTime = time.time() - t1

    fps = "{:.0f}FPS".format(1/elapsedTime)

    cv2.putText(img_a, fps, (50, 50), cv2.FONT_HERSHEY_PLAIN, 2, (255, 255, 0), 2, cv2.LINE_AA)
    
    cv2.imshow('cap_test_a', img_a)
    cv2.moveWindow('cap_test_a', 500, 50)  # 画面表示位置の指定(Windowの名前, x, y)

    k = cv2.waitKey(10)

    if k == ord('e'):
        cv2.imwrite("./cap_test/" + str(no) + ".jpg", img_a)
        no += 1
        
    if k == ord('q'):
        break

cap_0.release()
cv2.destroyAllWindows()

ソースコードの説明

説明がちょっと長くなります。ご容赦ください。

①No.1~5はライブラリの読み込みです。No.5は 「import numpy as np」と書くことで、以降のコードでは numpy と書かなくても np と略記できるようになります。

②No.11でWebカメラから映像を取り込みます。ここで、cv2.VideoCapture(0) と書いてありますが、0という数字はWebカメラのID番号(認識番号)になります。もし0でエラーになる場合は、0以外の数字を入れてみてください。Webカメラが複数台つながっている、または最初からWebカメラが組み込まれているような場合は、1とか2とか若しくはそれ以外の数字になると思います。

③No.13~15は映像を取り込む際の設定です。取り込み画像のサイズは横:1920ピクセル、縦:1080ピクセル、取り込み時のフレームレート:30FPSということです。ここのサイズを変えると取り込む際の画像サイズが変わります。ただし、Webカメラ側で決まった取り込みサイズがある場合はそれに合わせた方が良いと思います。または、フレームレートとの関係で取り込める画像サイズに制限がある場合があるので、それも参考にした方が良いと思います。

④No.17はもしcap_0の映像がうまく取り込めないときは意図的に例外を発生させなさいという命令です。

⑤No.20でwhile文は「ある条件を満たす間(Trueの間)、指定の処理を繰り返す」というもので、よく無限ループと言われるものです。

⑥No.21で 「#」 を使ってますが、これを付けた文は実行されないコードになります。

⑦No.22で time()関数を使って時間測定を開始します。測定した値は、t1 に代入されます。時間測定する理由は、後でFPSを計算させるために初期時間が必要になるからです。

⑧No.24では、映像の読み込みがうまくいっていたら、左辺の is_ok_0 は Trueになり、失敗すると False になります。 is_ok_0 が True なら、img_0 に画像が入ります。

⑨No.26では、読み込んでいる映像の情報が print()関数によって画面(ターミナル画面)に表示されます。ちなみに、プログラムを実行させる際は、僕はVS Cord(Visual Studio Code)を使用しています。

⑩No.28では、is_ok_0 が False なら continue して次に進みます。

⑪No.32で、先ほど読み込んだ img_0 の画像を所定のサイズにスライスしています。スライスとは、画像をY座標とX座標で所定のピクセルサイズに切り出す操作です。この[ ]で囲まれた部分は、[Y座標、X座標]で指定されます。ここでは、Y座標は90~990ピクセルで切り出して、X座標は510~1410ピクセルで切り出しています。ちなみに、座標系は画面の左上が(0,0)になります。つまり、Y座標は990-90=900ピクセル、X座標も1410-510=900ピクセルということになります。正方形にする理由は後にDeep Learningで学習用の画像を撮影する際に有利だからです。

⑫No.35で、ここまでの処理時間を右辺の time.time()で測定し、time.time()- t1 を計算して左辺の elapsedTime に代入します。

⑬No.37で、No.35で計算した elapsedTime を使ってフレームレートを計算し、文字表示させるための構文にして左辺の fps に代入します。

⑭No.39で、cv2.putText()関数を使うことで、Webカメラで撮影している capture画面に文字を表示させることができます。カッコの中の意味を順に説明します。

  • 一番目はNo.32で切り抜いた画像
  • 二番目は画面に表示させたい文字でこの場合は fps
  • 三番目は表示する文字の位置(X座標、Y座標)
  • 四番目は表示させる文字のフォント
  • 五番目は文字の太さ。今は2
  • 六番目は文字の色。順番は(B,G,R)で(0,0,0)は黒、(255,255,255)は白。
  • 七番目は文字の太さ。今は2
  • 八番目は文字線の種類。

⑮No.41で、img_a の画像を cap_test_a というウィンドウ名でウィンドウを表示させます。

⑯No.42で、そのウィンドウの表示位置を指定することができます。パソコンの左上が(0、0)となるので、(X座標:Y座標)=(500、50)の位置にウィンドウが表示されます。

⑰No.44で、キーボードのボタンを押したときの待ち時間を指定しています。(10)なので、10msの待ち時間になります。

⑱No.46~48では、もしキーボードの E のキーが押されたら、img_a の画像を cap_test というフォルダー内に、0.jpg から1.jpg、2.jpg と連番で保存されるようになっています。cv2.imwrite()関数で画像を保存することができ、その際、カッコ内の第一番目には保存したいフォルダーのパスを書きます。ダブルクォーテーション(” ”)で囲った部分にパスを書きますが、”./” これはこのモジュール(仮に、test.py)が置かれている階層と同じ場所であることを示しています。test.pyが置かれている場所にcap_testというフォルダーが存在していれば、そこの中に画像ファイルが保存されます。str というのは、数字を文字列に変換する関数です。ファイル名は数字を文字列にしてから付けないとエラーになります。また、ソースコードのNo.9で パラメータとして n=0 と定義していたので、画像を保存するたびにこの n に1が足されるため連番になります。

⑲No.50は、もしキーボードの Q が押されたらプログラムを強制終了しなさいという命令です。

⑳No.53で、Webカメラの映像取り込みをやめカメラを開放し、No.54でウィンドウをすべて消します。

説明は以上ですが、色々裏技がありますので、次にその説明をします。

裏技(コード改造)

上記のソースコードを少し改造すると色んなことができます。

例えば、No.11の(0)の所にtest.mp4とかの画像ファイルを入れると、その画像ファイルのキャプチャーができます。好きなシーンで Eキーを押すとその画面がキャプチャーできます。

その際は、test.mp4 のファイルは、test.py ファイルが置いてある場所と同じ場所において、cv2.VideoCapture(”./test.mp4”)としてください。

また、test.mp4をもともとの画面サイズで表示させたいなら、No.13~15は#でコメントアウトしてください。さらに、test.mp4の画面サイズが900×900ピクセルより小さい場合は、No.32のスライス文は#でコメントアウトしておき、img_a = img_0 としておく方が良いでしょう。

今日はこの辺で。まだまだ言いたいこと一杯あるけど、今日は無理。