【问题标题】:Cropping YUV_420_888 images for Firebase barcode decoding裁剪 YUV_420_888 图像以进行 Firebase 条形码解码
【发布时间】:2019-01-09 14:06:53
【问题描述】:

我正在使用 Camera2 API 以流式(实时)方式使用 Firebase-ML 条形码解码器。我这样做的方法是设置一个定期给我图像的 ImageReader。完整的图像是我相机的分辨率,所以它很大——它是一个 12MP 的相机。

条形码扫描仪在三星 S7 Edge 上处理图像大约需要 0.41 秒,因此我设置 ImageReaderListener 一次解码一个并丢弃任何后续帧,直到解码完成。

我使用的图像格式是 YUV_420_888,因为这是文档推荐的格式,并且因为如果您尝试向 ML 条形码解码器提供其他任何它抱怨的内容(运行时消息到调试日志)。

所有这些都有效,但我认为如果我可以裁剪图像,效果会更好。我想保持相机分辨率不变(这样我就可以显示一个宽 SurfaceView 以帮助用户将他的相机与条形码对齐),但我想给 Firebase 一个裁剪版本(基本上是一个中心矩形)。 “工作得更好”主要是指“更快”,但我也想消除干扰(尤其是可能位于图像边缘的其他条形码)。

这让我试图找出裁剪 YUV 图像的最佳方法,但我惊讶地发现几乎没有帮助。我在网上找到的大多数示例都有一个多步骤过程,您首先将 YUV 图像转换为 JPEG,然后将 JPEG 渲染为位图,然后对其进行缩放。这在我心中有几个问题

  • 这似乎会对性能产生重大影响(实时)。这将帮助我完成一些事情,包括减少一些功耗、缩短响应时间以及让我更快地通过 image.close() 将图像返回给 ImageReader。
  • 这种方法不会让您回到 Image,因此您必须改为向 Firebase 提供位图,这似乎也不起作用。我不知道 firebase 在内部做什么,但我有点怀疑它主要(可能完全)在 Y 平面之外工作,并且如果 Image -> JPEG -> Bitmap 的翻译混淆了它。

我四处寻找可能有帮助的 YUV 库。在野外有一个叫做 libyuv-android 的东西,但它不能完全按照 firebase-ml 想要的格式工作,而且它是一堆 JNI,它给了我跨平台的问题。

我想知道是否有其他人考虑过这一点并提出了一个更好的解决方案来在 Android 中裁剪 YUV_420_488 图像。我找不到这个是因为它是一个相对微不足道的操作吗?除其他事项外,还需要关注步幅和填充。我不是图像/颜色专家,我觉得我不应该自己尝试这个,我特别担心的是我发现了一些适用于我的设备但不适用于其他设备的东西。

更新:这实际上可能没有实际意义。作为一个实验,我查看了从 ImageReader 返回的图像。它是 ImageReader.SurfaceImage 的一个实例,它是一个私有(对于 ImageReader)类。它也有一堆本地搭配。因此,唯一的选择可能是执行压缩/解压缩方法,这似乎很蹩脚。我唯一能想到的另一件事是自己决定只使用 Y 平面并从中制作位图,然后看看 Firebase-ML 是否可以。这种方法对我来说仍然存在风险。

【问题讨论】:

  • 查看相关讨论:How to manipulate on the fly YUV Camera frame efficiently in Android?。无论如何,您可能看错了 ImageReader,它控制显示,而不是将 YUV 图像传递给条形码扫描仪。
  • 那种帮助 - 至少它向我展示了类似的东西。我现在认为我可能会裁剪图像,但我还有另一个问题:android.media.Image 是抽象的,但它的构造函数是包保护的。所以我不能继承 Image。这意味着我什至无法制作自己的图片(至少不能通过简单的子类化)
  • 您不能创建Image,但为什么需要这样呢?您可以裁剪现有的Image
  • Firebase Vision 忽略cropRect:我相信这值得new feature request,但甚至可能被视为一个错误。同时,在谷歌花时间解决这个问题之前,你可以使用FirebaseVisionImage.fromByteArray()。输入将从相机图像的 Y 平面裁剪。
  • @GMX 如果你有一个 ByteArray,你可以将它的一部分复制到另一个 ByteArray,并通过伪造的metadata 传递更小的维度。最简单的裁剪方法是制作与旧图像宽度相同的新图像。

标签: firebase android-camera2


【解决方案1】:

我今天尝试缩小 YUV_420_888 输出图像。我认为它与裁剪图像非常相似。

在我的例子中,我将把 Image.plane 中的 3 字节数组用于表示 Y、U 和 V。 yBytes = Image.plane[0] uBytes = Image.plane[1] vBytes = Image.plane[2]

然后我将其转换为 RGB 数组进行位图转换。我发现如果我通过第 2 步读取具有原始图像宽度和高度的 YUV 数组,那么我可以缩放一半的位图图像。

你应该准备什么: y字节, 超字节, 字节, 图像的宽度, 图像的高度, y 行步幅, uv RowStride, uv 像素步幅, 输出数组(输出数组长度的长度应该等于输出图像的宽度*高度。对我来说大小是原始宽度和高度的1/4)

这意味着如果你能在你的图像中找到裁剪区域的位置(图像的四个角),你就可以用 YUV 数据填充新的 RGB 数组。

希望它可以帮助您解决问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-07
    • 2014-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多