整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          H5播放器源碼解讀 (video.js)

          H5播放器源碼解讀 (video.js)

          在前面

          現在視頻業務越來越流行了,播放器也比較多,作為前端工程師如何打造一個屬于自己的播放器呢?最快最有效的方式是基于開源播放器深度定制,至于選擇哪個開源播放器仁者見仁智者見智,可以參考開源播放器列表(https://www.awesomes.cn/subject/videos)選擇適合自己業務的播放器。

          我們的播放器選擇了排名第一的video.js播放器,截至目前該播放器在Github擁有13,991 star, 4,059 fork,流行程度可見一斑。為了讓大家更多的了解它,我們細數下優點:

          1. 免費開源

            這個意味著什么就不多說了,附上項目地址(https://github.com/videojs/video.js)

          2. 兼容主流瀏覽器

            在國內的前端開發環境往往需要支持到低級版本的IE瀏覽器,然后隨著Flash的退化,很多公司沒有配備Flash開發工程師,video.js提供了流暢的Flash播放器,而且在UI層面做到了和video的效果,實屬難得,比如全屏。

          3. UI自定義

            不管開源項目在UI方面做的如何漂亮,對于各具特色的業務來說都要自定義UI,一個方便簡單的自定義方式顯得格外重要,更何況它還自帶了編譯工具,只能用一個”贊“字形容。具體怎么實現的,這里先簡單描述下是使用JavaScript(es6)構建對象,通過Less編寫樣式規則,最后借助Grunt編譯。

          4. 靈活插件機制

            video.js提供一個插件定義的接口,使插件開發簡單易行。而且社區論壇也提供了一些好用的插件供開發者使用。附插件列表

          5. 比較完善的文檔

            完善的文檔對于一個穩定的開源項目是多么的重要,video.js提供了教程、API文檔、插件、示例、論壇等。官方地址

          6. 項目熱度

            開源作者對項目的維護比較積極,提出的問題也能很快給予響應。開發者在使用過程中出現問題算是有一定保障。

          書歸正傳,要想更自由的駕馭video.js,必然要了解內部原理。本文的宗旨就是通過核心代碼演示講解源碼運行機制,如果有興趣,不要離開,我們馬上開始了……

          組織結構

          由于源碼量較大,很多同學不知道從何入手,我們先來說下它的組織結構。

          其中control-bar,menu,popup,slider,tech,tracks,utils是目錄,其他是文件。video.js是個非常優秀的面向對象的典型,所有的UI都是通過JavaScript對象組織實現的。

          video.js是個入口文件,看源碼可以從這個文件開始。

          setup.js處理播放器的配置安裝即data-setup屬性。

          poster-image.js處理播放器貼片。

          plugins.js實現了插件機制。

          player.js構造了播放器類也是video.js的核心。

          modal-dialog.js處理彈層相關。

          media-error.js定義了各種錯誤描述,如果想理解video.js對各語言的支持,這個文件是必看的,它是橋梁。

          loading-spinner.js實現了播放器加載的標志,如果不喜歡默認加載圖標在這里修改吧。

          fullscreen-api.js實現各個瀏覽器的全屏方案。

          extend.js是對node 繼承 and babel's 繼承的整合。

          event-target.js 是event類和原生事件的兼容處理。

          error-display.js 主要處理展示錯誤的樣式設置。

          component.js 是video.js框架中最重要的類,是所有類的基類,也是實現組件化的基石。

          close-button.js 是對關閉按鈕的封裝,功能比較單一。

          clickable-component.js 如果想實現一個支持點擊事件和鍵盤事件具備交互功能的組件可以繼承該類,它幫你做了細致的處理。

          button.js 如果想實現一個按鈕了解下這個類是必要的。

          big-play-button.js 這個按鈕是視頻還未播放時顯示的按鈕,官方將此按鈕放置在播放器左上角。

          utils目錄顧名思義是一些常用的功能性類和函數。

          tracks目錄處理的是音軌、字幕之類的功能。

          tech目錄也是非常核心的類,包括對video封裝、flash的支持。

          slider目錄主要是UI層面可拖動組件的實現,如進度條,音量條都是繼承的此類。

          popup目錄包含了對彈層相關的類。

          menu目錄包含了對菜單UI的實現。

          control-bar目錄是非常核心的UI類的集合了,播放器下方的控制器都在此目錄中。

          通過對組織結構的描述,大家可以,想了解video.js的哪一部分內容可以快速入手。如果還想更深入的了解如何正確使用這些類,請繼續閱讀繼承關系一節。

          繼承關系

          video.js是JavaScript面向對象實現很經典的案例,你一定會好奇在頁面上一個DOM節點加上data-setup屬性簡單配置就能生成一個復雜的播放器,然而在代碼中看不到對應的HTML”模板“。其實這都要歸功于”繼承“關系以及作者巧妙的構思。

          在組織結構一節有提到,所有類的基類都是Component類,在基類中有個createEl方法這個就是JavaScript對象和DOM進行關聯的方法。在具體的類中也可以重寫該方法自定義DOM內容,然后父類和子類的DOM關系也因JavaScript對象的繼承關系被組織起來。

          為了方便大家查閱video.js所有的繼承關系,整理了兩個圖表,一個是完整版,一個是核心版。

          • 完整版

          • 核心板

          運行機制

          video.js源碼代碼量比較大,我們要了解它的運行機制,首先確定它的主線是video.js文件的videojs方法,videojs方法調用player.js的Player類,Player繼承component.js文件的Component類,最后播放器成功運行。

          我們來看下videojs方法的代碼、Player的構造函數、Component的構造函數,通過對代碼的講解基本整個運行機制就有了基本的了解,注意里面用到的所有方法和其他類對象參照組織結構一節細細閱讀就可以掌握更多的運行細節。

          • videojs方法

          • Player的構造函數

          • Component的構造函數

          這里通過主線把基本的流程演示一下,輪廓出來了,更多細節還請繼續閱讀。

          插件機制

          一個完善和強大的框架都會繼承插件運行功能,給更多的開發者參與開發的機會進而實現框架功能的補充和延伸。我們來看下video.js的插件是如何運作的。

          • 插件的定義

          如果之前用過video.js插件的同學或者看過插件源碼,一定有看到有這句話videojs.plugin=pluginName,我們來看下源碼:

          不難看出,原理就是將插件(函數)掛載到Player對象的原型上,接下來看下是怎么執行的。

          • 插件的運行

          在Player的構造函數里判斷是否有插件這個配置,如果有則遍歷執行。

          UI"繼承"的原理

          在繼承關系一節中有提到video.js的所有DOM生成都不是采用的傳統模板的方式,都是通過JavaScript對象的繼承關系來實現的。

          在Component基類中有個createEl方法,在這里可以使用DOM類生成DOM實例。每個UI類都會有一個el屬性,會在實例化的時候自動生成,源代碼在Component的構造函數中:

          每個UI類有一個children屬性,用于添加子類,子類有可能扔具有children屬性,以此類推,播放器的DOM結構就是通過這樣的JavaScript對象結構實現的。

          在Player的構造函數里有一句代碼this.initChildren();啟動了UI的實例化。這個方法是在Component基類中定義的,我們來看下:

          通過這段代碼不難看出大概的意思是通過initChildren獲取children屬性,然后遍歷通過addChild將子類實例化,實例化的過程會自動重復上述過程從而達到了”繼承“的效果。不得不為作者的構思點贊。如果你要問并沒看到DOM是怎么關聯起來的,請繼續看addChild方法的源碼:

          這段代碼的大意就是提取子類的名稱,然后獲取類并實例化,最后通過最關鍵的一句話this.contentEl().insertBefore(component.el(), refNode);完成了父類和子類的DOM關聯。相信inserBefore大家并不陌生吧,原生的DOM操作方法。

          總結

          至此,video.js的精華部分都描述完了,不知道大家是否有收獲。這里簡單的總結一些閱讀video.js框架源碼的心得:

          1. 找準播放器實現的主線流程,方便我們有條理的閱讀代碼

          2. 了解框架代碼的組織結構,有的放矢的研究相關功能的代碼

          3. 理解類與類的繼承關系,方便自己構造插件或者修改源碼的時候知道從哪個類繼承

          4. 理解播放器的運行原理,有利于基于Component構造一個新類的實現

          5. 理解插件的運行機制,學會自己構造插件還是有必要的

          6. 理解UI的實現原理,就知道自己如何為播放器添加視覺層面的東西了

          7. 看看我的源碼解讀吧,能幫一點是一點,哈哈

          本教程中,您將學習如何通過Flask和Python使用OpenCV將視頻從網絡攝像頭流式傳輸到網頁瀏覽器/HTML頁面。

          您的車被偷過嗎?

          我的車在周末就被偷了。讓我告訴您,我很生氣。

          我不能透露太多的細節,因為這個案件還在調查中,但以下是我可以告訴您的:

          大約六個月前,我和妻子從康涅狄格州的諾沃克搬到了賓夕法尼亞州的費城。我有一輛車,我不經常開,但還是留著它以備不時之需。

          我們小區很難找到停車的地方,所以我需要一個車庫。

          我聽說有個車庫,就報名了,開始把車停在那里。

          直到上個星期天。

          我和妻子來到車庫取車。我們打算開車到馬里蘭去看望我的父母,吃一些螃蟹(馬里蘭的螃蟹很有名)。

          我走到我的車旁,取下車衣。

          我立馬就蒙圈了——這不是我的車。

          我的車#$&@去哪里了?

          幾分鐘后我就意識到一個現實——我的車被偷了。

          在過去的一周中,我為即將出版的《樹莓派電腦視覺》一書正在做的工作被打斷了——我一直在與停車場的主人、費城警察局和我車上的GPS跟蹤服務打交道,想弄清楚到底發生了什么。

          在事情解決之前,我不能公開透露任何細節,但是讓我告訴您,我正埋頭處理警察報告、律師信件、還有保險索賠等一大堆的文件。

          我希望下個月這個問題能得到解決——我討厭分心,尤其是讓我遠離我最喜歡做的事情——教計算機視覺和深度學習。

          我成功地利用我的挫折啟發了一篇新的安全相關的計算機視覺博客帖子。

          在這篇文章中,我們將學習如何使用Flask和OpenCV將視頻流式傳輸到網頁瀏覽器中。

          您可以在不到5分鐘的時間內將此系統部署到樹莓派上:

          • 簡單地安裝所需的包/軟件并啟動腳本
          • 然后打開您的計算機/智能手機瀏覽器,并導航到URL/IP地址,就可以看到傳輸過來的視頻了(并且確保您的任何東西沒有被偷)。

          沒什么比一個小視頻證據更能抓住小偷了。

          當我繼續與警察、保險等處理文書工作時,您就可以開始用樹莓派相機武裝自己,無論您在哪里生活和工作,都可以抓住壞人。

          要學習如何使用OpenCV和Flask將視頻流式傳輸到一個網頁瀏覽器的HTML頁面,請繼續閱讀!

          OpenCV——將視頻流式傳輸到網頁瀏覽器/HTML頁面

          在本教程中,我們將首先討論Flask,它是一個用于Python編程語言的微型web框架。

          我們將學習運動檢測的基本知識,以便我們可以將它應用到我們的項目中。我們將通過一個背景減法器來實現運動檢測。

          在此基礎上,我們將Flask與OpenCV結合,這樣我們就能夠:

          1. 訪問來自樹莓派相機模塊或USB網絡攝像頭的幀。
          2. 處理幀并應用一個任意的算法(這里我們將使用背景去除/運動檢測,但您也可以應用圖像分類,對象檢測等)。
          3. 將結果流式傳輸到一個網頁頁面/網頁瀏覽器。

          此外,我們將涉及的代碼將能夠支持多個客戶端(即,不止一個人/網頁瀏覽器/標簽頁能同時訪問流媒體),這是您在網上看到的絕大多數例子都無法處理的。

          把所有這些過程塊放在一起,我們就會得到一個能夠執行運動檢測的家庭監控系統,然后它會將視頻結果流式傳輸到您的網頁瀏覽器中。

          讓我們開始吧!

          Flask web框架

          圖1:Flask是Python的一個微型web框架(image source)。

          在本節中,我們將簡要討論Flask web框架以及如何在您的系統中安裝它。

          Flask是一個非常流行的用Python編程語言編寫的微型web框架。

          與Django一樣,Flask是使用Python構建web應用程序時最常見的web框架之一。

          但是,與Django不同的是,Flask非常輕量,這使得使用它構建基本的web應用程序非常容易。

          正如我們將在本節中看到的,我們只需要一小部分代碼就可以使用Flask實現實時流式傳輸視頻——其余的代碼包括(1)OpenCV和訪問我們的視頻流,或者(2)確保我們的代碼是線程安全的,并且可以處理多個客戶端。

          如果您需要在一個機器上安裝Flask,您只需要簡單地按以下命令進行操作:

          安裝了它之后,您可以繼續安裝NumPy、OpenCV和imutils:

          注意:如果您想要完整安裝包括“非免費”(專利)算法的OpenCV,那您一定要從源代碼來編譯OpenCV。

          項目結構

          在我們繼續之前,讓我們看看我們項目的目錄結構:

          為了執行背景去除和運動檢測,我們將實現一個名為SingleMotionDetector的類——該類將位于singlemotiondetector.py文件中pyimagesearch的motion_detection子模塊中。

          webstreaming.py文件將使用OpenCV訪問我們的網絡攝像頭,通過SingleMotionDetector執行運動檢測,然后通過Flask web框架將輸出幀提供給我們的網絡瀏覽器。

          為了讓我們網絡瀏覽器能有東西顯示,我們需要使用HTML填充index.html的內容來提供接收到的視頻。我們只需要插入一些基本的HTML標記——Flask將實際處理將視頻流發送到我們的瀏覽器的事情。

          實現一個基本的運動檢測器

          圖2:使用Raspberry Pi、OpenCV、Flask和網絡流媒體進行視頻監控。

          通過使用背景去除進行運動檢測,我們可以檢測到我在椅子上的運動。

          我們的運動檢測算法將通過背景減除的形式來檢測運動。

          大多數背景去除算法的工作原理是:

          1. 累積相加前N幀的加權平均值
          2. 取當前幀并從幀的加權平均值中減去它
          3. 對去除的輸出進行閾值處理,以突出像素值差異較大的區域(“白色”表示前景,“黑色”表示背景)
          4. 應用基本的圖像處理技術如腐蝕和膨脹來消除噪聲
          5. 利用輪廓檢測提取運動區域

          我們的運動檢測實現將位于SingleMotionDetector類中,該類可以在SingleMotionDetector .py中找到。

          我們稱之為一個“單一運動檢測器”,因為其算法本身只對尋找單一的、最大的運動區域感興趣。

          我們也可以很容易地擴展這個方法來處理多個運動區域。

          讓我們來實現該運動檢測器。

          打開singlemotiondetector.py文件,并插入以下代碼:

          第2-4行處理所需的導入。

          所有這些都是相當標準的,包括用于數字處理的NumPy、用于為我們提供方便函數的imutils和用于OpenCV綁定的cv2。

          然后我們在第6行定義SingleMotionDetector類。這個類接受一個可選的參數accumWeight,它是用于累積加權平均值的因數。

          accumWeight越大,在累積加權平均值時,背景(bg)被考慮的越少。

          相反地,accumWeight越小,在計算平均值時考慮背景(bg)就會越多。

          設置accumWeight=0.5會均勻地加權背景和前景——我經常建議使用這個設置作為起始值(然后您可以根據自己的實驗調整它)。

          接下來,讓我們定義update方法,它將接受一個輸入幀并計算加權平均值:

          為了防止我們的bg幀為None(這意味著update從未被調用),我們只需要存儲bg幀(第15-18行)。

          否則,我們會計算輸入幀、現有背景bg和相應的accumWeight因子之間的加權平均值。

          鑒于我們的背景bg,我們現在可以通過detect方法應用運動檢測:

          detect方法需要一個參數和一個可選參數:

          • image: 將要被應用運動檢測的輸入幀/圖像。
          • tVal: 用于將一個特定像素標記為 “運動” 或 非運動的閾值。

          給定我們的輸入image,我們計算該image和bg之間的絕對誤差(第27行)。

          任何差異>tVal的像素位置都被設置為255(白色;前景),否則設置為0(黑色;背景)(28行)。

          通過一系列的腐蝕和膨脹來消除噪聲和小的局部運動區域,否則這些區域會被認為是假陽性的(可能是由于反射或光線的快速變化)。

          下一步是應用輪廓檢測提取任何運動區域:

          第37-39行對我們的thresh圖像執行輪廓檢測。

          然后,我們初始化兩組記賬變量,以跟蹤包含任何運動的位置(第40行和第41行)。這些變量將形成“邊界框”,它將告訴我們運動發生的位置。

          最后一步是填充這些變量(假設運動存在于幀中,當然是這樣):

          在43-45行中,我們檢查我們的輪廓列表是否為空。

          如果是這種情況,那么在幀中就找不到運動,我們就可以放心地忽略它。

          否則,在幀中確實存在運動,所以我們就需要開始在輪廓線上進行循環(第48行)。

          對于每個輪廓,我們計算其邊界框,然后更新我們的記賬變量(第47-53行),找到所有運動發生的最小和最大(x, y)坐標。

          最后,我們將邊界框位置返回給調用函數。

          結合OpenCV和Flask

          圖3:OpenCV和Flask (一個Python微型網絡框架)是涉及Raspberry Pi和類似硬件的網絡流媒體和視頻監控項目的完美搭檔。

          讓我們繼續,將OpenCV和Flask結合起來,從一個視頻流(運行在樹莓派上)向一個網絡瀏覽器提供幀。

          打開您項目結構中的webstreaming.py 文件,并插入如下代碼:

          第2-12行處理我們需要的導入:

          • 第2行 導入我們上邊實現的SingleMotionDetector類
          • VideoStream類 (第3行 ) 將允許我們訪問我們的Raspberry Pi 相機模塊或 USB網絡攝像頭.
          • 第4到6行處理導入我們需要的Flask包——我們將使用這些包呈現我們的index.html模板,并將其提供給客戶端。
          • 第7行導入threading庫,確保我們可以支持并發 (比如,同時使用多個客戶端、網絡瀏覽器和選項卡)。

          讓我們繼續執行一些初始化:

          首先,我們在第17行初始化我們的outputFrame——這將是將提供給客戶端的幀(投遞運動檢測)。

          然后,我們在第18行創建一個lock,它將在更新ouputFrame時被用來確保線程安全行為(即確保某個幀在更新時不被任何線程嘗試讀取)。

          第21行初始化我們的Flask app本身,而第25-27行訪問我們的視頻流:

          • 如果您正在使用一個USB網絡攝像頭, 您可以保持代碼不變。
          • 否則,如果您正在使用一個RPi相機模塊,那您應該取消掉第25行的注釋,并將第26行注釋掉。

          下一個函數index將會渲染我們的index.html模板并提供輸出視頻流:

          這個函數非常簡單—它所做的就是在我們的HTML文件中調用Flask render_template。

          我們將在下一章查看該index.html文件,因此,我們將推遲對此文件內容的進一步討論直到那個時候。

          我們的下一個函數的功能是:

          • 對我們視頻流中的幀進行循環
          • 應用運動檢測
          • 在outputFrame上繪制任何結果

          而且,這個函數必須以線程安全的方式來執行所有這些操作,以確保支持并發。

          現在讓我們看看這個函數:

          我們的detection_motion函數接受單個參數frameCount,它是在SingleMotionDetector類中構建我們的背景bg所需的最小幀數:

          • 如果我們沒有至少frameCount幀,我們將會繼續計算累計加權平均值
          • 一旦frameCount達到了,我們將執行背景去除。

          第37行獲取對三個變量的全局引用:

          • vs: 我們實例化的VideoStream對象
          • outputFrame: 將提供給客戶端的輸出幀
          • lock: 在更新outputFrame之前我們必須獲得的線程鎖。

          第41行使用一個accumWeight=0.1值來初始化我們的SingleMotionDetector 類,這意味著在計算加權平均值時,bg值的權重會更高。

          第42行初始化到目前為止讀取的幀的total數——我們需要確保已經讀取了足夠多的幀來構建我們的背景模型。

          從那里,我們將能夠執行背景去除。

          這些初始化完成后,我們現在可以開始對來自相機的幀進行循環:

          第48行讀取來自我們相機的frame幀,而第49-51行執行預處理操作,包括:

          • 調整寬度為400 px (我們的輸入幀越小, 數據量就越小,因此,我們的算法運行的就越快)。
          • 轉換成灰階圖。
          • 高斯模糊(來減少噪聲)。

          然后,我們獲取當前時間戳并將其繪制在frame上(第54-57行)。

          在進行一次最終檢查之后,我們就可以執行運動檢測:

          在第62行中,我們確保我們至少讀取了frameCount幀來構建我們的背景去除模型。

          如果是這樣,我們將應用我們運動檢測器的.detect運動,它將返回單個變量motion。

          如果motion是None,那么我們就知道當前frame中沒有發生運動。否則,如果motion不是None(第67行),那么我們需要在frame上繪制運動區域的邊界框坐標。

          第76行更新我們的運動檢測背景模型,而第77行增加了迄今為止從攝像機讀取的幀的total數。

          最后,第81行獲得支持線程并發所需的lock,而第82行設置outputFrame。

          我們需要獲取鎖,以確保我們在試圖更新outputFrame變量時客戶機不會意外讀取它。

          我們的下一個函數,generate,是一個Python生成器,用于將我們的outputFrame編碼為JPEG數據——現在讓我們來看看它:

          第86行獲取對outputFrame和lock的全局引用,類似于detect_motion函數。

          然后generate在第89行啟動一個無限循環,這個循環將一直持續到我們結束腳本。

          在循環內部,我們:

          • 首先獲取lock (第91行 ).
          • 確保outputFrame非空(第94行 ), 如果一個幀被從攝像機傳感器中丟棄,那么這種情況就有可能發生。
          • 在第98行將frame編碼為一個JPEG 圖像——在這里執行JPEG壓縮以減少網絡負載,并且確保幀的快速傳輸。
          • 檢查成功flag是否失敗(第101行和102行),這意味著如果此JPEG壓縮過程失敗,我們就應該忽略該幀。
          • 最后,將編碼的JPEG以一個字節數組提供給一個可以解析它的網絡瀏覽器。

          在這么短的代碼中做了這么多工作,所以一定要檢查這個函數幾次,以確保您了解它是如何工作的。

          下一個函數video_feed會調用我們的generate函數:

          注意這個app.route函數簽名,就像上面的index函數一樣。

          這個app.route簽名告訴Flask這個函數是一個URL端點,數據是從http://your_ip_address/video_feed提供的。

          video_feed的輸出是實時運動檢測輸出,通過generate函數編碼為一個字節數組。您的網絡瀏覽器非常聰明,可以將這個字節數組作為一個實時輸出顯示在您的瀏覽器中。

          我們最后的代碼塊處理解析命令行參數和啟動Flask應用程序的任務:

          第118-125行處理解析命令行參數的任務。

          這里我們需要三個參數,包括:

          • --ip: 您運行webstream.py文件的系統的IP地址。
          • --port: Flask應用程序的運行端口號(對這個參數,您通常只需要提供一個值8000)。
          • --frame-count: 在執行運動檢測之前用于累計和構建背景模型的幀數。默認情況下,我們使用32幀來構建背景模型。

          第128-131行啟動一個線程用于執行運動檢測。

          使用一個線程確保detect_motion函數可以安全地在后臺運行——它將不斷地運行和更新我們的outputFrame,以便我們可以為我們的客戶端提供任何運動檢測結果。

          最后,第134和135行啟動Flask應用程序本身。

          HTML頁面結構

          正如我們在webstreaming.py中看到的,我們正在渲染一個名為index.html的HTML模板。

          該模板本身由Flask 網絡框架進行填充,然后提供給網絡瀏覽器。

          然后,您的網絡瀏覽器將生成的HTML呈現到您的屏幕上。

          讓我們檢查一下index.html文件的內容:

          我們可以看到,這是一個超級基礎的網頁;但是,請密切注意第7行——注意我們如何指示Flask動態呈現我們video_feed路徑的URL。

          由于video_feed函數負責提供來自我們網絡攝像頭的幀,所以圖像的src將會被自動填充上我們的輸出幀。

          然后,我們的網絡瀏覽器足夠智能,可以正確渲染網頁并提供實時視頻流。

          將各部分整合在一起

          現在我們已經寫完了我們項目的代碼,讓我們對其進行測試。

          打開一個終端,執行以下命令:

          正如您在視頻中看到的,我從多個瀏覽器打開了到Flask/OpenCV服務器的連接,每個瀏覽器都有多個選項卡。我甚至拿出我的iPhone,并打開了一些來自那里的連接。服務器沒有跳過一個幀,持續地使用Flask和OpenCV可靠地提供幀。

          加入嵌入式計算機視覺和深度學習革命!

          我第一次彈吉他是在20年前,那時我還在上中學。我不太擅長,幾年后我就放棄了。回顧過去,我堅信我沒有堅持下去的原因是因為我沒有以一種實際的、動手操作的方式來學習。

          相反,我的音樂老師一直向我腦子里灌輸理論——但作為一個11歲的孩子,我只是想弄清楚我是否喜歡彈吉他,更不用說我是否想研究音樂背后的理論了。

          大約一年半以前,我決定重新開始上吉他課。這一次,我特意找了一位能夠將理論與實踐相結合的老師,教我如何在學習理論技巧的同時演奏歌曲或即興重復樂段。

          結果呢?我的手指速度比以往任何時候都快,我的節奏也很準,我可以不斷地惹惱我的妻子,在我的Les Paul上演奏Sweet Child of Mine。

          我的觀點是,無論何時您在學習一項新技能時,無論是計算機視覺,還是使用樹莓派進行滲透,甚至是彈吉他,最快、最簡單的學習方法之一就是圍繞這項技能設計(小的)現實世界的項目,并嘗試解決它。

          對于吉他來說,這意味著學習短的即興重復,這不僅教會了我真正的歌曲的一部分,也給了我一個寶貴的技巧(例如,掌握一種特定的五聲音階)。

          在計算機視覺和圖像處理中,您的目標應該是思考一些小型項目,然后嘗試解決它們。不要太快就把事情復雜化,那是失敗的秘訣。

          相反,您可以拿一本我寫的《樹莓派計算機視覺》書,讀一讀,把它作為您個人項目的一個啟動平臺。

          當您讀完之后,回到那些最激勵您的章節,看看您能如何以某種方式擴展它們(即使只是將相同的技術應用到一個不同的場景中)。

          解決您思考的小項目不僅會讓您對這個主題感興趣(因為您自己想到了它們),而且它們還會教您實踐技能。

          今天的教程——運動檢測和流式傳輸到網絡瀏覽器——就是這樣一個迷您項目的一個很好的起點。我希望既然您已經閱讀了本教程,您已經對如何將這個項目擴展到您自己的應用程序進行了思考。

          但是,如果您有興趣學習更多……

          我的新書《樹莓派計算機視覺》中有40多個與嵌入式計算機視覺+物聯網(IoT)相關的項目。您可以根據書中的項目進行構建來解決家里、公司甚至客戶的問題。每一個項目都有著重方向:

          • 通過實踐進行學習
          • 卷起您的袖子
          • 對代碼和實現進行實際測試
          • 使用樹莓派構建實際的、實用的項目

          幾個精品項目包括:

          • 晝夜野生動物監視
          • 交通工具計數和車速檢測
          • 深度學習分類、對象檢測和資源受限設備上的實例分割
          • 手勢識別
          • 基本的機器人導航
          • 安全應用程序
          • 教室出勤情況
          • 等等更多!

          本書還涵蓋了使用谷歌Coral和Intel Movidius NCS協作處理器(Hacker + Complete Bundles)的深度學習。當需要更多的深度學習能力時,我們還將引入NVIDIA Jetson Nano(Complete Bundle)。

          如果您錯過了Kickstarter,您可以看看我的公告視頻:

          您準備好和我一起學習計算機視覺和如何應用嵌入式設備(如樹莓派、Google Coral和NVIDIA Jetson Nano)了嗎?

          如果是這樣,請使用下面的鏈接查看這本書!

          總結

          在本教程中,您學習了如何將幀從一個服務器機器流式傳輸到一個客戶端網絡瀏覽器。使用這個網絡流式傳輸程序,我們能夠構建一個基本的安全應用程序來監控我們房子里的一個房間的運動。

          背景去除是計算機視覺中最常用的一種方法。通常,這些算法的計算效率很高,這使得它們適合于資源受限的設備,如樹梅派。

          在實現了我們的背景去除器后,我們將其與Flask 網絡框架結合,這使得我們能夠:

          1. 從RPi攝像機模塊/USB網絡攝像頭訪問幀。
          2. 對每個幀應用背景去除/運動檢測。
          3. 將結果流式傳輸到一個網頁頁面/網頁瀏覽器。

          此外,我們的實現支持多個客戶端、瀏覽器或選項卡——這在大多數其它實現中都找不到。

          無論何時您需要將幀從一個設備流式傳輸到一個網絡瀏覽器時,一定要使用此代碼作為一個模板/起點。

          英文原文:https://www.pyimagesearch.com/2019/09/02/opencv-stream-video-to-web-browser-html-page

          譯者:Nothing

          取video.js

          video.js通過CDN活NPM方式便可正式使用。

          video.js的工作模式不僅包含HTML方式的<script>和<link>標簽方式,而且可以使用所有主流的bundlers/packagers/builders方式,如Browserify,Node等。

          請查閱開始文檔了解詳情。

          創建播放器

          注意:video.js可以使用<video>和<audio>標簽,但是為了簡單起見,我們將只有引用<video>標簽。

          一旦你使用video.js加載你的頁面,你就可以創建一個播放器!

          video.js的核心優勢在于,它提供了一個標準的<video>標簽(https://html.spec.whatwg.org/multipage/media.html#the-video-element),并且模擬了它的相關時間和API,同時還提供了一套基于dom可定制的UI。

          video.js支持<video>元素的所有屬性(如controls,preload等),同時擁有自己的選項。有兩種方法來創建video.js播放器并傳遞其選項,但它們都以一個標準的<video>元素開始,屬性class="video-js"。

          <video class="video-js">
            <source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
            <source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm">
          </video>

          你可以使用一個<video-js>標簽而不是<video>標簽。在某些情況下,使用<video>元素是不可取,因為瀏覽器可能會顯示無風格的控件或者試圖在播放器初始化之前去加載資源。使用<video-js>自定義標簽則不會發生這些問題。

          <video-js>
            <source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
            <source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm">
          </video-js>

          想了解所有嵌入方式的高級概述,請查閱嵌入video.js播放器頁面,然后繼續本頁面的其余部分。

          自動化設置

          默認情況下,當你的網頁完成加載時,Video.js將掃描具有data-setup屬性的媒體元素。data-setup屬性用于將選項傳遞給Video.js。一個最小的例子如下:

          <video class="video-js" data-setup='{}'>
            <source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
            <source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm">
          </video>

          注意:你必須使用單引號包裹data-setup的值,因為它將包含JSON。

          手動設置

          在現代網頁上,當頁面加載完成時<video>元素實際上是不存在的。在這種情況下,進行自動設置是不可能的,但是通過videojs提供的函數手動設置倒是可以。

          調用這個函數的一種方法是為它提供一個匹配<video>元素的id屬性的字符串:

          <video id="my-player" class="video-js">
            <source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
            <source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm">
          </video>
          videojs('my-player');

          然而,通常情況下并不總是接受一個id,也可提供一個Dom元素。

          <video class="video-js">
            <source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
            <source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm">
          </video>
          videojs(document.querySelector('.video-js'));

          獲取播放器相關信息

          一旦播放器被創建,videojs就會在內部持續跟蹤。我們通過一些方法可以獲取它們的信息。

          videojs()

          調通videojs()方法,并提供一個已經從在的播放器的id。則會返回這個播放器,而不會創建另一個播放器。

          如果沒有匹配到這個播放器,那么就會創建一個播放器。

          videojs.getPlayer()

          有時,您希望獲得對播放器的引用,而不是去調用videojs()本身的功能。這種情況可以通過調用videojs.getPlayer()傳遞匹配元素ID或元素本身的來實現。

          videojs.getPlayers()與videojs.players

          videojs.players返回所有已知的播放器對象,而videojs.getPlayers則返回相應匹配的播放器對象。

          Players是所有播放器的對象,鍵值與所綁定的ID相同。

          注意:由一個沒有ID創建的播放器,videojs將會自動給生成一個ID。

          Options

          注意:該指南之涵蓋播放器設置最基本的選項,需要了解所有選項的功能,請查看選項指南。

          有三種方法可以將選項傳遞給Video.js。因為video .js封裝了HTML5的<video>元素,許多<video>標簽的標準屬性選項也可以用的:

          <video controls autoplay preload="auto" ...>

          另外,您可以通過data-setup屬性將選項以JSON形式進行傳遞。這也是你如何設置<video>元素的非標準選項的方式:

          <video data-setup='{"controls": true, "autoplay": false, "preload": "auto"}'...>

          注意:data-setup屬性值的必須使用單引號包裹,應為它所包含的JSON中的字符串必須使用雙引號。

          最后,如果你沒有使用data-setup屬性來觸發播放器設置,你可以傳入一個播放器選項對象作為videojs函數的第二個參數:

          videojs('my-player', {
            controls: true,
            autoplay: false,
            preload: 'auto'
          });

          注意:不要播放器選項對象和data-setup同時使用。

          全局默認值

          所有播放器默認選項都可以在videojs中找到,并且可以直接修改。例如播放器的自定播放{autoplay: true}

          videojs.options.autoplay=true;

          <video>標簽應該注意的屬性

          許多屬性都是布爾類型的。這意味著它們要么打開,要么關閉。在這些情況下,屬性應該沒有值(或者應該用它的名稱作為它的值),它的存在就意味著這是一個真值,它的不存在的話,意味著一個假值。

          <video controls="true" ...>
          <video loop="true" ...>
          <video controls="false" ...>

          注意:controls="false"可能會打開控件,這可能會讓新的開發人員有些困惑

          這些都是正確的:

          <video controls ...>
          <video loop="loop" ...>
          <video ...>

          準備播放器

          因為Video.js技術具有異步加載的可能,所以在設置后立即與播放器進行交互并不安全。出于這個原因,Video.js的播放器有一個“準備就緒”的概念,這對于以前使用過jQuery的人來說是很熟悉的。

          本質上,可以為Video.js播放器定義任意數量的準備回調。有三種方法可以傳遞這些回調。在每個例子中,我們將為播放器添加一個相同的類:

          將回調函數作為第三個參數傳遞給videojs()函數:

          // Passing `null` for the options argument.
          videojs('my-player', null, function() {
            this.addClass('my-example');
          });

          傳遞調用播放器的ready()方法:

          var player=videojs('my-player');
          
          player.ready(function() {
            this.addClass('my-example');
          });

          監聽播放器的“ready”事件:

          var player=videojs('my-player');
          
          player.on('ready', function() {
            this.addClass('my-example');
          });

          在每種情況下,回調都是異步調用的。

          上述方法的一個重要區別是,使用on()為ready添加偵聽器必須在播放器準備好之前完成。對于player.ready(),如果播放器已經準備好了,函數將立即被調用。

          高級播放器的工作流程

          有關更高級的播放器工作流程的討論,請參閱播放器工作流程指南。


          主站蜘蛛池模板: 国模私拍福利一区二区| 午夜无码一区二区三区在线观看| 国产丝袜美女一区二区三区| 日产精品久久久一区二区| 国产精品免费视频一区| 变态拳头交视频一区二区| 国模一区二区三区| 麻豆高清免费国产一区| 国产AV一区二区三区传媒| 精品一区二区三区高清免费观看| 国产一区二区不卡老阿姨| 国内精品视频一区二区八戒| 国产成人一区二区三区视频免费| 日韩电影一区二区三区| 人妻内射一区二区在线视频| 韩国福利一区二区三区高清视频| 日本高清天码一区在线播放| 国产一区二区三区乱码在线观看| 99久久精品日本一区二区免费| 亚洲一区综合在线播放| 91视频一区二区三区| 日韩精品无码一区二区三区| 3d动漫精品啪啪一区二区中文| 精品一区二区三区免费视频| 亚洲av片一区二区三区 | 无码夜色一区二区三区| 亚欧在线精品免费观看一区| 2020天堂中文字幕一区在线观| 射精专区一区二区朝鲜| 国产一区二区三区播放心情潘金莲 | 精品国产一区二区三区免费| 日韩在线一区二区三区免费视频| 精品一区二区三区中文| 日本一区二区三区精品视频| 成人h动漫精品一区二区无码| 国产精品亚洲产品一区二区三区 | 国产精品亚洲一区二区三区在线| 国产在线精品一区二区不卡| 精品女同一区二区三区免费站| 国产一区二区三区韩国女主播| 国产一区二区三区樱花动漫|