一種是直接用 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



沒有留言:
張貼留言