2018/10/30

[Android] OpenCV SDK for Android in java

因為以前都只在 Windows上用 C/C++ 來寫 OpenCV,但如果要在 Android APP 上做影像處理的話,OpenCV 是有提供 SDK 可以包成 JNI 給 Android 用的。

在 Android 裡面有兩種寫 OpenCV 的方法:
一種是用 C++ 的語法寫,包給 java 用(參考這篇)。
這篇寫另外一種用 java 的語法呼叫 OpenCV 的函式寫。

測試的環境:
1. Android Studio 版本為 3.0.1 (Android Studio 載點)。
2. OpenCV SDK for Android 版本為 2.4.11 (OpenCV SDK 載點)

建立一個 Android 專案,在目錄中建立 libraries 資料夾,將 SDK 那包的 \sdk\java 整個資料夾複製到 libraries 裡面,把 java 資料夾重新命名為 opencv,接著在 opencv 資料夾內建立一個build.gradle 檔案。

build.gradle:
apply plugin: 'android-library'

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
    }
}

android {
    compileSdkVersion 26

    defaultConfig {
        minSdkVersion 15
        targetSdkVersion 26
    }

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            res.srcDirs = ['res']
            aidl.srcDirs = ['src']
        }
    }
}
其中這三個是依照版本而改
classpath 'com.android.tools.build:gradle:3.0.1
compileSdkVersion 26
targetSdkVersion 26

接著在 settings.gradle 裡面加入
include ':libraries:opencv'

設定 File>Project Structure>app>Dependencies,加入 Module dependency,選擇libraries:opencv。

再來,在專案中建立一個 jniLibs 資料夾,並且把 SDK 中的 \sdk\native\libs 複製過去。

到這裡,先 Rebuild 整個專案看看是否成功。

在做到這裏的時候有可能出現錯誤訊息:
Error: A problem occurred starting process 'command 'C:\Users\Chris\AppData\Local\Android\sdk\ndk-bundle\toolchains\mips64el-linux-android-4.9\prebuilt\windows-x86_64\bin\mips64el-linux-android-strip'‘

解決方法: SDK Manager > SDK Tools tab > uncheck NDK > Apply and OK

準備就緒之後,就可以開始來寫一個,將攝影機的影像轉成灰階 (RGB to Gray) 的程式了。

在 xml 加入 JavaCameraView 來秀攝影機畫面
<org.opencv.android.JavaCameraView
    android:id="@+id/id_opencvView"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    tools:layout_editor_absoluteX="0dp"
    tools:layout_editor_absoluteY="0dp" />

建立一個OpenCVClass.java
public class OpenCVClass implements CameraBridgeViewBase.CvCameraViewListener2{

    private Mat mRgbCamMat, mGrayMat; // Create a mat

    @Override
    public void onCameraViewStarted(int width, int height) {
        mRgbCamMat = new Mat();
        mGrayMat = new Mat();
    }

    @Override
    public void onCameraViewStopped() {
        mRgbCamMat.release();
        mGrayMat.release();
    }

    @Override
    public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
        mRgbCamMat = inputFrame.rgba(); // get camera image
        Imgproc.cvtColor(mRgbCamMat , mGrayMat, Imgproc.COLOR_RGB2GRAY); // in java
        return mGrayMat; // return camera image mat
    }
}
在 MainActivity.java 引入:
private CameraBridgeViewBase mOpenCvCameraView; // view to show camera image
private OpenCVClass mOpenCVClass; // your opencv class

@Override
protected void onCreate(){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
     
    mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.id_opencvView);
     mOpenCVClass = new OpenCVClass();
     mOpenCvCameraView.setCvCameraViewListener(mOpenCVClass);
 }

@Override
public void onPause(){
    super.onPause();
     if (mOpenCvCameraView != null)
         mOpenCvCameraView.disableView();
 }

@Override
public void onResume(){
    super.onResume();
     if (!OpenCVLoader.initDebug()) {
        // load opencv tool
         OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_11, this, mLoaderCallback);
     } else { 
        mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS); 
    } 
}

@Override
public void onDestroy() {
    super.onDestroy();
    if (mOpenCvCameraView != null)
        mOpenCvCameraView.disableView();
}

private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { 
    @Override 
    public void onManagerConnected(int status) { 
        switch (status) { 
            case LoaderCallbackInterface.SUCCESS: 
                mOpenCvCameraView.enableView();
                 break; 
            default: 
                super.onManagerConnected(status);  
               break;
         } 
    }
 };
在 AndroidManifest.xml 加入開啟攝影機的權限
<uses-permission android:name="android.permission.CAMERA"/>
程式執行後會發現攝影機的畫面被轉了 90 度,修正方法在 AndroidManifest.xml 把 app 設定成橫向就好了
<activity android:name=".MainActivity"
            android:screenOrientation="landscape">

執行後就會看到正確的攝影機畫面轉成灰階了。

另外有一些常用的 OpenCV 函式,如果要用可以參考看看:
Imgproc.cvtColor(src, dst, Imgproc.COLOR_RGB2GRAY);
Imgproc.threshold(dst, dst, Threshold ,255, Imgproc.THRESH_BINARY);
Imgproc.Canny(src, dst, 100, 200);
Imgproc.dilate(dst, dst, new Mat());
Imgproc.findContours(dst, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);
如果要把 Mat 轉成 java 的 view 元件,可以用以下方法轉換
Bitmap bm = Bitmap.createBitmap(resultMat.cols(), resultMat.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(resultMat, bm);
以上是用 java 的語法寫 OpenCV 的方法


(END)

沒有留言:

張貼留言