作者: Sam (甄峰) sam_code@hotmail.com
D: 在Activity中添加:
mOpenCvCameraView.setOnTouchListener(Puzzle15Activity.this);
mOpenCvCameraView.enableView();
} break;
default:
{
super.onManagerConnected(status);
} break;
}
}
};
注意:OpenCV文档中明确说: OnManagerConnected callback
will be called in UI
thread
由于不允许在Initialization Finish完成前调用OpenCV API或Native API。所以任何OpenCV相关调用都应该在此Callback之后。
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;
public class SpinnerActivity extends Activity implements CvCameraViewListener {
private static final String TAG = "SamInfo";
private CameraBridgeViewBase mOpenCvCameraView;
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
mOpenCvCameraView.enableView();
} break;
default:
{
super.onManagerConnected(status);
} break;
}
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mOpenCvCameraView.setCvCameraViewListener(this);
@Override
public void onResume() {
@Override
public void onPause() {
@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;
}
1.2.2:应用程序使用静态初始化方式使用OpenCV Library:
本文参考:
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.
B: 导入异步初始化的例子程序--15 puzzle:
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");
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的子类。
抽象类不是无法实例化么? 但其实可以这样理解,在这里创建了一个匿名的类为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. |
由于不允许在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);
}
}
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;
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");
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);
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);
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);
}
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();
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 方式为宜。
B:把OpenCV Library 作为Jar库加入Application:
例如:
# 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中添加:
此时,编译中,会自动把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
附录:OpenCV API:
http://dalab.se.sjtu.edu.cn/docs/opencv/docs.opencv.org/java/index-all.html