安卓相機實時幀處理 Day01 《OpenCV環境配置》

前言

  • 主題:
    由於一個項目需要用到相機實時預覽幀處理,而網上的資料很多均不全,所以決定自己寫一個教程;

  • 實現方案:

  1. 方案一: 採用Android Camera SurfaceView + JNI 實現;
  2. 方案二: 採用OpenCV Camera框架 + JNI實現
  3. 方案三:採用Android Camera GLSurfaceView + OpenGL實現
  • 工具:
    Android Studio 3.3.2 、NDK R16

OpenCV 環境搭建

構建項目

  • 注意:
    由於需要使用JNI來實現幀處理,所以創建工程時選擇支持Native C++的工程
    創建step1
    填寫自己的項目名和包名,API等級可以默認可以自己選,但是應當注意,API >= 23時需要請求動態運行權限;
    Step2
    選擇需要採用的C++標準,根據習慣選擇即可
    在這裏插入圖片描述此時會得到一個默認的ManActivity.java文件,運行次文件可以驗證支持JNI的項目工程是否搭建成功在這裏插入圖片描述
    運行測試一下,獲得下面界面表示成功:
    在這裏插入圖片描述

構建OpenCV環境

第0步,改變JNI函數的調用

由於幀處理需要大量的C++函數,所以個人建議將C++的Native函數放在一個Java類文件裏聲明,

Step 1:新建一個class,命名爲NDKInterface.java
在這裏插入圖片描述
Step 2:修改native-lib.cpp文件

#include <jni.h>
#include <string>

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

/************* update ************/
#include <jni.h>
#include <string>

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

由兩個差別可以看出,修改的各部分代表含義
Java_<Package_Name><Ndk_Interface_Class_Name><Native_Function_Name>
Step 3: 修改NDKInterface.java

public class NDKInterface {
	//load the native library
    static {
        System.loadLibrary("native-lib");
    }
    // claim the native c++ function
    public native String stringFromJNI();
}

Step 4 : 修改MainActivity調用這個Native函數

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        TextView tv = findViewById(R.id.sample_text);
        tv.setText(new NDKInterface().stringFromJNI());
    }
}

Step 5 : 測試出現上文相同手機界面即可

第一步,下載OpenCV for Android

Opencv for Android下載頁面
我選擇的時3.4.1版本

第1步,導入OPenCV Module

File --> New --> Import Module
在這裏插入圖片描述
在文件夾中找到你所需要的OpenCV Java Module,一般相對路徑OpenCV-android-sdk/sdk/java
在這裏插入圖片描述
接下來默認Next --> Finish 即可。

第2步,修改OPenCV的配置

現在sync是無法通過的需要修改兩個地方:
第一:openCVLibrary/build.gradle
在這裏插入圖片描述
修改成和當前工程相同的
compileSdkVersion
minSdkVersion
targetSdkVersion

apply plugin: 'com.android.library'

android {
    compileSdkVersion 28
    buildToolsVersion "28.0.3"

    defaultConfig {
        minSdkVersion 17
        targetSdkVersion 28
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }
}

第二:openCV/AndroidManifest.xml
刪除倒數第二行,修改成下面樣式

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="org.opencv"
      android:versionCode="3410"
      android:versionName="3.4.1">
</manifest>

再次同步即可通過

第3步,導入OPenCV的依賴

File --> Project Structure 中添加
在這裏插入圖片描述在這裏插入圖片描述

第4步,修改app/build.gradle配置

Step 1 :在dependencies中添加一句

implementation fileTree(dir: "$buildDir/native-libs", include: 'native-libs.jar')

更改爲類似與下面

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation fileTree(dir: "$buildDir/native-libs", include: 'native-libs.jar')
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation project(':openCVLibrary341')
}

Step 2 :在文末添加

task nativeLibsToJar(type: Jar, description: 'creat a jar archive of the native libs') {
    destinationDir file("$buildDir/native-libs")
    baseName 'native-libs'
    from fileTree(dir: 'libs', include: '**/*.so')
    into 'lib/'
}

tasks.withType(JavaCompile) {
    compileTask -> compileTask.dependsOn(nativeLibsToJar)
}

恭喜,到此Java環境的opencv已經可以使用,測試一下:
修改MAinActivity內容

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "OpenCV";

    static {
        if (OpenCVLoader.initDebug()) {
            Log.i(TAG, "Loaded successfully...");
        } else {
            Log.i(TAG, "Can not to loaded...");
        }
    }
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

在Logcat中可以看見
在這裏插入圖片描述

第5步:修改CMakeList.txt

#這裏修改成你的android-opencv jni文件夾路徑
set(OpenCV_DIR /home/hirah/OpenCV-android-sdk/sdk/native/jni)

find_package(OpenCV REQUIRED)
if (OpenCV_FOUND)
    include_directories(${OpenCV_INCLUDE_DIRS})
    message(STATUS "    include path: ${OpenCV_INCLUDE_DIRS}")
else (OpenCV_FOUND)
    message(FATAL_ERROR "OpenCV library not found")
endif (OpenCV_FOUND)

set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--exclude-libs,libippicv.a -Wl,--exclude-libs,libippiw.a")

add_library(
        native-lib
        SHARED
        native-lib.cpp)

find_library(
        log-lib
        log)
#添加需要的OpenCV庫
target_link_libraries(
        native-lib
        jnigraphics
        ${OpenCV_LIBS}
        ${log-lib})

第6步 : 測試

恭喜你萬里長征已經還差最後一步 測試
step 1: 在res/drawable中添加一個圖片
在這裏插入圖片描述
Step 2: 修改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:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".MainActivity">

  <Button
      android:id="@+id/bt_toCanny"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_alignParentStart="true"
      android:layout_alignParentTop="true"
      android:layout_marginStart="11dp"
      android:layout_marginTop="29dp"
      android:text="Canny"/>
      
  <ImageView
      android:id="@+id/imageView"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_centerHorizontal="true"
      android:layout_centerVertical="true"
      android:contentDescription="@string/todo"
      android:scaleType="fitCenter"
      android:src="@drawable/test"/>
  	<!-- 上面的test爲你的圖片名-->
</RelativeLayout>

Step 3: 修改MAinActivity.java

public class MainActivity extends AppCompatActivity {
    private static int cannyID = 0;
    private static final String TAG = "OpenCV";


    // OpenCV庫靜態加載並初始化
    static {
        if (OpenCVLoader.initDebug()) {
            Log.i(TAG, "Load successfully...");
        } else {
            Log.i(TAG, "Fail to load...");
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        Button btCanny = findViewById(R.id.bt_toCanny);
        btCanny.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                cannyID = (cannyID + 1) % 2;
                imageToCanny();
            }
        });
    }
    
    private void imageToCanny() {
        ImageView imageView = findViewById(R.id.imageView);
        Bitmap image = BitmapFactory.decodeResource(this.getResources(), R.drawable.test);
        if (cannyID == 1) {
            new NDKInterface().getEdge(image);
        }
        imageView.setImageBitmap(image);
    }
}

Step 3: 修改native-lib.cpp

#include <jni.h>
#include <android/bitmap.h>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

extern "C" {
// Canny edge detect
JNIEXPORT void JNICALL
Java_cn_angry_opencvcamera_JniInterface_getEdge(JNIEnv *env, jobject, jobject bitmap) {
    AndroidBitmapInfo info;
    void *pixels;

    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);
    if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
        Mat temp(info.height, info.width, CV_8UC4, pixels);
        Mat gray;
        cvtColor(temp, gray, COLOR_RGBA2GRAY);
        Canny(gray, gray, 100, 185);
        cvtColor(gray, temp, COLOR_GRAY2RGBA);
    } else {
        Mat temp(info.height, info.width, CV_8UC2, pixels);
        Mat gray;
        cvtColor(temp, gray, COLOR_RGB2GRAY);
        Canny(gray, gray, 100, 185);
        cvtColor(gray, temp, COLOR_GRAY2RGB);
    }
    AndroidBitmap_unlockPixels(env, bitmap);
}
}

Step 4: 修改NDKInterface.java

public class JniInterface {
    static {
        System.loadLibrary("native-lib");
    }
    
    /**
     * This is a method for image processing.
     * You can see detail in native-lib.cpp
     * @param:
     *          bitmap for processing
     * @return:
     *          void
     */
    public native void getEdge(Object bitmap);
    }

在這裏插入圖片描述
到此OpenCV-Android的Java環境和Jni環境配置完畢,撒花

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章