OpenCV Android 之 VideoCapture類
1. 介紹
首先,需要明確一個(gè)根本問題。OpenCV 是一個(gè)基于 Apache2.0 許可(開源)發(fā)行的跨平臺(tái)計(jì)算機(jī)視覺和機(jī)器學(xué)習(xí)軟件庫(kù)。它實(shí)際上各種圖像處理和計(jì)算機(jī)視覺方面的通用算法的集中庫(kù)。
簡(jiǎn)而言之就是:處理圖片。
通常都是使用 OpenCV 來進(jìn)行各種圖片處理和計(jì)算。所以它并不是一個(gè)視頻編解碼庫(kù)。不要想著使用 OpenCV 來進(jìn)行視頻播放
所有使用 OpenCV 進(jìn)行播放視頻,實(shí)際上都是將視頻轉(zhuǎn)圖片了,再一張張圖片在切換顯示,編解碼和效率是遠(yuǎn)遠(yuǎn)沒有專門的視頻播放器效率高的。
如果要播放視頻,還是建議使用 FFmpeg 處理。
而我們可以通過OpenCV將視頻進(jìn)行解碼成Mat文件,進(jìn)行操作,并將編輯之后的結(jié)果存儲(chǔ)為視頻。
可以將相機(jī)拍攝的結(jié)果,進(jìn)行實(shí)時(shí)處理之后。存儲(chǔ)為視頻等操作。
而使用到的就是VideoWriter? 和 VideoCapture類了。
以下內(nèi)容基于:OpenCV 4.6.0 版本API進(jìn)行的介紹和使用。
2. VideoCapture
用于從視頻文件、圖像序列或相機(jī)捕獲視頻的類。這個(gè)類提供了針對(duì)視頻的各種捕獲方法。
提供了幾種方法:
1.獲取每一幀數(shù)據(jù),轉(zhuǎn)為Mat。
2.獲取視頻的一些配置信息,例如時(shí)長(zhǎng),F(xiàn)PS,幀數(shù),寬高等等。
初始化如下:
我們其實(shí)在創(chuàng)建過程中的時(shí)候,也可以進(jìn)行初始化傳參。這些構(gòu)造初始化時(shí)傳的參數(shù)和調(diào)用open()方法傳的參數(shù)實(shí)際是一樣的。
PS:使用 OpenCV 的方法時(shí),請(qǐng)注意需要提前進(jìn)行初始化加載 OpenCV 庫(kù)。否則會(huì)出現(xiàn)相關(guān)類找不到而崩潰
OpenCVLoader.initDebug(false);//加載OpenCV庫(kù)
2.1 加載 open() 方法
下面不管是相機(jī)加載,還是網(wǎng)絡(luò)地址加載。我在 Android 端上沒有成功。只有加載本地視頻成功了。
加載攝像頭應(yīng)該是 Android 本身不支持的原因造成的。嘗試了各種 cameraId 值和相關(guān) apiPreference 都失敗了。(我們可以使用CameraX加載攝像頭并進(jìn)行處理和存儲(chǔ))
加載網(wǎng)絡(luò)視頻失敗我估計(jì),應(yīng)該是因?yàn)?openCV 默認(rèn)編譯的 Android SDK 中沒有相關(guān)依賴造成的。
(如果是缺少依賴庫(kù)造成的,希望能夠有明白的小伙伴指點(diǎn)一下吧。各種嘗試我都失敗了)。
open方法傳遞主要是以下一種參數(shù):
- String filename:文件地址,可以是Url地址也可以是本地文檔地址。
- int index:相機(jī)id, 如果0 會(huì)調(diào)用設(shè)備默認(rèn)的后置攝像頭。
- int apiPreference: api首選項(xiàng)。該參數(shù)為:Videoio.CAP_ANY,Videoio.CAP_DSHOW,Videoio.CAP_ANDROID等.
VideoCapture? 中傳入的apiPrefreence的可選參數(shù)列表如下所示:
調(diào)用open()方法后,如果加載成功了就會(huì)返回true,失敗則返回false。
由于,我只是加載本地視頻能夠?qū)崿F(xiàn)成功加載。所以下面的介紹也是基于該成功之后進(jìn)行的。
在Android端中,如果想能夠正確的打開視頻并進(jìn)行解析。apiPrefreence的值只有:
Videoio.CAP_ANY? 或者 Videoio.CAP_ANDROID才能正確加載視頻
返回的isOpen才是true。示例如下:
我有嘗試過使用CAP_FFMPEG當(dāng)做值,進(jìn)行加載。
應(yīng)該是默認(rèn)的OpenCV Android SDK中。并沒有FFmpeg相關(guān)庫(kù)。
所以想通過https或者rtsp等協(xié)議加載在線視頻也失敗。原因在于openCV 預(yù)編譯的Android SDK中,并沒有那么多第三方項(xiàng)目。可能是需要我們自己配置吧。
PS:自己配置編譯,有點(diǎn)繁瑣。我也沒有進(jìn)行過嘗試。
當(dāng)我們加載成功視頻之后。就可以進(jìn)行解析操作了。
2.2 解析 read(),grab()和retrieve()方法
這三個(gè)方法主要就是用來獲取視頻的每一幀的數(shù)據(jù),并將幀數(shù)據(jù)轉(zhuǎn)為Mat對(duì)象。
請(qǐng)注意哦,它們獲取的Mat對(duì)象是BGR格式的。
例如:獲取當(dāng)前幀:
除此之外,還有以下方法也可以獲取當(dāng)前幀:
其實(shí)read()? 是grab()+retrieve()方法的合集。
grab()方法只是檢測(cè)視頻幀,不會(huì)解析視頻幀。所以它速度比較快。
retrieve()?方法會(huì)進(jìn)行視頻幀的解析。會(huì)比grab()方法更耗時(shí)。這兩個(gè)方法通常都是一起使用的。
但是,大部分情況下都是使用read()+循環(huán),遍歷整個(gè)視頻的所有幀,并進(jìn)行處理。
read():方法返回的false時(shí),代表視頻已經(jīng)沒有下一幀了。也就是解析到最后一幀了。
通過循環(huán)的方式,可以快速的解析視頻中的每一幀數(shù)據(jù),并轉(zhuǎn)為Mat進(jìn)行處理。
注意,VideoCapture 在調(diào)用 read()? 獲取視頻幀之后。一直獲取到最后之后。不會(huì)回到第一幀獲取。我們只能重新調(diào)用open()方法再次加載才行。
2.3 修改 set()和get()方法
我們除了可以遍歷視頻幀數(shù)據(jù)以外。還可以通過get()方法獲取視頻的相關(guān)信息。
示例如下:
這個(gè)方法要傳入的是 propId 值,該值的取值參數(shù)有如下:
但是,我們很多時(shí)候使用上面的關(guān)鍵字進(jìn)行獲取的數(shù)據(jù),結(jié)果值都是0
這是因?yàn)?openCV 使用的解析器在獲取視頻時(shí),如果正確獲取了相關(guān)配置項(xiàng)參數(shù)就會(huì)返回具體指。如果沒有正確獲取就會(huì)返回0了。
在我的實(shí)際使用過程中,大部分都是取不到真實(shí)數(shù)據(jù)。而寬高等數(shù)據(jù),還得讀取過一幀數(shù)據(jù)之后,才能取到值。
而set()方法,就是將這些配置信息修改到 VideoCapture 中。
如果在open()方法中調(diào)用的解碼器支持的話。就可以將這些配置信息添加到解碼器中。進(jìn)行生效了。
我們?nèi)绻皇菃渭冋{(diào)用 openCV 的 API。那么set()方法使用空間不大了。
2.4 關(guān)閉 release()
當(dāng)我們遍歷完畢,可以調(diào)用release()方法 關(guān)閉文件的加載。釋放內(nèi)存。
同時(shí)底層C++代碼中的相關(guān)方法也會(huì)進(jìn)行釋放。
3. 小結(jié)
總的來說,我們可以使用VideoCapture進(jìn)行視頻幀的遍歷,并在遍歷過程中對(duì)每一幀數(shù)據(jù)進(jìn)行編輯修改操作。
我們?nèi)绻胧褂?openCV 對(duì)視頻每一幀進(jìn)行操作之后,再存儲(chǔ)為視頻。那么就還需要結(jié)合VideoWriter 進(jìn)行存儲(chǔ)。
默認(rèn)情況下Android下,是可以實(shí)現(xiàn)視頻的每一幀獲取,并修改然后存儲(chǔ)為新的視頻文件的。
通過這些方法可以實(shí)現(xiàn),例如視頻添加水印,背景替換,黑白轉(zhuǎn)換等等。圖片能實(shí)現(xiàn)的一些編輯操作都可以通過獲取每一幀,處理完畢后。再將每一幀存儲(chǔ)為視頻來實(shí)現(xiàn)。
openCV 官網(wǎng)說明文檔:
https://docs.opencv.org/4.6.0/d4/d15/groupvideoioflags__base.html#ga023786be1ee68a9105bf2e48c700294d
下一篇簡(jiǎn)單介紹下VideoWriter的相關(guān)使用吧。