1. 程式人生 > >使用AndroidStudio打包OpenCV和C++程式碼並在安卓上執行

使用AndroidStudio打包OpenCV和C++程式碼並在安卓上執行

使用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