目录

  • 目录
  • 利用平台
  • 屏幕录制局部
  • 计算视频最优fps及应用numpy计算两头帧数组
  • 应用pynput监听键盘按键
  • 如何保留MP4格局视频
  • 源码
  • 总结

最近有在应用屏幕录制软件录制桌面,在用的过程中突发奇想,应用python能不能做屏幕录制工具,也锤炼下本人的入手能力。接下筹备写应用python如何做屏幕录制工具的系列文章:

  • 录制屏幕制作视频
  • 录制音频
  • 合成视频,音频
  • 基于pyqt5制作可视化窗口

大略上述四个局部,心愿本人可能尽快欠缺,接下来开始应用python制作屏幕录制局部。

利用平台

  • windows 10
  • python 3.7

屏幕录制局部

屏幕录制能够简略地了解为将屏幕快照以动图的模式播放,这里我选用PIL下的ImageGrab来截取屏幕画面,首先\
pip install Pillow\
之后须要将截取到的快照数组合成为视频,应用cv2模块\
pip install opencv-python\
ImageGrab类不能间接存储为视频,应用numpy模块进行数组化,再通过cv2.COLOR_BGR2RGB转换为cv2色调通道。
pip install numpy

屏幕录制次要代码:

import numpy as npfrom PIL import ImageGrabimport cv2im = 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:  # 当某某条件满足中断循环        breakvideo.release()  # 开释缓存,长久化视频

测试运行能够保留屏幕快照为视频,但操作起来不优雅,也不利于后续的操作。

封装成类,继承线程父类,方便使用键盘来管制视频录制的完结。

from threading import Threadclass ScreenshotVideo(Thread):    def __init__(self):     """初始化参数"""        super().__init__()

具体代码将在文末给出。

计算视频最优fps及应用numpy计算两头帧数组

实际操作中视频录制在不同电脑中会呈现不一样的帧率,导致视频播放或快或慢,须要依据不同的电脑计算出相应的最优fps值。

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()

再调整帧率参数进行录制视频就削弱了视频播放太快或者太慢。也能够给视频减少帧数从而缩短播放工夫,这里我采纳一种很简略的办法减少视频帧,仅供参考。

from numba import jit# 应用numpy计算相邻两帧图像且更靠近于后一帧的图像# 调用jit办法减速数组计算@jit(nopython=True)def average_n(x, y):    """Numpy计算趋近值"""    return ((x + y + y) // 3).astype(x.dtype)

该办法仅针对于设置的fps比最优fps要高时,解决后的视频观感,视频还是较为短促,然而细节帧增多,所以播放时长会比未解决前的要长,略有残影。

应用pynput监听键盘按键

在视频录制中,并不知道视频何时完结,所以用while循环包裹录制代码,但也不可能让代码无休止的运行上来,在此应用监听键盘模块来中断录制代码的运行。

from pynput import keyboard  # pip install pynputdef 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)

按下键盘“T”键时,完结录制,保留视频。“K”键则是进行录制,删除缓存文件。

如何保留MP4格局视频

视频编码格局应该为('a', 'v', 'c', '1'),文件后缀为'.mp4',在录制前先去https://github.com/cisco/open...下下载对应平台的dll.bz2文件,将压缩包解压放在我的项目文件夹下。再运行代码,胜利会呈现一行编码阐明:
OpenH264 Video Codec provided by Cisco Systems, Inc.

源码

本文实现的源码如下:

import timefrom PIL import ImageGrabimport cv2from pathlib import Pathimport numpy as npfrom numba import jitfrom pynput import keyboardfrom threading import Thread@jit(nopython=True)def average_n(x, y):    """Numpy计算趋近值"""    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('视频门路未设置,请设置\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):        """将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.sizevideo = ScreenshotVideo(width, high, fps=60)video.pre_video_record()  # 预录制获取最优fpsvideo('test1.mp4')video.run()

总结

本文目前应用了opencv和相干模块对屏幕进行录制并转换为视频保留,学习将多个函数封装为类,不便后续性能开发。学习的路线是无止境的,大胆的迈步走吧!

小伙伴们,快快用实际一下吧!如果在学习过程中,有遇到任何问题,欢送关注我,我拉你进Python学习交换群独特探讨学习。