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,保留我们需要的文件即可。