Android平台下OpenCV移植与使用---基于C/C++
在《Android Studio增加NDK代码编译支持--Mac环境》和《Mac平台下Opencv开发环境搭建》两篇文章中,介绍了如何使用NDK环境和Opencv环境搭建与测试使用,现在,在PC端对图像处理算法测试没问题后,该在移动端进行功能移植了,ios平台的很简单,直接把类库拷进工程就行了,android的稍微麻烦点,这里就以android平台为例说明移植步骤。
为了更好的模块移植,这里使用Android源码的make文件写法:*.mk,Android源码是一个很大的工程,它的编译采用一个大的mk文件,通过脚本文件的配置来自定义编译的,在build/core/下面的Android.mk文件就是总的编译文件入口:
这里写的opencv安卓模块也使用mk文件写法来编译so库。这里新建了一个测试工程,可以在GitHub上download或fork来查看源码: https://github.com/linjk/TestOpenCV
下面开始移植步骤:
1. 新建测试工程OpenCVTest:
2. 拷贝下载的opencv的android平台的开发包,这里下载3.1.0版本的:
这里把sdk目录下的native目录拷贝到工程根目录,这个目录下是c/c++语法的,java目录是已封装好的一些java接口,按需选择吧,为了更好的算法移植而不用每次改写,这里选择native库,复制后工程结构如下:
3. 新建jni目录,用于编写本地c++代码:
在src目录单击右键,按下图操作:
结果如下:
4. 编写java类的本地接口声明,用于给java层调用:
这里声明一个很简单的opencv本地方法,用于把一副图像编程灰度图像,当然,这个效果用安卓的图像矩阵来处理就行了,但是,复杂一点的功能,如边缘检测、身份证识别就要借助opencv来弄了,这里仅做功能测试:
5. 生成本地方法桥接头文件:
命令行进入src/main/java路径,然后执行命令: javah -jni cn.linjk.jniBridge.OpenCVUtils, -jni参数后面参数格式是:包名+类名,结果如下:
我们把这个文件移动到jni目录下,并新建一个同名的cpp类cn_linjk_jniBridge_OpenCVUtils.cpp:
6. 由于之前使用了android studio生成的jni目录,因此,编译上可能会和使用mk文件编译生成so库不一样,这里取消它的路径属性:
在app/build.gradle文件的android块下增加这个配置:
sourceSets{
main{
jni.srcDirs = []
}
}
可以发现,jni目录由蓝色变成了黄色:
7. 编写编译规则文件,指定ndk路径:
7.1 指定ndk路径:
7.2 在jni目录下新建两个mk编译文件,内容分别如下:
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 := testopencv LOCAL_SRC_FILES := cn_linjk_jniBridge_OpenCVUtils.cpp LOCAL_LDLIBS += -lm -llog -landroid include $(BUILD_SHARED_LIBRARY)
LOCAL_MODULE声明的是模块名称,必须与在OpenCVUtils声明的加载库名一样。
Application.mk文件内容如下:
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := arm64-v8a armeabi armeabi-v7a mips mips64
APP_ABI声明了声明针对那些CPU架构的so库。
7.3 在app/build.gradle声明一个task,用于编译生成so库。
7.3.1 编辑jni的cpp文件,内容如下,为测试能否调用库,这里先在函数打印cv版本:
//
// Created by LinJK on 21/11/2016.
// #include "cn_linjk_jniBridge_OpenCVUtils.h" #include <opencv2/opencv.hpp> #include <Android/log.h>
#include <Android/asset_manager.h>
#include <Android/asset_manager_jni.h> #define TAG "cn.linjk.opencvtest.jni"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) extern "C" { JNIEXPORT void JNICALL Java_cn_linjk_jniBridge_OpenCVUtils_img2gray
(JNIEnv *env , jclass objClass, jstring imgFilePath) { LOGI("OpenCV version: %s", CV_VERSION); } }
7.3.2 增加生成so库的task:
7.3.3 执行任务,生成so库:
执行命令"gradle cv_ndkBuild",结果如下:
把对应得so库复制到app/libs目录下对应cpu架构目录下:
8. 在MainActivity调用看能否输出opencv库版本:
MainActivity.java内容:
public class MainActivity extends AppCompatActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); OpenCVUtils.img2gray("");
}
}
运行后,发现出现如下错误:
这是我们指定生成动态链接库,因此,还需要libopencv_java3.so这个库,复制到我们的libs下,再次运行,结果如下:
输出正确,可以继续下一步了。
附:
也可以使用命令“arm-none-eabi-readelf -d libtestopencv.so”查看其需要的链接库,命令执行结果如下:
.so文件是ELF(Excutable and Linking Formar)格式的缩写,最初由UNIX系统实验室发布,它是应用程序二进制接口的一部分,ELF文件以节(section)的方式组织在一起,“节”描述了文件的各项信息,例如代码、数据、符号表、重定位表、全局偏移表等。
9. 编写图像处理类:
9.1 这里使用照相机获取输入图像,代码看github的源码就行,这里主要看看c++最终代码:
cn_linjk_jniBridge_OpenCVUtils.cpp
//
// Created by LinJK on 21/11/2016.
// #include "cn_linjk_jniBridge_OpenCVUtils.h" #include <opencv2/opencv.hpp>
#include <string>
#include <iostream> #include <Android/log.h>
#include <Android/asset_manager.h>
#include <Android/asset_manager_jni.h> #define TAG "cn.linjk.opencvtest.jni"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) using namespace std;
using namespace cv; class ImageUtils{
public:
void imageToGray(Mat inputImg, string outFilePath);
}; void ImageUtils::imageToGray(Mat inputImg, string outFilePath) {
Mat gray; Mat input = inputImg.clone(); cvtColor(input, gray, COLOR_BGR2GRAY); imwrite(outFilePath, gray);
} extern "C" { JNIEXPORT void JNICALL Java_cn_linjk_jniBridge_OpenCVUtils_img2gray
(JNIEnv *env , jclass objClass, jstring imgFilePath) { LOGI("OpenCV version: %s", CV_VERSION); char buf[128];
const char *str = env->GetStringUTFChars(imgFilePath, 0);
LOGD("图像路径: %s", str); Mat img = imread(str);
if (!img.data) {
LOGE("-----CV: 读取相片数据出错");
}
else {
LOGD("-----CV: 读取相片数据成功");
ImageUtils().imageToGray(img, str);
}
} }
MainActivity.java方法内容如下:
package cn.linjk.opencvtest; import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Display;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView; import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; import cn.linjk.jniBridge.OpenCVUtils; public class MainActivity extends AppCompatActivity { private Button btnOpenCamera;
private ImageView ivImgOutput;
private Button btnImgGray; private String imageFilePath; private static final int CAMERA_RESULT = 1112; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); btnOpenCamera = (Button)findViewById(R.id.btn_open_camera);
btnOpenCamera.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View pView) {
openCamera();
}
}); ivImgOutput = (ImageView)findViewById(R.id.img_output); btnImgGray = (Button)findViewById(R.id.img_gray);
btnImgGray.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View pView) {
OpenCVUtils.img2gray(imageFilePath);
//
BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
bmpFactoryOptions.inJustDecodeBounds = true;
Bitmap bmp = BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions); bmpFactoryOptions.inSampleSize = calculateInSampleSize(bmpFactoryOptions, 1280, 800); bmpFactoryOptions.inJustDecodeBounds = false; bmp = BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions); ivImgOutput.setImageBitmap(bmp);
}
});
} @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) {
BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
bmpFactoryOptions.inJustDecodeBounds = true;
Bitmap bmp = BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions); bmpFactoryOptions.inSampleSize = calculateInSampleSize(bmpFactoryOptions, 1280, 800); bmpFactoryOptions.inJustDecodeBounds = false; bmp = BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions); ivImgOutput.setImageBitmap(bmp); saveBitmap(bmp);
}
} private void openCamera() {
imageFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/testImage.jpg";
File imageFile = new File(imageFilePath);
Uri imageFileUri = Uri.fromFile(imageFile); Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(MediaStore.EXTRA_OUTPUT, imageFileUri);
startActivityForResult(i, CAMERA_RESULT);
} private int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) { final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int heightRatio = Math.round((float) height
/ (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth); inSampleSize = heightRatio < widthRatio ? widthRatio : heightRatio;
} return inSampleSize;
} private void saveBitmap(Bitmap bm) {
File f = new File(imageFilePath);
if (f.exists()) {
f.delete();
}
try {
FileOutputStream out = new FileOutputStream(f);
bm.compress(Bitmap.CompressFormat.PNG, 90, out);
out.flush();
out.close();
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果如下:
这里,opencv在android平台的移植和简单功能测试已经完成了,后面更多精彩opencv算法就可以继续实现啦~~
详细代码移步我的GitHub查阅即可:
https://github.com/linjk/TestOpenCV
Android平台下OpenCV移植与使用---基于C/C++的更多相关文章
- [转帖]Android平台下OpenGL初步
原文请看 Android平台下OpenGL初步 本文只关注于如何一步步实现在Android平台下运用OpenGl. 1.GLSurfaceView GLSurfaceView是Android应用程序中 ...
- Android平台下的TCP/IP传输(客户端)
在工科类项目中,嵌入式系统与软件系统或后台数据库之间的信息传输是实现“物联网”的一种必要的途径,对已简单概念的物联网,通常形式都是一个单片机/嵌入式系统实现数据的采集及其处理,通过蓝牙,wifi或者是 ...
- Android平台下Dalvik层hook框架ddi的研究
通过adbi,可以对native层的所有代码进行hook.但对于Android系统来说,这还远远不够,因为很多应用都还是在Dalvik虚拟机中运行的. 那么,有没有什么办法可以对Dalvik虚拟机中跑 ...
- Android平台下OpenGL初步
Android OpenGL ES 开发教程 从入门到精通 http://blog.csdn.net/zhoudailiang/article/details/50176143 http://blog ...
- Android平台下OpenGL图形编程
ref: Jayway Team Blog中OpenGL ES简明开发教程https://blog.jayway.com/tag/opengl-es/ OpenGL ES 开发教程http://www ...
- Mac平台下Opencv开发环境搭建
OpenCV(Open Source Computer Vision Library),是一个开源的跨平台的计算机视觉库,它实现了图像处理和计算机视觉领域的很多通用算法,可以在多种计算机平台上运行,支 ...
- <2014 05 14> Android平台下2D/3D开发攻略
Android通过OpenGL包含了对高性能2D和3D图形的支持,尤其支持OpenGLES API.OpenGL是一个跨平台的图形API,提供了软件操作3D图形硬件的接口.OpenGLES是一个专用于 ...
- 【原创】--linux平台下opencv安装
1.到opencv官网下载源码 也可以下载此链接http://pan.baidu.com/s/1mgId5ZM 2.解压到任意目录 可以使用右键-提取到此处,也可以在命令行中使用指令解压(linux中 ...
- Android平台下的JNI开发
JNI是Java Native Interface的缩写,通过JNI可以方便我们在Android平台上进行C/C++编程.要用JNI首先必须安装Android的NDK,配置好NDK环境之后就可以在Ec ...
随机推荐
- javascript 中断函数的使用 setInterval();
<script type="text/javascript"> var i=1; var IR1 = setInterval("myMethod()" ...
- 第五次团队作业——【Alpha版本】随笔汇总
031402304 陈燊 031402342 许玲玲 031402337 胡心颖 03140241 王婷婷 031402203 陈齐民 031402209 黄伟炜 031402233 郑扬涛 [Alp ...
- 面向对象和面向过程的js版选项卡
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...
- qq菜单的折叠与展示
敲出每个小例子是一种进步 html结构: <body> <ul id="list"> <li class="lis"> &l ...
- markdown测试
测试 1.第一点 这一点 代码块 @requires_authorization def somefunc(param1='', param2=0): '''A docstring''' if par ...
- 选择QT作为自己的图形库
图形库太多,公司里面一直使用自己的图形库,换一家公司,就换个图形库,现在公司没有对我开放图形库代码. 想来想去还是自己要有一套图形库,拿来主义最方便,选来选去感觉还是QT比较方便.同时能学习一下C++ ...
- Two-Pointer 之 Run Length Coding (RLC)
游程编码(Run Length Coding, RLC)是串处理中常见的预处理方法.其写法是典型的双指针(Two-Pointer).下面总结其写法1.输入为一串整数可以不把整数存在数组里
- 微信学习总结 09 解析接口中的消息创建时间CreateTime
1 消息的创建时间 网页超链接的作用以及如何在文本消息中使用网页超链接 2. 具体实现 刘峰博主的博文已经分析的很清楚了,直接去看就行了 .http://blog.csdn.net/lyq8479/a ...
- <<< tomcat启动报错StandardServer.await: create[8005]
启动tomcat的时候出现异常 严重: StandardServer.await: create[8005]: java.net.BindException: Address already in u ...
- apache 多网站日志配置禁止ip访问
#禁止IP访问服务器AcceptFilter http noneAcceptFilter https none<VirtualHost 192.168.1.220>ServerName 1 ...