在本节中,我们将扩展上一节中创建的空 OpenCV 应用程序以支持摄像头。我们将获取摄像头帧并将其显示在屏幕上。
告诉系统我们需要相机权限。将以下代码添加到文件MyApplication/app/src/main/AndroidManifest.xml
:
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
TextView
这也可以在代码或拆分模式下通过从 XML 文件中删除块来完成。
-
将相机视图添加到布局中:
在布局描述中添加方案:
xmlns:opencv = "http://schemas.android.com/apk/res-auto"
TextView
用小部件替换org.opencv.android.JavaCameraView
:
XML
<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" />
- 如果你收到布局警告,请用for和properties替换
fill_parent
值match_parent``android:layout_width``android:layout_height
您将获得如下代码:
XML
<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" >
<!-- [camera_view] -->
<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" />
<!-- [camera_view] -->
</FrameLayout>
- 从 继承主类。CameraActivity 实现相机权限请求和
org.opencv.android.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
- 它应该在应用程序启动时创建(
- 您可以选择禁止手机调暗屏幕或锁定:
java
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
最终的源码:
java
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 loaded successfully");
} else {
Log.e(TAG, "OpenCV initialization failed!");
(Toast.makeText(this, "OpenCV initialization failed!", 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
类对象。
笔记
不要保存或使用回调CvCameraViewFrame
之外的对象onCameraFrame
。此对象没有自己的状态,并且其在回调之外的行为不可预测!