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
Callback
method, called after OpenCV library initialization.
void
Callback
method, called in case the package installation is needed.
注意:OpenCV文档中明确说: OnManagerConnectedcallback
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
Version,
android.content.Context
Loads
and initializes OpenCV library using OpenCV Engine service.
static boolean
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: