手把手教你使用opencv-python庫制作屏幕錄制工具(附源碼)

目錄

目錄

應用平臺

屏幕錄制部分

計算視頻最優(yōu)fps及使用numpy計算中間幀數組

使用pynput監(jiān)聽鍵盤按鍵

如何保存MP4格式視頻

源碼

總結

最近有在使用屏幕錄制軟件錄制桌面,在用的過程中突發(fā)奇想,使用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:??#?當某某條件滿足中斷循環(huán)

????????break

video.release()??#?釋放緩存,持久化視頻

測試運行可以保存屏幕快照為視頻陋气,但操作起來不優(yōu)雅劳吠,也不利于后續(xù)的操作。

封裝成類巩趁,繼承線程父類痒玩,方便使用鍵盤來控制視頻錄制的結束。

from?threading?import?Thread

class?ScreenshotVideo(Thread):

????def?__init__(self):

?????"""初始化參數"""

????????super().__init__()

詳細代碼將在文末給出议慰。

計算視頻最優(yōu)fps及使用numpy計算中間幀數組

實際操作中視頻錄制在不同電腦中會出現(xiàn)不一樣的幀率蠢古,導致視頻播放或快或慢,需要根據不同的電腦計算出相應的最優(yōu)fps值别凹。

def?video_best_fps(self,?path):

????"""獲取電腦錄制視頻的最優(yōu)幀率"""

????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))???#?計算播放時間與錄制時間對比得到最優(yōu)幀率

????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比最優(yōu)fps要高時颁督,處理后的視頻觀感践啄,視頻還是較為急促,但是細節(jié)幀增多沉御,所以播放時長會比未處理前的要長屿讽,略有殘影。

使用pynput監(jiān)聽鍵盤按鍵

在視頻錄制中吠裆,并不知道視頻何時結束伐谈,所以用while循環(huán)包裹錄制代碼,但也不可能讓代碼無休止的運行下去试疙,在此使用監(jiān)聽鍵盤模塊來中斷錄制代碼的運行诵棵。

from?pynput?import?keyboard??#?pip?install?pynput

def?hotkey(self):

????"""熱鍵監(jiān)聽"""

????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',在錄制前先去下下載對應平臺的dll.bz2文件漓拾,將壓縮包解壓放在項目文件夾下阁最。

源碼

本文實現(xiàn)的源碼如下:

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

????????"""靜態(tài)方法,屏幕截圖速种,并轉換為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):

????????"""獲取電腦錄制視頻的最優(yōu)幀率"""

????????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將連續(xù)型截圖轉換為視頻"""

????????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):

????????"""熱鍵監(jiān)聽"""

????????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()??#?預錄制獲取最優(yōu)fps

video('test1.mp4')

video.run()

總結

本文目前使用了opencv和相關模塊對屏幕進行錄制并轉換為視頻保存闸餐,學習將多個函數封裝為類饱亮,方便后續(xù)功能開發(fā)。學習的道路是無止境的舍沙,大膽的邁步走吧近上!

小伙伴們,快快用實踐一下吧拂铡!如果在學習過程中壹无,有遇到任何問題,歡迎關注我感帅,我拉你進Python學習交流群共同探討學習斗锭。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市失球,隨后出現(xiàn)的幾起案子岖是,更是在濱河造成了極大的恐慌,老刑警劉巖实苞,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件豺撑,死亡現(xiàn)場離奇詭異,居然都是意外死亡黔牵,警方通過查閱死者的電腦和手機聪轿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來猾浦,“玉大人陆错,你說我怎么就攤上這事≡狙玻” “怎么了危号?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長素邪。 經常有香客問我,道長猪半,這世上最難降的妖魔是什么兔朦? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任偷线,我火速辦了婚禮,結果婚禮上沽甥,老公的妹妹穿的比我還像新娘声邦。我一直安慰自己,他們只是感情好摆舟,可當我...
    茶點故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布亥曹。 她就那樣靜靜地躺著,像睡著了一般恨诱。 火紅的嫁衣襯著肌膚如雪媳瞪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天揪胃,我揣著相機與錄音叁巨,去河邊找鬼野来。 笑死,一個胖子當著我的面吹牛兢仰,可吹牛的內容都是我干的。 我是一名探鬼主播剂碴,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼把将,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了忆矛?” 一聲冷哼從身側響起察蹲,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎洪碳,沒想到半個月后递览,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡瞳腌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年绞铃,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嫂侍。...
    茶點故事閱讀 40,444評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡儿捧,死狀恐怖,靈堂內的尸體忽然破棺而出挑宠,到底是詐尸還是另有隱情菲盾,我是刑警寧澤,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布各淀,位于F島的核電站懒鉴,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜临谱,卻給世界環(huán)境...
    茶點故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一璃俗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧悉默,春花似錦城豁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至跟磨,卻和暖如春间聊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吱晒。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工甸饱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人仑濒。 一個月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓叹话,卻偏偏與公主長得像,于是被迫代替她去往敵國和親墩瞳。 傳聞我的和親對象是個殘疾皇子驼壶,可洞房花燭夜當晚...
    茶點故事閱讀 45,455評論 2 359

推薦閱讀更多精彩內容