Android 使用OpenCV的三种方式(Android Studio)
http://blog.csdn.net/sbsujjbcy/article/details/49520791
其实最早接触OpenCV是很久很久之前的事了,大概在2013年的5,6月份,当时还是个菜逼(虽然现在也是个菜逼),在那一段时间,学了一段时间的Android(并不算学,一个月都不到),之后再也没接触android,而是一直在接触Javaweb。那次接触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构建工具)
android.useDeprecatedNdk=true
- 1
- 1
在local.properties文件中配置ndk目录
ndk.dir=D\:\\AndroidSDK\\sdk\\ndk-bundle
- 1
- 1
编辑build.gradle,在android节点中增加下面的代码
sourceSets.main.jni.srcDirs = []
//禁止自带的ndk功能
sourceSets.main.jniLibs.srcDirs = ['src/main/libs','src/main/jniLibs']
//重定向so目录为src/main/libs和src/main/jniLibs,原来为src/main/jniLibs
task ndkBuild(type: Exec, description: 'Compile JNI source with NDK') {
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def ndkDir = properties.getProperty('ndk.dir')
if (org.apache.tools.ant.taskdefs.condition.Os.isFamily(org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS)) {
commandLine "$ndkDir/ndk-build.cmd", '-C', file('src/main/jni').absolutePath
} else {
commandLine "$ndkDir/ndk-build", '-C', file('src/main/jni').absolutePath
}
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuild
}
task ndkClean(type: Exec, description: 'Clean NDK Binaries') {
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def ndkDir = properties.getProperty('ndk.dir')
if (org.apache.tools.ant.taskdefs.condition.Os.isFamily(org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS)) {
commandLine "$ndkDir/ndk-build.cmd",'clean', '-C', file('src/main/jni').absolutePath
} else {
commandLine "$ndkDir/ndk-build",'clean', '-C', file('src/main/jni').absolutePath
}
}
clean.dependsOn 'ndkClean'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
在之前新建的Application.mk中增加下面的内容
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi armeabi-v7a
APP_PLATFORM := android-8
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
在Android.mk中增加下面的内容
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
OpenCV_INSTALL_MODULES := on
OpenCV_CAMERA_MODULES := off
OPENCV_LIB_TYPE :=STATIC
ifeq ("$(wildcard $(OPENCV_MK_PATH))","")
include ..\..\..\..\native\jni\OpenCV.mk
else
include $(OPENCV_MK_PATH)
endif
LOCAL_MODULE := OpenCV
LOCAL_SRC_FILES :=
LOCAL_LDLIBS += -lm -llog
include $(BUILD_SHARED_LIBRARY)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
这时候,使用gradle构建一下,如果能成功构建出so,说明配置没问题,如下图,点击as右侧的gradle展开,双击ndkBuild进行构建
下面开始讲第一种方法,纯jni层的代码,该方法基于上面的所有步骤,为静态链接库
声明java层的native方法
public class OpenCVHelper {
static {
System.loadLibrary("OpenCV");
}
public static native int[] gray(int[] buf, int w, int h);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
使用javah命令生成头文件,内容如下
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class cn_edu_zafu_opencv_OpenCVHelper */
#ifndef _Included_cn_edu_zafu_opencv_OpenCVHelper
#define _Included_cn_edu_zafu_opencv_OpenCVHelper
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: cn_edu_zafu_opencv_OpenCVHelper
* Method: gray
* Signature: ([III)[I
*/
JNIEXPORT jintArray JNICALL Java_cn_edu_zafu_opencv_OpenCVHelper_gray
(JNIEnv *, jclass, jintArray, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
新建cpp文件,实现对应的方法,就是灰度处理
#include "cn_edu_zafu_opencv_OpenCVHelper.h"
#include <stdio.h>
#include <stdlib.h>
#include <opencv2/opencv.hpp>
using namespace cv;
extern "C" {
JNIEXPORT jintArray JNICALL Java_cn_edu_zafu_opencv_OpenCVHelper_gray(
JNIEnv *env, jclass obj, jintArray buf, int w, int h);
JNIEXPORT jintArray JNICALL Java_cn_edu_zafu_opencv_OpenCVHelper_gray(
JNIEnv *env, jclass obj, jintArray buf, int w, int h) {
jint *cbuf;
cbuf = env->GetIntArrayElements(buf, JNI_FALSE );
if (cbuf == NULL) {
return 0;
}
Mat imgData(h, w, CV_8UC4, (unsigned char *) cbuf);
uchar* ptr = imgData.ptr(0);
for(int i = 0; i < w*h; i ++){
//计算公式:Y(亮度) = 0.299*R + 0.587*G + 0.114*B
//对于一个int四字节,其彩色值存储方式为:BGRA
int grayScale = (int)(ptr[4*i+2]*0.299 + ptr[4*i+1]*0.587 + ptr[4*i+0]*0.114);
ptr[4*i+1] = grayScale;
ptr[4*i+2] = grayScale;
ptr[4*i+0] = grayScale;
}
int size = w * h;
jintArray result = env->NewIntArray(size);
env->SetIntArrayRegion(result, 0, size, cbuf);
env->ReleaseIntArrayElements(buf, cbuf, 0);
return result;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
之后,需要将cpp文件编译进去,在Andorid.mk文件中加入
LOCAL_SRC_FILES := cn_edu_zafu_opencv_OpenCVHelper.cpp
- 1
- 1
然后在java层写个测试方法测试一下是否进行灰度化了
Bitmap bitmap = ((BitmapDrawable) getResources().getDrawable(
R.drawable.ic)).getBitmap();
int w = bitmap.getWidth(), h = bitmap.getHeight();
int[] pix = new int[w * h];
bitmap.getPixels(pix, 0, w, 0, 0, w, h);
int [] resultPixes=OpenCVHelper.gray(pix,w,h);
Bitmap result = Bitmap.createBitmap(w,h, Bitmap.Config.RGB_565);
result.setPixels(resultPixes, 0, w, 0, 0,w, h);
img.setImageBitmap(result);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
运行效果如下,灰度化后的结果
上面的这种方法生成的so库的大小见下图,大约有1.4M左右
第二种方法也是纯jni的,但是是动态链接库,在第一种基础上,修改Android.mk文件为
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
OpenCV_INSTALL_MODULES := on
OpenCV_CAMERA_MODULES := off
OPENCV_LIB_TYPE := SHARED
ifeq ("$(wildcard $(OPENCV_MK_PATH))","")
include ..\..\..\..\native\jni\OpenCV.mk
else
include $(OPENCV_MK_PATH)
endif
LOCAL_MODULE := OpenCV
LOCAL_SRC_FILES := cn_edu_zafu_opencv_OpenCVHelper.cpp
LOCAL_LDLIBS += -lm -llog
include $(BUILD_SHARED_LIBRARY)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
注意上面的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层的测试方法
OpenCVLoader.initDebug();
Mat rgbMat = new Mat();
Mat grayMat = new Mat();
Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic);
Bitmap grayBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), Bitmap.Config.RGB_565);
Utils.bitmapToMat(srcBitmap, rgbMat);//convert original bitmap to Mat, R G B.
Imgproc.cvtColor(rgbMat, grayMat, Imgproc.COLOR_RGB2GRAY);//rgbMat to gray grayMat
Utils.matToBitmap(grayMat, grayBitmap); //convert mat to bitmap
img.setImageBitmap(grayBitmap);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
注意使用OpenCVLoader.initDebug();进行初始化而不是使用OpenCVLoader.initAsync()
这种方法的特点是处理都在java层,不怎么会涉及jni层的代码,除非java层完成不了的工作会转移到jni层去。
三种方法各有各的优点,根据自己的情况进行选择。
- 如果c++特别好的,建议使用第一种方法
- 如果更习惯java代码的,并且java层的函数都能进行处理的,建议选择第三种方法
- 第二种方法建议在第三种方法不满足条件的情况下进行辅助使用,因为使用了第三种方法的前提是使用第二种方法的动态链接库。
最后附上源码
Android 使用OpenCV的三种方式(Android Studio)的更多相关文章
- OpenCV4Android释疑: 透析Android以JNI调OpenCV的三种方式(让OpenCVManager永不困扰)
OpenCV4Android释疑: 透析Android以JNI调OpenCV的三种方式(让OpenCVManager永不困扰) 前文曾详细探讨了关于OpenCV的使用,原本以为天下已太平.但不断有人反 ...
- uni-app&H5&Android混合开发三 || uni-app调用Android原生方法的三种方式
前言: 关于H5的调用Android原生方法的方式有很多,在该片文章中我主要简单介绍三种与Android原生方法交互的方式. 一.H5+方法调用android原生方法 H5+ Android开发规范官 ...
- Android录制音频的三种方式
对于录制音频,Android系统就都自带了一个小小的应用,可是使用起来可能不是特别的灵活.所以有提供了另外的俩种. 下边来介绍下这三种录制的方式; 1.通过Intent调用系统的录音器功能,然后在录制 ...
- Android 生成LayoutInflater的三种方式
通俗的说,inflate就相当于将一个xml中定义的布局找出来. 因为在一个Activity里如果直接用findViewById()的话,对应的是setConentView()的那个layout里的组 ...
- 【Android进度条】三种方式实现自定义圆形进度条ProgressBar
一.通过动画实现 定义res/anim/loading.xml如下: <?xml version="1.0" encoding="UTF-8"?> ...
- Android数据存储的三种方式:SharePreferences , file , SQLite
(1)SharePreferences: 存入: SharedPreferences setter = this.getSharedPreferences("spfile", 0) ...
- Android处理XML的三种方式
http://www.cnblogs.com/zhangdongzi/archive/2011/04/14/2016434.html http://blog.csdn.net/zzp16/articl ...
- 【OpenCV】三种方式操作图像像素
OpenCV中,有3种访问每个像素的方法:使用at方法.使用迭代器方法.使用指针 运行如下程序后可以发现使用at方法速度最快. 代码如下: //操作图像像素 #include <opencv2/ ...
- Android 接入 OpenCV库的三种方式
OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux.Windows.Android和Mac OS操作系统上.它轻量级而且高效——由一系列 C 函数和少 ...
随机推荐
- Swift 开源 Linux Ubuntu Install
Swift 开源了,它现在变成跨平台的了,开源后的 Swift 不止能运行在 MAC 和 iOS 平台,现在也可以运行在 Linux 平台了.swift.org 网站上面提供了在 Linux 上面安装 ...
- Sphinx 安装与使用(1)-- 安装Coreseek
Coreseek就是Sphinx的中文版 官方网站 http://www.coreseek.cn/ 一.安装 1.修改LANG 永久修改: vim /etc/locale.conf LANG=&quo ...
- spark 1.3 发布了
悄悄地,spark 还是像往常一样,发布了1.3版本,从release notes可以看出,这一版本比较大的变化是1. 增加了DataFrame API,这样以后操作一些结构化的数据集时将会变的非常方 ...
- 2015 Spark 将走向哪里?
在刚刚过去的spark submit上,Matei Zahara简单回顾了下2014年spark的发展,可用一个词来概括那就是"Amazing"!!! 那么2015年,spark ...
- 面向Internet的编程
面向Internet的编程 1994年秋天我返回工作时,这个公司的景象已经完全改变.他们决定Oak语言——跨平台的.安全的.易传输的代码——时理想的面向Internet的语言.同时他们在制作名为Web ...
- 在Amazon cloud 升级CentOS5.4 到 5.9
升级前一定要备份重要资料,做好最坏的打算,最好的准备! 老板不知道为什么,喜欢升级服务器,劝过好几次都坚持要升级. 好吧,you are boss. 升级前当然免不了google一番.发现CentOS ...
- Python gevent学习笔记-2
在上一篇里面介绍了gevent的最主要的功能,先来来了解一下gevent里面一些更加高级的功能. 事件 事件是一种可以让greenlet进行异步通信的手段. ? 1 2 3 4 5 6 7 8 9 1 ...
- Win7系统安装 MySQL 5.7.23
1. 下载 MySQL 5.7版本:https://dev.mysql.com/downloads/mysql/5.7.html#downloads 2. 解压到指定文件夹,mysql根目录下创建my ...
- 【BZOJ3073】[Pa2011]Journeys 线段树+堆优化Dijkstra
[BZOJ3073][Pa2011]Journeys Description Seter建造了一个很大的星球,他准备建造N个国家和无数双向道路.N个国家很快建造好了,用1..N编号,但是他发现道路实在 ...
- 详解Amazon S3上传/下载数据
AWS简单储存服务(Amazon S3)是非常坚牢的存储服务,拥有99.999999999%的耐久性(记住11个9的耐久性). 使用CloudBerry Explorer,从Amazon S3下载数据 ...