一種是直接用 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:
最後也要記得加入開放 Camera 權限
- 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;
- }
- }
- };
- }
AndroidManifest.xml:
畫面旋轉的問題也一樣在 AndroidManifest.xml 設定app為橫向就好了
- <uses-permission android:name="android.permission.CAMERA" />
- <activity android:name=".MainActivity"
- android:screenOrientation="landscape">
END
沒有留言:
張貼留言