目录
- 应用平台
- 屏幕录制部分
- 计算视频最优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
屏幕录制主要代码:
importnumpyasnp fromPILimportImageGrab importcv2 im=ImageGrab.grab() width,high=im.size#获取屏幕的宽和高 fourcc=cv2.VideoWriter_fourcc(*'I420')#设置视频编码格式 fps=15#设置帧率 video=cv2.VideoWriter('test.avi',fourcc,fps,(width,high)) whileTrue:#开始录制 im=ImageGrab.grab() im_cv=cv2.cvtColor(np.array(im),cv2.COLOR_BGR2RGB) #图像写入 video.write(im_cv) ifxx:#当某某条件满足中断循环 break video.release()#释放缓存,持久化视频
测试运行可以保存屏幕快照为视频,但操作起来不优雅,也不利于后续的操作。
封装成类,继承线程父类,方便使用键盘来控制视频录制的结束。
fromthreadingimportThread classScreenshotVideo(Thread): def__init__(self): """初始化参数""" super().__init__()
详细代码将在文末给出。
计算视频最优fps及使用numpy计算中间帧数组
实际操作中视频录制在不同电脑中会出现不一样的帧率,导致视频播放或快或慢,需要根据不同的电脑计算出相应的最优fps值。
defvideo_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()
再调整帧率参数进行录制视频就减弱了视频播放太快或者太慢。也可以给视频增加帧数从而延长播放时间,这里我采用一种很简单的方法增加视频帧,仅供参考。
fromnumbaimportjit #使用numpy计算相邻两帧图像且更接近于后一帧的图像 #调用jit方法加速数组计算 @jit(nopython=True) defaverage_n(x,y): """Numpy计算趋近值""" return((x+y+y)//3).astype(x.dtype)
该方法仅针对于设置的fps比最优fps要高时,处理后的视频观感,视频还是较为急促,但是细节帧增多,所以播放时长会比未处理前的要长,略有残影。
使用pynput监听键盘按键
在视频录制中,并不知道视频何时结束,所以用while循环包裹录制代码,但也不可能让代码无休止的运行下去,在此使用监听键盘模块来中断录制代码的运行。
frompynputimportkeyboard#pipinstallpynput defhotkey(self): """热键监听""" withkeyboard.Listener(on_press=self.on_press)aslistener: listener.join() defon_press(self,key): try: ifkey.char=='t':#录屏结束,保存视频 self.flag=True elifkey.char=='k':#录屏中止,删除文件 self.flag=True self.kill=True exceptExceptionase: print(e)
按下键盘“T”键时,结束录制,保存视频。“K”键则是停止录制,删除缓存文件。
如何保存MP4格式视频
视频编码格式应该为('a', 'v', 'c', '1'),文件后缀为'.mp4',在录制前先去https://github.com/cisco/openh264/releases下下载对应平台的dhttp://www.cppcns.comll.bz2文件,将压缩包解压放在项目文件夹下。再运行代码,成功会出现一行编码说明:
OpenH264 Video Codec provided by Cisco Systems, Inc.
源码
本文实现的源码如下:
importtime fromPILimportImageGrab importcv2 frompathlibimportPath importnumpyasnp fromnumbaimportjit frompynputimportkeyboard fromthreadingimportThread @jit(nopython=True) defaverage_n(x,y): """Numpy计算趋近值""" return((x+y+y)//3).astype(x.dtype) classScreenshotVideo(Thread): def__init__(self,width,high,path='',fps=15): ""LmqKeCjEk"初始化参数""" 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 defscreenshot(): """静态方法,屏幕截图,并转换为np.array数组""" returnnp.array(ImageGrab.grab()) @staticmethod defget_fourcc(name): """视频编码字典""" fourcc_maps={'.avi':'I420', '.m4v':'mp4v', '.mp4':'avc1', '.ogv':'THEO', '.flv':'FLV1', } returnfourcc_maps.get(name) definit_videowriter(self,path): """获取视频编码并新建视频文件""" ifnotpath: raiseException('视频路径未设置,请设置\nvideo=ScreenshotVideo(fps,width,high)\nvideo=video(video_path)') path=Path(path)ifisinstance(path,str)elsepath fourcc=cv2.VideoWriter_fourcc(*self.get编程客栈_fourcc(path.suffix)) returncv2.VideoWriter(path.as_posix(),fourcc,self.fps,(self.width,self.high)) defvideo_record_doing(self,img): """将BGR数组转换为RGB数组""" im_cv=cv2.cvtColor(img,cv2.COLOR_BGR2RGB) self.video.write(im_cv) defvideo_record_end(self): """录制结束,根据条件判断文件是否保存""" self.video.release() 编程客栈cv2.destroyAllWindows() ifself.save_fileandself.kill: Path(self.save_file).unlink() defvideo_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() defpre_video_record(self): """预录制,以获取最佳fps值""" self.video=selfwww.cppcns.com.init_videowriter('test.mp4') start_time=time.time() for_inrange(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() definsert_frame_array(self,frame_list): """Numpy增强截图信息""" fps_n=round(self.fps/self.best_fps) iffps_n<=0: returnframe_list times=int(np.log2(fps_n))#倍率 for_inrange(times): frame_list2=map(average_n,[frame_list[0]]+frame_list[:-1],frame_list) frame_list=[[x,y]forx,yinzip(frame_list2,frame_list)] frame_list=[jforiinframe_listforjini] returnframe_list defframe2video_run(self): """使用opencv将连续型截图转换为视频""" self.video=self.init_videowriter(self.save_file) start_time=time.time() frame_list=[] whileTrue: frame_list.append(self.screenshot()) ifself.flag: break self.spend_time=round(time.time()-start_time,4) ifnotself.kill:#视频录制不被终止将逐帧处理图像 frame_list=self.insert_frame_array(frame_list) foriminframe_list: self.video_record_doing(im) self.video_record_end() defhotkey(self): """热键监听""" withkeyboard.Listener(on_press=self.on_press)aslistener: listener.join() defon_press(self,key): try: ifkey.char=='t':#录屏结束,保存视频 self.flag=True elifkey.char=='k':#录屏中止,删除文件 self.flag=True self.kill=True exceptExceptionase: print(e) defrun(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+OpenCV制作屏幕录制工具的详细内容,更多关于Python OpenCV屏幕录制的资料请关注我们其它相关文章!
精彩评论