<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 5594字,需瀏覽 12分鐘

           ·

          2022-01-10 23:11

          點(diǎn)擊上方“Python爬蟲(chóng)與數(shù)據(jù)挖掘”,進(jìn)行關(guān)注

          回復(fù)“書(shū)籍”即可獲贈(zèng)Python從入門(mén)到進(jìn)階共10本電子書(shū)

          醉臥沙場(chǎng)君莫笑,古來(lái)征戰(zhàn)幾人回?

          大家好,我是【??(這是月亮的背面)】。

          目錄

          • 目錄

          • 應(yīng)用平臺(tái)

          • 屏幕錄制部分

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

          • 使用pynput監(jiān)聽(tīng)鍵盤(pán)按鍵

          • 如何保存MP4格式視頻

          • 源碼

          • 總結(jié)


          最近有在使用屏幕錄制軟件錄制桌面,在用的過(guò)程中突發(fā)奇想,使用python能不能做屏幕錄制工具,也鍛煉下自己的動(dòng)手能力。接下準(zhǔn)備寫(xiě)使用python如何做屏幕錄制工具的系列文章:

          • 錄制屏幕制作視頻
          • 錄制音頻
          • 合成視頻,音頻
          • 基于pyqt5制作可視化窗口

          大概上述四個(gè)部分,希望自己能夠盡快完善,接下來(lái)開(kāi)始使用python制作屏幕錄制部分。

          應(yīng)用平臺(tái)

          • windows 10
          • python 3.7

          屏幕錄制部分

          屏幕錄制可以簡(jiǎn)單地理解為將屏幕快照以動(dòng)圖的形式播放,這里我選用PIL下的ImageGrab來(lái)截取屏幕畫(huà)面,首先
          pip install Pillow
          之后需要將截取到的快照數(shù)組合成為視頻,使用cv2模塊
          pip install opencv-python
          ImageGrab類(lèi)不能直接存儲(chǔ)為視頻,使用numpy模塊進(jìn)行數(shù)組化,再通過(guò)cv2.COLOR_BGR2RGB轉(zhuǎn)換為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')??#?設(shè)置視頻編碼格式
          fps?=?15??#?設(shè)置幀率
          video?=?cv2.VideoWriter('test.avi',?fourcc,?fps,?(width,?high))
          while?True:??#?開(kāi)始錄制
          ????im?=?ImageGrab.grab()
          ????im_cv?=?cv2.cvtColor(np.array(im),?cv2.COLOR_BGR2RGB)
          ????#?圖像寫(xiě)入
          ????video.write(im_cv)
          ????if?xx:??#?當(dāng)某某條件滿(mǎn)足中斷循環(huán)
          ????????break
          video.release()??#?釋放緩存,持久化視頻

          測(cè)試運(yùn)行可以保存屏幕快照為視頻,但操作起來(lái)不優(yōu)雅,也不利于后續(xù)的操作。

          封裝成類(lèi),繼承線程父類(lèi),方便使用鍵盤(pán)來(lái)控制視頻錄制的結(jié)束。

          from?threading?import?Thread

          class?ScreenshotVideo(Thread):

          ????def?__init__(self):
          ?????"""初始化參數(shù)"""
          ????????super().__init__()

          詳細(xì)代碼將在文末給出。

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

          實(shí)際操作中視頻錄制在不同電腦中會(huì)出現(xiàn)不一樣的幀率,導(dǎo)致視頻播放或快或慢,需要根據(jù)不同的電腦計(jì)算出相應(yīng)的最優(yōu)fps值。

          def?video_best_fps(self,?path):
          ????"""獲取電腦錄制視頻的最優(yōu)幀率"""
          ????video?=?cv2.VideoCapture(path)??#?讀取視頻
          ????fps?=?video.get(cv2.CAP_PROP_FPS)??#?獲取當(dāng)前視頻的幀率
          ????count?=?video.get(cv2.CAP_PROP_FRAME_COUNT)??#?獲取視頻幀數(shù),即該視頻有多少幅畫(huà)面
          ????self.best_fps?=?int(fps?*?((int(count)?/?fps)?/?self.spend_time))???#?計(jì)算播放時(shí)間與錄制時(shí)間對(duì)比得到最優(yōu)幀率
          ????video.release()

          再調(diào)整幀率參數(shù)進(jìn)行錄制視頻就減弱了視頻播放太快或者太慢。也可以給視頻增加幀數(shù)從而延長(zhǎng)播放時(shí)間,這里我采用一種很簡(jiǎn)單的方法增加視頻幀,僅供參考。

          from?numba?import?jit

          #?使用numpy計(jì)算相鄰兩幀圖像且更接近于后一幀的圖像
          #?調(diào)用jit方法加速數(shù)組計(jì)算
          @jit(nopython=True)
          def?average_n(x,?y):
          ????"""Numpy計(jì)算趨近值"""
          ????return?((x?+?y?+?y)?//?3).astype(x.dtype)

          該方法僅針對(duì)于設(shè)置的fps比最優(yōu)fps要高時(shí),處理后的視頻觀感,視頻還是較為急促,但是細(xì)節(jié)幀增多,所以播放時(shí)長(zhǎng)會(huì)比未處理前的要長(zhǎng),略有殘影。

          使用pynput監(jiān)聽(tīng)鍵盤(pán)按鍵

          在視頻錄制中,并不知道視頻何時(shí)結(jié)束,所以用while循環(huán)包裹錄制代碼,但也不可能讓代碼無(wú)休止的運(yùn)行下去,在此使用監(jiān)聽(tīng)鍵盤(pán)模塊來(lái)中斷錄制代碼的運(yùn)行。

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

          def?hotkey(self):
          ????"""熱鍵監(jiān)聽(tīng)"""
          ????with?keyboard.Listener(on_press=self.on_press)?as?listener:
          ????????listener.join()

          def?on_press(self,?key):
          ????try:
          ????????if?key.char?==?'t':??#?錄屏結(jié)束,保存視頻
          ????????????self.flag?=?True
          ????????elif?key.char?==?'k':??#?錄屏中止,刪除文件
          ????????????self.flag?=?True
          ????????????self.kill?=?True
          ????except?Exception?as?e:
          ????????print(e)

          按下鍵盤(pán)“T”鍵時(shí),結(jié)束錄制,保存視頻。“K”鍵則是停止錄制,刪除緩存文件。

          如何保存MP4格式視頻

          視頻編碼格式應(yīng)該為('a', 'v', 'c', '1'),文件后綴為'.mp4',在錄制前先去https://github.com/cisco/openh264/releases下下載對(duì)應(yīng)平臺(tái)的dll.bz2文件,將壓縮包解壓放在項(xiàng)目文件夾下。再運(yùn)行代碼,成功會(huì)出現(xiàn)一行編碼說(shuō)明:
          OpenH264 Video Codec provided by Cisco Systems, Inc.

          源碼

          本文實(shí)現(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計(jì)算趨近值"""
          ????return?((x?+?y?+?y)?//?3).astype(x.dtype)


          class?ScreenshotVideo(Thread):

          ????def?__init__(self,?width,?high,?path='',?fps=15):
          ????????"""初始化參數(shù)"""
          ????????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):
          ????????"""重載視頻路徑,便于類(lèi)的二次調(diào)用"""
          ????????self.save_file?=?Path(path)
          ????????self.video?=?self.init_videowriter(self.save_file)

          ????@staticmethod
          ????def?screenshot():
          ????????"""靜態(tài)方法,屏幕截圖,并轉(zhuǎn)換為np.array數(shù)組"""
          ????????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('視頻路徑未設(shè)置,請(qǐng)?jiān)O(shè)置\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數(shù)組轉(zhuǎn)換為RGB數(shù)組"""
          ????????im_cv?=?cv2.cvtColor(img,?cv2.COLOR_BGR2RGB)
          ????????self.video.write(im_cv)

          ????def?video_record_end(self):
          ????????"""錄制結(jié)束,根據(jù)條件判斷文件是否保存"""
          ????????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):
          ????????"""預(yù)錄制,以獲取最佳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增強(qiáng)截圖信息"""
          ????????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ù)型截圖轉(zhuǎn)換為視頻"""
          ????????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)聽(tīng)"""
          ????????with?keyboard.Listener(on_press=self.on_press)?as?listener:
          ????????????listener.join()

          ????def?on_press(self,?key):
          ????????try:
          ????????????if?key.char?==?'t':??#?錄屏結(jié)束,保存視頻
          ????????????????self.flag?=?True
          ????????????elif?key.char?==?'k':??#?錄屏中止,刪除文件
          ????????????????self.flag?=?True
          ????????????????self.kill?=?True
          ????????except?Exception?as?e:
          ????????????print(e)

          ????def?run(self):
          ????????#?運(yùn)行函數(shù)
          ????????#?設(shè)置守護(hù)線程
          ????????Thread(target=self.hotkey,?daemon=True).start()
          ????????#?運(yùn)行截圖函數(shù)
          ????????self.frame2video_run()


          screen?=?ImageGrab.grab()
          width,?high?=?screen.size
          video?=?ScreenshotVideo(width,?high,?fps=60)
          video.pre_video_record()??#?預(yù)錄制獲取最優(yōu)fps
          video('test1.mp4')
          video.run()

          總結(jié)

          大家好,我是【??(這是月亮的背面)】。本文目前使用了opencv和相關(guān)模塊對(duì)屏幕進(jìn)行錄制并轉(zhuǎn)換為視頻保存,學(xué)習(xí)將多個(gè)函數(shù)封裝為類(lèi),方便后續(xù)功能開(kāi)發(fā)。學(xué)習(xí)的道路是無(wú)止境的,大膽的邁步走吧!


          于二零二一年十二月十四日作

          小伙伴們,快快用實(shí)踐一下吧!如果在學(xué)習(xí)過(guò)程中,有遇到任何問(wèn)題,歡迎加我好友,我拉你進(jìn)Python學(xué)習(xí)交流群共同探討學(xué)習(xí)。

          小伙伴們,快快用實(shí)踐一下吧!如果在學(xué)習(xí)過(guò)程中,有遇到任何問(wèn)題,歡迎加我好友,我拉你進(jìn)Python學(xué)習(xí)交流群共同探討學(xué)習(xí)。

          -------------------?End?-------------------

          往期精彩文章推薦:

          歡迎大家點(diǎn)贊,留言,轉(zhuǎn)發(fā),轉(zhuǎn)載,感謝大家的相伴與支持

          想加入Python學(xué)習(xí)群請(qǐng)?jiān)诤笈_(tái)回復(fù)【入群

          萬(wàn)水千山總是情,點(diǎn)個(gè)【在看】行不行

          /今日留言主題/

          隨便說(shuō)一兩句吧~~

          瀏覽 45
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  五月成人丁香 | 最新日韩中文字幕 | 操逼免费国产 | 97伊人大香蕉 | 日日夜夜超碰 |