文档结构  
翻译进度:已翻译     翻译赏金:0 元 (?)    ¥ 我要打赏

本教程将指导你在 Android 项目中使用 OpenCV 库。

本教程在编写时是在Windows7环境下的,但它在 OpenCV4Android SDK 支持的其他任何操作系统下也应该是有效的。

本教程假设你已经安装如下软件:

  • JDK

  • Android SDK 和 NDK

  • Eclipse IDE

  • Eclipse 下的 ADT 和 CDT 插件

关于上述如果你需要任何帮助,你可以参考我们 Android 开发介绍 教程。

本教程还假设你的开发设备已经安装有 OpenCV4Android SDK 和 OpenCV Manager。关于这些如需帮助,你可以查阅我们的 OpenCV4Android SDK 教程。

第 1 段(可获 2 积分)

如果你在这些步骤中遇到任何错误,请通过 OpenCV4Android 讨论组 或 OpenCV Q&A 论坛联系我们,我们会尽力给予帮助。

在你的安卓工程中使用 OpenCV 库

在这一节,我们将说明如何在已有工程中使用 OpenCV。使用安卓 2.4.2 版本时,OpenCV Manager 可以用来提供 OpenCV 最可用版本的应用。你可以在 Android OpenCV Manager 和这个文档查看更多信息。


Java

使用异步初始化的应用开发

第 2 段(可获 2 积分)

使用异步初始化是应用开发的推荐方式。它使用的 OpenCV Manager 来访问外部安装在目标系统中的 OpenCV 库。

在大多数情况下,OpenCV Manager 可能会从 Google Play 自动安装。但某种情况下Google Play 不可用,比如在仿真器、开发板等上做开发时,你可以使用 ADB 工具手动安装。请参阅 如何选择 OpenCV Manager 详情正确的版本

第 3 段(可获 2 积分)

 下面是一段实现异步初始化的基础代码片。它演示了基本的原理。具体查看 OpenCV 的 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);
    }

    ...
}

 

第 4 段(可获 2 积分)

在这种情况下,应用以异步的方式使用 OpenCV Manager。 OnManagerConnected 回调将在 UI 线程调用,初始化时完成。请注意,在完成此回调之前不允许使用 OpenCV 调用或加载 OpenCV 依赖的本地库。成功初始化 OpenCV 后再加载依赖OpenCV 的你自己的本地库。BaseLoaderCallback 实现默认将应用上下文视为 Activity,所以如果初始化失败则应调用 Activity.finish() 方法退出。重写BaseLoaderCallback 类的 finish() 方法,并实现你自己的方法。

第 5 段(可获 2 积分)

使用静态初始化的方式开发应用

使用这种方法将会把 OpenCV 的所有二进制文件都包含到你的应用包中。它主要用于开发目的。这种生成代码的做法已经过时,发布包时建议通过上述异步初始化的方式与 OpenCV Manager 协同即可。

1. 如果你的应用工程中没有 JNI,那么从 <OpenCV-2.4.11-android-sdk>/sdk/native/libs/<target_arch> 拷贝一份 OpenCV 本地库到工程目录中的 libs/<target_arch> 即可。

如果有 JNI,就不需要手动拷贝库了,而是修改  Android.mk 文件:添加如下两段代码行到 include $(CLEAR_VARS) 下面,include path_to_OpenCV-2.4.11-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

之后,OpenCV 库将在 JNI build.v 的过程中被拷贝到你的应用的 libs 文件夹中。

Eclipse 会自动包含 libs 文件夹中所有的库到应用包(APK)中。

2. 在应用中使用 OpenCV 的最后一步是调用 OpenCV API 之前的Java初始化代码。这是可以实现,例如,在 Activity 类的静态部分:

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");
    }
}

 

第 6 段(可获 2 积分)

本地/C++

构建自己的Android应用程序,使用OpenCV作为本地部分,应采取以下步骤:

  1. 你可以使用环境变量指定OpenCV包的位置,或者在你的项目中将绝对或相对路径硬编码到jni/Android.mk文件中。

  2. 文件jni/Android.mk使用通用规则编写,适用于当前应用程序。

    详细信息参见Android NDK归档文件中的Android NDK文档,文件位于 <path_where_NDK_is_placed>/docs/ANDROID-MK.html.

  3. 下面这行:

    include C:\Work\OpenCV4Android\OpenCV-2.4.11-android-sdk\sdk\native\jni\OpenCV.mk
    

    应该插入到文件jni/Android.mk下面这行的后面:

    include $(CLEAR_VARS)
    
  4. 可用于自定义OpenCV的几个变量,当你的程序通过OpenCV Manager API使用异步初始化时,你不需要使用它们。

    注意

    这些变量需要在 "include .../OpenCV.mk" 这行之前设置:

    OPENCV_INSTALL_MODULES:=on
    

    将需要的OpenCV 动态库复制到项目里的libs文件夹下,以将其纳入APK.。

    OPENCV_CAMERA_MODULES:=off
    

    跳过本地OpenCV的摄像头相关库复制到项目libs文件夹。

    OPENCV_LIB_TYPE:=STATIC
    

    执行静态链接OpenCV。默认情况下使用动态链接,项目得JNI库取决于libopencv_java.so。

  5. 存在Application.mk文件并包含以下这些行::

    APP_STL := gnustl_static
    APP_CPPFLAGS := -frtti -fexceptions
    

    Also, the line like this one:

    APP_ABI := armeabi-v7a
    

    应指定应用程序目标平台。

    在某些情况下,一个链接错误 (如"In function 'cv::toUtf16(std::basic_string<...>... undefined reference to 'mbstowcs'") 在构建一个应用程序JNI库时发生,这取决于OpenCV。下面这行在文件Application.mk中通常可以修复这个错误:

    APP_PLATFORM := android-9
    
  6. 在(再次)构建java部分以及创建一个APK之前,可以手动生成 ndk-build调用或者设置Eclipse CDT Builder 建立本地jni库。

第 7 段(可获 2 积分)

Hello OpenCV 例程

以下是基本步骤来指导你创建一个简单的 OpenCV 应用。它将能够访问照相机输出、处理、并显示结果的。

  1. 打开 Eclipse IDE,创建一个新的工作空间,创建一个新的Android 项目 File ‣ New ‣ Android Project
  2. 相应地设置名称,目标,包和的 minSdkVersion。与OpenCV4Android SDK 对应的最小 SDK 版本的构建应设为 11。最小的设备 API级别(应用程序清单)设为8。
  3. 允许 Eclipse 创建默认的活动。命名活动为 HelloOpenCvActivity。
  4. 选择全屏幕布局的空白活动。命名布局为 HelloOpenCvLayout。
  5. 导入 OpenCV 库项目到工作区中。
  6. 像 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>
  7. 在 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"/>

     

  8. 通过在 AndroidManifest.xml 文件中添加如下主题来隐藏标题和系统按钮。

    <application  android:icon="@drawable/icon" android:label="@string/app_name" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" 

     

  9. 添加 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);
    }

     

  10. 定义你的活动以实现 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();
     }

     

  11. 在设备或仿真器中运行你的应用。

第 8 段(可获 2 积分)

让我们讨论一些最重要的步骤。每一个带UI的Android应用程序必须实现 Activity 和视图。第一步骤中,我们创建空白 Activity,并使用默认布局。最简单的 OpenCV 应用也必须实现 OpenCV 的初始化,创建了自己的视图,从而显示相机的预览并通过实现 CvCameraViewListener2 接口来从摄像头获取帧并进行处理。

一开始我们使用 XML 布局创建一个应用程序视图。我们的布局只包含一个全屏幕的组件类 org.opencv.android.JavaCameraView。这个类由 OpenCV 库内部实现。它继承了扩展 SurfaceView 类的 CameraBridgeViewBase 类并使用标准的安卓相机 API。或者您可以使用 org.opencv.android.NativeCameraView 类,它实现了相同的接口,但使用 VideoCapture 类访问相机。opencv:show_fps=“true”和opencv:camera_id=“any” 选项用于打开 FPS 信息,允许你使用设备上的任何摄像。应用程序会先尝试使用后置摄像头。

第 9 段(可获 2 积分)

创建布局后,我们需要实现 Activity 类。如上已经讨论了 OpenCV 初始化的过程。在此示例中,我们使用了异步初始化。通过实现 CvCameraViewListener 接口你可以在从相机抓取帧后,屏幕上显示前的时间点上添加处理步骤。最重要的功能是 onCameraFrame,这是回调函数,它在摄像头获取到帧后被调用。这个回调的输入是 CvCameraViewFrame 类的一个对象,用于表示从摄像机得到的帧。

注意:

不要在 onCameraFrame 回调函数之外保存或使用 CvCameraViewFrame 对象。这个对象没有自己的状态,并且其回调之外的行为是不可预测的!

它具有 rgba() 和 gray() 方法,允许分别获得帧的 RGBA 和灰度。据预计,onCameraFrame 函数返回的 RGBA 帧将用于屏幕绘制。

第 10 段(可获 2 积分)

文章评论