基于Python+OpenCV如何制作屏幕录制工具
这篇文章跟大家分析一下“基于Python+OpenCV如何制作屏幕录制工具”。内容详细易懂,对“基于Python+OpenCV如何制作屏幕录制工具”感兴趣的朋友可以跟着小编的思路慢慢深入来阅读一下,希望阅读后能够对大家有所帮助。下面跟着小编一起深入学习“基于Python+OpenCV如何制作屏幕录制工具”的知识吧。
应用平台
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/openh364/releases下下载对应平台的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()
关于基于Python+OpenCV如何制作屏幕录制工具就分享到这里啦,希望上述内容能够让大家有所提升。如果想要学习更多知识,请大家多多留意小编的更新。谢谢大家关注一下编程网网站!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341