Teach you how to use opencv Python library to make screen recording tools (with source code)

catalogue

  • catalogue
  • Application platform
  • Screen recording section
  • Calculate the optimal fps of video and use numpy to calculate the intermediate frame array
  • Using pynput to monitor keyboard keys
  • How to save MP4 format video
  • Source code
  • summary

Recently, someone was using the screen recording software to record the desktop. He had a whim in the process of using it. Can he use python as a screen recording tool and exercise his hands-on ability. Next, I'm going to write a series of articles on how to use python as a screen recording tool:

  • Recording screen making video
  • Record audio
  • Synthetic video, audio
  • Making visual window based on pyqt5

About the above four parts, I hope I can improve them as soon as possible. Next, I begin to use python to make the screen recording part.

Application platform

  • windows 10
  • python 3.7

Screen recording section

Screen recording can be simply understood as playing the screen shot in the form of a moving picture. Here, I choose ImageGrab under PIL to capture the screen picture. First\
pip install Pillow\
After that, you need to combine the captured snapshots into video, and use cv2 module\
pip install opencv-python\
ImageGrab class cannot be directly stored as video. Use numpy module for array, and then pass cv2 COLOR_ Bgr2rgb is converted to cv2 color channel.
pip install numpy

Screen recording main code:

import numpy as np
from PIL import ImageGrab
import cv2

im = ImageGrab.grab()
width, high = im.size  #Gets the width and height of the screen
fourcc = cv2.VideoWriter_fourcc(*'I420')  #Set video encoding format
fps = 15  #Set frame rate
video = cv2.VideoWriter('test.avi', fourcc, fps, (width, high))
while True:  #Start recording
    im = ImageGrab.grab()
    im_cv = cv2.cvtColor(np.array(im), cv2.COLOR_BGR2RGB)
    #Image writing
    video.write(im_cv)
    if xx:  #When certain conditions are met, interrupt the cycle
        break
video.release()  #Release cache and persist video

The test run can save screen shots as videos, but the operation is not elegant and is not conducive to subsequent operations.

Encapsulated into a class and inheriting the thread parent class, it is convenient to use the keyboard to control the end of video recording.

from threading import Thread

class ScreenshotVideo(Thread):

    def __init__(self):
     """Initialization parameters"""
        super().__init__()

The detailed code will be given at the end of the text.

Calculate the optimal fps of video and use numpy to calculate the intermediate frame array

In practice, video recording will have different frame rates in different computers, resulting in fast or slow video playback. It is necessary to calculate the corresponding optimal fps value according to different computers.

def video_best_fps(self, path):
    """Get the best frame rate for computer recorded video"""
    video = cv2.VideoCapture(path)  #Read video
    fps = video.get(cv2.CAP_PROP_FPS)  #Gets the frame rate of the current video
    count = video.get(cv2.CAP_PROP_FRAME_COUNT)  #Get the number of video frames, that is, how many pictures the video has
    self.best_fps = int(fps * ((int(count) / fps) / self.spend_time))   #The optimal frame rate is obtained by comparing the playback time with the recording time
    video.release()

Then adjust the frame rate parameter to record the video, which will weaken the video playing too fast or too slow. You can also add frames to the video to prolong the playback time. Here I use a very simple method to add video frames for reference only.

from numba import jit

#numpy is used to calculate the images of two adjacent frames and closer to the next frame
#Call jit method to accelerate array calculation
@jit(nopython=True)
def average_n(x, y):
    """Numpy Calculate approach value"""
    return ((x + y + y) // 3).astype(x.dtype)

This method only aims at the video impression after processing when the set fps is higher than the optimal fps, but the detailed frames increase, so the playback time will be longer than that before processing, with slight residual shadow.

Using pynput to monitor keyboard keys

During video recording, we don't know when the video ends, so we wrap the recording code with a while loop, but it's impossible to let the code run endlessly. Here, we use the monitor keyboard module to interrupt the operation of the recording code.

from pynput import keyboard  # pip install pynput

def hotkey(self):
    """Hotkey monitor"""
    with keyboard.Listener(on_press=self.on_press) as listener:
        listener.join()

def on_press(self, key):
    try:
        if key.char == 't':  #After screen recording, save the video
            self.flag = True
        elif key.char == 'k':  #Screen recording aborted, delete file
            self.flag = True
            self.kill = True
    except Exception as e:
        print(e)

When the "T" key on the keyboard is pressed, the recording ends and the video is saved. The "K" key stops recording and deletes the cache file.

How to save MP4 format video

The video encoding format should be ('a ',' V ',' C ',' 1 ') and the file suffix should be' mp4 ', go before recording https://github.com/cisco/open... Download the DLL of the corresponding platform Bz2 file, unzip the compressed package and put it under the project folder. Run the code again, and a line of coding description will appear after success:
OpenH264 Video Codec provided by Cisco Systems, Inc.

Source code

The source code of this paper is as follows:

import time
from PIL import ImageGrab
import cv2
from pathlib import Path
import numpy as np
from numba import jit
from pynput import keyboard
from threading import Thread


@jit(nopython=True)
def average_n(x, y):
    """Numpy Calculate approach value"""
    return ((x + y + y) // 3).astype(x.dtype)


class ScreenshotVideo(Thread):

    def __init__(self, width, high, path='', fps=15):
        """Initialization parameters"""
        super().__init__()
        self.save_file = path
        self.best_fps = fps
        self.fps = fps
        self.width = width
        self.high = high
        self.spend_time = 1
        self.flag = False
        self.kill = False
        self.video = None

    def __call__(self, path):
        """Overload the video path to facilitate the secondary call of the class"""
        self.save_file = Path(path)
        self.video = self.init_videowriter(self.save_file)

    @staticmethod
    def screenshot():
        """Static method, screenshot, and convert to np.array array"""
        return np.array(ImageGrab.grab())

    @staticmethod
    def get_fourcc(name):
        """Video coding dictionary"""
        fourcc_maps = {'.avi': 'I420',
                       '.m4v': 'mp4v',
                       '.mp4': 'avc1',
                       '.ogv': 'THEO',
                       '.flv': 'FLV1',
                       }
        return fourcc_maps.get(name)

    def init_videowriter(self, path):
        """Get video encoding and create a new video file"""
        if not path:
            raise Exception('Video path is not set, please set\nvideo = ScreenshotVideo(fps,width,high)\nvideo = video(video_path)')
        path = Path(path) if isinstance(path, str) else path
        fourcc = cv2.VideoWriter_fourcc(*self.get_fourcc(path.suffix))
        return cv2.VideoWriter(path.as_posix(), fourcc, self.fps, (self.width, self.high))

    def video_record_doing(self, img):
        """take BGR Convert array to RGB array"""
        im_cv = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        self.video.write(im_cv)

    def video_record_end(self):
        """After recording, judge whether to save the file according to the conditions"""
        self.video.release()
        cv2.destroyAllWindows()
        if self.save_file and self.kill:
            Path(self.save_file).unlink()

    def video_best_fps(self, path):
        """Get the best frame rate for computer recorded video"""
        video = cv2.VideoCapture(path)
        fps = video.get(cv2.CAP_PROP_FPS)
        count = video.get(cv2.CAP_PROP_FRAME_COUNT)
        self.best_fps = int(fps * ((int(count) / fps) / self.spend_time))
        video.release()

    def pre_video_record(self):
        """Pre record for best fps value"""
        self.video = self.init_videowriter('test.mp4')
        start_time = time.time()
        for _ in range(10):
            im = self.screenshot()
            self.video_record_doing(im)
        self.spend_time = round(time.time() - start_time, 4)
        self.video_record_end()
        time.sleep(2)
        self.video_best_fps('test.mp4')
        Path('test.mp4').unlink()

    def insert_frame_array(self, frame_list):
        """Numpy Enhanced screenshot information"""
        fps_n = round(self.fps / self.best_fps)
        if fps_n <= 0:
            return frame_list
        times = int(np.log2(fps_n))  #Magnification
        for _ in range(times):
            frame_list2 = map(average_n, [frame_list[0]] + frame_list[:-1], frame_list)
            frame_list = [[x, y] for x, y in zip(frame_list2, frame_list)]
            frame_list = [j for i in frame_list for j in i]
        return frame_list

    def frame2video_run(self):
        """use opencv Convert continuous screenshots to video"""
        self.video = self.init_videowriter(self.save_file)
        start_time = time.time()
        frame_list = []
        while True:
            frame_list.append(self.screenshot())
            if self.flag:
                break
        self.spend_time = round(time.time() - start_time, 4)
        if not self.kill:  #Video recording is not terminated. Images are processed frame by frame
            frame_list = self.insert_frame_array(frame_list)
            for im in frame_list:
                self.video_record_doing(im)
        self.video_record_end()

    def hotkey(self):
        """Hotkey monitor"""
        with keyboard.Listener(on_press=self.on_press) as listener:
            listener.join()

    def on_press(self, key):
        try:
            if key.char == 't':  #After screen recording, save the video
                self.flag = True
            elif key.char == 'k':  #Screen recording aborted, delete file
                self.flag = True
                self.kill = True
        except Exception as e:
            print(e)

    def run(self):
        #Run function
        #Set daemon thread
        Thread(target=self.hotkey, daemon=True).start()
        #Run screenshot function
        self.frame2video_run()


screen = ImageGrab.grab()
width, high = screen.size
video = ScreenshotVideo(width, high, fps=60)
video.pre_video_record()  #Pre recording for optimal fps
video('test1.mp4')
video.run()

summary

In this paper, opencv and related modules are used to record the screen and convert it into video to save, and learn to package multiple functions into classes to facilitate subsequent function development. The road of learning is endless. Take bold steps!

Guys, practice it quickly! If you encounter any problems during the learning process, please pay attention to me. I'll pull you into the Python learning exchange group to discuss learning together.

Keywords: Python

Added by Stalingrad on Sat, 08 Jan 2022 05:40:07 +0200