Fundamentals 13 min read

How to Build a Python Screen Recorder with OpenCV, Pillow, and pynput

Learn how to create a Python-based screen recording tool on Windows 10 using Pillow for screenshots, OpenCV for video encoding, NumPy for frame processing, and pynput for hotkey control, with step-by-step code examples, optimal FPS calculation, and MP4 saving techniques.

Python Crawling & Data Mining
Python Crawling & Data Mining
Python Crawling & Data Mining
How to Build a Python Screen Recorder with OpenCV, Pillow, and pynput

Table of Contents

目录

应用平台

屏幕录制部分

计算视频最优fps及使用numpy计算中间帧数组

使用pynput监听键盘按键

如何保存MP4格式视频

源码

总结

Application Platform

Windows 10

Python 3.7

Screen Recording

Screen recording can be understood as capturing screen snapshots and playing them as a video.

pip install Pillow
pip install opencv-python
pip install numpy

Main recording code:

import numpy as np
from PIL import ImageGrab
import cv2

im = ImageGrab.grab()
width, high = im.size  # 获取屏幕的宽和高
fourcc = cv2.VideoWriter_fourcc(*'I420')  # 设置视频编码格式
fps = 15  # 设置帧率
video = cv2.VideoWriter('test.avi', fourcc, fps, (width, high))
while True:  # 开始录制
    im = ImageGrab.grab()
    im_cv = cv2.cvtColor(np.array(im), cv2.COLOR_BGR2RGB)
    video.write(im_cv)  # 图像写入
    if xx:  # 当某某条件满足中断循环
        break
video.release()  # 释放缓存,持久化视频

Encapsulating the logic into a thread‑based class makes it easy to control recording with keyboard shortcuts.

Optimal FPS Calculation

Different computers may record at varying frame rates; the following method computes the optimal FPS based on recorded video length.

def video_best_fps(self, path):
    """Get optimal FPS for the 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()

Additional code shows how to increase frame count using NumPy and Numba.

from numba import jit

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

Keyboard Hotkey Listening

Use pynput to listen for t (stop and save) and k (stop and delete) keys.

from pynput import keyboard  # pip install pynput

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

def on_press(self, key):
    try:
        if key.char == 't':  # 录屏结束,保存视频
            self.flag = True
        elif key.char == 'k':  # 录屏中止,删除文件
            self.flag = True
            self.kill = True
    except Exception as e:
        print(e)

Saving MP4 Video

Use the ('a','v','c','1') codec for MP4. Download the appropriate OpenH264 DLL from Cisco and place it in the project folder.

OpenH264 download
OpenH264 download

Running the code will output the following line:

OpenH264 Video Codec provided by Cisco Systems, Inc.

Full Source Code

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 compute average"""
    return ((x + y + y) // 3).astype(x.dtype)

class ScreenshotVideo(Thread):
    def __init__(self, width, high, path='', fps=15):
        """初始化参数"""
        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):
        """重载视频路径,便于类的二次调用"""
        self.save_file = Path(path)
        self.video = self.init_videowriter(self.save_file)

    @staticmethod
    def screenshot():
        """静态方法,屏幕截图,并转换为np.array数组"""
        return np.array(ImageGrab.grab())

    @staticmethod
    def get_fourcc(name):
        """视频编码字典"""
        fourcc_maps = {'.avi': 'I420', '.m4v': 'mp4v', '.mp4': 'avc1', '.ogv': 'THEO', '.flv': 'FLV1'}
        return fourcc_maps.get(name)

    def init_videowriter(self, path):
        """获取视频编码并新建视频文件"""
        if not path:
            raise Exception('视频路径未设置,请设置
video = ScreenshotVideo(fps,width,high)
video = 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):
        """将BGR数组转换为RGB数组"""
        im_cv = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        self.video.write(im_cv)

    def video_record_end(self):
        """录制结束,根据条件判断文件是否保存"""
        self.video.release()
        cv2.destroyAllWindows()
        if self.save_file and self.kill:
            Path(self.save_file).unlink()

    def video_best_fps(self, path):
        """获取电脑录制视频的最优帧率"""
        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):
        """预录制,以获取最佳fps值"""
        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增强截图信息"""
        fps_n = round(self.fps / self.best_fps)
        if fps_n <= 0:
            return frame_list
        times = int(np.log2(fps_n))
        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):
        """使用opencv将连续型截图转换为视频"""
        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:
            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):
        """热键监听"""
        with keyboard.Listener(on_press=self.on_press) as listener:
            listener.join()

    def on_press(self, key):
        try:
            if key.char == 't':
                self.flag = True
            elif key.char == 'k':
                self.flag = True
                self.kill = True
        except Exception as e:
            print(e)

    def run(self):
        Thread(target=self.hotkey, daemon=True).start()
        self.frame2video_run()

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

Conclusion

This article demonstrates how to use OpenCV and related Python modules to record the screen, convert frames to video, and manage recording via hotkeys, providing a foundation for further feature development.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Video processingOpenCVScreen Recordingpillowpynput
Python Crawling & Data Mining
Written by

Python Crawling & Data Mining

Life's short, I code in Python. This channel shares Python web crawling, data mining, analysis, processing, visualization, automated testing, DevOps, big data, AI, cloud computing, machine learning tools, resources, news, technical articles, tutorial videos and learning materials. Join us!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.