博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
简单的Android Camera2与BoofCV
阅读量:3533 次
发布时间:2019-05-20

本文共 5782 字,大约阅读时间需要 19 分钟。

目录


本文将演示BoofCV如何大大简化在Android上使用相机的工作。

介绍

本文将演示如何极大地简化了,处理相机图像以及可视化结果。Android开发和  Camera2API都非常复杂,例如    示例所示。这个例子涵盖了你需要做的事情的大约1/2

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

我们将在本文中讨论的是编写自己的自动聚焦例程。虽然不像使用最新的深度学习那样性感(例如,对所有物体进行分类,老化你的脸),但这篇文章是关于实用和制作有用的东西。不管你的计算机视觉算法有多么令人惊讶,如果应用程序总是崩溃!一旦你理解了这段代码是如何工作的,你就可以将它与其他算法结合起来,以便将相机专注于狗而不是人。

自动聚焦算法

https://www.codeproject.com/KB/android/1266363/focus_example.jpg

聚焦和失焦图像的示例。请注意焦点图像中边缘强度如何变大?

内置自动对焦的行为因制造商和计算机视觉应用程序而异,我的经验是他们倾向于专注于除了你想要的任何东西。为了解决这个问题,我们将通过手动控制相机的焦点并使用最大图像最清晰找到焦点值来进行自己的自动对焦。图像的清晰度由其梯度强度决定。图像的梯度是其xy空间导数。梯度的强度可以以不同的方式定义。在这里,我们使用欧几里德范数,即sqrt(dx**2 + dy**2)

https://www.codeproject.com/KB/android/1266363/focus_fsm.png

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

源代码亮点

此时,您应该查看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,您需要授予自己对相机的访问权限:

第四步:MainActivity:OnCreate()

您现在应该打开    文件。注意这个类如何扩展  。如果您希望在屏幕上呈现某些内容,那么您应该扩展该活动。否则, 如果您只需要相机框架,则可以扩展  

看看  onCreate() 功能。这个功能正在发生很多事情获取相机权限,指定输出图像的类型,所需的分辨率以及关闭双缓冲。让我们一行一行进行。

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

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

在这里,我们请求用户访问摄像机。这已经在整个web中广泛地被覆盖,因此请查看源代码,了解如何完成此操作。

requestCameraPermission();

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

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

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

targetResolution = 640*480;

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

bitmapMode = BitmapMode.NONE;

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

startCamera(surface,view);

第五步:摄像头控制

默认情况下,摄像机配置为处于全自动模式。我们不希望这样,所以我们用自己的逻辑覆盖默认configureCamera()函数。BoofCV定义configureCamera()并且它不是内置的Camera2API函数。直接使用Camera2API完成这项工作需要做很多工作

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

@Overrideprotected 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图像转换为您理解的图像。它还为您启动了一个处理图像的线程。这意味着您可以随意使用而不会崩溃!如果您的图像处理无法跟上相机的进给速度,则会丢弃相框。

@Overrideprotected 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渲染正在处理的实际图像(不是实时视频输入)并将视觉效果与精确像素对齐,请参阅

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

第八步:响应用户触摸

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

@Overridepublic 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使您能够进入商业领域并且以最少的繁琐工作创建酷的计算机视觉应用程序的情况下,就完成了此任务!

 

原文地址:

转载地址:http://zzzhj.baihongyu.com/

你可能感兴趣的文章
StringBuilder
查看>>
集合,Collection
查看>>
泛型详解
查看>>
泛型实现斗地主
查看>>
List集合
查看>>
ArrayList集合,LinkedList集合,Vector集合
查看>>
HashSet集合
查看>>
并发与并行,线程与进程
查看>>
方法引用,通过对象名引用成员变量
查看>>
常用工具类 Math:数学计算 Random:生成伪随机数 SecureRandom:生成安全的随机数 2020-2-13
查看>>
Java的异常Exception 2020-2-13
查看>>
Java标准库定义的常用异常,自定义异常 2020-2-15
查看>>
Java问题百度/Google记录 2020-2-16
查看>>
【PADS9.5】9,对比ECO核心板,Router移动元件后布线消失,Router找不到自动布线策略文件丢失或损坏
查看>>
【STM32+w5500汇总】23,HTTP_Client 连接到ONENET上传了一段数据之后会断开,数据上传格式的设置
查看>>
【STM32+W5500+MQTT】24,所有功能都可以通过API函数的调用来实现;HTTP接入ONENET,API开发手册和打包函数,串口软件HTTP连接服务器上传数据,2018年12月28日
查看>>
【STM32+W5500+HTTPClient】25,路由器DHCP租赁IP时间为2h,NetBios可以很好的解决IP变化的问题,DNS,2018年12月25日
查看>>
【STM32+MQTT+ONENET】26,MQTT协议接入OneNET
查看>>
【STM32+W5500+MQTT+ONENET】27,MQTT协议接入OneNET实际编程操作 2018年12月27日
查看>>
【STM32Cube+FreeRTOS 】28,KEIL5的F12不起作用;***JLink Error: Can not read register x while CPU is running
查看>>