https://blog.youkuaiyun.com/youngpan1101/article/details/53614588
本系列文章由 youngpan1101 出品,转载请注明出处。
文章链接: http://blog.youkuaiyun.com/youngpan1101/article/details/53614588
作者:宋洋鹏(youngpan1101)
邮箱: yangpeng_song@163.com
简要介绍
Android Studio 2.2 正式版发布后,默认使用 CMake 编译原生库,对于在 Linux 上使用 CMake 来构建工程的小伙伴来说会有种很熟悉的感觉。 下面将介绍在 Android Studio 上使用 OpenCV 库的两种方式(两种方式都无需在手机端安装 OpenCVManager,安装这个东东真挺影响体验的),当然我的亲测也是参考了其他的博客的,这里我先列出可供参考的博客或代码链接:
- 在 Android Studio 2.2 中愉快地使用 C/C++
- OpenCV4Android释疑: 透析Android以JNI调OpenCV的三种方式(让OpenCVManager永不困扰)
- Android 使用OpenCV的三种方式(Android Studio)
- AndroidStudio2.2.2下通过CMake编译方式开发OpenCV
- Github: googlesample_android-ndk_hello-libs
使用 OpenCV 库的两种方式:
1. 使用动态的 OpenCV 库的方式。
2. 同时使用 Java 的 API 又使用 JNI 的接口的方式,此时编译时一般使用的是动态链接 OpenCV 库的方式。
——————————– 分割线<< 家有小武,如有一母
>>分割线 ——————————–
配置环境
- Android Studio 2.2, SDK, JDK, NDK 和其他插件下载
- AS安装可以参考:Windows 环境下 Android Studio v1.0 安装教程
-
下载 NDK 和构建工具
NDK: 让你能在 Android 上面使用 C 和 C++ 代码的工具集。
CMake: 外部构建工具。如果你准备只使用 ndk-build 的话,可以不使用它。
LLDB: Android Studio 上面调试本地代码的工具。
你可以使用 SDK Manager 来安装上述组件:- 打开一个项目,从菜单栏中选择 Tools > Android > SDK Manager
- 点击 SDK Tools 选项卡
- 勾选 LLDB,CMake 和 NDK ,然后安装
-
opencv for android (这里使用的是 opencv 2.4.11)
——————————– 分割线<< 家有小武,如有一母
>>分割线 ——————————–
预备工作
正式介绍三种方式之前,我们需要做一些预备工作。
新建工程,命令为 OpenCVJNI , 这里 不需要勾选
Include C++ Support
添加 javah 命令: File >> Settings >> External Tools >> [+]
Tool settings Program $JDKPath$\bin\javah Parameters -d $ModuleFileDir$\src\main\jni -jni $FileClass$ Working directory $ModuleFileDir$\build\intermediates\classes\debug
- 1
- 2
- 3
- 4
新建 hwj.opencvjni.OpenCVHelper.java
public class OpenCVHelper { public static native String getStringTmp(); }
- 1
- 2
- 3
Build >> Make Project
点选 OpenCVHelper.java , 右键 External Tools >> javah ,生成 hwj_opencvjni_OpenCVHelper.h 文件。
新建 jni / MyLib.cpp 文件
#include <iostream> #include <sstream> #include "hwj_opencvjni_OpenCVHelper.h" JNIEXPORT jstring JNICALL Java_hwj_opencvjni_OpenCVHelper_getStringTmp(JNIEnv *env, jclass instance){ std::stringstream ss; ss << "Hello from c++ " << std::endl; return env->NewStringUTF(ss.str().c_str()); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
新建 app / CMakeLists.txt :
# cmake version cmake_minimum_required(VERSION 3.4.1) # 支持 -std=gnu++11 set(CMAKE_VERBOSE_MAKEFILE on) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") # CMAKE_SOURCE_DIR 是 CMakeLists.txt 所在的目录 add_library( my-lib SHARED ${CMAKE_SOURCE_DIR}/src/main/jni/MyLib.cpp ) find_library( log-lib log ) target_link_libraries( my-lib ${log-lib} )
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
app / build.gradle 添加代码:
apply plugin: 'com.android.application' android { compileSdkVersion 24 buildToolsVersion "24.0.2" defaultConfig { applicationId "hwj.opencvjni" minSdkVersion 15 targetSdkVersion 24 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" // code block added by myself externalNativeBuild { cmake { // 指定编译器参数 cppFlags "-std=c++11 -frtti -fexceptions" // 生成.so库的目标平台 abiFilters 'armeabi', 'armeabi-v7a' } } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } // code block added by myself // 指定 CMakeLists.txt 相对此 build.gradle 文件的路径 externalNativeBuild { cmake { path "CMakeLists.txt" } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:24.2.1' testCompile 'junit:junit:4.12' }
- 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
- 43
编译出 libmy-lib.so 文件
- View >> Tool Windows >> Gradle >> build 生活生成 so 库
- so 库的存储路径
修改 activity_main.xml 文件
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="hwj.opencvjni.MainActivity"> <TextView android:id="@+id/sample_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" /> </RelativeLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
修改 OpenCVHelper.java :
public class OpenCVHelper { static { System.loadLibrary("my-lib"); } public static native String getStringTmp(); }
- 1
- 2
- 3
- 4
- 5
- 6
修改 MainActivity.java :
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView tv = (TextView) findViewById(R.id.sample_text); tv.setText(OpenCVHelper.getStringTmp()); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
在手机端跑 Demo
到这里算是一个 JNI 的调用 CMake 方式编译出来的 so 库的一个小 demo ,通过
Build >> Analyze APK 可以看到 APK 中是否打包了 so 库:
——————————– 分割线<< 家有小武,如有一母
>>分割线 ——————————–
使用动态 OpenCV 库的方式
新建 main / OpenCVLib 文件夹, 将 OpenCV SDK 目录 sdk\native\libs 下的 armeabi 和 armeabi-v7a 复制到 OpenCVLib 目录下:
app / build.gradle :
apply plugin: 'com.android.application' android { compileSdkVersion 24 buildToolsVersion "24.0.2" defaultConfig { applicationId "hwj.opencvjni" minSdkVersion 15 targetSdkVersion 24 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" externalNativeBuild { cmake { cppFlags "-std=c++11 -frtti -fexceptions" abiFilters 'armeabi', 'armeabi-v7a' } } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } externalNativeBuild { cmake { path "CMakeLists.txt" } } // adding sourceSets{ main{ // let gradle pack the shared library into apk jniLibs.srcDirs = ['src/main/OpenCVLib'] } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:24.2.1' testCompile 'junit:junit:4.12' }
- 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
- 43
- 44
- 45
CMakeLists.txt :
# cmake version cmake_minimum_required(VERSION 3.4.1) # 支持 -std=gnu++11 set(CMAKE_VERBOSE_MAKEFILE on) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") # opencv import libs set(pathToOpenCV D:/Opencv/OpenCV-2.4.11-android-sdk) include_directories( ${pathToOpenCV}/sdk/native/jni/include ) add_library( lib_opencv SHARED IMPORTED ) set_target_properties( lib_opencv PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/OpenCVLib/${ANDROID_ABI}/libopencv_java.so ) # build application's shared lib add_library( my-lib SHARED ${CMAKE_SOURCE_DIR}/src/main/jni/MyLib.cpp ) find_library( log-lib log ) target_link_libraries( my-lib ${log-lib} lib_opencv)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
OpenCVHelper.java :
public class OpenCVHelper { static { System.loadLibrary("my-lib"); } public static native String getStringTmp(); // image gray processing public native int[] getGrayImage(int[] pixels, int w, int h); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
hwj_opencvjni_OpenCVHelper.h 添加 :
JNIEXPORT jintArray JNICALL Java_hwj_opencvjni_OpenCVHelper_getGrayImage (JNIEnv *env, jobject, jintArray buf, int w, int h);
- 1
- 2
MyLib.cpp 添加 :
#include <iostream> #include <sstream> #include <jni.h> #include <opencv2/opencv.hpp> #include "hwj_opencvjni_OpenCVHelper.h" JNIEXPORT jstring JNICALL Java_hwj_opencvjni_OpenCVHelper_getStringTmp(JNIEnv *env, jclass thiz){ std::stringstream ss; ss << "Hello from c++ " << std::endl; return env->NewStringUTF(ss.str().c_str()); } JNIEXPORT jintArray JNICALL Java_hwj_opencvjni_OpenCVHelper_getGrayImage(JNIEnv *env, jobject, jintArray buf, int w, int h){ jint *pixels = env->GetIntArrayElements(buf, NULL); if(pixels == NULL){ return NULL; } cv::Mat imgData(h, w, CV_8UC4, pixels); uchar *ptr = imgData.ptr(0); for(int i=0; i<w*h; i++){ int grayScale = (int)(ptr[4*i+2]*0.299 + ptr[4*i+1]*0.587 + ptr[4*i+0]*0.114); ptr[4*i+0] = (uchar)grayScale; ptr[4*i+1] = (uchar)grayScale; ptr[4*i+2] = (uchar)grayScale; } int size = w * h; jintArray result = env->NewIntArray(size); env->SetIntArrayRegion(result, 0, size, pixels); env->ReleaseIntArrayElements(buf, pixels, 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
复制一个 jpg 格式的图片到 res / drawable 文件夹下 :
activity_main.xml 添加 ImageView 控件 :
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="hwj.opencvjni.MainActivity"> <TextView android:id="@+id/sample_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" /> <ImageView android:id="@+id/img" android:layout_width="wrap_content" android:layout_height="wrap_content" app:srcCompat="@drawable/Boy" android:layout_centerInParent="true" /> <Button android:id="@+id/bt_Gray" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" android:text="Gray" /> </RelativeLayout>
- 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
MainActivity.java :
public class MainActivity extends AppCompatActivity { private Button bt_photo = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView tv = (TextView) findViewById(R.id.sample_text); tv.setText(OpenCVHelper.getStringTmp()); // adding bt_photo = (Button) findViewById(R.id.bt_Gray); bt_photo.setOnClickListener(new Button.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub ImageView img = (ImageView)findViewById(R.id.img); Bitmap bitmap = ((BitmapDrawable) getResources().getDrawable( R.drawable.boy)).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.getGrayImage(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
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
运行程序