先說下題外話哈,最近做了個(gè)領(lǐng)取電商平臺(tái)優(yōu)惠券的小程序,掃碼支持下哈~
image
最近看了一些opencv的相關(guān)內(nèi)容,這里做一下記錄以及學(xué)習(xí)中的體會(huì),和大家分享一下,本人在學(xué)習(xí)的時(shí)候也是網(wǎng)上查閱很多人的博客。
現(xiàn)在都是開源時(shí)代,opencv也是一個(gè)開源庫,也有官網(wǎng)和社區(qū),所以簡介什么的大家去官網(wǎng)看看,了解什么是opencv,用來干什么的,這里就不羅嗦了。
1、下載
可以根據(jù)自己的工作平臺(tái)下載對(duì)應(yīng)的資源,本人實(shí)在android的領(lǐng)域下使用opencv,所以下載的是3.4.5 Android pack,后面的內(nèi)容也是針對(duì)opencv在android開發(fā)中的使用。

2、sdk結(jié)構(gòu)
下載解壓后的文件結(jié)構(gòu)如下

- apk:這個(gè)包下面是opencv-manager安裝包,具體作用后面在細(xì)說。
- samples:是opencv官方提供的幾個(gè)demo工程,有工程源代碼,也有打包好的apk。
- sdk:這個(gè)是重點(diǎn),以后開發(fā)的時(shí)候也是用的這里面的東西。
- etc:識(shí)別相關(guān)的級(jí)聯(lián)分類器之類的,目前我也不太懂,前期學(xué)習(xí)也用不到。
- java:這是opencv官方提供的一個(gè)opencv的android庫工程,提供了完整的opencv能力,因?yàn)閛pencv底層是用c/c++寫的,但是現(xiàn)在編程語言很多,java、python等等,所以官方就針對(duì)不同的語言平臺(tái),對(duì)底層庫進(jìn)行了二次封裝,使用的時(shí)候?qū)⒃撛摴こ讨苯幼鳛閹鞂?dǎo)入即可,后面會(huì)細(xì)說。
- native:一些native層的庫
- 3rdparty:第三方的一些庫
- jni:一些cmake編譯腳本和動(dòng)態(tài)庫的頭文件
- libs:官方根據(jù)不同平臺(tái)架構(gòu)打好的.so動(dòng)態(tài)庫,提供完整的opencv能力,體積稍大,單個(gè)架構(gòu)對(duì)應(yīng)的.so文件體積在10M以上
- staticlibs:將不同的功能分別做成.a靜態(tài)庫,可以根據(jù)使用到的opencv能力,選擇加載相應(yīng)的.a靜態(tài)庫,有利于降低應(yīng)用體積。
3、開發(fā)機(jī)制
opencv底層是c/c++寫的,語言門檻高,另外涉及的數(shù)學(xué)、圖形學(xué)等知識(shí)也加大了學(xué)習(xí)難度,所以官方針對(duì)不同水平和應(yīng)用方向的人員提供了不同的開發(fā)方式,以造福廣大的opencv能力需要者。
1、要不要在項(xiàng)目中配置sdk中native庫?
在使用opencv能力之前,需要加載opencv提供的native庫,opencv提供了兩種方式:
- 從應(yīng)用本地包下面加載,要求相關(guān)的native層的庫、頭文件等要拷貝到項(xiàng)目工程中,并配置好,應(yīng)用運(yùn)行時(shí)就會(huì)按照配置從應(yīng)用本地進(jìn)行加載。
- 設(shè)備上提前安裝 opencv-manager,是一個(gè)apk安裝包,以aidl的方式向其他應(yīng)用提供opencv能力,所以開發(fā)應(yīng)用時(shí)就不需要在拷貝native庫什么的到項(xiàng)目中,因?yàn)閍pp運(yùn)行時(shí)可以通過aidl方式,從opencv-manager中進(jìn)行加載。
為什么opencv要提供這兩種方式呢?
原因就在于上面說過opencv的native庫文件體積比較大。
如果是從本地包進(jìn)行加載,設(shè)備上安裝的應(yīng)用很多都使用opencv的話,每個(gè)應(yīng)用下都要拷貝一份native庫文件,浪費(fèi)存儲(chǔ)空間,另外項(xiàng)目中配置native庫也相對(duì)繁瑣。
雖然設(shè)備上提前安裝 opencv-manager的方式節(jié)省了存儲(chǔ)空間,但是一個(gè)硬傷就是不能保證用戶設(shè)備上實(shí)現(xiàn)安裝了opencv-manager,再說了,用戶也不會(huì)為了你的一個(gè)app而提前安裝另外一個(gè)app,用戶才不會(huì)聽你的。
所以,自己學(xué)習(xí)或者開發(fā)demo的時(shí)候可以用提前安裝 opencv-manager的方式,省去了拷貝、配置native庫的操作,也節(jié)省了手機(jī)空間。但是如果開發(fā)正式的app還是老老實(shí)實(shí)的從本地包加載為好。
2、涉不涉及ndk開發(fā)?
android開發(fā)目前來說java仍是主流,kotlin雖然越來越流行,但是對(duì)java是全兼容的。 opencv底層是c/c++實(shí)現(xiàn)的。 所以android中使用opencv能力就需要解決兩種語言通信的問題,使用的方案當(dāng)然是jni啦,opencvsdk的java工程對(duì)jni調(diào)用opencv方法進(jìn)行了封裝,開發(fā)時(shí)直接調(diào)用封裝好的java方法即可實(shí)現(xiàn)opencv能力的調(diào)用,很爽吧,不用和c/c++打交道。
但是畢竟opencv底層是c/c++實(shí)現(xiàn)的,想要100%使用opencv的能力,或者sdk的java庫中提供的方法不能滿足需求,還是需要自己使用c/c++去實(shí)現(xiàn)一部分功能,自行封裝成jni方法使用。再說了現(xiàn)在都是團(tuán)隊(duì)開發(fā),可以有專門的小伙伴實(shí)現(xiàn)底層的工作,對(duì)吧,這就需要拷貝、配置相關(guān)的native庫,使用ndk進(jìn)行開發(fā)。
當(dāng)然了,更厲害的大神可以下載源碼進(jìn)行修改,自行編譯,不僅可以得到最新的sdk,而且能夠根據(jù)需要,自行實(shí)現(xiàn)底層相關(guān)算法之類的。
這里對(duì)不同的開發(fā)機(jī)制的適用場景進(jìn)行簡單的總結(jié):
- java庫+opencv-manager.apk:適用于個(gè)人學(xué)習(xí)或者做demo,在設(shè)備上事先安裝opencv-manager.apk,然后項(xiàng)目中導(dǎo)入java庫工程,即可進(jìn)行后續(xù)開發(fā)。
- java庫+native庫:對(duì)opencv能力沒有特殊要求的情況下,開發(fā)線上應(yīng)用。
- java庫+native庫+自行封裝實(shí)現(xiàn)的jni方法:開發(fā)線上應(yīng)用,但是java庫提供的opencv能力不能滿足業(yè)務(wù)需求時(shí)使用這種方式。
4、開發(fā)實(shí)戰(zhàn)
經(jīng)過上面的討論,對(duì)android下opencv的開發(fā)機(jī)制有了大概了解,下面的分別對(duì)幾種開發(fā)方式進(jìn)行簡單的實(shí)現(xiàn)。
1、java庫+opencv-manager.apk
準(zhǔn)備
需要準(zhǔn)備的東西是sdk的java庫工程以及設(shè)備平臺(tái)對(duì)應(yīng)的opencv-manager.apk
配置
- 在設(shè)備上安裝opencv-manager.apk
- 在android studio中新建一個(gè)工程,已經(jīng)有的話直接打開
- 導(dǎo)入sdk的java庫工程:
File-New-Import module,定位到j(luò)ava庫工程,導(dǎo)入后庫工程的module name 為openCVLibrary343 - 配置主module依賴openCVLibrary343:在主modlue的guild.gradle文件的dependencies節(jié)點(diǎn)下添加
implementation project(':openCVLibrary343'),然后同步代碼即可。
開發(fā)
在 Activity 的 onResume 方法中初始化opencv
//使用OpenCV Engine service,需要運(yùn)行設(shè)備事先安裝OpenCV Manager
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION, this, new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
if (status == LoaderCallbackInterface.SUCCESS) {
Log.d(TAG, "onManagerConnected: success");
} else {
super.onManagerConnected(status);
}
}
});
對(duì)圖片進(jìn)行均值模糊
//本地加載圖片
Bitmap originImg = BitmapFactory.decodeFile("filePath");
//初始化Mat
Mat src = new Mat(originImg.getHeight(), originImg.getWidth(), CvType.CV_8UC4);
//bitmap轉(zhuǎn)mat
Utils.bitmapToMat(originImg, src);
//均值模糊
Imgproc.blur(src, src, new Size(3, 3));
//初始化處理后bitmap
Bitmap processedImg = Bitmap.createBitmap(src.cols(), src.rows(), Bitmap.Config.ARGB_8888);
//mat轉(zhuǎn)bitmap
Utils.matToBitmap(src, processedImg);
2、java庫+native庫
和上種方式的區(qū)別在于:
- 不需要事先安裝opencv-manager.apk
- 需要在項(xiàng)目中配置native庫
準(zhǔn)備
需要準(zhǔn)備的東西是sdk的java庫工程、native庫(sdk-native-libs)
配置
- 導(dǎo)入java庫工程,并配置依賴,具體步驟在【java庫+opencv-manager.apk】開發(fā)方式中有描述
- 將libs文件夾復(fù)制拷貝到項(xiàng)目工程的src-main-jniLibs路徑下,如果jniLis文件夾不存在就自己新建一個(gè)
- 打開module的build.gradle文件,在 android 節(jié)點(diǎn)下添加如下代碼,然后同步代碼即可
sourceSets {
main {
jniLibs.srcDirs = ['src/main/jniLibs/libs']
}
}
開發(fā)
和【java庫+opencv-manager.apk】開發(fā)方式初始化opencv的時(shí)候有區(qū)別,其他都一樣。
在 Activity 的 onResume 方法中初始化opencv
//使用應(yīng)用本地native庫
OpenCVLoader.initDebug();
3、java庫+native庫+自行封裝實(shí)現(xiàn)的jni方法
這種方式是在【java庫+native庫】開發(fā)方式的基礎(chǔ)上使用ndk進(jìn)行jni開發(fā)。
準(zhǔn)備
需要準(zhǔn)備的東西是sdk的java庫工程、native庫(sdk-native-libs)、native庫頭文件(sdk-native-jni-include)
配置
- 參考博客【音視頻開發(fā)01–AS3.x NDK開發(fā)環(huán)境搭建】建立支持ndk開發(fā)的項(xiàng)目。
- 完成【java庫+native庫】開發(fā)方式中的相關(guān)配置。
- 將native庫頭文件(sdk-native-jni-include)拷貝到src-main-cpp目錄下(如果沒有該目錄,請(qǐng)先按照【音視頻開發(fā)01–AS3.x NDK開發(fā)環(huán)境搭建】的過程進(jìn)行操作)
- 編輯 CMakeLists.txt 文件內(nèi)容(如果沒有該文件,請(qǐng)先按照【音視頻開發(fā)01–AS3.x NDK開發(fā)環(huán)境搭建】的過程進(jìn)行操作)
- 配置比較繁瑣,如果出現(xiàn)問題,請(qǐng)積極搜索,學(xué)習(xí)android studio cmake ndk開發(fā)的相關(guān)知識(shí)。
cmake_minimum_required(VERSION 3.4.1)
include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)
add_library(libopencv_java3 SHARED IMPORTED)
set_target_properties(libopencv_java3 PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/libs/${ANDROID_ABI}/libopencv_java3.so)
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
target_link_libraries( # Specifies the target library.
native-lib libopencv_java3
# Links the target library to the log library
# included in the NDK.
${log-lib} )
開發(fā)
看demo吧,就是jni開發(fā)那一套。
總結(jié)
android中使用opencv進(jìn)行開發(fā)時(shí),針對(duì)不同的開發(fā)和應(yīng)用場景選擇開發(fā)方式,總的來說涉及的東西挺多的,android知識(shí)、ndk/jni知識(shí)、c/c++知識(shí)、opencv知識(shí)、數(shù)學(xué)知識(shí)等。
學(xué)習(xí)的時(shí)候以android 開發(fā)為基礎(chǔ),通過本文搭建起開發(fā)框架,在寫demo的過程中學(xué)習(xí)opencv api和相關(guān)的理論知識(shí),同時(shí)也學(xué)習(xí)了ndk/jni和c/c++。