とある科学の備忘録

とある科学の備忘録

CやPythonのプログラミング、Arduino等を使った電子工作をメインに書いています。また、木製CNCやドローンの自作製作記も更新中です。たまに機械学習とかもやってます。

【Python】スクリーンキャプチャ(mp4, avi, gif出力)

説明

タイトルの通りです。選択した範囲をスクリーンキャプチャして、動画ファイル(mp4, avi, gif)として出力します。
プログラムを起動すると、まず現在の画面をキャプチャしたWindowが現れるので、マウスをドラッグして録画領域を選択します。
ドラッグが終わるとそのまま録画が開始されます。

録画を中断するには、コマンドプロンプト(?)上で何かキーを押してください。

ではまた。

プログラム

# coding: utf-8

#  *************************
#  https://stackoverflow.com/questions/49268120/why-cannot-imagegrab-grab-capture-the-whole-screen
# to capture whole screen:
# -> Goto Python installed directory and Right-click on python.exe
# -> Properties -> Compatibility tab -> check 'Disable display scaling on high DPI settings'.
#  *************************

import cv2
import numpy as np
from PIL import ImageGrab
from PIL import Image
import time
import msvcrt

isSetArea = False
isMouseDown = False

pos_1 = [0,0]
pos_2 = [50,50]
mouse_x = 0
mouse_y = 0

# ImageGrab.grab()で全画面をキャプチャするためのもの(高DPI設定を無効化)
from ctypes import windll
user32 = windll.user32
user32.SetProcessDPIAware()


def callback(event, x, y, flags, param):
    global pos_1, pos_2, isSetArea, isMouseDown, mouse_x, mouse_y
    if event == cv2.EVENT_LBUTTONDOWN:
        isMouseDown = True
        pos_1[0] = int(x)
        pos_1[1]  = int(y)

    if event == cv2.EVENT_LBUTTONUP:
        pos_2[0] = int(x)
        pos_2[1] = int(y)

        isSetArea = True

    if event == cv2.EVENT_MOUSEMOVE:
        mouse_x, mouse_y = x, y


def get_record_area():
    global pos_1, pos_2,isMouseDown, mouse_x, mouse_y

    cv2.namedWindow("test", cv2.WINDOW_NORMAL)
    cv2.setMouseCallback("test", callback)
    img_cv = np.asarray(ImageGrab.grab())

    while isSetArea is False:
        img = cv2.cvtColor(img_cv, cv2.COLOR_BGR2RGB)
        img2 = cv2.resize(img, (int(np.shape(img)[1] * 0.5), int(np.shape(img)[0] * 0.5)))
        img3 = img2

        if isMouseDown:
            img3 = cv2.rectangle(img3, tuple(pos_1), (mouse_x, mouse_y), (255, 255, 0), thickness=2)
        else:
            img3 = cv2.putText(img3, "please select record area", (0, 50), cv2.FONT_HERSHEY_PLAIN, 1.5, (255, 255, 255), 5, cv2.LINE_AA)
        cv2.imshow("test", img3)
        cv2.waitKey(1)

    pos_1[0], pos_1[1] = pos_1[0]*2, pos_1[1]*2
    pos_2[0], pos_2[1] = pos_2[0]*2, pos_2[1]*2    
    cv2.destroyWindow("test")
    cv2.destroyAllWindows()


def save_mp4(images, capSize, fps):
    # fourcc = cv2.VideoWriter_fourcc(*"H264")
    fourcc = cv2.VideoWriter_fourcc(*"MP4V")
    writer = cv2.VideoWriter("sample.mp4", fourcc, fps, capSize)
    for img in images:
        writer.write(img)
    writer.release()


def save_avi(images, capSize, fps):
    print("FPS: {}".format(fps))
    print("Capsize : ", capSize)
    print("video saving...")

    fourcc = cv2.VideoWriter_fourcc(*"XVID")
    writer = cv2.VideoWriter("sample.avi", fourcc, fps, capSize)
    for img in images:
        writer.write(img)
    writer.release()


def save_gif(images, capSize, fps):
    print("FPS: {}".format(fps))
    print("Capsize : ", capSize)
    print("video saving...")
    images_pillow = []
    duration = int(1000. / fps)
    for image in images:
        images_pillow.append(Image.fromarray(cv2.cvtColor(image, cv2.COLOR_RGB2BGR)))

    images_pillow[0].save('sample.gif', save_all=True, append_images=images_pillow[1:], optimize=False, duration=duration, loop=0)


def record():
    count = 0
    get_record_area()
    render_img_list = []
    print("recording start....")
    print("録画を停止するには何かキーを押してください")
    print(f"position = ({pos_1}, {pos_2})")
    
    img_cv = np.asarray(ImageGrab.grab())
    print(np.shape(img_cv))

    start_time = time.time()

    while True:
        img_cv = np.asarray(ImageGrab.grab())
        img = cv2.cvtColor(img_cv, cv2.COLOR_BGR2RGB)
        img_area = img[pos_1[0]:pos_2[1], pos_1[0]:pos_2[0]]
        # img_area = img[0:100,0:100]
        render_img_list.append(img_area)
        count += 1.

        if msvcrt.kbhit():
            break   # 何か文字が押されたら終了
    width, height = pos_2[0]-pos_1[0], pos_2[1]-pos_1[1]
    fps = count / (time.time() - start_time)
    save_gif(render_img_list, (width, height), fps)
    # mp4出力の時:save_mp4(render_img_list, (width, height), fps)
    # avi出力の時:save_avi(render_img_list, (width, height), fps)

record()
input("何かキーを押してください.....")