65.9K
CodeProject 正在变化。 阅读更多。
Home

使用BoofCV轻松实现Android Camera2

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2018年11月13日

CPOL

7分钟阅读

viewsIcon

14571

本文演示了 BoofCV 如何极大地简化 Android 上的相机操作。

引言

本文演示了 BoofCVAndroid 库如何极大地简化了 Camera2 API 的操作、相机图像处理和结果可视化。Android 开发和 Camera2 API 都非常复杂,例如,Google 的 Camera2Basic 示例就证明了这一点。该示例涵盖了您需要完成的大约一半的工作。

使用 BoofCV,您只需告诉它您想要的分辨率,实现一个图像处理函数,并(可选地)编写一个可视化函数。BoofCV 将选择相机、打开相机、创建线程池、同步数据结构、处理 Android 生命周期(正确打开/关闭相机、停止线程)、转换 YUV420 图像、正确对齐输入像素到屏幕像素,并根据请求更改相机设置。

本文将讨论如何编写自己的自动对焦程序。虽然不如深度学习(例如,对所有对象进行分类、人脸老化)那样吸引人,但本文的重点是实用性并制作出可用的东西。如果应用程序不断崩溃,那么您的计算机视觉算法再出色也无济于事!一旦您理解了这段代码的工作原理,您可以将其与其他算法结合,例如,让相机只对焦在狗而不是人身上。

如果您想查看使用此库编写的其他 Android 计算机视觉示例,请查看 BoofCV 演示应用程序

Google Play 商店上的 APK

自动对焦代码正在运行。这是硬件和计算机视觉的有趣组合,您通常不会看到这样的示例。

自动对焦算法

对焦内和对焦外图像的示例。注意到对焦内图像的边缘强度更大吗?

内置自动对焦的行为因制造商而异,在计算机视觉应用中,我的经验是它们倾向于对焦除了您想要对焦的任何东西。为了解决这个问题,我们将通过手动控制相机的对焦并找到图像“最锐利”的对焦值来自己实现自动对焦。图像的锐度由其梯度强度决定。图像的梯度是其 x 和 y 空间导数。梯度的强度可以用不同的方式定义。在这里,我们使用欧几里得范数,即 sqrt(dx**2 + dy**2)

自动对焦算法是一个有限状态机,如上图所示。它穷尽所有对焦值以选择具有最大边缘强度的一个。一旦找到最大边缘强度,它就会固定对焦。您可以通过点击屏幕重新开始该过程。

源代码亮点

此时,您应该查看 Github 上的源代码并稍微探索一下代码。我们将逐步介绍设置您自己的项目中的关键步骤以及源代码本身中的重要行。假设您已经对 Android 开发有所了解,并且为了简洁起见,跳过了一些步骤。

第一步:在 Android Studio 中创建一个新项目

本文被标记为中级,所以我假设您可以在没有图片和视频的情况下完成此操作。最低 SDK 必须是 22。这是 Camera 2 API 中一个恼人 bug 被修复的第一个版本。

第二步:依赖项

将以下内容添加到 app/build.gradle 中的依赖项字段。

dependencies {
    ['boofcv-android', 'boofcv-core'].each {
        String a ->
            implementation group: 'org.boofcv', name: a, version: '0.32-SNAPSHOT'
    }
}

您还需要排除一些传递依赖项,因为 Android 包含了它们自己的版本,并且会出现冲突。

configurations {
    all*.exclude group: "xmlpull", module: "xmlpull"
    all*.exclude group: "org.apache.commons", module: "commons-compress"
    all*.exclude group: "com.thoughtworks.xstream", module: "commons-compress"
}

然后让 Android Studio 同步您的 Gradle 文件。

第三步:Android 权限

app/src/main/AndroidManifest.xml 中,您需要授予自己相机访问权限

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera2.full" />

第四步:MainActivity: OnCreate()

您现在应该打开 MainActivity.java 文件。注意这个类如何扩展 VisualzieCamera2Activity。如果您想在屏幕上渲染某些内容,这是您应该扩展的活动。否则,如果您只需要相机帧,则可以扩展 SimpleCamera2Activity

查看 onCreate() 函数。此函数中正在进行很多操作;获取相机权限、指定输出图像类型、所需分辨率以及关闭双缓冲。让我们逐行讲解。

TextureView 显示原始相机预览。FrameLayout 用于绘制视觉效果。我们还添加了一个触摸监听器,以便用户可以重新启动对焦算法。稍后会详细介绍。

TextureView view = findViewById(R.id.camera_view);
FrameLayout surface = findViewById(R.id.camera_frame);
surface.setOnClickListener(this);

在这里,我们向用户请求相机访问权限。这在网络上已经广泛涵盖,因此请查看源代码以获取有关如何完成此操作的详细信息。

requestCameraPermission();

BoofCV 需要知道我们想要的图像格式。在这里,我们告诉它给我们一个灰度 8 位图像。

setImageType(ImageType.single(GrayU8.class));

相机通常支持多种分辨率。BoofCV 将为我们处理分辨率选择,但我们应该告诉它一些关于我们要求的信息。下面这行代码的作用是告诉 BoofCV 找到一个具有大约那么多像素的图像分辨率。通过覆盖 selectResolution() 函数可以实现更复杂的逻辑。

targetResolution = 640*480;

BoofCV 还会自动渲染捕获的帧。在这种情况下,我们不希望这样做,只想显示原始相机馈送。所以让我们关闭该行为。

bitmapMode = BitmapMode.NONE;

最后,我们告诉它启动相机并使用以下 surface 和视图进行可视化。

startCamera(surface,view);

第五步:相机控制

默认情况下,相机配置为全自动模式。我们不希望这样,因此我们用自己的逻辑覆盖默认的 configureCamera() 函数。BoofCV 定义了 configureCamera(),它不是内置的 Camera2 API 函数。直接使用 Camera2 API 来做这件事需要更多的工作。

下面的代码注释描述了每个代码块正在做什么。实质上,它实现了上面的有限状态机。

@Override
protected void configureCamera(CameraDevice device, 
     CameraCharacteristics characteristics, CaptureRequest.Builder captureRequestBuilder) {
   // set focus control to manual
   captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);

   // get a list of acceptable values
   Float minFocus = characteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE);

   switch( state ) {
      case INITIALIZE:{
         if( minFocus == null ) {
            Toast.makeText(this,"manual focus not supported", Toast.LENGTH_SHORT).show();
            state = State.UNSUPPORTED;
         } else {
            focusBestIndex = 0;
            focusBestValue = 0;
            focusIndex = 0;
            focusTime = System.currentTimeMillis()+FOCUS_PERIOD;
            state = State.FOCUSING;
            captureRequestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, 0f);
         }
      }break;

      case PENDING:{
         focusIndex++;
         if( focusIndex < FOCUS_LEVELS ) {
            focusTime = System.currentTimeMillis()+FOCUS_PERIOD;
            captureRequestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, 
                                      minFocus*focusIndex/(FOCUS_LEVELS-1));
            state = State.FOCUSING;
         } else {
            captureRequestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, 
                                      minFocus*focusBestIndex/(FOCUS_LEVELS-1));
            state = State.FIXED;
         }
      }break;
   }
}

第六步:图像处理

BoofCV 提供了自己的图像处理函数,该函数自动将流式 YUV420_888 图像转换为您理解的图像。它还为您启动了一个线程来处理图像。这意味着您可以随心所欲地处理,而不会崩溃!如果您的图像处理无法跟上相机的数据流,则会简单地丢弃帧。

@Override
protected void processImage(ImageBase image) {
   // We specified earlier to give us an 8-bit gray scale image
   GrayU8 gray = (GrayU8)image;
   
   // Ensure that work space images are the appropriate size
   derivX.reshape(gray.width,gray.height);
   derivY.reshape(gray.width,gray.height);
   intensity.reshape(gray.width,gray.height);
   
   // Compute the gradient and Euclidean edge intensity
   GImageDerivativeOps.gradient(DerivativeType.SOBEL,gray,derivX,derivY, BorderType.EXTENDED);
   GGradientToEdgeFeatures.intensityE(derivX, derivY, intensity);

   // Find the average edge value
   edgeValue = ImageStatistics.mean(intensity);
}

第七步:绘制

通常,您需要为 SurfaceView 创建一个回调,但这也会为您处理。您仍然需要覆盖下面的 onDrawFrame 函数。下面的代码是相当标准的 Android 绘图,这就是为什么它没有被突出显示。如果您想了解如何使用 BoofCV 渲染正在处理的实际图像(而不是实时视频流)并将视觉效果与精确像素对齐,请参阅 QR 码示例

@Override
protected void onDrawFrame(SurfaceView view, Canvas canvas) {
   super.onDrawFrame(view, canvas);
   ....
}

第八步:响应用户触摸

onCreate() 中,我们添加了一个触摸监听器。这允许用户重新启动自动对焦。下面的代码显示了如何完成此操作。通过等到自动对焦处于 FIXED 状态,我们无需担心相机是否忙碌,从而简化了代码。

@Override
public void onClick(View v) {
   // If the user touches the screen and it has already finished focusing, start again
   if( state == State.FIXED ) {
      state = State.INITIALIZE;
      changeCameraConfiguration();
   }
}

使用其他计算机视觉库

在此示例中,我们使用 BoofCV 进行图像处理。您完全可以使用其他库,例如 OpenCV 或您喜欢的深度学习库(例如 Torch)。您需要做的是将 BoofCV 图像转换为其他库能够理解的图像。这比首先获取图像要容易得多!您可以完全访问 BoofCV 图像中的原始字节数组和图像特性(例如 widthheightstride)。

其他示例

BoofCV 的存储库包含一些额外的极简 Android 示例。

结论

我们成功地在 Android 上创建了一个功能齐全的计算机视觉应用程序,该应用程序在库中操作相机设置,同时处理图像并向用户显示信息。这一切都无需深入了解 Android 生命周期、Camera2 API 的工作原理或 YUV420 是什么,使您能够轻松地投入工作并创建酷炫的计算机视觉应用程序!

历史

  • 2018 年 11 月 13 日:初始版本
© . All rights reserved.