五道口宅男 - Android NDK and OpenCV Development With Android Studio

http://hujiaweibujidao.github.io/

http://hujiaweibujidao.github.io/blog/archives/

五道口宅男

http://hujiaweibujidao.github.io/blog/2014/10/22/android-ndk-and-opencv-development-with-android-studio/


—————- If you do NOT know Chinese, you can just skip this part —————-

一直打算将原来的XFace进行改进,最近终于有了些时间可以动手了,改进计划如下:开发上使用Android Studio作为新的开发环境,配上新的构建系统Gradle;应用上将修改原来的UI设计,内部代码也将有很大的变化,可能会用上 ContentProvider和Service等略高级内容;算法上打算让应用扩展性增强以适应不同的算法,并结合强大的Android Studio和Gradle让这个项目变得更加丰富。说了一堆废话,言归正传,本文的重点是介绍如何在Android Studio中进行NDK开发(目前它还不完全支持NDK开发),难点是NDK中还包含OpenCV的动态库。最后的最后,本文剩下部分将使用英文,因为 它要成为我在StackOverflow上的处女答,么么哒 ~O(∩_∩)O~

—————————- Here is the right stuff you may need ——————————–

This post shows how to develop an Android NDK application with OpenCV included using Android Studio and Gradle. If you’re working on migrating your original Eclipse Project to Android Studio, you may find this post is what exactly you want!

OK,Let’s start!

Section 1: Three things you must know

Creating a new Project with Android Studio

Building Your Project with Gradle

Gradle Plugin User Guide or you may want to read a Chinese commented version in my blog here.

2.Secondly, if your android ndk project is not that complicated(for example, having no opencv included), you may wanna see ph0b ‘s introduction here, it’s quite a nice job with a video recorded! (you can also follow Section 2 in this post to get a simple Android NDK demo application)

ph0b’s post: ANDROID STUDIO, GRADLE AND NDK INTEGRATION

3.Thirdly, if those above two do not meet your needs, then I think you may want to customize the Android.mk with Gradle in Android Studio. Thanks to Gaku Ueda , he had made a great job explaining how to achieve that goal. Actually I have found another nicer solution without adding that many codes and also achieve that goal. :-) Find it out in the next sections.

Gaku Ueda’s post: Using custom Android.mk with Gradle/Android Studio

OK, I will cover all above and give another nice solution in the end, have fun!

Section 2: A simple Android NDK demo application

This section shows creating a simple Android NDK demo application, if you already know, you can directly go the section 3.

1.Create a new Android project named NDKDemo with a blank Activity in AS(=Android Studio).

2.Give an id to the TextView in activity_my.xml such as android:id="@+id/textview", then add these codes in MyActivity.java.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 
@Override protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_my);   TextView textView = (TextView) findViewById(R.id.textview);  textView.setText(hello()); }  static {  System.loadLibrary("hello"); }  public native String hello(); 

3.Create a new directory jni in folder app/src/main, then you have java, jni and res in this folder.

4.This step is very important! You can add a external tool to run the javah command without typing that much code!

Open AS’s Preferences, then find External Tools in IDE Settings, click + to add one tool with the following configurations. (Make sure you have add JDK tools in your system path, if you don’t know how, click here)

image

With the help of this tool, each time we right click on a class file, then choose Android Tools -> javah to run this tool, it will automatically generate a C head file for us in the target folder $ModuleFileDir$/src/main/jni , in this case, it is app/src/main/jni. Try this on MyActivity.java file now! The console will print out a log like:

/usr/bin/javah -v -jni -d /Users/hujiawei/AndroidStudioProjects/NDKDemo/app/src/main/jni com.android.hacks.ndkdemo.MyActivity [Creating file RegularFileObject[/Users/hujiawei/AndroidStudioProjects/NDKDemo/app/src/main/jni/ com_android_hacks_ndkdemo_MyActivity.h]]

Then you get a com_android_hacks_ndkdemo_MyActivity.h file in jni folder with the following content.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_android_hacks_ndkdemo_MyActivity */  #ifndef _Included_com_android_hacks_ndkdemo_MyActivity #define _Included_com_android_hacks_ndkdemo_MyActivity #ifdef __cplusplus extern "C" { #endif /*  * Class:     com_android_hacks_ndkdemo_MyActivity  * Method:    hello  * Signature: ()Ljava/lang/String;  */ JNIEXPORT jstring JNICALL Java_com_android_hacks_ndkdemo_MyActivity_hello  (JNIEnv *, jobject);  #ifdef __cplusplus } #endif #endif 

5.Write a simple C implementation file named main.c in jni folder

1 2 3 4 5 6 7 
#include <jni.h> #include "com_android_hacks_ndkdemo_MyActivity.h"  JNIEXPORT jstring JNICALL Java_com_android_hacks_ndkdemo_MyActivity_hello  (JNIEnv * env, jobject obj){  return (*env)->NewStringUTF(env, "Hello from JNI");  } 

6.In the build.gradle file under app module, add the following codes to configure ndk in defaultConfig element, here we just give the uni module a name hello, you can find other configurations in Gradle Plugin User Guide.

1 2 3 4 5 6 7 8 9 10 11 
defaultConfig {  applicationId "com.android.hacks.ndkdemo"  minSdkVersion 16  targetSdkVersion 20  versionCode 1  versionName "1.0"   ndk{  moduleName "hello"  } } 

7.In order to let Gradle run ndk-build command (in some task, maybe NdkCompile task), we should configure the ndk.dir in local.properties file in Project root.

1 2 
sdk.dir=/Volumes/hujiawei/Users/hujiawei/Android/android_sdk ndk.dir=/Volumes/hujiawei/Users/hujiawei/Android/android_ndk 

8.OK, everything is ready, click Run to give it a try, you will see the result like

image

All right, so what’s happening inside?

Since you have a jni folder, Gradle will consider it as a default native code folder. When Gradle builds the app, it will run ndk-build command(since you have configured ndk.dir, Gradle knows where to find it) with a generated Android.mk file(locates in app/build/intermediates/ndk/debug/Android.mk), after compiling the native codes, it will generate the libs and obj folder into folder app/build/intermediates/ndk/debug/. Gradle will then package the libs into final apk file in folder app/build/outputs/apk/app-debug.apk(you can unarchive this file to check whether libs is contained)

app/build/intermediates/ndk/debug (lib and obj folders)

image

app/build/outputs/apk/app-debug.apk (and files within it)

image

Secontion 3: Using OpenCV

If your project do not use OpenCV, then the section 2 is just enough. But what if you wanna use OpenCV to do other stuff? Of course, we want to use OpenCV for Android instead of JavaCV here, and Of course, we need to package OpenCV library for Android into our application’s APK file (then users who use this app does not have to install OpenCV Manager). So, how can we achieve these goals?

The simplest way has been posted by TGMCians on Stack Overflow here, that is, let the main app include the OpenCV library as a dependency, and copy all <abi>/*.so files in OpenCV for Android SDK to jniLibs folder under app/src/main/, Gradle will automatically package these <abi>/*.so files into libs folder within the final APK file. Of course, this method will work, but it has a few backwards: (1) Unless you only copy the needed *.so files, you will always have a large APK due to this reason; (2) How about the building of the jni files? How to run ndk-build if these files contain opencv related codes?

So, here comes to our Using custom Android.mk with Gradle and Android Studio part. For testing, we first creat an Android.mk and an Application.mk file under jni folder.

Android.mk

1 2 3 4 5 6 7 8 9 
LOCAL_PATH := $(call my-dir)  include $(CLEAR_VARS)  LOCAL_SRC_FILES := main.c LOCAL_LDLIBS += -llog LOCAL_MODULE := hello  include $(BUILD_SHARED_LIBRARY) 

Application.mk

1 2 
APP_ABI := armeabi APP_PLATFORM := android-16 

Thanks to Gaku Ueda, he had made a great job explaining how to achieve that goal with this post. The core idea of his method is to run ndk-build command in some task, then zip the <abi>/*.so files under the output app/build/libs/ folder into a jar file which is finally put in app/build/libs/ folder, then add a compile dependency to this jar file. The key code for his method listed below

Notice 1: When using custom Android.mk, we should first disable Gradle to build the jni folder as before, and sourceSets.main.jni.srcDirs = [] just does this job!

Notice 2: The code is not exactly the same with Gaku Ueda’s code: tasks.withType(Compile) to tasks.withType(JavaCompile), because Compile is deprecated.

Notice 3: You can get $ndkDir variable with project.plugins.findPlugin('com.android.application').getNdkFolder() or you can define it in grade.properties file under Project root, so you need to add ndkDir=path/to/your/ndk in that file, if the file is not created, simply create a new one.

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 
android{  ...  sourceSets.main.jni.srcDirs = []   task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') {  ndkDir = project.plugins.findPlugin('com.android.application').getNdkFolder()  //on Windows, you need to add ".cmd" after "ndk-build" below  commandLine "$ndkDir/ndk-build",  'NDK_PROJECT_PATH=build',  'APP_BUILD_SCRIPT=src/main/jni/Android.mk',  'NDK_APPLICATION_MK=src/main/jni/Application.mk'  }   task ndkLibsToJar(type: Zip, dependsOn: 'ndkBuild', description: 'Create a JAR of the native libs') {  destinationDir new File(buildDir, 'libs')  baseName 'ndk-libs'  extension 'jar'  from(new File(buildDir, 'libs')) { include '**/*.so' }  into 'lib/'  }   tasks.withType(JavaCompile) {  compileTask -> compileTask.dependsOn ndkLibsToJar  }  ... }  dependencies {  compile fileTree(dir: 'libs', include: ['*.jar'])  // add begin  compile fileTree(dir: new File(buildDir, 'libs'), include: '*.jar')  // add end } 

But we can still do a little improvements here. We have already know that Gradle will take jniLibs folder as its default native libraries folder, so we can simply output the libs/<abi>/*.so files generated by ndk-build command into jniLibs folder, so there’s no need to zip these *.so files into a jar file.

The final build.gradle file under app module

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 
apply plugin: 'com.android.application'  android {  compileSdkVersion 20  buildToolsVersion "20.0.0"   defaultConfig {  applicationId "com.android.hacks.ndkdemo"  minSdkVersion 16  targetSdkVersion 20  versionCode 1  versionName "1.0"  }   // add begin  sourceSets.main.jni.srcDirs = []   task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') {  ndkDir = project.plugins.findPlugin('com.android.application').getNdkFolder()  commandLine "$ndkDir/ndk-build",  'NDK_PROJECT_PATH=build/intermediates/ndk',  'NDK_LIBS_OUT=src/main/jniLibs',  'APP_BUILD_SCRIPT=src/main/jni/Android.mk',  'NDK_APPLICATION_MK=src/main/jni/Application.mk'  }   tasks.withType(JavaCompile) {  compileTask -> compileTask.dependsOn ndkBuild  }  // add end   buildTypes {  release {  runProguard false  proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'  }  } }  dependencies {  compile fileTree(dir: 'libs', include: ['*.jar']) } 

So simple, right? 'NDK_LIBS_OUT=src/main/jniLibs' helps us do the right job!

For testing, you can also add some lines relating with OpenCV in your Android.mk file and some line in your main.c to check whether everything is readlly working. For example, add #include <opencv2/core/core.hpp> in main.c file, and change Android.mk to

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 
LOCAL_PATH := $(call my-dir)  include $(CLEAR_VARS)  #opencv OPENCVROOT:= /Volumes/hujiawei/Users/hujiawei/Android/opencv_sdk OPENCV_CAMERA_MODULES:=on OPENCV_INSTALL_MODULES:=on OPENCV_LIB_TYPE:=SHARED include ${OPENCVROOT}/sdk/native/jni/OpenCV.mk  LOCAL_SRC_FILES := main.c LOCAL_LDLIBS += -llog LOCAL_MODULE := hello  include $(BUILD_SHARED_LIBRARY) 

In Gradle Console window, you can see these similar lines

image

*.so files relating with OpenCV has been packaged into the final APK

image

One More Thing

Of course, maybe you don’t want to change your build.grale file with that much code, and Of course, you also don’t want to run ndk-build outside the IDE, then copy the <abi>/*.so files into jniLibs folder each time you want to rebuild the native codes!

At last, I came out another nicer solution, if you like, that is to create a ndk-build external tool in Android Studio, and every time you want to rebuild the native codes, simply run the external tool, then it automatically generates the libs/<abi>/*.so files into jniLibs folder, so everything is ready to run this app, :-)

The configuration is simple

Parameters: NDK_PROJECT_PATH=$ModuleFileDir$/build/intermediates/ndk NDK_LIBS_OUT=$ModuleFileDir$/src/main/jniLibs NDK_APPLICATION_MK=$ModuleFileDir$/src/main/jni/Application.mk APP_BUILD_SCRIPT=$ModuleFileDir$/src/main/jni/Android.mk V=1

image

OK, I hope it is helpful. Let me know if it is really helpful, or tell me what’s your problem. :-)

android

<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(130) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
CSDN海神之光上传的代码均可运行,亲测可用,直接替换数据即可,适合小白; 1、代码压缩包内容 主函数:main.m; 调用函数:其他m文件;无需运行 运行结果效果图; 2、代码运行版本 Matlab 2019b或2023b;若运行有误,根据提示修改;若不会,私信博主; 3、运行操作步骤 步骤一:将所有文件放到Matlab的当前文件夹中; 步骤二:双击打开main.m文件; 步骤三:点击运行,等程序运行完得到结果; 4、仿真咨询 如需其他服务,可私信博主或扫描博客文章底部QQ名片; 4.1 博客或资源的完整代码提供 4.2 期刊或参考文献复现 4.3 Matlab程序定制 4.4 科研合作 功率谱估计: 故障诊断分析: 雷达通信:雷达LFM、MIMO、成像、定位、干扰、检测、信号分析、脉冲压缩 滤波估计:SOC估计 目标定位:WSN定位、滤波跟踪、目标定位 生物电信号:肌电信号EMG、脑电信号EEG、心电信号ECG 通信系统:DOA估计、编码译码、变分模态分解、管道泄漏、滤波器、数字信号处理+传输+分析+去噪(CEEMDAN)、数字信号调制、误码率、信号估计、DTMF、信号检测识别融合、LEACH协议、信号检测、水声通信 1. EMD(经验模态分解,Empirical Mode Decomposition) 2. TVF-EMD(时变滤波的经验模态分解,Time-Varying Filtered Empirical Mode Decomposition) 3. EEMD(集成经验模态分解,Ensemble Empirical Mode Decomposition) 4. VMD(变分模态分解,Variational Mode Decomposition) 5. CEEMDAN(完全自适应噪声集合经验模态分解,Complementary Ensemble Empirical Mode Decomposition with Adaptive Noise) 6. LMD(局部均值分解,Local Mean Decomposition) 7. RLMD(鲁棒局部均值分解, Robust Local Mean Decomposition) 8. ITD(固有时间尺度分解,Intrinsic Time Decomposition) 9. SVMD(逐次变分模态分解,Sequential Variational Mode Decomposition) 10. ICEEMDAN(改进的完全自适应噪声集合经验模态分解,Improved Complementary Ensemble Empirical Mode Decomposition with Adaptive Noise) 11. FMD(特征模式分解,Feature Mode Decomposition) 12. REMD(鲁棒经验模态分解,Robust Empirical Mode Decomposition) 13. SGMD(辛几何模态分解,Spectral-Grouping-based Mode Decomposition) 14. RLMD(鲁棒局部均值分解,Robust Intrinsic Time Decomposition) 15. ESMD(极点对称模态分解, extreme-point symmetric mode decomposition) 16. CEEMD(互补集合经验模态分解,Complementary Ensemble Empirical Mode Decomposition) 17. SSA(奇异谱分析,Singular Spectrum Analysis) 18. SWD(群分解,Swarm Decomposition) 19. RPSEMD(再生相移正弦辅助经验模态分解,Regenerated Phase-shifted Sinusoids assisted Empirical Mode Decomposition) 20. EWT(经验小波变换,Empirical Wavelet Transform) 21. DWT(离散小波变换,Discraete wavelet transform) 22. TDD(时域分解,Time Domain Decomposition) 23. MODWT(最大重叠离散小波变换,Maximal Overlap Discrete Wavelet Transform) 24. MEMD(多元经验模态分解,Multivariate Empirical Mode Decomposition) 25. MVMD(多元变分模态分解,Multivariate Variational Mode Decomposition)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值