Android SDK:获取原始预览相机图像而不显示它

Posted

技术标签:

【中文标题】Android SDK:获取原始预览相机图像而不显示它【英文标题】:Android SDK: Get raw preview camera image without displaying it 【发布时间】:2012-05-27 17:33:53 【问题描述】:

我想对原始图像进行图像处理而不在屏幕上显示,这显然会降低性能。

根据该线程Taking picture from camera without preview 的答案,这在 android 1.5 中是不可能的,但有人知道在 Android 4(API 级别 15)中是否可能吗?

【问题讨论】:

刚刚在三星 Galaxy s5 上尝试了虚拟 SurfaceTexture 解决方案(方法 2)。几帧后它确实失败了。我的解决方法是实际调用 updateTexImage,但使用带有有效 GL 上下文的无效纹理名称。将产生的异常静音后,一切正常。 【参考方案1】:

在 Android 4 中,接收原始图像数据而不将其显示在屏幕上的最简单方法是使用 Camera.setPreviewTexture() 调用将预览帧路由到 GPU。

您可以通过两种方式使用它:

    在 GPU 上进行实际处理:设置 OpenGL 上下文 (OpenGL ES 2 tutorial),并在该上下文中创建 SurfaceTexture 对象。然后将该对象传递给 setPreviewTexture,并开始预览。然后,在您的 OpenGL 代码中,您可以调用 SurfaceTexture.updateTexImage,与 SurfaceTexture 关联的纹理 ID 将更新为来自相机的最新预览帧。如果需要,您还可以使用 glReadPixels 将 RGB 纹理数据读回 CPU 进行进一步处理。 在 CPU 上进行处理:您可以简单地创建一个虚拟 SurfaceTexture 对象,而无需设置任何 OpenGL 上下文。传递您想要的任何整数作为纹理 ID,并使用 setPreviewTexture 将 SurfaceTexture 连接到相机。只要您不调用 updateTexImage,SurfaceTexture 就会简单地丢弃相机传递给它的所有数据。然后使用setPreviewCallback 设置预览回调,并使用该数据(通常为 YUV 格式)进行 CPU 处理。这可能比 #1 效率低,但不需要了解 OpenGL。

【讨论】:

有没有人遇到过在 SurfaceTexture.updateTexImage 返回后使用 glReadPixels 的简单示例? @wobbals:您不能直接glReadPixels() 相机输出(与***.com/questions/19366660/… 相同的问题)。首先将其渲染到 pbuffer。一些指向示例的指针:***.com/questions/20710204/…【参考方案2】:

因为我不能发表评论。关于艾迪的回答。您需要在 NDK 中使用它,因为使用 Java 接口会抵消任何性能优势。从性能的角度来看,必须使用 PixelBuffer 绝对是疯狂的。您从 RGBA888 到 YUV 的转换也需要在 C 中完成。

不要尝试使用 TextureView,因为这样会更糟。在转换为 YUV 之前,您必须将像素复制到位图中,然后从位图复制到数组中。这本身就占用了全新 Nexus 7 2013 品牌近 30% 的 CPU 利用率。

最有效的方法是直接与 Camera.h 对话并绕过所有 Android API。您可以创建自己的缓冲区并在 YUV 数据流向其他任何地方之前对其进行拦截。

【讨论】:

虽然预览回调没有我想要的那么高效,但它们并不糟糕。您每帧获得一个字节 [] 的 YUV 数据,可以通过 JNI 将其发送到零副本的本机处理代码。无需转换为位图或其他任何东西。直接使用 Camera.h 是个坏主意,因为该接口是私有的并且随时可能更改。 预览回调仅适用于 Surface。问题是如何在不显示它的情况下做到这一点。没有 Surface 的唯一方法是使用 PixelBuffer。从性能的角度来看,带有 Surface 的标准回调更好。每个回调有两个周期。相机复制到内部缓冲区;然后进入 RGBA 表面缓冲区。第二个循环将内部缓冲区复制到回调缓冲区中。 我真的不确定你所说的 PixelBuffer 是什么意思 - 我知道 Android 中没有那个名称的 API(你是说位图吗?)。除了 setPreviewDisplay() 之外,预览回调还可以与 setPreviewTexture() 一起正常工作,并且前者不需要对任何 UI 元素进行绘图预览。我同意本机和 JNI 层中的副本会拖累性能,这就是为什么我还建议使用 GPU 处理方法,这种方法没有开销。 PixelBuffer 是在为相机提供纹理而不是表面时使用的低级缓冲区。 PixelBuffer 支持 GL 缓冲区。您实际上可以创建一个“假”纹理并强制 Camera API 将原始数据转储到 PixelBuffer 中。这都是隐藏的。 android_platform_cts 有一个或两个如何做到这一点的例子。我非常不喜欢我不能在这里创建段落。 opengl.org/wiki/Pixel_Buffer_Object FWIW,捕获相机预览并将其保存为 MPEG 文件而不显示任何内容的示例:bigflake.com/mediacodec/#CameraToMpegTest。 Surface是像素去的地方;它不一定与屏幕合成器相关联。此外,对于“图像处理”的一些有限定义(例如将颜色转换为黑白),您可以在 GPU 上完成所有操作,这几乎是您可能获得的效率。【参考方案3】:

在屏幕上显示预览不会对性能产生影响。在我遇到的所有设备上,相机输出都“连接”到表面或纹理,不涉及 CPU,所有颜色转换和缩放都由专用硬件负责。

“隐藏”预览可能还有其他原因,但请记住,出于隐私和安全原因,API 最初的目的是确保最终用户看到从相机到达应用程序的任何内容。

【讨论】:

以上是关于Android SDK:获取原始预览相机图像而不显示它的主要内容,如果未能解决你的问题,请参考以下文章

iOS:在相机预览期间捕获图像而不采取行动

Android:访问硬件相机预览帧数据而不绘制它们

Android相机预览始终是侧面的

Android中的相机,如何获得最佳尺寸,预览尺寸,图片尺寸,视图尺寸,图像失真

将相机图像作为位图 Android 示例

Android SDK 相机 API 演示崩溃