使用AndroidStudio打包OpenCV和C++代码并在安卓上运行
阅读原文时间:2021年04月20日阅读:1

使用AndroidStudio打包OpenCV和C++代码并在安卓上运行

在为服务器部署OpenCV和C++的过程中尝试了很多方法,这里记录一下在AndroidStudio上打包OpenCV和C++的过程。

1.准备开发环境

这里我直接在mac上开发,没有在虚拟机中。
安装AndroidStudio,jdk等,参考:https://blog.csdn.net/wu__di/article/details/78556724
opencv官网下载Android sdk:https://opencv.org/releases.html
cmake安装:https://blog.csdn.net/baimafujinji/article/details/78588488

2.创建工程

创建一个空项目,注意勾选【Include C++ support】,参考:http://www.sohu.com/a/250633784_663371

3.准备文件

现在项目中有一些基础的文件。

其中,jniLibs文件名是固定的,不能自定义,这个是AndroidStudio的默认搜索路径。
将OpenCV-android-sdk/sdk/native/libs下的库文件拷贝到jniLibs目录下,即图上的7个文件夹。AndroidStudio导入其他so文件同理,将对应架构的so文件拷贝到图上文件夹对应的目录中,AS会自动加载这些文件,并在安装时拷贝到APP的库目录下。

native-lib.cpp代码:

#include <jni.h>
#include <string>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
extern "C" {
    // 注意这里的函数名格式:Java_各级包名_类名_函数名(参数...),需严格按照这种格式,否则会出错
    JNIEXPORT jintArray JNICALL Java_com_example2_apple_myapplication_MainActivity_gray(
            JNIEnv *env,
            jobject instance,
            jintArray buf,
            jint w,
            jint h) {
        jint *cbuf = env->GetIntArrayElements(buf, JNI_FALSE );
        if (cbuf == NULL) { return 0; }
        Mat imgData(h, w, CV_8UC4, (unsigned char *) cbuf);
        uchar* ptr = imgData.ptr(0);
        for(int i = 0; i < w*h; i ++){
            //计算公式:Y(亮度) = 0.299*R + 0.587*G + 0.114*B
            // 对于一个int四字节,其彩色值存储方式为:BGRA
            int grayScale = (int)(ptr[4*i+2]*0.299 + ptr[4*i+1]*0.587 + ptr[4*i+0]*0.114);
            ptr[4*i+1] = grayScale; ptr[4*i+2] = grayScale;
            ptr[4*i+0] = grayScale;
        }
        int size = w * h;
        jintArray result = env->NewIntArray(size);
        env->SetIntArrayRegion(result, 0, size, cbuf);
        env->ReleaseIntArrayElements(buf, cbuf, 0);
        return result;
    }

    JNIEXPORT jstring JNICALL Java_com_example2_apple_myapplication_MainActivity_stringFromJNI(
            JNIEnv* env,
            jobject /* this */) {
        std::string hello = "Hello from C++";
        return env->NewStringUTF(hello.c_str());
    }
}

MainActivity代码:

package com.example2.apple.myapplication;

import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;


public class MainActivity extends AppCompatActivity {
    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib"); 
        System.out.println(System.getProperty("java.library.path"));
    }

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Bitmap bitmap = ((BitmapDrawable) getResources().getDrawable( R.drawable.pic)).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=gray(pix,w,h);
        Bitmap result = Bitmap.createBitmap(w,h, Bitmap.Config.RGB_565);
        result.setPixels(resultPixes, 0, w, 0, 0,w, h);
        ImageView img = (ImageView)findViewById(R.id.img2);
        img.setImageBitmap(result);
        System.out.println(stringFromJNI());
    }
    /** * A native method that is implemented by the 'native-lib' native library, * which is packaged with this application. */
    public native int[] gray(int[] buf, int w, int h);
    public native String stringFromJNI();
}

res/layout/activity_main.xml代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="16dp"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="16dp"
    android:orientation="vertical"
    tools:context="com.example2.apple.myapplication.MainActivity">

    <TextView android:id="@+id/txt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="原图:" />

    <ImageView android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/pic"/>
        <!--在res/drawable目录下随便放一张图片pic.png并在此加载,图片尺寸最好小些-->

    <TextView android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="处理后的图:" />

    <ImageView android:id="@+id/img2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

build.gradle代码:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26 //根据自己的版本设置
    defaultConfig {
        applicationId "com.example2.apple.myapplication"
        minSdkVersion 15 //根据自己的版本设置
        targetSdkVersion 26 //根据自己的版本设置
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11 -frtti -fexceptions" // 根据情况设置
                abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' // 根据情况设置
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }

// 这段代码功能是将工程打包为jar供第三方调用,还需一些其他改动,参考:
// https://blog.csdn.net/qq_24056881/article/details/81017450
// https://blog.csdn.net/xiayiye5/article/details/79639044
//    task makeJar(type: Copy) {
//        //删除存在的
//        delete 'build/libs/myjar.jar'
//        //设置拷贝的文件
//        from('build/intermediates/packaged-classes/debug/')
//        //打进jar包后的文件目录
//        into('build/libs/')
//        //将classes.jar放入build/libs/目录下
//        //include ,exclude参数来设置过滤
//        //(我们只关心classes.jar这个文件)
//        include('classes.jar')
//        //重命名
//        rename('classes.jar', 'myjar.jar')
//    }
//    makeJar.dependsOn(build)
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
}

CMakeLists代码:

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html 

#设置OpenCv sdk的路径变量
set(pathToOpenCv /Users/apple/Downloads/OpenCV-android-sdk/)

#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")

#配置加载native依赖
include_directories(${pathToOpenCv}/sdk/native/jni/include)

#动态方式加载
add_library(lib_opencv STATIC IMPORTED )

#引入libopencv_java3.so文件
set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_java3.so )

# 自己的源文件
add_library( 
       # Sets the name of the library.
        native-lib 

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        src/main/cpp/native-lib.cpp
        )

#查找库
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
)

#链接库
target_link_libraries(
        # Specifies the target library.
        native-lib

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib} android -ljnigraphics lib_opencv
)

通过这些设置opencv就可以在Android中运行了,编译运行一下:
我这里在模拟器中运行:

参考资料:
http://www.sohu.com/a/250633784_663371
https://www.cnblogs.com/xiaoxiaoqingyi/p/6676096.html
https://sriraghu.com/2017/03/11/opencv-in-android-an-introduction-part-1/
AndroidStudio项目CMakeLists解析:https://www.cnblogs.com/chenxibobo/p/7678389.html
https://blog.csdn.net/bailsong/article/details/77527773