Android studio JNI NDK 开发(opencv,opencl 调用等等,更新中)
2018-01-05 15:59
741 查看
转载请注明 cwyf
身为FAE一直处于什么都要干的情况,开发和客户端支持,一直在做(看着那可怜的薪水心疼).最近做的工作点有点多,所以来写个博记录下一些关键点.自从加入sunny后作了2年的PC端MFC,C# winform,现在开始在领导要求下转行,做数据库,服务器搭建,还有开始尝试Qualcomm底层开发+上层Android JAVA APK应用开发,主要负责camera相机相关和图像算法这块的部分
如有纰漏之处请大家指正
由于领导都奔着商化这个目标去,就不可避免的涉及到了算法以及效率等,所以需要使用OpenCV支持各类算法和OpenCL硬件加速算法这两块,今天就随便记录下相关的需要注意点
在上层Android上使用JAVA进行复杂算法处理大家都知道是很慢的,JAVA和C#虽然有GC机制,帮助了很多新手的,但是这样做代码上的效率就难以上去。
1。在Android studio 使用JNI(个人使用2.2.2版本的studio)
创建工程时钩选“Include C++ Support”,然后在工程目录下可以见到一个默认自动创建的CPP文件夹,里面有一个也是自动创建的“native-lib.cpp”文件,至此一次基础的带JNI调用的工程创建好了,自动创建的MainActivity里面已经自带了引用JNI的so,并且调用了hello JNI的一个方法。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201801/4f31b9e35c917efc7d02b2defd7ff235)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201801/0ea86e93e3d048c8cce300d7557f8156)
2。添加简单的JNI函数
如果仅仅只是想用C++快速处理一些数据,没有太多逻辑类关联的话,建议直接在官方默认创建的native-lib.cpp文件里面编写函数,这样不用去修改CMakeList.txt。
如果要加复杂的逻辑,生成其他so等,请跳到3
extern“C”
jstring
Java_你的包名_JAVA端调用的函数名(JNIEnv *env,jobject
/* this */){。。。}
写JNI函数时包名里面的"."用“_”代替,可以参照自动创建的官方示例,传入参数可以自己增加
由于是我主攻camera和图像方向,所以举个示例也是图像方面的3*3sobel模板计算梯度和边缘检测处理
JAVA端调用如下:
3.JNI编写生成其他类的so文件
在之前系统给我们创建的cpp目录下,再手动创建2个文件,(或者可以在AS的左侧CPP文件夹单击右键弹出New->C/C++ source file, 还有C/C++ hader file),然后按照C++的规范在.cpp和.h文件里面添加类里面的变量和函数.
关键仅添加这两个是不会生成新的.so的,如果把这两个引用到系统自动生成的那个native-lib.cpp里面去,也不会有其他so生成.
此时需要去修改在主目录下的那个CMakeList.txt文件,这个文件有点类似于linux下的.mk文件,但是更加高度集成化
我们可以在系统生成的CMakeList.txt里面添加我们自己需要其生成的so
例如我的类放在src/main/cpp/TestOutput.h和src/main/cpp/TestOutput.cpp文件里,类名如下
则往CMakeList.txt添加
往target_link_libraries里面添加libTestOutput即可
修改好后AS会提示去同步NDK的信息,点击同步即可,(不提示的话可以点build里面的rebuild都没问题)
生成好以后打开,\app\build\outputs\apk,找到项目的APK,用压缩工具打开,在lib文件夹下就可以看到armeabi-v7a下面就有libTestOutput.so了,否则只有native-lib.so.
另外可能\app\build\outputs\apk\你的APK.apk 里面的lib文件夹存在很多的子目录,这个是为了各个系统环境准备的,博主这里只是用了armeabi-v7a这个,没有让其他的生成。(通过修改app/build.gradle,可以实现仅生成1个文件夹,在gradle里面添加ndk那个中括号即可, android{...defaultConfig{...ndk{abiFilters 'armeabi-v7a'}...}...} )
这种生成的so可以直接从apk压缩包里面拿出来在linux下使用,尝试过在高通开发板的HAL层可以调用到编好的so
在linux的高通平台底层下调用方式如下
1。添加#include <dlfcn.h>
2。动态连接模式
//连接到库的句柄
Void* handle = dlopen(“soname.so", RTLD_LAZY);
if(! handle)
{
LOGE("can'tfind or link so!");
return;
}
error=(char*)dlerror();
LOGE(“link so success”);
//调用extern函数
typedefint (*Test)(int );
static Test t= (Test)dlsym(handle , “YourFunctionName");
if((error=(char*)dlerror()) != NULL)
{
LOGE("callTest () failed! %s./n", error);
return;
}
int results = t(55);
4。APK端JAVA上使用第三方SO
生成so的方式可以同上,如果要在其他工程下的APK java端 调用时so的方法,名字必须符合1里面的要求,要知道包名
把so放到app/libs/armeabi-v7a文件夹下(如果你需要多个环境的支持,可以在libs下所有子目录都添加so)
app/build.gradle里面的defaultConfig同级,android子集,添加
这样就把libs这个给包括进去了,然后再APK的MainActivity.java里面添加
接下来就按照方法直接调用即可
5。JNI调用OpenCV(博主由于需要基本不会再JAVA端使用opencv,在JNI调用opencv其实很简单)
1.下载OpenCv官网最新压缩包
https://opencv.org/releases.html
2.修改CMakeList.txt
添加
set(OpenCV_DIR D:/program/OpenCV-android-sdk/sdk/native/jni)
find_package(OpenCV REQUIRED)
target_link_libraries(….
….
${OpenCV_LIBS})
3.在你要使用opencv的cpp文件里面添加例如#include <opencv2/opencv.hpp>等类似头文件即可
6。JNI实用OpenCL
1.下载OpenCL最新头文件,需要对应OpenCL版本号的头文件
地址:https://github.com/KhronosGroup/OpenCL-Headers/
2.把对应机器里面的OpenCL库拉出来放到app/libs/
armeabi-v7a/下面去(armeabi-v7a根据gradle变)
例如高通的OpenCL在system/vendor/lib下面
3.在Android总目录下,包含OpenCL的头文件CL文件夹到src/main/cpp下,
4.在CMakeList.txt里面添加如下
INCLUDE_DIRECTORIES(src/main/cpp)
INCLUDE_DIRECTORIES(src/main/cpp/CL)
AUX_SOURCE_DIRECTORY(src/main/cpp/CL OPENCL_INCLUDE)
add_library(libOpenCLSHARED IMPORTED)
set_target_properties(libOpenCLPROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libOpenCL.so)
5. 在JNI的CPP代码里面引用OpenCL头文件
#include "CL/cl.h"
#include "CL/cl_platform.h"
6. 按照OpenCL规范编写代码(下面是一个作3*3均值滤波的opencl写法,图像输入是13M的一张浮点数据,4208*3120)
身为FAE一直处于什么都要干的情况,开发和客户端支持,一直在做(看着那可怜的薪水心疼).最近做的工作点有点多,所以来写个博记录下一些关键点.自从加入sunny后作了2年的PC端MFC,C# winform,现在开始在领导要求下转行,做数据库,服务器搭建,还有开始尝试Qualcomm底层开发+上层Android JAVA APK应用开发,主要负责camera相机相关和图像算法这块的部分
如有纰漏之处请大家指正
由于领导都奔着商化这个目标去,就不可避免的涉及到了算法以及效率等,所以需要使用OpenCV支持各类算法和OpenCL硬件加速算法这两块,今天就随便记录下相关的需要注意点
在上层Android上使用JAVA进行复杂算法处理大家都知道是很慢的,JAVA和C#虽然有GC机制,帮助了很多新手的,但是这样做代码上的效率就难以上去。
1。在Android studio 使用JNI(个人使用2.2.2版本的studio)
创建工程时钩选“Include C++ Support”,然后在工程目录下可以见到一个默认自动创建的CPP文件夹,里面有一个也是自动创建的“native-lib.cpp”文件,至此一次基础的带JNI调用的工程创建好了,自动创建的MainActivity里面已经自带了引用JNI的so,并且调用了hello JNI的一个方法。
2。添加简单的JNI函数
如果仅仅只是想用C++快速处理一些数据,没有太多逻辑类关联的话,建议直接在官方默认创建的native-lib.cpp文件里面编写函数,这样不用去修改CMakeList.txt。
如果要加复杂的逻辑,生成其他so等,请跳到3
extern“C”
jstring
Java_你的包名_JAVA端调用的函数名(JNIEnv *env,jobject
/* this */){。。。}
写JNI函数时包名里面的"."用“_”代替,可以参照自动创建的官方示例,传入参数可以自己增加
由于是我主攻camera和图像方向,所以举个示例也是图像方面的3*3sobel模板计算梯度和边缘检测处理
extern "C" void Java_com_example_cwyf_sunnycameradpc_MainPreviewActivity_sobelProcess( JNIEnv *env, jobject, jbyteArray pOriginalImage, //input,JAVA传入的数据,调用地方为camera的onFramePreview(),传入byte data[] jbyteArray pProcessedImage,//output,JAVA端创建的byte[]数组,作为JNI处理好以后的结果值,在JAVA端读取 jint width,//图像长 jint height,//图像宽 jfloat th//sobel的阈值,暂时未使用 ) { jbyte* originalImage = (jbyte*)malloc(sizeof(jbyte) * width * height);//创建输入图像Y值内存区域 memset(originalImage, 0, width * height); env->GetByteArrayRegion(pOriginalImage, 0, width * height, originalImage);//获取原始YUV,Y部分全图,(data[]里面的UV部分没考虑,所以只要提取前长*宽的Y部分即可) jbyte* processImage = (jbyte*)malloc(sizeof(jbyte) * width * height);//创建处理好的Y值内存区域 memset(processImage, 0, width * height); //3 * 3区域sobel //-1 0 +1 -1 -2 -1 //-2 0 +2 0 0 0 //-1 0 +1 +1 +2 +1 float maxVal = 0; for(int r = 1; r < height - 1;r++) { for(int c = 1; c < width - 1;c++) { float tempValueX = - originalImage[(r - 1) * width + (c - 1)] + originalImage[(r - 1) * width + (c + 1)] - 2 * originalImage[(r) * width + (c - 1)] + 2 * originalImage[(r) * width + (c + 1)] - originalImage[(r + 1) * width + (c - 1)] + originalImage[(r + 1) * width + (c + 1)]; float tempValueY = - originalImage[(r - 1) * width + (c - 1)] - 2 * originalImage[(r - 1) * width + (c)] - originalImage[(r - 1) * width + (c + 1)] + originalImage[(r + 1) * width + (c - 1)] + 2 * originalImage[(r + 1) * width + (c)] + originalImage[(r + 1) 12a20 * width + (c + 1)]; float finalResult = sqrt(tempValueX * tempValueX + tempValueY * tempValueY); if(finalResult > maxVal) maxVal = finalResult; processImage[(r) * width + (c)] = ((int)finalResult & 0xff); } }
env->SetByteArrayRegion(pProcessedImage, 0, width * height, processImage);//把处理好的Y图的内存区域拷贝到JAVA端创建的内存中去 free(originalImage);//释放内存 free(processImage); }
JAVA端调用如下:
public native void sobelProcess(byte[] originalImage, byte[] resultImage, int width, int height, float th);
mCamera_main.setPreviewCallback(new Camera.PreviewCallback() { @Override public void onPreviewFrame(byte[] data, Camera camera) { sobelProcess(data, mPreview_main.currentPreviewImage, mPreview_main.mPreviewSize.width, mPreview_main.mPreviewSize.height, 0.7); } });
3.JNI编写生成其他类的so文件
在之前系统给我们创建的cpp目录下,再手动创建2个文件,(或者可以在AS的左侧CPP文件夹单击右键弹出New->C/C++ source file, 还有C/C++ hader file),然后按照C++的规范在.cpp和.h文件里面添加类里面的变量和函数.
关键仅添加这两个是不会生成新的.so的,如果把这两个引用到系统自动生成的那个native-lib.cpp里面去,也不会有其他so生成.
此时需要去修改在主目录下的那个CMakeList.txt文件,这个文件有点类似于linux下的.mk文件,但是更加高度集成化
我们可以在系统生成的CMakeList.txt里面添加我们自己需要其生成的so
例如我的类放在src/main/cpp/TestOutput.h和src/main/cpp/TestOutput.cpp文件里,类名如下
class TestOutput { ... }
则往CMakeList.txt添加
add_library( libTestOutput SHARED src/main/cpp/TestValue.cpp src/main/cpp/TestOutput.cpp)
往target_link_libraries里面添加libTestOutput即可
target_link_libraries( # Specifies the target library. native-lib libTestOutput # Links the target library to the log library # included in the NDK. ${log-lib} )
修改好后AS会提示去同步NDK的信息,点击同步即可,(不提示的话可以点build里面的rebuild都没问题)
生成好以后打开,\app\build\outputs\apk,找到项目的APK,用压缩工具打开,在lib文件夹下就可以看到armeabi-v7a下面就有libTestOutput.so了,否则只有native-lib.so.
另外可能\app\build\outputs\apk\你的APK.apk 里面的lib文件夹存在很多的子目录,这个是为了各个系统环境准备的,博主这里只是用了armeabi-v7a这个,没有让其他的生成。(通过修改app/build.gradle,可以实现仅生成1个文件夹,在gradle里面添加ndk那个中括号即可, android{...defaultConfig{...ndk{abiFilters 'armeabi-v7a'}...}...} )
这种生成的so可以直接从apk压缩包里面拿出来在linux下使用,尝试过在高通开发板的HAL层可以调用到编好的so
在linux的高通平台底层下调用方式如下
1。添加#include <dlfcn.h>
2。动态连接模式
//连接到库的句柄
Void* handle = dlopen(“soname.so", RTLD_LAZY);
if(! handle)
{
LOGE("can'tfind or link so!");
return;
}
error=(char*)dlerror();
LOGE(“link so success”);
//调用extern函数
typedefint (*Test)(int );
static Test t= (Test)dlsym(handle , “YourFunctionName");
if((error=(char*)dlerror()) != NULL)
{
LOGE("callTest () failed! %s./n", error);
return;
}
int results = t(55);
4。APK端JAVA上使用第三方SO
生成so的方式可以同上,如果要在其他工程下的APK java端 调用时so的方法,名字必须符合1里面的要求,要知道包名
把so放到app/libs/armeabi-v7a文件夹下(如果你需要多个环境的支持,可以在libs下所有子目录都添加so)
app/build.gradle里面的defaultConfig同级,android子集,添加
sourceSets.main.jniLibs.srcDirs = ['libs']
这样就把libs这个给包括进去了,然后再APK的MainActivity.java里面添加
static { System.loadLibrary("native-lib"); System.loadLibrary("libTestOutput"); }
接下来就按照方法直接调用即可
5。JNI调用OpenCV(博主由于需要基本不会再JAVA端使用opencv,在JNI调用opencv其实很简单)
1.下载OpenCv官网最新压缩包
https://opencv.org/releases.html
2.修改CMakeList.txt
添加
set(OpenCV_DIR D:/program/OpenCV-android-sdk/sdk/native/jni)
find_package(OpenCV REQUIRED)
target_link_libraries(….
….
${OpenCV_LIBS})
3.在你要使用opencv的cpp文件里面添加例如#include <opencv2/opencv.hpp>等类似头文件即可
6。JNI实用OpenCL
1.下载OpenCL最新头文件,需要对应OpenCL版本号的头文件
地址:https://github.com/KhronosGroup/OpenCL-Headers/
2.把对应机器里面的OpenCL库拉出来放到app/libs/
armeabi-v7a/下面去(armeabi-v7a根据gradle变)
例如高通的OpenCL在system/vendor/lib下面
3.在Android总目录下,包含OpenCL的头文件CL文件夹到src/main/cpp下,
4.在CMakeList.txt里面添加如下
INCLUDE_DIRECTORIES(src/main/cpp)
INCLUDE_DIRECTORIES(src/main/cpp/CL)
AUX_SOURCE_DIRECTORY(src/main/cpp/CL OPENCL_INCLUDE)
add_library(libOpenCLSHARED IMPORTED)
set_target_properties(libOpenCLPROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libOpenCL.so)
5. 在JNI的CPP代码里面引用OpenCL头文件
#include "CL/cl.h"
#include "CL/cl_platform.h"
6. 按照OpenCL规范编写代码(下面是一个作3*3均值滤波的opencl写法,图像输入是13M的一张浮点数据,4208*3120)
const char* src2[] = //每块kernal执行代码 { "__kernel void blurtest( \n" "__global float *input, \n" "__global float *out, \n" "__global int *width, \n" "__global int *height \n" ") \n" "{ \n" " int idx = get_global_id(0); \n" " if(idx >= 0 && idx <width[0]) \n" " { \n" " out[idx] = 0; \n" " } \n" " else if(idx % width[0] == 0) \n" " { \n" " out[idx] = 0; \n" " } \n" " else if(idx % width[0] == width[0] - 1) \n" " { \n" " out[idx] = 0; \n" " } \n" " else if(idx >= width[0] * (height[0] - 1)) \n" " { \n" " out[idx] = 0; \n" " } \n" " else \n" " { \n" " int count2 = idx; \n" " int up = count2 - width[0]; \n" " int down = count2 + width[0]; \n" " int left = count2 - 1; \n" " int right = count2 + 1; \n" " int upleft = count2 - width[0] - 1; \n" " int upright = count2 - width[0] + 1; \n" " int downleft = count2 + width[0] - 1; \n" " int downright = count2 + width[0] + 1; \n" " out[idx] = (input[count2] + \n" " input[up] + \n" " input[down] + \n" " input[left] + \n" " input[right] + \n" " input[upleft] + \n" " input[upright] + \n" " input[downleft] + \n" " input[downright])/9; \n" //" out[idx] = 0; \n" " } \n" "} \n" }; int ImageWidth = 4208; int ImageHeight = 3120; cl_mem ImageBufferInput,ImageBufferOutPut,ImageBufferWidth,ImageBufferHeight; cl_int ImageTestError = 0; // Used to handle error codes cl_platform_id* ImageTestPlatform; cl_context ImageTestContext; cl_command_queue ImageTestQueue; cl_device_id* ImageTestDevice; cl_program ImageTestProgram; cl_kernel ImageTestKernal;
float test2() { float *InputImage = (float*)malloc(sizeof(float) * ImageWidth * ImageWidth);//待处理图像buff float *OutputImage = (float*)malloc(sizeof(float) * ImageWidth * ImageWidth);//处理完毕图像buff for(int i=0;i<ImageWidth * ImageHeight;i++)//默认给初始值都是1的灰图 { InputImage[i]=1; } //初始化opencl硬件 ImageTestError = clGetPlatformIDs(0, 0, &num_platform); ImageTestPlatform=(cl_platform_id*)malloc(sizeof(cl_platform_id)*num_platform); ImageTestError = clGetPlatformIDs(num_platform, ImageTestPlatform, NULL); ImageTestError=clGetDeviceIDs(ImageTestPlatform[0],CL_DEVICE_TYPE_GPU,0,NULL,&num_device); ImageTestDevice =(cl_device_id*)malloc(sizeof(cl_device_id)*num_device); ImageTestError=clGetDeviceIDs(ImageTestPlatform[0],CL_DEVICE_TYPE_GPU,num_device,ImageTestDevice,NULL); //创建机器上下文和命令队列 ImageTestContext=clCreateContext(NULL,num_device,ImageTestDevice,NULL,NULL,&ImageTestError); ImageTestQueue=clCreateCommandQueue(ImageTestContext,ImageTestDevice[0],0,&ImageTestError); //创见及输入内存数据 ImageBufferInput=clCreateBuffer(ImageTestContext,CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR,sizeof(float)*ImageWidth*ImageHeight,InputImage,&ImageTestError);//读取进来的内存数据 size_t contentLength = sizeof(float) * ImageWidth * ImageHeight; ImageBufferOutPut=clCreateBuffer(ImageTestContext,CL_MEM_WRITE_ONLY,sizeof(float)*ImageWidth*ImageHeight,0,&ImageTestError);//处理结果数据 int WidthBuf[1], HeightBuf[1]; WidthBuf[0]=ImageWidth; HeightBuf[0]=ImageHeight; ImageBufferWidth=clCreateBuffer(ImageTestContext,CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR,sizeof(int)*1,WidthBuf,&ImageTestError); ImageBufferHeight=clCreateBuffer(ImageTestContext,CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR,sizeof(int)*1,HeightBuf,&ImageTestError); //根据src2创建编译内核执行指令 ImageTestProgram=clCreateProgramWithSource(ImageTestContext, LEN(src2), src2, NULL, &ImageTestError); ImageTestError=clBuildProgram(ImageTestProgram,num_device,ImageTestDevice,NULL,NULL,NULL); ImageTestKernal = clCreateKernel(ImageTestProgram, "blurtest", &ImageTestError); //传入参数 ImageTestError=clSetKernelArg(ImageTestKernal,0,sizeof(cl_mem),&ImageBufferInput);//设置传入参数 ImageTestError=clSetKernelArg(ImageTestKernal,1,sizeof(cl_mem),&ImageBufferOutPut); ImageTestError=clSetKernelArg(ImageTestKernal,2,sizeof(cl_mem),&ImageBufferWidth); ImageTestError=clSetKernelArg(ImageTestKernal,3,sizeof(cl_mem),&ImageBufferHeight); const size_t globalWorkSize[1]={ImageWidth * ImageHeight}; const size_t localWorkSize[1]={ImageWidth * ImageHeight}; ImageTestError=clEnqueueNDRangeKernel(ImageTestQueue,ImageTestKernal,1,NULL,globalWorkSize,NULL,0,NULL,NULL); clFinish(ImageTestQueue); //执行 ImageTestError=clEnqueueReadBuffer(ImageTestQueue,ImageBufferOutPut,CL_TRUE,0,sizeof(float)*ImageWidth*ImageHeight,OutputImage,0,NULL,NULL); //回收变量和内存 if(ImageTestPlatform != NULL) free(ImageTestPlatform); if(ImageTestDevice != NULL) free(ImageTestDevice); clReleaseContext(ImageTestContext); clReleaseCommandQueue(ImageTestQueue); clReleaseMemObject(ImageBufferInput); clReleaseMemObject(ImageBufferOutPut); clReleaseProgram(ImageTestProgram); clReleaseKernel(ImageTestKernal); float results = 0.0; for(int i = 0; i < ImageWidth * ImageHeight;i++) { results += OutputImage[i]; } free(InputImage); free(OutputImage); return results;//返回图像数据累加,当然也可以把处理好的图片输出,一样的道理 }
相关文章推荐
- 【转】Android开发:安装NDK,移植OpenCV2.3.1,JNI调用OpenCV全过程
- [转]Android通过NDK调用JNI,使用opencv做本地c++代码开发配置方法
- Android(安卓)开发通过NDK调用JNI,使用opencv做本地c++代码开发配置方法实现边缘检测代码(2)
- Android开发:安装NDK,移植OpenCV2.3.1,JNI调用OpenCV全过程
- AndroidStudio2.2.3 JNI与NDK开发之一:生成可调用.so库
- Android Studio + NDK JNI调用openCV图像处理
- Android Studio NDK开发 正确调用jni 及加入第三方so库需要注意的问题
- Android(安卓)开发通过NDK调用JNI,使用opencv做本地c++代码开发配置方法 边缘检测 范例代码
- Android开发:安装NDK,移植OpenCV2.3.1,JNI调用OpenCV全过程
- Android(安卓)开发通过NDK调用JNI,使用opencv做本地c++代码开发配置方法 边缘检测 范例代码
- Android开发:安装NDK,移植OpenCV2.3.1,JNI调用OpenCV全过程
- Android Studio NDK开发 正确调用jni 及加入第三方so库需要注意的问题 .
- Android的NDK开发(1)————Android JNI简介与调用流程
- [原创]Android 基于NDK的JNI开发 C调用java和java调用C的进阶教程
- Android的NDK开发(1)————Android JNI简介与调用流程
- Android开发之JNI调用本地C库专题(二):Android增量更新
- Android的NDK开发(1)————Android JNI简介与调用流程
- Android-NDK开发之基础--Android JNI实例代码(一)-- 在JNI中执行Java方法--C/C++调用Java
- Android的NDK开发(1)————Android JNI简介与调用流程
- Android的NDK开发(1)————Android JNI简介与调用流程