上一篇教程: Android 开发入门
下一篇教程: 如何在 Android 设备上运行深度网络
| |
| 原始作者 | Alexander Panov, Rostislav Vasilikhin |
| 兼容性 | OpenCV >= 4.9.0 |
本教程旨在帮助您在 Android 项目中使用 OpenCV 库。
本指南已在 Ubuntu 上进行检查,但不包含任何平台相关的部分,因此应与 Android Studio 和 OpenCV4Android SDK 支持的任何操作系统兼容。
本教程假设您已安装并配置以下各项
如果您在以上任何方面需要帮助,您可以参考我们的 Android 开发入门 指南。
如果在彻底遵循这些步骤后遇到任何错误,请随时通过 OpenCV 论坛 与我们联系。 我们将尽最大努力帮助您。
带有 SDK 的 Hello OpenCV 示例
在本节中,我们将创建一个简单的应用程序,除了 OpenCV 加载之外什么也不做。 在下一节中,我们将扩展它以支持相机。
除了本说明之外,您还可以使用一些视频指南,例如 这个
- 打开 Android Studio 并通过选择Empty Views Activity创建空项目
- 设置项目
- 选择Java语言
- 选择Groovy DSL构建配置语言
- 选择最小 SDK,其版本号不低于 OpenCV 4 Android 构建期间使用的版本
- 如果您不知道它,您可以在文件
OpenCV-android-sdk/sdk/build.gradle 中的 android -> defaultConfig -> minSdkVersion 找到它
- 单击File -> New -> Import module...并选择 OpenCV SDK 路径
- 将模块名称设置为
OpenCV 并按 Finish
OpenCV 还提供实验性的 Kotlin 支持。 请将 Android Kotlin 插件添加到 MyApplication/OpenCV/build.gradle 文件
plugins {
id 'org.jetbrains.kotlin.android' version '1.7.10' #版本可能因您的设置而异
}
像这样: 
如果您不这样做,可能会收到错误
Task failed with an exception.
-----------
* Where
Build file '/home/alexander/AndroidStudioProjects/MyApplication/opencv/build.gradle' line: 4
* What went wrong
A problem occurred evaluating project ':opencv'.
> Plugin with id 'kotlin-android' not found.
解决方案可以在 这里 找到
OpenCV 项目使用 buildConfig 功能。 请在 MyApplication/OpenCV/build.gradle 文件中启用它到 android 块
buildFeatures{
buildConfig true
}
像这样: 
如果您不这样做,可能会收到错误
JavaCameraView.java:15: error: cannot find symbol import org.opencv.BuildConfig; ^ symbol: class BuildConfig location: package org.opencv
解决方案可以在 这里 和 这里 找到
- 将模块添加到项目
- 单击File -> Project structure... -> Dependencies -> All modules -> + (Add Dependency button) -> Module dependency
- 在使用任何 OpenCV 函数之前,您必须首先加载库。 如果您的应用程序包含其他依赖于 OpenCV 的本机库,您应该在 OpenCV 初始化之后加载它们。 添加以下代码以在应用程序启动时加载库
if (OpenCVLoader.initLocal()) {
Log.i(TAG, "OpenCV 加载成功");
} else {
Log.e(TAG, "OpenCV 初始化失败!");
(Toast.makeText(this, "OpenCV 初始化失败!", Toast.LENGTH_LONG)).show();
return;
}
像这样: 
- 选择一个设备以检查示例,然后按
run 按钮运行代码
带有 Maven Central 的 Hello OpenCV 示例
从 OpenCV 4.9.0 开始,OpenCV for Android 包可通过 Maven Central 获得,并且可以作为 Gradle 依赖项自动安装。 在本节中,我们将创建一个简单的应用程序,除了使用 Maven Central 进行 OpenCV 加载之外什么也不做。
- 打开 Android Studio 并通过选择Empty Views Activity创建空项目
- 设置项目
- 选择Java语言
- 选择Groovy DSL构建配置语言
- 选择最小 SDK,其版本号不低于 OpenCV 支持的版本。 对于 4.9.0,最小 SDK 版本为 21。
- 编辑
build.gradle 并将 OpenCV 库添加到 Dependencies 列表,如下所示dependencies {
implementation 'org.opencv:opencv:4.9.0'
}
4.9.0 可以替换为任何可用的 官方版本。
- 在使用任何 OpenCV 函数之前,您必须首先加载库。 如果您的应用程序包含其他依赖于 OpenCV 的本机库,您应该在 OpenCV 初始化之后加载它们。 添加以下代码以在应用程序启动时加载库
if (OpenCVLoader.initLocal()) {
Log.i(TAG, "OpenCV 加载成功");
} else {
Log.e(TAG, "OpenCV 初始化失败!");
(Toast.makeText(this, "OpenCV 初始化失败!", Toast.LENGTH_LONG)).show();
return;
}
像这样: 
- 选择一个设备以检查示例,然后按
run 按钮运行代码
相机视图示例
在本节中,我们将扩展在上一节中创建的空 OpenCV 应用程序以支持相机。 我们将获取相机帧并在屏幕上显示它们。
- 告诉系统我们需要相机权限。 将以下代码添加到文件
MyApplication/app/src/main/AndroidManifest.xml<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"/>
像这样: 
- 转到
activity_main.xml 布局并删除带有文本“Hello World!”的 TextView
也可以通过从 XML 文件中删除 TextView 块在 Code 或 Split 模式下完成此操作。
将相机视图添加到布局
- 将方案添加到布局描述中
xmlns:opencv="http://schemas.android.com/apk/res-auto"
- 将
TextView 替换为 org.opencv.android.JavaCameraView 小部件<org.opencv.android.JavaCameraView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:visibility="gone"
android:id="@+id/tutorial1_activity_java_surface_view"
opencv:show_fps="true"
opencv:camera_id="any" />
- 如果收到布局警告,请将
android:layout_width 和 android:layout_height 属性的 fill_parent 值替换为 match_parent
您将获得类似这样的代码
<FrameLayout 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/tutorial1_activity_java_surface_view"
opencv:show_fps="true"
opencv:camera_id="any" />
</FrameLayout>
- 从
org.opencv.android.CameraActivity 继承主类。 CameraActivity 实现相机权限请求和 CV 应用程序所需的一些其他实用程序。 我们感兴趣要覆盖的方法是 onCreate, onDestroy, onPause, onResume 和 getCameraViewList
- 实现接口
org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2 onCameraFrame 方法应该返回带有渲染内容的 Mat 对象。 该示例仅返回相机帧以进行预览: return inputFrame.rgba();
- 分配
org.opencv.android.CameraBridgeViewBase 对象
- 应该在应用程序启动时创建它(
onCreate 方法),并且此类应该设置为侦听器
- 在暂停/恢复时(
onPause, onResume 方法)它应该被禁用/启用
- 应该在应用程序完成时禁用它(
onDestroy 方法)
- 应该在
getCameraViewList 中返回
(可选)您可以禁止手机调暗屏幕或锁定
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
最后,您将获得类似于这样的源代码
package org.opencv.samples.tutorial1;
import org.opencv.android.CameraActivity;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Mat;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;
import android.view.WindowManager;
import android.widget.Toast;
import java.util.Collections;
import java.util.List;
public class Tutorial1Activity extends CameraActivity implements CvCameraViewListener2 {
private static final String TAG = "OCVSample::Activity";
private CameraBridgeViewBase mOpenCvCameraView;
public Tutorial1Activity() {
Log.i(TAG, "实例化新的 " + this.getClass());
}
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "调用 onCreate");
super.onCreate(savedInstanceState);
if (OpenCVLoader.initLocal()) {
Log.i(TAG, "OpenCV 加载成功");
} else {
Log.e(TAG, "OpenCV 初始化失败!");
(Toast.makeText(this, "OpenCV 初始化失败!", Toast.LENGTH_LONG)).show();
return;
}
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.tutorial1_surface_view);
mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.tutorial1_activity_java_surface_view);
mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
mOpenCvCameraView.setCvCameraViewListener(this);
}
@Override
public void onPause()
{
super.onPause();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}
@Override
public void onResume()
{
super.onResume();
if (mOpenCvCameraView != null)
mOpenCvCameraView.enableView();
}
@Override
protected List<? extends CameraBridgeViewBase> getCameraViewList() {
return Collections.singletonList(mOpenCvCameraView);
}
@Override
public void onDestroy() {
super.onDestroy();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}
@Override
public void onCameraViewStarted(int width, int height) {
}
@Override
public void onCameraViewStopped() {
}
@Override
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
return inputFrame.rgba();
}
}
就是这样! 现在您可以在设备上运行代码来检查它。
让我们讨论一些最重要的步骤
每个带有 UI 的 Android 应用程序都必须实现 Activity 和 View。 通过第一步,我们创建空白活动和默认视图布局。 最简单的以 OpenCV 为中心的应用程序必须执行 OpenCV 初始化,创建一个视图来显示来自相机的预览,并实现 CvCameraViewListener2 接口以从相机获取帧并处理它们。
首先,我们使用 XML 布局创建应用程序视图。 我们的布局由类 org.opencv.android.JavaCameraView 的唯一一个全屏组件组成。 此 OpenCV 类继承自 CameraBridgeViewBase,后者扩展了 SurfaceView 并在后台使用标准 Android 相机 API。
CvCameraViewListener2 接口允许您在从相机抓取帧之后和在屏幕上渲染帧之前添加一些处理步骤。 最重要的方法是 onCameraFrame。 这是一个回调函数,在从相机检索帧时调用它。 它期望 onCameraFrame 函数返回将在屏幕上绘制的 RGBA 帧。
回调将来自相机的帧作为 CvCameraViewFrame 类的对象传递给我们的类。 此对象具有 rgba() 和 gray() 方法,允许用户获取彩色或单通道灰度帧作为 Mat 类对象。
- 注意
- 不要在
onCameraFrame 回调之外保存或使用 CvCameraViewFrame 对象。 此对象没有自己的状态,并且在回调之外的行为是不可预测的!