一種是直接用 java 的語法呼叫 OpenCV 的函式寫(參考這篇)。
這篇就來寫另外一種用 C++ 的語法寫,包給 java 用。
測試環境:
1. Android Studio 版本為 3.0.1 (Android Studio 載點)。
2. OpenCV SDK for Android 版本為 2.4.11 (OpenCV SDK 載點)
建立一個 Android 專案,選擇 File > New > Import module,選 SDK\sdk\java。
接著選 File > Project Structure > app > Dependencies > Add Module dependency
選:openCVLibrary4211
在 gradle.properties 檔案中加入這行
android.useDeprecateNdk = true
建立一個 OpencvNativeClass.java:
public class OpencvNativeClass { public native static int convertGray(long matAddrRgba, long matAddrGray); }
做到這邊,先 build 一次專案看看是否成功。
在Android Studio 編譯器中的 Terminal 視窗輸入指令
依序輸入下列兩個指令
輸入: cd app/src/main
輸入: javah -d jni -classpath ../../build/intermediates/classes/debug com.example.chris.opencvcppsample.OpencvNativeClass
在 app\src\main\jni 資料夾中建立以下四個檔案
com_example_chris_opencvcppsample_OpencvNativeClass.h:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> #include <stdio.h> #include <opencv2/opencv.hpp> using namespace std; using namespace cv; /* Header for class com_example_chris_opencvcppsample_OpencvNativeClass */ #ifndef _Included_com_example_chris_opencvcppsample_OpencvNativeClass #define _Included_com_example_chris_opencvcppsample_OpencvNativeClass #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_chris_opencvcppsample_OpencvNativeClass * Method: convertGray * Signature: (JJ)I */ int toGray(Mat img, Mat& gray); JNIEXPORT jint JNICALL Java_com_example_chris_opencvcppsample_OpencvNativeClass_convertGray (JNIEnv *, jclass, jlong, jlong); #ifdef __cplusplus } #endif #endif
主要的影像處理寫法就是會寫在這個 .cpp 裡面
com_example_chris_opencvcppsample_OpencvNativeClass.cpp:
#include <com_example_chris_opencvcppsample_OpencvNativeClass.h> JNIEXPORT jint JNICALL Java_com_example_chris_opencvcppsample_OpencvNativeClass_convertGray (JNIEnv *, jclass, jlong addrRgba, jlong addrGray){ Mat& mRgb = *(Mat*)addrRgba; Mat& mGray = *(Mat*)addrGray; int conv; jint retVal; conv = toGray(mRgb, mGray); retVal = (jint)conv; return retVal; } int toGray(Mat img, Mat& gray){ cvtColor(img, gray, CV_RGBA2GRAY); if(gray.rows == img.rows && gray.cols == img.cols) return 1; return 0; }
Android.mk:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) #opencv OPENCVROOT:= C:\AndroidApps\OpenCV-2.4.11-android-sdk\OpenCV-android-sdk OPENCV_CAMERA_MODULES:=on OPENCV_INSTALL_MODULES:=on OPENCV_LIB_TYPE:=SHARED include ${OPENCVROOT}/sdk/native/jni/OpenCV.mk LOCAL_SRC_FILES := com_example_chris_opencvcppsample_OpencvNativeClass.cpp LOCAL_LDLIBS += -llog LOCAL_MODULE := MyOpencvLibs include $(BUILD_SHARED_LIBRARY)
Application.mk:
APP_STL := gnustl_static APP_CPPFLAGS := -frtti -fexceptions APP_ABI := armeabi-v7a APP_PLATFORM := android-16
解決方法:
下載以前的版本: NDK version 17 (載點),解壓縮之後放到 C:\Users\User\AppData\Local\Android\sdk\ 這個目錄下,重新命名資料夾為 ndk-bundle。
在 app 的 build.gradle 裡面加入:
sourceSets.main { jni.srcDirs = [] //disable automatic ndk-build call } task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') { commandLine "C:/Users/Chris/AppData/Local/Android/sdk/ndk-bundle/ndk-build.cmd", 'NDK_PROJECT_PATH=build/intermediates/ndk', 'NDK_LIBS_OUT=src/main/jniLibs', 'APP_BUILD_SCRIPT=src/main/jni/Android.mk', 'NDK_APPLICATION_MK=src/main/jni/Application.mk' } tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn ndkBuild }
其中確認 commandLine "C:/Users/User/AppData/Local/Android/sdk/ndk-bundle/ndk-build.cmd" 路徑對不對。
最後Rebuild 整個專案,成功的話會產生 jniLibs 這個資料夾。
接著就可以開始寫程式了!
先在 Activity_main.xml 加入 JavaCameraView :
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.chris.opencvcppsample.MainActivity"> <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" /> </android.support.constraint.ConstraintLayout>
建立一個 OpenCVClass.java
package com.example.chris.opencvcppsample; import org.opencv.android.CameraBridgeViewBase; import org.opencv.core.Mat; public class OpenCVClass implements CameraBridgeViewBase.CvCameraViewListener2 { Mat mRgbCamMat, mGrayMat; @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(); OpencvNativeClass.convertGray(mRgbCamMat.getNativeObjAddr(), mGrayMat.getNativeObjAddr()); return mGrayMat; } }
其中發現這行寫法就跟 java 的版本寫法不一樣了
OpencvNativeClass.convertGray(mRgbCamMat.getNativeObjAddr(), mGrayMat.getNativeObjAddr());
最後就是 MainActivity.java:
package com.example.chris.opencvcppsample; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import org.opencv.android.BaseLoaderCallback; import org.opencv.android.JavaCameraView; import org.opencv.android.LoaderCallbackInterface; import org.opencv.android.OpenCVLoader; public class MainActivity extends AppCompatActivity{ private static String TAG = "MainActivity"; private JavaCameraView mOpenCvCameraView; private OpenCVClass mOpenCVClass; static { System.loadLibrary("MyOpencvLibs"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mOpenCvCameraView = (JavaCameraView) 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(); // load OpenCV tool if (!OpenCVLoader.initDebug()) { 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(); } BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { @Override public void onManagerConnected(int status) { switch (status){ case BaseLoaderCallback.SUCCESS: mOpenCvCameraView.enableView(); break; default: super.onManagerConnected(status); break; } } }; }最後也要記得加入開放 Camera 權限
AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />畫面旋轉的問題也一樣在 AndroidManifest.xml 設定app為橫向就好了
<activity android:name=".MainActivity" android:screenOrientation="landscape">
END
沒有留言:
張貼留言