OpenCV是一個基于C與C++的跨平臺計算機視覺處理庫,高效且輕便,支持多平臺,參考:OpenCV支持平臺及相關介紹。目前國內網絡上充斥著各種OpenCV“教程”,但大多基于C++語言,或者Python語言,使用的OpenCV版本也非常老舊,而且國外的OpenCV官網上關于Android平臺的介紹也寥寥無幾,大部分講解了OpenCV內部的算法原理而非API,對于我們Android開發者(使用Kotlin或Java)來說,想入門OpenCV似乎相當困難。
在這里兩行哥給大家帶來OpenCV4Android的入門教程,主要分為API系列和算法系列。API系列主要聚焦常用API及用法(略小白),幫助Android開發者快速完成企業開發需求。算法系列主要聚焦OpenCV內部數學算法,供有一定數學基礎的開發者一窺究竟。
一、開發環境
(一)基于OpenCV 3最新版(V3.4.10):OpenCV SDK下載地址
OpenCV SDK有多個分支,4.X、3.X及2.X。各個分支除了集成方式不同外,4.X和3.X在某些場景下的效率也比2.X有所提升。具體區別各位讀者可以自己查閱相關資料。本教程基于V3.4.10(兩行哥發現4.X版本部分API雖然執行速度提升,但是某些場景處理效果較3.X版本有出入,請讀者自行查驗)。
(二)基于AndroidStudio
請不要再看任何用著Eclipse開發Android的教程,實在太古老了。本文使用的是AS 3.6.3。
(三)基于Java
本來想用Kotlin,想想受眾,還是用Java吧,用Kotlin開發Android的基本上都會Java,反過來,用Java開發Android的不一定都會Kotlin。
二、OpenCV SDK集成
相信我,真沒有其他人寫的那么復雜,不需要那么多種集成方式,那是基于2.X版本的。對于3.X版本的集成,很簡單,大家不要有畏難心理。
(一)下載SDK
建立一個Demo工程,然后根據上文提供的下載地址,下載OpenCV4AndroidSDK,如圖1。
下載完成后解壓,看一下解壓后的內容,如圖2。
這里一共有3個文件夾:
1.samples:這里都是例子工程,包括了編譯好的apk文件和項目源碼。
2.apk:里面是針對各個架構編譯好OpenCVManager.apk文件,這是干什么用的呢?因為OpenCV中的jni庫體積比較大,需要集成到App中,如果用戶手機中有很多個使用到OpenCV庫的App,那么這些App就可以不集成OpenCV的jni庫,直接安裝對應架構的OpenCVManager,OpenCVManager內就含有OpenCV需要的jni庫,所有的App通過OpenCVManager共享一份jni庫就好了,而且OpenCVManager可以很方便地管理和升級jni庫。那么這些App是如何和OpenCVManager通訊的呢?看一下OpenCVLibrary源碼就知道了,嗯,aidl。
說了這么多,到底有什么用呢?假如我們要運行第2個文件夾samples中OpenCV的Demo,你會發現這些Demo體積都非常小,因為它們都沒集成OpenCV的jni庫。我們裝上這十來個Demo后,再裝上對應架構的OpenCVManager,它們就能愉快地共用一份jni庫了......實際開發中不會這么做,一定是把OpenCV的jni庫集成到項目內部的,直接使用。我們總不能要求用戶裝好我們的App,然后引導用戶到OpenCV的官網或者其他地方去下載OpenCVManager吧?大概是腦抽,覺得自己的App活膩了,用戶也覺得你的App實在是欠刪。好了,說了這么多,其實就是扯蛋,在實際開發中,第一個文件夾里的東西......其實沒什么卵用。
3.sdk:就是我們要的sdk了。
(二)引入SDK
首先,我們將文件夾sdk中的java文件夾作為module導入到我們建立好的Demo工程中,如圖3、圖4所示,這里自定義Module name為OpenCVLib。
導入完成后,配置項目依賴,如圖5所示。
然后進入OpenCVLib包中的build.gradle,修改編譯版本,與app包中的build.gradle一致,如圖6所示。
(三)配置jniLibs
接著開始配置jniLibs,sdk中包含的jniLibs如圖7所示:
有兩個選擇:
選擇1:將這些jniLibs文件原封不動地保留在OpenCVLib module的native/libs目錄下,原封不動地保留build.gradle(OpenCVLib)中對jniLibs路徑的定義:
sourceSets {
main {
jniLibs.srcDirs = ['native/libs']
java.srcDirs = ['java/src']
aidl.srcDirs = ['java/src']
res.srcDirs = ['java/res']
manifest.srcFile 'java/AndroidManifest.xml'
}
}
選擇2:復制圖7所示目錄下所有的文件夾到你定義的jniLibs路徑下,我們習慣把jniLibs路徑定義在圖8所示的app/src/main/jniLibs包下,如果jniLibs包不存在則新建一個。
然后在build.gradle(app)中定義jniLibs路徑,代碼如下:
sourceSets {
main {
jniLibs.srcDirs = ['src/main/jniLibs']
}
}
最終完成在app module中定義jniLibs,如圖9所示:
如果之前你自定義了其他的jniLibs包的路徑,請復制到你自定義的包路徑下,不要生搬硬套。
這里提醒一下,如果你的項目之前就引入過其他jniLibs,則之前引入過多少種架構,這里也選擇性地粘貼同樣的架構文件夾,多一個不行,少一個也不行。如:之前有armeabi和armeabi-v7a,那么這次你也要選擇這兩個架構文件夾粘貼就好了,否則在運行時會發生崩潰,提示找不到對應架構的jniLibs。
順便插一嘴,通常情況下我們選擇armeabi、armeabi-v7a和arm64-v8a就夠了,如果為了App體積考慮,只引入armeabi也夠了。具體各種架構有什么區別,對應什么CPU,兩行哥這里就不詳細解釋了,請讀者自行查閱相關資料。
強烈建議配置支持的ndk類型,如圖10所示,引入了多少架構包,就寫多少種類型。
如果項目引入了某個使用了jniLibs的第三方庫,這個第三方庫支持的架構比你現有項目中架構多,運行時一樣會發生上文所說的崩潰現象,就需要進行圖10的配置。
例如:項目通過implementation "com.github.pqpo:SmartCropper:v1.1.3" 方式引入了SmartCropper這個第三方庫,SmartCropper庫支持多種架構,而你的項目僅僅引入了armeabi jniLibs,那么就可能在運行時發生崩潰。此時你需要在圖9的位置配置 abiFilters "armeabi",不寫其他架構,表示項目僅僅支持armeabi架構,而不支持其他架構。
(四)初始化OpenCV
至此,SDK配置已經完成,在Application或者MainActivity中初始化OpenCV:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initOpenCV();
}
public void initOpenCV() {
OpenCVLoader.initDebug()
}
}
其實OpenCVLib module中還有很多普通開發者用不到的文件,大家可以嘗試精簡掉,具體可以參照下文的Demo,保留我們需要的文件即可。