上一教程: Android 开发入门
下一教程: 如何在 Android 设备上运行深度网络
| |
原始作者 | Alexander Panov、Rostislav Vasilikhin |
兼容性 | OpenCV >= 4.9.0 |
创建本教程是为了帮助你在 Android 项目中使用 OpenCV 库。
此指南已在 Ubuntu 上检查过,但不包含依赖于平台的部分,因此应该与 Android Studio 和 OpenCV4Android SDK 支持的任何操作系统兼容。
本教程假定你已安装并配置以下内容
- Android Studio
- JDK
- Android SDK 及 NDK
- 可选:从 GitHub 上的官方 发布页面 或 SourceForge 下载 OpenCV for Android SDK。高级:或者可以根据 wiki 上的说明 从源代码构建 SDK。
如果你对上述任何内容需要帮助,可以参阅我们的 Android 开发入门 指南。
如果你在仔细按照这些步骤操作后遇到任何错误,请随时通过 OpenCV 论坛 联系我们。我们将尽力帮助你解决问题。
带 SDK 的 Hello OpenCV 示例
在本部分中,我们将创建一个简单的应用,除了加载 OpenCV 外什么也不做。在下一部分中,我们将对其进行扩展以支持相机。
除了本说明,还可以使用一些视频指南,例如 这个
- 打开 Android Studio 并通过选择Empty Views Activity 创建一个空项目
- 设置项目
- 选择Java 语言
- 选择Groovy DSL 构建配置语言
- 选择Minimum 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' #version may differ for your setup
}
像这样:
如果不执行此操作,您可能会收到错误
任务失败,出现异常。
-----------
* 所在位置
构建文件 '/home/alexander/AndroidStudioProjects/MyApplication/opencv/build.gradle',行:4
* 出错原因
在评估项目“:opencv”时出现问题。
> 找不到 ID 为“kotlin-android”的插件。
在这里发现了修复方法 here
OpenCV 项目使用 buildConfig
功能。在 MyApplication/OpenCV/build.gradle
文件中启用它,改为 android
块
buildFeatures{
buildConfig true
}
如下所示:
如果不执行此操作,您可能会收到错误
JavaCameraView.java:15: 错误:找不到符号导入 org.opencv.BuildConfig; ^ 符号:类 BuildConfig 位置:包 org.opencv
在这里发现了修复方法 here 和 here
- 将模块添加到项目中
- 点击文件 -> 项目结构... -> 依赖关系 -> 所有模块 -> +(添加依赖关系按钮) -> 模块依赖关系
- 在使用任何 OpenCV 函数之前,您必须先加载库。如果您的应用程序包含其他依赖于 OpenCV 的本机库,则应在初始化 OpenCV 之后加载这些库。添加以下代码以在应用程序启动时加载库
if (OpenCVLoader.initLocal()) {
Log.i(TAG, "OpenCV 加载成功");
} else {
Log.e(TAG, "OpenCV 初始化失败!");
(Toast.makeText(this, "OpenCV 初始化失败!", Toast.LENGTH_LONG)).show();
return;
}
如下所示:
- 选择一台设备,在该设备上检查示例,然后通过按
运行
按钮运行代码
具有 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 构建配置语言
- 选择最小 API 级别,其版本号不低于 OpenCV 支持的版本号。对于 4.9.0,最小 API 等级为 21。
- 编辑
build.gradle
,并将 OpenCV 库添加到依赖关系列表,如下所示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;
}
如下所示:
- 选择一台设备,在该设备上检查示例,然后通过按
运行
按钮运行代码
摄像头视图示例
在本节中,我们将扩展在前一节中创建的空 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
块在代码或拆分模式下执行此操作。
将摄像头视图添加到布局中
- 将模式添加到布局说明中
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, "Instantiated new " + this.getClass());
}
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "called 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。第一步,我们创建空白 Activity 和默认视图布局。最简单的以 OpenCV 为中心的应用程序必须执行 OpenCV 初始化、创建一个用于显示来自摄像头的预览的视图并实现 CvCameraViewListener2
接口以从摄像头获取帧并对其进行处理。
首先,我们使用 XML 布局创建我们的应用程序视图。我们的布局仅包含一个 org.opencv.android.JavaCameraView
类的全屏组件。此 OpenCV 类是从 CameraBridgeViewBase
继承的,它扩展了 SurfaceView
,并且实际上使用了标准 Android 摄像头 API。
CvCameraViewListener2
接口允许您在从摄像头获取帧之后和在屏幕上呈现帧之前,添加一些处理步骤。最重要的方法是 onCameraFrame
。这是一个回调函数,在从摄像头中检索到帧时会调用该函数。它期望 onCameraFrame
函数返回将在屏幕上绘制的 RGBA 帧。
回调以 CvCameraViewFrame
类对象的格式将来自摄像头的帧传递给我们类。此对象具有 rgba()
和 gray()
方法,可让用户以 Mat
类对象的形式获取彩色帧或单通道灰度帧。
- 注意
- 请勿在
onCameraFrame
回调之外保存或使用 CvCameraViewFrame
对象。此对象没有自己的状态,在回调之外的行为不可预测!