OpenCV的Android开发

该教程会教会您在Android项目中如何使用OpenCV库。
教程在Windows 7环境编写,当然可以运行在OpenCV For Android SDK所支持的其他操作系统之上。
该教程假设您已经完成了如下的安装和配置:

  • JDK
  • Android SDK and NDK
  • Eclipse IDE
  • ADT and CDT plugins for Eclipse

如果你需要上述环境搭建、配置的帮助,你可以参考《Android开发介绍》。

该教程还会假设你已经在你的电脑上安装了OpenCV For Android,并且OpenCV Manager已经运行在你的测试设备上了。如果你需要这方面的帮助你参考《OpenCV For Android SDK》。

如果在实践下面的教程中出了问题,你可以通过OpenCV For Android 讨论组联系我们,或这OpenCV通过Q&A forum。我们会尽快的为您解决问题。

在你的Android项目中使用OpenCV库

Java

程序开发中使用异步初始化

在应用开过过程中异步初始化是被推荐使用的一个初始化方式。它通过OpenCV Manager来访问安装在目标系统中的OpenCV库。

  1. 添加OpneCV项目到你的workspace中。菜单File -> Import -> Existing project in your workspace。
    点击浏览按钮定位到OpenCV4Android SDK(OpenCV-2.4.9-android-sdk/sdk)。
  2. android opencv 开发 opencv 安卓开发_android

  3. 在程序内添加一个OpenCV Java SDK的关联(reference)Project -> Properties -> Android -> Library -> Add select OpenCV Library-2.4.9。
  4. android opencv 开发 opencv 安卓开发_android_02

在大多数情况下OpenCV Manager可能已经通过你的Google Play进行了自动安装。另外一种情况就是Google Play没有被安装,或者模拟器、开发板。你可以通过adb工具进行手动安装。
这段代码很简单的实现了异步初始化。详细参看sample ‘15-puzzle’

public class Sample1Java extends Activity implements CvCameraViewListener {
    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 onResume()
    {
        super.onResume();
        OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_6, this, mLoaderCallback);
    }
    ...
}

在这种情况下应用程序的工作通过OpenCV Manager异步完成。当初始化完成之后,回调OnManagerConnected会被UI线程调用。请注意回调返回之前,不允许调用OpenCV和OpenCV本地依赖。OpenVC初始化完成之后,在加载依赖于OpenCV的本地库。默认情况下BaseLoaderCallback实现需要使用Activity的Context来创建,因为在初始化失败的时候会调用 Activity.finish()方法。撤销这行为你需要重写BaseLoaderCallback 类中的finish()方法,实现你自己的完成方法。

程序开发中使用静态初始化

根据这个方式所有的OpenCV库都需要包含在你应用程序中。它的设计主要是以开发为目的,这种方式是过时的代码生产方式,发布的包推荐使用上述方式,去链接OpenCV Manager实现异步初始化。

  1. 使用与异步初始化相同的方式添加OpenCV库项目到你的workspace里面。用户菜单File->Import->Existing project in your workspace,点击浏览按钮选择OpenCV SDK的路径 在 (Opencv-2.4.9-android-sdk/sdk)。
  2. android opencv 开发 opencv 安卓开发_android

  3. 在程序内添加一个OpenCV Java SDK的关联(reference)Project -> Properties -> Android -> Library -> Add select OpenCV Library-2.4.9。
  4. android opencv 开发 opencv 安卓开发_Android_04

  5. 如果你的应用程序不包含JNI部分,在<OpenCV-2.4.9-android-sdk>/sdk/native/libs/<target_arch>拷贝相应的OpenCV本地库到你项目的libs/<target_arch>
    在项目包含JNI part的情况,取代手动拷贝你需要修改你的Android.mk文件:在"include $(CLEAR_VARS)"后面,和在` “include path_to_OpenCV-2.4.9-android-sdk/sdk/native/jni/OpenCV.mk”签名添加如下两行代码。
OPENCV_CAMERA_MODULES:=on
 OPENCV_INSTALL_MODULES:=on

修改完成后应该类似于下面这样:

include $(CLEAR_VARS)

# OpenCV
OPENCV_CAMERA_MODULES:=on
OPENCV_INSTALL_MODULES:=on
include ../../sdk/native/jni/OpenCV.mk

在JNI build期间OpenCV库将被拷贝到你程序的libs目录,Eclipse会自动的将这些库放在APK中。

  1. 最后一步是在应用程序调用OpenCV API之前,在Java代码中开启OpenCV。这是一个例子,在Activity类的static段里面:
static {
    if (!OpenCVLoader.initDebug()) {
        // Handle initialization error
    }
}

如果你的应用程序包含其他OpenCV依赖的本地库,你应当先加载他们,再初始化OpenCV。

static {
    if (!OpenCVLoader.initDebug()) {
        // Handle initialization error
    } else {
        System.loadLibrary("my_jni_lib1");
        System.loadLibrary("my_jni_lib2");
    }
}

Native/C++

将OpenCV作为你应用程序的一部分来编译Android程序,应采用一下的步骤:

  1. 你可以使用一个环境变量指定你的OpenCV的位置,或者在jni/Android.mk中将相对地址或者绝对地址写死。
  2. 使用共同的规则,文件jni/Android.mk为现在的程序而写。详细信息参看Android NDK文档,该文档在<path_where_NDK_is_placed>/docs/ANDROID-MK.html
  3. 这面这行
include C:\Work\OpenCV4Android\OpenCV-2.4.9-android-sdk\sdk\native\jni\OpenCV.mk

应当插入到jni/Android.mk文件的这一行之后:

include $(CLEAR_VARS)
  1. OpenCV有多个自定义变量,当你使用OpenCV Manager API进行异步加载的时候,并不需要使用他们。

提示:这个变量应当设置在”include …/OpenCV.mk”之前:
OPENCV_INSTALL_MODULES:=on

拷贝所需要的OpenCV动态库到项目libs目录中,因为APK需要包含他们。

OPENCV_CAMERA_MODULES:=off

不将OpenCV camera发布的库拷贝到项目目录。

OPENCV_LIB_TYPE:=STATIC

执行静态链接OpenCV。默认的是动态链接,项目的JNI会依赖于libopencv_java.so.
5. Application.mk应当存在,并且需要包含下面这几行:

APP_STL := gnustl_static
 APP_CPPFLAGS := -frtti -fexceptions

像这样一行:

APP_ABI := armeabi-v7a

需要指定应用程序的目标平台。
当编译应用程序的时候,JNI库会出现链接错误,例如:(like "In function 'cv::toUtf16(std::basic_string<...>... undefined reference to 'mbstowcs'")。Application.mk里面的这一行就可以对其修正:

APP_PLATFORM := android-9
  1. 无论使用ndk-build手册调用还是安装Eclipse CDT Builder来编译本地JNI库,编译java部分,都会创建一个APK。

Hello OpenCV Sample

这里是一个基础教程,指导你创建一个OpenCV-centric应用程序的simple。他能访问照相机的输出,执行它并显示出来。

  1. 打开Eclipse IDE创建一个干净的workspace,创建一个Android项目File->New->Android Project。
  2. 设置名称、目标、包名和minSDK版本。OpenCV4AndroidSDK支持的最低Android编译版本是11.最低设备API版本是8.
  3. 允许Eclipse图创建默认的activity。将Activity名称定义为HelloOpenCvActivity。
  4. 选择Blank Activity全屏layout。设置layout的名称为HelloOpenCvLayout。
  5. 引入OpenCV库到你的项目workspace中。
  6. 关联OpenCV库到你的项目属性中。
  7. 编译你的layout xml文件并且粘贴如下的内容:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
  xmlns:opencv="http://schemas.android.com/apk/res-auto"
  android:layout_width="match_parent"
  android:layout_height="match_parent" >

   <org.opencv.android.JavaCameraView
       android:layout_width="fill_parent"
       android:layout_height="fill_parent"
       android:visibility="gone"
       android:id="@+id/HelloOpenCvView"
       opencv:show_fps="true"
       opencv:camera_id="any" />

</LinearLayout>
  1. 在AndroidManifest.xml文件中添加如下权限:
</application>

<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
<uses-feature android:name="android.hardware.camera.front" android:required="false"/>
<uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>
  1. 在AndroidManifest.xml中设置应用程序的风格,隐藏title和系统按钮。
<application
   android:icon="@drawable/icon"
   android:label="@string/app_name"
   android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
  1. 在Activity中添加OpenCV库的初始化。
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 onResume()
{
super.onResume();
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_6, this, mLoaderCallback);
}
  1. 你的Activity类需要实现CvCameraViewListener2接口YourActivity extends Activity implements CvCameraViewListener2,onCreate、onDestroy、onPause实现下面的代码。
private CameraBridgeViewBase mOpenCvCameraView;
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "called onCreate");
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.HelloOpenCvLayout);
mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.HelloOpenCvView);
mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
mOpenCvCameraView.setCvCameraViewListener(this);
}
@Override
public void onPause()
{
super.onPause();
if (mOpenCvCameraView != null)
    mOpenCvCameraView.disableView();
}
public void onDestroy() {
super.onDestroy();
if (mOpenCvCameraView != null)
    mOpenCvCameraView.disableView();
}
public void onCameraViewStarted(int width, int height) {
}
public void onCameraViewStopped() {
}
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
return inputFrame.rgba();
}
  1. 运行你的用于程序。
    来讨论一下非常重要的步骤。每个Android程序必须实现Activity和View。在第一步我们创建了Blank Activity并且定义了view的layout。OpenCV-centric必须实现OpenCV的初始化,创建自己的view来预览相机并且实现CvCameraViewListener2接口来获取相机的帧数据。
    首先我们创建了我们自己的应用程序,view使用layout。我自己的layout内容仅仅是一个全屏的组件类org.opencv.android.JavaCameraView. 。这个类通过OpenCV library来实现。它继承CameraBridgeViewBase,extends SurfaceView 使用Android的相机API。

然后创建了laoyout我们需要实现Activity类。OpenCV的初始化在上面已经讨论过了。在这个sample里面使用了异步初始化。实现CvCameraViewListener接口,允许你添加执行抓取帧数据,并且将数据显示在屏幕上。必须实现函数onCameraFrame。它是一个回调函数,并且它会取回相机的帧数据。回调的输入对象是CvCameraViewFrame类。

提示 不要保存或者使用CvCameraViewFrame对象的onCameraFrame回调。这个对象没有它自己的状态和行为。

它有rgba()和gray()方法允许得到RGBA帧数据,和一个灰度通道的Mat。它希望通过onCameraFrame方法返回RGBA真,这样就可以绘制到屏幕上了。