Quantcast
Channel: Sam的技术Blog
Viewing all articles
Browse latest Browse all 158

Android Developent with OpenCV3.1

$
0
0
作者: Sam (甄峰)  sam_code@hotmail.com

本文参考:
http://docs.opencv.org/2.4/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.html#dev-with-ocv-on-android

http://docs.opencv.org/2.4/doc/tutorials/introduction/android_binary_package/O4A_SDK.html#o4a-sdk

0. 准备工作:
在开发机上:JDK, Android SDK, NDK, Eclipse,  ADT和CDT Eclipse插件等。并且安装了OpenCV4Android.
 
在运行环境中,OpenCV_3.1.0_Manager_3.10_armeabi-v7a.apk需要安装(SDK中提供不同Core和指令集的APK,根据运行环境不同而安装不同APK)


1.  Android Project使用OpenCV Library:
1.1:OpenCV Manager介绍
从OpenCV-2.4.2 For Android 版本开始, OpenCV Manager就用作为Android Project提供支持,帮助它使用OpenCV library.
 
OpenCV Manager是一个Android Service, 用来在用户终端上管理OpenCV Library. 它允许在同一设备上,多个应用程序访问同一套共享库,并提供以下好处:
A:节约内存空间,所有APP均使用Service中同一份二进制代码,并且不需要把native library编译进每一个APP中。
B:  对所有支持平台均提供硬件优化。
C:  支持update和bug fix.
 
 
1.2:Java代码使用OpenCV:
1.2.1: 使用程序异步初始化方式使用OpenCV
官方推荐使用异步初始化方式, 它通过之前安装好的OpenCV Manager访问OpenCV Library。
具体路径如下:
A:增加OpenCV Library Project到workspace中
File -> Import -> Existing project in your workspace.

编译之。则会生成opencv_library-3.1.0.jar
 
B: 导入异步初始化的例子程序--15 puzzle


C: 把OpenCV Library 作为Jar库加入Application


注意:Application的Project Build target与OpenCV Library Project的Build Target相同。
 

D:  在Activity中添加
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS:
                {
                    Log.i(TAG, "OpenCV loaded successfully");
                   
                    mOpenCvCameraView.setOnTouchListener(Puzzle15Activity.this);
                    mOpenCvCameraView.enableView();
                } break;
                default:
                {
                    super.onManagerConnected(status);
                } break;
            }
        }
    };
这里要注意这个抽象类:
abstract class BaseLoaderCallback
抽象类不是无法实例化么? 但其实可以这样理解,在这里创建了一个匿名的类为BaseLoaderCallback的子类。
作为Activity的内部类。
 
这个抽象类有两个方法:
Method Summary
 void onManagerConnected(int status)
          Callback method, called after OpenCV library initialization.
 void onPackageInstall(int operation, InstallCallbackInterface callback)
          Callback method, called in case the package installation is needed.
注意:OpenCV文档中明确说: OnManagerConnected callback will be called in UI thread
由于不允许在Initialization Finish完成前调用OpenCV API或Native API。所以任何OpenCV相关调用都应该在此Callback之后。
 
E:OpenCVLoader:
Helper class provides common initialization methods for OpenCV library.
Method Summary
static boolean initAsync(java.lang.String Version, android.content.Context AppContext, LoaderCallbackInterface Callback)
          Loads and initializes OpenCV library using OpenCV Engine service.
static boolean initDebug()
          Loads and initializes OpenCV library from current application package.
 
 @Override
    public void onResume()
    {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }
 
initDebug():
Loads and initializes OpenCV library from current application package. Roughly, it's an analog of system.loadLibrary("opencv_java").
从本应用程序包内载入和初始化OpenCV Library,相当于:system.loadLibrary("opencv_java3");
 
如果本地APP包含了OpenCV Library, 则返回True。 否则,返回false.
这里,我们使用OpenCV Manager, 并没有加入OpenCV Native Library。所以返回false.
 
 
initAsync():
Loads and initializes OpenCV library using OpenCV Engine service.
使用OpenCV Manager 初始化。
 
整个逻辑关系就是:
在onResume()时,使用initDebug()判断是否为本App Package OpenCV  Library. 如果是,则手动调用:
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
如果不是,则说明应该使用initAsync()调用OpenCV Manager来初始化。 OpenCV Manager 初始化完成后,则会调用onManagerConnected
 
 
F:如果要使用Camera,则还需要添加Camera权限。
 
完整例子,从15-puzzle为基础做的例子,它使用Async Init模式,所以完全不需要把Native库加进来。
 
package com.android.example.spinner;
 

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Mat;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener;
import org.opencv.android.JavaCameraView;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemSelectedListener;

public class SpinnerActivity extends Activity implements CvCameraViewListener {
 private static final String  TAG = "SamInfo";
 private CameraBridgeViewBase mOpenCvCameraView;
 
 
 private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS:
                {
                    Log.i(TAG, "OpenCV loaded successfully");
                   
                   
                    mOpenCvCameraView.enableView();
                } break;
                default:
                {
                    super.onManagerConnected(status);
                } break;
            }
        }
    };
 
 
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
       
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.main);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        Log.d(TAG, "Creating and setting view");
        mOpenCvCameraView = (CameraBridgeViewBase) new JavaCameraView(this, -1);
        setContentView(mOpenCvCameraView);
        mOpenCvCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
// 这里创建一个JavaCameraView,它是CameraBridgeViewBase的子Class. 而CameraBridgeViewBase又是SurfaceView的子Class。
// 同时,直接把这个CameraBridegViewBase设置为Layout。且可见,而且屏幕又不会灭

        mOpenCvCameraView.setCvCameraViewListener(this);
//这句话很关键,指出当前CameraBridgeViewBase被谁所监听。监听者会在开始,结束,有数据时被调用。

       
    }
 

   
    @Override
    public void onResume() {
       
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
// 此前解释过
       
    }
   
    @Override
    public void onPause() {
       
        super.onPause();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }
 

 @Override
 public void onCameraViewStarted(int width, int height) {
  // TODO Auto-generated method stub
  Log.e(TAG, String.format("onCameraViewStarted: w:%d. H:%d", width, height));
  
 }
 

 @Override
 public void onCameraViewStopped() {
  // TODO Auto-generated method stub
  Log.e(TAG, "onCameraViewStopped()");
 }
 

 @Override
 public Mat onCameraFrame(Mat inputFrame) {
  // TODO Auto-generated method stub
  Log.e(TAG, "onCameraFrame()");
  return inputFrame;
 }
 //这里每帧有数据时被调用,可以处理这个数据,处理后的数据返回,返回值则被显示在SurfaceView。
}
 

1.2.2:应用程序使用静态初始化方式使用OpenCV  Library:
除了使用Async init 的方式使用OpenCV Library外,还有个Static Init方式使用。但如果使用这个方式,则意味着所有OpenCV 二进制要包含进本App Package。这个方法大都使用在开发阶段,产品release阶段,还是以Async init 方式为宜。
 
A:增加OpenCV Library Project到workspace中
File -> Import -> Existing project in your workspace.

 
B:把OpenCV Library 作为Jar库加入Application


C:如果App没有JNI部分,则直接copy OpenCV-android-sdk/sdk/native/libs//libopencv_java3.so 到工程libs/
例如:
# cp OpenCV-android-sdk/sdk/native/libs/armeabi-v7a/libopencv_java3.so OpenCV-android-sdk/samples/Spinner/libs/armeabi-v7a/
 
此时,因为本地Package内包含 libopencv_java3.so .  所以initDebug()返回True。
 

 

 

D:如果APP有JNI部分,则不用再去手动copy动态库,而是在Android.mk中添加:


OPENCV_CAMERA_MODULES:=on
OPENCV_INSTALL_MODULES:=on

此时,编译中,会自动把libopencv_java3.so copy过来,和jni模块编译出的库放在一起。

 

这是,在Linux版本中,常会出现类似:ndk-build.cmd无法使用等。

需要设置两点:

1. NDK目录。

2. ndk-build 指令。

 

分别设置如下:

1. NDK 目录

方法一: Window -->Preferences-->Android-->NDK



方法二:

window-->Preferences-->C/C++-->build variables



2. 修改ndk-build.cmd问题:

这个在Eclipse下比较难找;

右击项目, Build Path-->Configure Build Path -->C++ build



把ndk-build.cmd 修改为ndk-build即可

 

 

附录:OpenCV API:

http://dalab.se.sjtu.edu.cn/docs/opencv/docs.opencv.org/java/index-all.html


 

Viewing all articles
Browse latest Browse all 158

Trending Articles