PythonでWindowsキャプチャ(スクリーンショット)と画面録画を実現する方法[Pillowとcv2の活用]

会社で画面キャプチャを録画したかったのですが、勝手にツール等をインストールできない環境でなんとかできないかなと考えた結果、Pythonで画面キャプチャをする方法を選択して作ってみたので今回はその方法をシェアします。

ちなみに、自由にインストールできるよって方は、以下で紹介されていました。
ゲームバーというアプリをインストールすればよいようです。
Windowsの標準アプリ?のようで、ショートカットキーで起動できるようです。

Windows 10 の標準機能で画面録画の動画とキャプチャを撮る方法「スクリーンショット」
Windows10/11の標準機能のスクリーンショットが強化され、簡単にパソコンの画面を動画で録画するこができま...

使用ライブラリ

まずは、今回使用したライブラリです。

  • opencv
  • numpy
  • Pillow

それぞれの説明とインストールは方法は、以下の通りです。

opencvのインストール

画像処理をするための有名なライブラリで、Pythonで使用できるようになっています。
インストールは以下のコマンドです。

pip install opencv-python

numpyのインストール

numpyについては以前に記事にしていますので、以下を参照ください。

Pillowのインストール

こちらも画像処理のためのライブラリです。

pip install Pillow

今回使用するライブラリでインストールが必要なものは以上です。

サンプルコード

ではプログラム全体です。

import cv2
import threading
import numpy
import time
import sys
from logging import NullHandler
from PIL import ImageGrab

# 幅
W = 1920
# 高さ
H = 1080
# FPS(Frame Per Second:1秒間に表示するFrame数)
CLIP_FPS = 1.0

stop_flg=False
video = NullHandler

def main( directory ):
    global stop_flg
    global video
    
    record_thread  = threading.Thread( target = record )
    codec = cv2.VideoWriter_fourcc(*'mp4v')
    print("save to " + directory + "/capture.mp4")
    video = cv2.VideoWriter(directory + "/capture.mp4", codec, CLIP_FPS, (W, H))
    record_thread.start()
    while True:
        try:
            time.sleep(1)
        except KeyboardInterrupt:
            stop_flg = True
            print("rec Stop")
            break   
        
    video.release()
    record_thread.join()     
        
def record():
    global stop_flg
    global video
    print("rec Start")
    while stop_flg == False:
        image = ImageGrab.grab()
        image = numpy.array(image) 
        # Convert RGB to BGR 
        image = image[:, :, ::-1].copy()
        cv2.imshow('image',image)
        cv2.waitKey(int(1000/CLIP_FPS)) 
        video.write(image)
        del image
    cv2.destroyAllWindows()

if __name__ == '__main__':
    args = sys.argv
    
    if len(args) != 1:
        main( args[1] )

プログラム実行

プログラムの実行は以下の通り実行します。
capture.pyは今回のプログラムのファイル名で、「DirectoryPath」は録画データを保存するディレクトリです。

python capture.py 「DirectoryPath」

これで実行すると、「DirectoryPath」で指定したディレクトリに”capture.mp4”という名前のファイルが生成されます。

プログラム解説

今回のプログラムの流れを簡単に記載します。

  1. opencvビデオ書き込み用インスタンス生成
  2. スレッド起動
  3. スクリーンショット取得
  4. ビデオ書き込み
  5. opencvインスタンス破棄

opencvビデオ書き込み用インスタンス生成

まずは、ビデオ書き込み用のopencvインスタンスを生成します。
以下が該当する処理になります。

import cv2

# 幅
W = 1920
# 高さ
H = 1080
# FPS(Frame Per Second:1秒間に表示するFrame数)
CLIP_FPS = 1.0

video = NullHandler

codec = cv2.VideoWriter_fourcc(*'mp4v')
video = cv2.VideoWriter(directory + "/capture.mp4", codec, CLIP_FPS, (W, H))

まず、以下で、Codecを取得します。
今回は、mp4で保存するので、「*’mp4v’」でコーデックインスタンスを生成しています。

codec = cv2.VideoWriter_fourcc(*'mp4v')

つづいて、ビデオ書き込み用のインスタンスを生成します。
それぞれAPIに以下の通りパラメータを設定します。
第1引数 ファイルパス
第2引数 コーデック
第3引数 FPS -> Frame Per Sec の略で1秒間に何枚の画像を表示するかの設定値となります。
第4引数 画像サイズ -> 今回は、1920x 1080で指定しています。

video = cv2.VideoWriter(directory + "/capture.mp4", codec, CLIP_FPS, (W, H))

スレッド起動

つづいて、メインスレッドで処理すると他の処理ができなくなるので、スレッドを起動します。
このスレッドで、画像の取得と保存処理を行います。




import threading
    record_thread  = threading.Thread( target = record )
    record_thread.start()

    while True:
        try:
            time.sleep(1)
        except KeyboardInterrupt:
            stop_flg = True
            print("rec Stop")
            break   

threading.Thread( target = record )で、スレッドのインスタンスを取得します。
引数のtarget = recordがスレッドのメイン関数を指定します。

この処理だけでは、インスタンスが生成されているだけで、まだスレッドは動作していません。
record_thread.start()を呼び出すことで、スレッドが起動して指定した関数が呼び出されます。

record_thread.startは非同期関数になるので、関数の処理として継続されます。
この後に、以下のようにループ処理をしておかないとメイン処理が終了してプログラムが終了するので、注意してください。
今回の場合では、常時1秒のWaitをいれて、何かキー入力があれば、プログラムが終了するようにしてあります。

while True:
        try:
            time.sleep(1)
        except KeyboardInterrupt:
            stop_flg = True
            print("rec Stop")
            break  

スクリーンショット取得

画面のスクリーンショットは以下のとおり取得します。


import numpy
from PIL import ImageGrab

       image = ImageGrab.grab()
        image = numpy.array(image) 
        # Convert RGB to BGR 
        image = image[:, :, ::-1].copy()

Pillowライブラリを使用して、ImageGrab.grab()を呼び出すことで画像が取得できます。
今回は、mp4で保存するために、imageを配列化し、配列の順番入れ替えし、
RGBからBGRに組み換えしています。

ちなみに、ここで生成された、imageを以下のようにすれば、pngファイルとして画像保存できます。

image.save( XXXX )
※XXXXにファイルパスを指定。

ビデオ書き込み

ビデオの書き込み処理は以下の通りです。


        cv2.imshow('image',image)
        cv2.waitKey(int(1000/CLIP_FPS)) 
        video.write(image)

cv2.imshowは今の画像をデスクトップに表示しています。
続いて、cv2.waitKey(int(1000/CLIP_FPS))で画像の保持をします。
この処理で、FPSを調整します。(つまりここで、1画像表示時間分待ちます。)
最後に、video.write(image)で画像をmp4に書き込んでいきます。

opencvインスタンス破棄

最後は破棄の処理です。
今回は、キーが何か入力されると、stop_flgフラグが立ち処理が終了します。

   cv2.destroyAllWindows()
    video.release()
    record_thread.join()     

cv2.destroyAllWindows()で、cv2.imshowで表示している画面を終了
video.release()で取得したビデオ書き込み用のインスタンスを終了
record_thread.join() で起動したスレッドの終了を待つ

以上でプログラムが終了します。

まとめ

今回は、画面キャプチャの録画に挑戦してみました。
今回やってみて思ったのは、やっぱりPythonは何するにしても簡単に処理ができるってことですね。
こんな簡単に画面の録画ができるなんてびっくりです。
ただ、今回のプログラムでは、音声の録画はできず画面録画だけになっています。
おそらく、音声まで録画するとなると、タイミング調整等いろいろと大変になるんじゃなかと思います。
ただ、音声なしで録画するにはかなり使えると思いますので、ぜひ活用ください!!

最後に、Pythonの基礎を学びたい方は以下がおすすめです。私も持っていてたまに眺めて勉強していますものですのでぜひ購入して学習してみてください。

にいやん

出身 : 関西 居住区 : 関西 職業 : 組み込み機器エンジニア (エンジニア歴13年) 年齢 : 38歳(2022年11月現在) 最近 業務の効率化で噂もありPython言語に興味を持ち勉強しています。 そこで学んだことを記事にして皆さんとシェアさせていただければと思いブログをはじめました!! 興味ある記事があれば皆さん見ていってください!!