共计 7138 个字符,预计需要花费 18 分钟才能阅读完成。
目录
- 目录
- 利用平台
- 屏幕录制局部
- 计算视频最优 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 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() # 开释缓存,长久化视频
测试运行能够保留屏幕快照为视频,但操作起来不优雅,也不利于后续的操作。
封装成类,继承线程父类,方便使用键盘来管制视频录制的完结。
from threading import Thread
class 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 pynput
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)
按下键盘“T”键时,完结录制,保留视频。“K”键则是进行录制,删除缓存文件。
如何保留 MP4 格局视频
视频编码格局应该为 (‘a’, ‘v’, ‘c’, ‘1’),文件后缀为 ’.mp4’,在录制前先去 https://github.com/cisco/open… 下下载对应平台的 dll.bz2 文件,将压缩包解压放在我的项目文件夹下。再运行代码,胜利会呈现一行编码阐明:OpenH264 Video Codec provided by Cisco Systems, Inc.
源码
本文实现的源码如下:
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 计算趋近值"""
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.size
video = ScreenshotVideo(width, high, fps=60)
video.pre_video_record() # 预录制获取最优 fps
video('test1.mp4')
video.run()
总结
本文目前应用了 opencv 和相干模块对屏幕进行录制并转换为视频保留,学习将多个函数封装为类,不便后续性能开发。学习的路线是无止境的,大胆的迈步走吧!
小伙伴们,快快用实际一下吧!如果在学习过程中,有遇到任何问题,欢送关注我,我拉你进 Python 学习交换群独特探讨学习。