from: http://blog.csdn.net/sbsujjbcy/article/details/49520791
其实最早接触OpenCV是很久很久之前的事了,大概在2013年的5,6月份,当时还是个菜逼(虽然现在也是个菜逼),在那一段时间,学了一段时间的Android(并不算学,一个月都不到),之后再也没接触android,而是一直在接触Java Web。那次接触OpenCV是因为一个学长的毕业设计,这次接触OpenCV是因为自己的毕业设计。2013年那年技术太菜,ndk环境都搭不好,当初还是eclipse环境,一直按照网上的教程去搭,下什么cygwin,简直就是个坑,网上的文章转来转去,都是过时的。后来一个机会看到了google官方的一个文档,就像发现了新大陆一样,发现ndk环境根本不需要装cygwin,装了你就坑了,装这个东西有好多G呢,时间浪费不说,简直误人子弟啊。后来在那年7月写下一篇博客
这段时间在填自己毕业设计的坑,要用到OpenCV,首先得下载到sdk吧,这个从官网上下载就好了
OpenCV for Android
注意下载的是OpenCV for android。当前版本是3.0
解压后,里面的内容如下
samples目录下是样例代码,sdk目录下是我们需要用到的Java层和jni层的代码。apk目录是manager的apk安装包
其实OpenCV最简单的使用方式是使用manager,也就是使用apk目录下的安装包,安装对应的apk,将java层代码导入,使用OpenCVLoader.initAsync()加载库,之后你就可以直接用java代码调用Opencv相关的功能了。
但是这种方式除了安装我们自己的apk还需要安装上面提到的manager的apk,用户体验十分不好,不推荐使用,本文的三种方式将完全脱离这个manager的apk。
本文下面的三种方式的内容参考自文章 OpenCV4Android释疑: 透析Android以JNI调OpenCV的三种方式(让OpenCVManager永不困扰)
本篇文章使用android studio作为开发环境,由于实验性的构建工具对ndk支持还不好,所以使用旧的构建方式,在原来写的一篇博客基础上修改即可android studio下ndk开发
这正式介绍三种方式之前,我们需要做一些前期准备。
首先新建一个项目,将OpenCV中sdk目录下的native目录拷到项目根目录
然后新建Jni目录
在里面新建两个文件
编辑gradle.properties文件,增加下面的属性使用旧版的ndk功能(不添加会使用实验性的ndk构建工具)
<code class="hljs bash has-numbering">android.useDeprecatedNdk=<span class="hljs-literal">true</span></code><ul style="" class="pre-numbering"><li>1</li></ul><ul style="" class="pre-numbering"><li>1</li></ul>
在local.properties文件中配置ndk目录
<code class="hljs tex has-numbering">ndk.dir=D<span class="hljs-command">\:</span><span class="hljs-command">\\</span>AndroidSDK<span class="hljs-command">\\</span>sdk<span class="hljs-command">\\</span>ndk-bundle</code><ul style="" class="pre-numbering"><li>1</li></ul><ul style="" class="pre-numbering"><li>1</li></ul>
编辑build.gradle,在android节点中增加下面的代码
<code class="hljs makefile has-numbering"><span class="hljs-constant">APP_STL</span> := gnustl_static <span class="hljs-constant">APP_CPPFLAGS</span> := -frtti -fexceptions <span class="hljs-constant">APP_ABI</span> := armeabi armeabi-v7a <span class="hljs-constant">APP_PLATFORM</span> := android-8 </code>
这时候,使用gradle构建一下,如果能成功构建出so,说明配置没问题,如下图,点击as右侧的gradle展开,双击ndkBuild进行构建
下面开始讲第一种方法,纯jni层的代码,该方法基于上面的所有步骤,为静态链接库
声明java层的native方法
<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> OpenCVHelper { <span class="hljs-keyword">static</span> { System.loadLibrary(<span class="hljs-string">"OpenCV"</span>); } <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> native <span class="hljs-keyword">int</span>[] <span class="hljs-title">gray</span>(<span class="hljs-keyword">int</span>[] buf, <span class="hljs-keyword">int</span> w, <span class="hljs-keyword">int</span> h); } </code>
注意上面的OPENCV_LIB_TYPE属性的改动,从STATIC改为了SHARED,这时候再用ndkBuild一下,你会发现会输出一些警告以及一部分红色的内容
生成的so库的大小为310k,小了好几倍
这时候如果你直接取运行程序,会报错误
原因是我们使用的是动态库加载方式,还需要将依赖的so加进去,这个so就是图中的libopencv_java3.so,他在我们的最开始加到项目里的native目录中
将它拷到我们的jniLibs目录中去,这里只拷贝armeabi和armeabi-v7a中的,至于其他的按需拷贝
这时候运行就不会报错了。
既然我们使用了动态链接库,那么我们同样也可以使用java层的接口,优点是java开发速度相对快一点。第三种方法在第二种方法基础上,使用纯java层代码进行处理。
在此之前,我们需要将sdk目录中的java代码拷到项目中去
但是org.opencv.engine包中是一个aidl,我们需要将它剪贴到aidl目录中去,就像这样子
最后还有一个资源文件attrs.xml,拷过来
build一下项目,不出意外应该会报错,这时候找到该类,引入自己的R文件包就可以了
再次build应该就不会有什么问题了。
java层的测试方法
<code class="hljs avrasm has-numbering">OpenCVLoader<span class="hljs-preprocessor">.initDebug</span>()<span class="hljs-comment">;</span> Mat rgbMat = new Mat()<span class="hljs-comment">;</span> Mat grayMat = new Mat()<span class="hljs-comment">;</span> Bitmap srcBitmap = BitmapFactory<span class="hljs-preprocessor">.decodeResource</span>(getResources(), R<span class="hljs-preprocessor">.drawable</span><span class="hljs-preprocessor">.ic</span>)<span class="hljs-comment">;</span> Bitmap grayBitmap = Bitmap<span class="hljs-preprocessor">.createBitmap</span>(srcBitmap<span class="hljs-preprocessor">.getWidth</span>(), srcBitmap<span class="hljs-preprocessor">.getHeight</span>(), Bitmap<span class="hljs-preprocessor">.Config</span><span class="hljs-preprocessor">.RGB</span>_565)<span class="hljs-comment">;</span> Utils<span class="hljs-preprocessor">.bitmapToMat</span>(srcBitmap, rgbMat)<span class="hljs-comment">;//convert original bitmap to Mat, R G B.</span> Imgproc<span class="hljs-preprocessor">.cvtColor</span>(rgbMat, grayMat, Imgproc<span class="hljs-preprocessor">.COLOR</span>_RGB2GRAY)<span class="hljs-comment">;//rgbMat to gray grayMat</span> Utils<span class="hljs-preprocessor">.matToBitmap</span>(grayMat, grayBitmap)<span class="hljs-comment">; //convert mat to bitmap</span> img<span class="hljs-preprocessor">.setImageBitmap</span>(grayBitmap)<span class="hljs-comment">;</span></code>
注意使用OpenCVLoader.initDebug();进行初始化而不是使用OpenCVLoader.initAsync()
这种方法的特点是处理都在java层,不怎么会涉及jni层的代码,除非java层完成不了的工作会转移到jni层去。
三种方法各有各的优点,根据自己的情况进行选择。
- 如果c++特别好的,建议使用第一种方法
- 如果更习惯java代码的,并且java层的函数都能进行处理的,建议选择第三种方法
- 第二种方法建议在第三种方法不满足条件的情况下进行辅助使用,因为使用了第三种方法的前提是使用第二种方法的动态链接库。
最后附上源码