公司没招到安卓程序员,让我和老大两个搞过安卓的来做研发,新买的两个开发板之间一人一个,要把正在运行的C#项目改成安卓项目,我C#技能树没点啊!完全没头绪,但是现在就我是新来的,就我闲着,老大还干着维护的活,so,开搞吧。
查官网是不可能的,这辈子都不可能,英文又不好,算法也不会,只能抄抄博客勉强维持生活的样子:
https://blog.csdn.net/weixin_43215867/article/details/88543922
没错,就这一篇,因为要求用android studio开发,而且我有升级强迫症,其他找的大多是eclipse或者Android.mk,android studio 3以后推荐用CMakeList.txt了,这东西我也花了一天时间摸索大概搞懂。
这里不得不再次吐槽,我按上面的博客装好opencv后,问算法的大佬,结果大佬说他用的是旧版本3.3.0的,得重新下载又花了一个小时,重新配,还好配好了之后只要替换就行了。
Android studio 版本:3.4
OpenCV 版本:3.3.0
public class OpenCVUtil {
static {
System.loadLibrary("opencv_java3");
System.loadLibrary("opencv-lib");
}
public native void Canny(Object bitmap);
public native int YUV2RGB(byte[] data,int width,int height,String path);
}
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
set(OpenCV_DIR F:/opencv/opencv-3.4.5-android-sdk/OpenCV-android-sdk/sdk/native/jni) #OpenCV jni根目录
find_package(OpenCV REQUIRED)
include_directories(${CMAKE_SOURCE_DIR}/src/main/jni) #opencv的include目录
add_library(libopencv_java3 SHARED IMPORTED)
set_target_properties(libopencv_java3 PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libopencv_java3.so) #OpenCV 在libs下的so包
set(CMAKE_VERBOSE_MAKEFILE on)
set(libs ${PROJECT_SOURCE_DIR}/libs)
include_directories(${PROJECT_SOURCE_DIR}/src/main/jni)
#add_library(libopencv_java3 SHARED IMPORTED )
#set_target_properties(libopencv_java3 PROPERTIES
# IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_java3.so)
#add_library(libcpufeatures STATIC IMPORTED )
#set_target_properties(libcpufeatures PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libcpufeatures.a)
#
#add_library(libIlmImf STATIC IMPORTED )
#set_target_properties(libIlmImf PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libIlmImf.a)
#
#add_library(liblibjasper STATIC IMPORTED )
#set_target_properties(liblibjasper PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/liblibjasper.a)
#
#add_library(liblibjpeg STATIC IMPORTED )
#set_target_properties(liblibjpeg PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/liblibjpeg.a)
#
#add_library(liblibpng STATIC IMPORTED )
#set_target_properties(liblibpng PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/liblibpng.a)
#
#add_library(liblibprotobuf STATIC IMPORTED )
#set_target_properties(liblibprotobuf PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/liblibprotobuf.a)
#
#add_library(liblibtiff STATIC IMPORTED )
#set_target_properties(liblibtiff PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/liblibtiff.a)
#
#add_library(liblibwebp STATIC IMPORTED )
#set_target_properties(liblibwebp PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/liblibwebp.a)
#
#add_library(libopencv_calib3d STATIC IMPORTED )
#set_target_properties(libopencv_calib3d PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libopencv_calib3d.a)
#
#add_library(libopencv_core STATIC IMPORTED )
#set_target_properties(libopencv_core PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libopencv_core.a)
#
#add_library(libopencv_dnn STATIC IMPORTED )
#set_target_properties(libopencv_dnn PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libopencv_dnn.a)
#
#add_library(libopencv_features2d STATIC IMPORTED )
#set_target_properties(libopencv_features2d PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libopencv_features2d.a)
#
#add_library(libopencv_flann STATIC IMPORTED )
#set_target_properties(libopencv_flann PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libopencv_flann.a)
#
#add_library(libopencv_highgui STATIC IMPORTED )
#set_target_properties(libopencv_highgui PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libopencv_highgui.a)
#
#add_library(libopencv_imgcodecs STATIC IMPORTED )
#set_target_properties(libopencv_imgcodecs PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libopencv_imgcodecs.a)
#
#add_library(libopencv_imgproc STATIC IMPORTED )
#set_target_properties(libopencv_imgproc PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libopencv_imgproc.a)
#
#add_library(libopencv_ml STATIC IMPORTED )
#set_target_properties(libopencv_ml PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libopencv_ml.a)
#
#add_library(libopencv_objdetect STATIC IMPORTED )
#set_target_properties(libopencv_objdetect PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libopencv_objdetect.a)
#
#add_library(libopencv_photo STATIC IMPORTED )
#set_target_properties(libopencv_photo PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libopencv_photo.a)
#
#add_library(libopencv_shape STATIC IMPORTED )
#set_target_properties(libopencv_shape PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libopencv_shape.a)
#
#add_library(libopencv_stitching STATIC IMPORTED )
#set_target_properties(libopencv_stitching PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libopencv_stitching.a)
#
#add_library(libopencv_superres STATIC IMPORTED )
#set_target_properties(libopencv_superres PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libopencv_superres.a)
#
#add_library(libopencv_video STATIC IMPORTED )
#set_target_properties(libopencv_video PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libopencv_video.a)
#
#add_library(libopencv_videoio STATIC IMPORTED )
#set_target_properties(libopencv_videoio PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libopencv_videoio.a)
#
#add_library(libopencv_videostab STATIC IMPORTED )
#set_target_properties(libopencv_videostab PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libopencv_videostab.a)
#
#add_library(libtbb STATIC IMPORTED )
#set_target_properties(libtbb PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libtbb.a)
#
#add_library(libtegra_hal STATIC IMPORTED )
#set_target_properties(libtegra_hal PROPERTIES
# IMPORTED_LOCATION ${libs}/${ANDROID_ABI}/libtegra_hal.a)
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -fexceptions -frtti")
#支持-std=gnu++11
set(CMAKE_VERBOSE_MAKEFILE on)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -Wall -DGLM_FORCE_SIZE_T_LENGTH")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGLM_FORCE_RADIANS")
#include_directories(D:/Projects/Android/CLMAndroid/OpenCV-android-sdk/sdk/native/jni/include )
#set(OpenCV_DIR D:/Projects/Android/CLMAndroid/OpenCV-android-sdk/sdk/native/jni)
#find_package(OpenCV REQUIRED)
#target_link_libraries(${OpenCV_LIBS})
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
opencv-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/jni/com_my_opencv_OpenCVUtil.cpp)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
opencv-lib android log GLESv2 EGL dl
jnigraphics
# libcpufeatures
# libIlmImf
# liblibjasper
# liblibjpeg
# liblibpng
# liblibprotobuf
# liblibtiff
# liblibwebp
libopencv_java3
# libopencv_calib3d
# libopencv_core
# libopencv_dnn
# libopencv_features2d
# libopencv_flann
# libopencv_highgui
# libopencv_imgcodecs
# libopencv_imgproc
# libopencv_ml
# libopencv_objdetect
# libopencv_photo
# libopencv_shape
# libopencv_stitching
# libopencv_superres
# libopencv_video
# libopencv_videoio
# libopencv_videostab
# libtbb
# libtegra_hal
${OpenCV_LIBS}
# Links the target library to the log library
# included in the NDK.
${log-lib})
看见那么多注释没?全是踩的坑,以为opencv啥组件没加,然后一个个试的,结果根本不是这个原因导致编译不过,是NDK版本太新的问题。
com_my_opencv_OpenCVUtil.cpp
com_my_opencv是包名,点换成了下划线,OpenCVUtil是java类名,具体内容直接是上面博客里的,我自己加了个YUV2RGB的方法:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_my_opencv_OpenCVUtil */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace cv;
using namespace std;
void BitmapToMat2(JNIEnv *env, jobject& bitmap, Mat& mat, jboolean needUnPremultiplyAlpha) {
AndroidBitmapInfo info;
void *pixels = 0;
Mat &dst = mat;
try {
CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
CV_Assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ||
info.format == ANDROID_BITMAP_FORMAT_RGB_565);
CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
CV_Assert(pixels);
dst.create(info.height, info.width, CV_8UC4);
if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
Mat tmp(info.height, info.width, CV_8UC4, pixels);
if (needUnPremultiplyAlpha) cvtColor(tmp, dst, COLOR_mRGBA2RGBA);
else tmp.copyTo(dst);
} else {
// info.format == ANDROID_BITMAP_FORMAT_RGB_565
Mat tmp(info.height, info.width, CV_8UC2, pixels);
cvtColor(tmp, dst, COLOR_BGR5652RGBA);
}
AndroidBitmap_unlockPixels(env, bitmap);
return;
} catch (const cv::Exception &e) {
AndroidBitmap_unlockPixels(env, bitmap);
// jclass je = env->FindClass("org/opencv/core/CvException");
// if (!je) je = env->FindClass("java/lang/Exception");
jclass je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, e.what());
return;
} catch (...) {
AndroidBitmap_unlockPixels(env, bitmap);
jclass je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, "Unknown exception in JNI code {nBitmapToMat}");
return;
}
}
void BitmapToMat(JNIEnv *env, jobject& bitmap, Mat& mat) {
BitmapToMat2(env, bitmap, mat, false);
}
void MatToBitmap2
(JNIEnv *env, Mat& mat, jobject& bitmap, jboolean needPremultiplyAlpha) {
AndroidBitmapInfo info;
void *pixels = 0;
Mat &src = mat;
try {
CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
CV_Assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ||
info.format == ANDROID_BITMAP_FORMAT_RGB_565);
CV_Assert(src.dims == 2 && info.height == (uint32_t) src.rows &&
info.width == (uint32_t) src.cols);
CV_Assert(src.type() == CV_8UC1 || src.type() == CV_8UC3 || src.type() == CV_8UC4);
CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
CV_Assert(pixels);
if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
Mat tmp(info.height, info.width, CV_8UC4, pixels);
if (src.type() == CV_8UC1) {
cvtColor(src, tmp, COLOR_GRAY2RGBA);
} else if (src.type() == CV_8UC3) {
cvtColor(src, tmp, COLOR_RGB2RGBA);
} else if (src.type() == CV_8UC4) {
if (needPremultiplyAlpha)
cvtColor(src, tmp, COLOR_RGBA2mRGBA);
else
src.copyTo(tmp);
}
} else {
// info.format == ANDROID_BITMAP_FORMAT_RGB_565
Mat tmp(info.height, info.width, CV_8UC2, pixels);
if (src.type() == CV_8UC1) {
cvtColor(src, tmp, COLOR_GRAY2BGR565);
} else if (src.type() == CV_8UC3) {
cvtColor(src, tmp, COLOR_RGB2BGR565);
} else if (src.type() == CV_8UC4) {
cvtColor(src, tmp, COLOR_RGBA2BGR565);
}
}
AndroidBitmap_unlockPixels(env, bitmap);
return;
} catch (const cv::Exception &e) {
AndroidBitmap_unlockPixels(env, bitmap);
// jclass je = env->FindClass("org/opencv/core/CvException");
// if (!je) je = env->FindClass("java/lang/Exception");
jclass je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, e.what());
return;
} catch (...) {
AndroidBitmap_unlockPixels(env, bitmap);
jclass je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");
return;
}
}
void MatToBitmap(JNIEnv *env, Mat& mat, jobject& bitmap) {
MatToBitmap2(env, mat, bitmap, false);
}
extern "C"
JNIEXPORT void JNICALL Java_com_my_opencv_OpenCVUtil_Canny
(JNIEnv *env, jobject instance,jobject bitmap){
// TODO
Mat mat_1;
//bitmap转化成mat
BitmapToMat(env, bitmap, mat_1);
//将原始图转化为灰度图
cvtColor(mat_1, mat_1, COLOR_BGR2GRAY);
//先使用3*3内核来降噪
blur(mat_1, mat_1, Size(3, 3));
//运行canny算子
Canny(mat_1, mat_1, 20, 50, 3);
MatToBitmap(env, mat_1, bitmap);
}
std::string jstring2str(JNIEnv* env, jstring jstr)
{
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("GB2312");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr,mid,strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr,JNI_FALSE);
if(alen > 0)
{
rtn = (char*)malloc(alen+1);
memcpy(rtn,ba,alen);
rtn[alen]=0;
}
env->ReleaseByteArrayElements(barr,ba,0);
env->DeleteLocalRef(clsstring);
env->DeleteLocalRef(strencode);
std::string stemp(rtn);
free(rtn);
return stemp;
}
extern "C"
JNIEXPORT jint JNICALL Java_com_my_opencv_OpenCVUtil_YUV2RGB
(JNIEnv *env, jobject instance,jbyteArray yuv,jint width,jint height,jstring path){
jbyte * pBuf = (jbyte*)env->GetByteArrayElements(yuv, 0);
Mat image(height + height/2,width,CV_8UC1,(unsigned char *)pBuf);
Mat mBgr;
cvtColor(image, mBgr, CV_YUV2BGR_I420);
bool result = imwrite(jstring2str(env,path),mBgr);
env->ReleaseByteArrayElements(yuv, pBuf, 0);
env->DeleteLocalRef(instance);
env->DeleteLocalRef(path);
if (result){
return 0;
}
return -1;
}
在defaultConfig节点下加入:
// 使用Cmake工具
externalNativeBuild {
cmake {
cppFlags "-std=c++11","-frtti", "-fexceptions"
abiFilters "armeabi"
}
}
在android节点下加入:
// 配置CMakeLists.txt路径
externalNativeBuild {
cmake {
path "CMakeLists.txt" // 设置所要编写的c源码位置,以及编译后so文件的名字
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
完整代码如下:
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.1"
defaultConfig {
applicationId "com.my.mycvapplication"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// 使用Cmake工具
externalNativeBuild {
cmake {
cppFlags "-std=c++11","-frtti", "-fexceptions"
abiFilters "armeabi"
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
// 配置CMakeLists.txt路径
externalNativeBuild {
cmake {
path "CMakeLists.txt" // 设置所要编写的c源码位置,以及编译后so文件的名字
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
新建的模块里build.gradle里的android目录下添加:
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
引入上面的java接口类,注意包名不能改:
在activity.xml里拖一个imgview,和button,在MainActivity里调用jni:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.testhand);
new OpenCVUtil().Canny(bitmap);
iv.setImageBitmap(bitmap);
启动,真机调试,都没问题。
调试也过了,没啥问题,把代码打包给算法大佬,大佬在cpp里加了几个方法,识别人体部位健康的一些代码,结果编译过不了,报
error: undefined reference to 'cv::HOGDescriptor::compute(cv::_InputArray const&, std::__ndk1::vector >&, cv::Size_, cv::Size_, std::__ndk1::vector, std::__ndk1::allocator > > const&) const'
这简直莫名其妙,卡了我三天,上面CMakeList.txt那么多注释就是找这个问题试验的,开始以为是opencv哪个模块没加进去的,一个个试了之后不行,然后又去stackoverflow里查,找到相似问题了但是没答案,还是死马当活马医的看一个博客说降低NDK版本,去下了个14b的版本,重新编译,过了。
写个博客以作记录。