为了账号安全,请及时绑定邮箱和手机立即绑定

Android上的AI视觉:CameraX图像分析+ MediaPipe+ Compose手部手势识别实战

Illustration of a brain in a circuit board pattern图片来源: Steve Johnson,感谢 Steve Johnson 提供图片

上周我们学习了 CameraX 库的基础知识。这为一个非常令人兴奋的东西奠定了基础……AI 视觉!现在我们可以使用你的 Android 设备来理解和分析我们周围的物理世界。

AI视觉拥有巨大的潜力,比如识别照片中的内容,分割或遮罩图像中的特定区域,或者识别身体姿势、笑容等。所有这些都可以在你的手机上运行——无需上网,也不必将摄像头数据发送给第三方。

制作一个手势识别器

在这篇文章里,我们将通过做一个手部手势识别器来展示这一点,就像这样,

Animated image of me performing hand gestures, and the phone converting them to emojis

CameraX 提供实时流给一个AI,该AI可以识别我的手势动作

比如,我的手势被转换成了表情。我们将用到两个库。

  • CameraX 是 Android Jetpack 的一个库,它让在 Android 上使用相机功能变得简单得多。(如果你之前没用过它来做相机相关的工作,你可能会觉得这很难 😆)。
  • MediaPipe 是一个跨平台的库,用于设备上的 AI 任务。它涵盖音频、视频和文本相关的 AI 任务的模型。我们会用它自带的手势识别模型来识别手部动作。

我们先从CameraX这边开始,获取来自相机的实时帧。然后我们将这些帧传递给AI模型进行分析。所有的处理都将在这台设备上完成,当然。

本文中提到的所有代码的示例应用程序可以在这里找到:https://github.com/tdcolvin/MediaPipeCameraXDemo

提示:CameraX 的应用场景:

CameraX 适合完成四个特定的任务,它称之为 使用案例。在我的上一篇文章里,我们构建了一个使用了其中两个使用案例的示例

  • 预览功能允许我们显示相机的画面(就像透过取景器)
  • 拍摄照片功能允许我们拍张照片

另外两个用例是录视频(用来录视频)和分析图像(用来接收实时视频流)。

我们将在这里使用图像分析案例。它提供的实时帧序列将直接传输给MediaPipe,MediaPipe将利用这些帧来推进行我们的AI模型。

第一步:添加 CameraX 库支持并设置预览用例

在我的上一篇文章中,我展示了如何在Gradle中添加CameraX依赖项,如何将PreviewView链接到Preview用例,以及如何通过CameraProvider将所有这些组件绑定在一起。

如果你之前完全没有使用过CameraX,我建议你从CameraX开始。在开始这篇文章之前,你需要先了解一些概念。

这里我们将从之前构建的CameraPreview可组合项版本开始。和之前一样,但为了简化,我已去除了相机切换、缩放和图像捕获功能的支持。

第二步:创建图像分析的应用场景

接下来我们需要用ImageAnalysis用例来处理实时视频流。和其他CameraX中的用例一样,它是通过构建者模式创建的。

这个 imageAnalyzer 函数将会由 CameraX 每当有图像准备好处理时调用。之后我们会用它来执行 MediaPipe 的代码。目前,我们只需要在 ViewModel 里添加一个空的实现。

我们需要关闭掉我们收到的图片,以便CameraX知道我们准备好下一张了。

步骤 3:关联图像分析(ImageAnalysis)用例

现在我们已经创建了图像分析用例,接下来需要让CameraX实际使用它。CameraProvider.bindToLifecycle(...)函数就像胶水一样,将物理摄像机与用例紧密地绑定在一个特定的生命周期中。在我们的演示应用中,这是通过CameraPreview可组合项中的rebindCameraProvider函数来实现的。因此,我们需要将图像分析用例传递给这个可组合项。

太棒了!现在如果我们运行这个应用程序,就能看到相机预览。

更重要的是,我们可以看到有一连串的帧不断发送到我们的图像分析器。

A load of logcat entries which say “Received frame”

LogCat日志条目显示我们的imageAnalyzer被调用了。

哇哦!相机部分搞定了。现在我们用MediaPipe来检测手势吧。

步骤 4:添加 MediaPipe 依赖项

我们要使用的MediaPipe库是tasks-vision,可以在Gradle构建工具中这样添加。

附注:MediaPipe 版本依赖问题

在撰写本文时,当前MediaPipe的版本是0.10.20。出于某种原因,几年前发布的MediaPipe库版本号是根据日期形成的:0.20230731。这可能是错误——不管是什么原因,这意味着Android Studio会认为这是最新版本。

Android Studio warning on MediaPipe version: A newer version of com. google. mediapipe:tasks-vision than 0.10.20 is available: 0.20230731 More… \(Ctrl+F1\)

20230731 大于 10,对吧?不对。不要接受这种变化,这可能会导致问题。你还得手动检查 MediaPipe 发布页面 看看有没有新版本,直到 v1.0.0 发布为止,因为 Android Studio 总是会弄错。

步骤五:现在来添加AI模型吧

MediaPipe只是一个司机而已。它需要一辆车来驾驶,也就是一个AI模型,也就是说,需要一个可以输入并输出结果的AI模型。

MediaPipe 可以接受几乎所有为 LiteRT(以前称为 Tensorflow Lite)构建的 AI 模型,不过,有些模型可能太大,无法适应手机的内存。HuggingFace 是一个很好的资源,你可以在这里搜索并下载模型。

对于手部姿态识别,我使用了gesture_recognizer.task。我在MediaPipe的示例代码中发现了这个文件,但没有找到为原作者提供适当署名的信息(或许这个文件是专门为该示例创建的)。如果您知道原作者是谁,请告诉我,我会妥善署名的!

MediaPipe 希望在资产目录或源集中找到这个文件,所以我们将它放在那里。

The gesture_recognizer.task in the assets source set

视频 VS 图像模型

我们的手势识别器任务是在单张静态图像上操作。因此,它也适用于视频,因为视频本质上就是一系列连续的单张图像。我们将对每个帧单独运行模型,而且模型本身不会使用或记住之前帧的数据。

这类模型专门设计用于处理视频内容,通常需要较大的内存。

步骤六:实现手势识别功能

我们已经成功安装了MediaPipe库,并且AI模型也已经到位了。为了使用这个模型,我们需要一个MediaPipe的手势识别器实例。接下来,我们将用它来传递图像帧。

就像 CameraX 那样,MediaPipe 也大量采用了建造者模式。所以我们构建了一个 GestureRecognizerOptions 对象,其中用到了一个 BaseOptions 对象,它指示模型文件的位置。然后从这个 GestureRecognizerOptions 对象创建出 GestureRecognizer。

我们将要使用的GestureRecognizerOptions方法有,:

  • setRunningMode(RunningMode.LIVE_STREAM) 表示我们将从实时视频流中传递帧,并且它将异步发送持续的结果给我们。(另一种选择是 RunningMode.IMAGE,我们将提供单张图片,它将同步返回一个答案。)
  • setResultListener(…) 指定一个在模型结果可用时异步调用的函数。

一旦创建了 GestureRecognizerOptions 实例,我们就可以通过 GestureRecognizer.createFromOptions(…) 来创建 GestureRecognizer 实例。

步骤 7:把帧送到手势识别器那边。

好的,现在CameraX已经把帧传给了imageAnalyzer函数来自步骤2,并且这个手势识别器已经准备好了分析这张图像。

咱们把这两头连起来吧,这样一来,手势识别就能得到帧了。

我们从CameraX获取的图像将会是以相机的原始方向呈现——这不一定与实际的设备方向一致,也不一定与手机/平板的握持方向一致。因此,我们需要旋转图像以使其方向与设备方向一致。

此外,现代相机拍出的照片对于大多数AI任务而言太大了。通常,AI模型——特别是LiteRT——运行在非常小的图片上。给我们的手势识别器提供超过500px大小的图片是不必要的,即使是500px也有些大了。更大的图片只会增加延时,因为模型需要处理更多内容。

我们也得把图片调大一点。

我们将把缩放和调整大小的功能添加到imageAnalyzer(图像分析器)中,这是步骤2中的内容。

最后一步,我们需要将处理过的图片传递到我们的手势识别器。

当有结果时,我们会把它们传递给 handleGestureRecognizerResult 方法。现在我们来把这个方法填一下。

第 8 步:处理手势识别器得到的结果

现在,图像正在传给模型,模型在处理图像并给出反馈。这个反馈告诉我们它识别了哪些手势。所以我们来解析结果,并将它们以表情符号的形式显示出来。

响应是一个GestureRecognizerResult实例的形式,它包含一个gestures()函数。这个函数返回一个已识别手势的列表,每个手势都有可能的选项列表,说明它可能代表什么。

这挺复杂的。我们来举个例子吧。

假如它看到了这张图片

Photo of thumbs up and thumbs down

感谢 Mike Murray 提供图片。

那儿有两个手势。如果咱们的模型效果不错,结果会显示识别出了两个手势。

让我们假设第一个是指左边的手势。希望这个手势能够毫无疑问地显示为竖大拇指。但由于AI并不完美,这个竖大拇指可能会被识别为其他东西。因此,结果会列出这个手势可能是什么的列表,每个都有一个分数。这个分数在0到1之间,其中1表示它非常确定。例如:

  • 竖大拇指 ,得分:0.9
  • 握拳 ,得分:0.2
  • 竖起手指 ,得分:0.05

这里大概率是一个竖大拇指的手势,但也有可能只是一个握紧的拳头,几乎不可能是某个竖起来的手指。

而且第二个手势也会有一个类似的可能列表,包括相应的置信度评分。

在人工智能领域中,每个可能的手势被称为一个类别。我们将选择第一个被识别的手势,并为该手势选择置信度得分最高的那个类别。

我们最后会把这个类别转换成一个表情符号,并在界面上展示出来。

就这样好了!终于我们的App将检测手部手势。

Me doing a thumbs-up

结束

我们已经知道如何:

  • 使用 CameraX 的 ImageAnalysis 来实时获取相机画面。
  • 添加 MediaPipe tasks-vision 库并用它来识别手势。
  • 调整并旋转 CameraX 的输出。
  • 处理手势识别的结果,并理解其置信度。

你可以在我的 GitHub 上查看 上述所有示例代码以及一个运行中的应用

我希望这应该是一个有用的教程,但请随时告诉我你的反馈或问题。你可以在评论区、领英(LinkedIn)或蓝空(BlueSky)上找到我。(请务必在BlueSky上关注我哦!我刚加入不久,快来关注我吧!😆)

需要开发使用AI或相机功能的应用程序吗?或者任何应用程序,无论是Android、iOS还是网页应用?我是一名专业的Android开发顾问,也是 Apptaura 的联合创始人,一家专注于应用程序开发的公司。如果需要帮忙进行您的最新项目,请 通过LinkedIn联系我

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消