20、Camera 深度与RAW:Depth16/DepthPointCloud格式、RAW10/RAW16输出、RAW处理管线、DNG保存

这一章我们来聊聊两个比较「硬核」的话题——深度数据和RAW原始数据。说实话,这两个东西在Android相机框架里属于「高阶玩法」。大部分应用开发者可能一辈子都用不到,但一旦你需要做AR、3D扫描、专业摄影或者计算摄影,你就绕不开它们。

我个人习惯把深度和RAW比作相机的「底牌」。JPEG是洗好的照片,RAW是底片,深度数据则是场景的「三维地图」。今天我们就来翻翻这些底牌。

深度数据:Depth16与DepthPointCloud

先说说深度。Android从API 23开始支持深度相机,但真正成熟是在API 26之后。深度数据有两种主要格式:Depth16DepthPointCloud

Depth16:紧凑的深度图

Depth16,顾名思义,每个像素用16位来存储深度值。单位是毫米,范围0~65535毫米,也就是大约65米。这个精度对于手机上的AR应用来说,基本够用。

我在项目中遇到过一个问题:Depth16的数据是大端序的。嗯,这里要注意,Android的Depth16强制使用大端(Big Endian),不管你底层传感器是什么字节序。如果你直接按小端去读,出来的深度图会像「雪花屏」一样乱。

关键点:Depth16的深度值 = (buffer[i] << 8) | buffer[i+1],单位毫米。

Depth16的置信度信息也藏在里面。每个像素的高4位其实是置信度,低12位才是深度值。但说实话,大部分设备这个置信度字段都是填0,表示「我信我自己」。

DepthPointCloud:稀疏的点云

DepthPointCloud就不一样了。它不是一张图,而是一组三维点。每个点包含x、y、z三个float32坐标,单位也是毫米。说白了,这就是一个稀疏的点云。

你想想看,Depth16是稠密的——每个像素都有深度值。但DepthPointCloud是稀疏的——只有那些「特征点」才有深度。为什么会有这种区别?

因为有些深度传感器(比如ToF)输出的是整张深度图,而有些(比如双目立体视觉)只能算出特征点的深度。Android为了兼容这两种情况,就提供了两种格式。

我的建议:如果你做AR应用,优先用Depth16。因为稠密深度图在做遮挡渲染时效果更好。点云更适合做3D重建或者测量。

RAW数据:RAW10与RAW16输出

聊完深度,我们来说RAW。RAW是传感器最原始的数据,没有经过ISP处理。它保留了最多的动态范围和色彩信息。

Android支持两种RAW格式:RAW10RAW16。注意,这里的数字不是位深,而是存储格式

格式 实际位深 存储方式 典型场景
RAW10 10位 4个像素打包成5字节 老设备、低功耗
RAW16 10~14位 每个像素2字节(对齐到16位) 新设备、高质量

RAW10的打包方式有点绕。4个10位的像素,总共40位,正好5个字节。我刚开始接触时,被这个位操作搞晕过。后来写了个小工具专门解包,才搞清楚。

// RAW10解包示例:4个像素 -> 5字节
// 像素0: 10位, 像素1: 10位, 像素2: 10位, 像素3: 10位
// 字节0: 像素0[9:2]
// 字节1: 像素0[1:0] | 像素1[9:4] << 2
// 字节2: 像素1[3:0] | 像素2[9:6] << 4
// 字节3: 像素2[5:0] | 像素3[9:8] << 6
// 字节4: 像素3[7:0]

RAW16就简单多了。每个像素占2字节,低N位是有效数据,高位补0。比如12位的传感器,每个像素存成16位,高4位是0。

曾经踩过的坑:我曾经以为RAW16就是16位有效数据。结果发现大部分手机传感器只有10~12位。如果你直接按16位去处理,会看到一堆噪声——因为高位全是0,低位才是真实信号。正确的做法是先读取传感器的实际位深,再做归一化。

RAW处理管线

RAW数据拿到手之后,怎么变成我们能看的照片?这就涉及到RAW处理管线了。Android的Camera2 API提供了两种方式:

  • 方式一:用系统ISP处理。你只需要设置好输出格式为YUV或JPEG,剩下的交给系统。
  • 方式二:自己处理RAW。你拿到RAW数据,自己做去马赛克、白平衡、色彩校正、伽马校正等。

方式一简单,但灵活性差。方式二复杂,但你能控制每一个环节。我个人的经验是:如果你做专业摄影App,必须走方式二。因为系统ISP的处理策略是「通用优化」,不会针对你的场景做特殊调优。

RAW处理管线的基本流程如下:

  1. 黑电平校正:减去传感器的暗电流偏移
  2. 去马赛克:把Bayer格式插值成RGB
  3. 白平衡:调整R/G/B增益,让白色看起来是白色
  4. 色彩校正矩阵:把传感器色彩空间映射到标准色彩空间
  5. 伽马校正:把线性数据映射到人眼感知的非线性空间
  6. 色调映射:压缩动态范围,适应显示设备

这里面每一步都有坑。比如黑电平校正,不同传感器的黑电平值不一样。我见过有人直接用固定值去减,结果暗部细节全丢了。

避坑指南:黑电平值可以从CameraCharacteristics的SENSOR_BLACK_LEVEL_PATTERN中获取。不要自己猜,不要用固定值。

DNG保存:把RAW变成标准格式

最后说说DNG。DNG是Adobe推出的RAW标准格式,Android从API 23开始支持直接输出DNG。说白了,DNG就是一个「容器」,里面装着RAW数据加上一堆元数据。

用DNG保存的好处很明显:

  • 通用性:Lightroom、Photoshop等专业软件直接打开
  • 无损:保留所有传感器原始信息
  • 可逆:你可以随时重新处理RAW,调整白平衡、曝光等

Android的DNG输出是通过DngCreator类实现的。用法很简单:

// 伪代码示例
DngCreator dngCreator = new DngCreator(characteristics, captureResult);
dngCreator.setOrientation(orientation);
dngCreator.setDescription("My RAW photo");

// 写入文件
FileOutputStream fos = new FileOutputStream(outputFile);
dngCreator.writeInputStream(fos, rawBuffer);
dngCreator.close();
fos.close();

但这里有个细节:DngCreator需要你传入CameraCharacteristicsCaptureResult。这两个对象里包含了传感器校准数据、色彩矩阵、黑电平等信息。如果你传错了,生成的DNG文件在Lightroom里打开颜色会偏得离谱。

注意:不是所有设备都支持DNG输出。你需要先检查INFO_SUPPORTED_HARDWARE_LEVEL是否为FULLLEVEL_3。另外,DNG文件体积很大——一张1200万像素的RAW,大约25~40MB。存储空间要留够。

知识体系总览

下面这张图总结了本章的核心内容,从深度数据到RAW数据,再到DNG保存的完整链路:

Camera深度与RAW知识体系 深度数据 Depth16 稠密深度图,16位/像素 DepthPointCloud 稀疏点云,float32坐标 应用场景:AR、3D扫描、测距 RAW原始数据 RAW10 4像素打包5字节 RAW16 每像素2字节对齐 应用场景:专业摄影、计算摄影 RAW处理管线 黑电平校正 → 去马赛克 → 白平衡 → 色彩校正 → 伽马校正 → 色调映射 DNG标准格式保存 兼容Lightroom/Photoshop,无损可逆

从这张图可以看得很清楚:深度数据和RAW数据是两条并行的技术路线,最终都汇聚到「处理管线」这一层。深度数据经过处理变成点云或深度图,RAW数据经过处理变成可用的图像,最后都可以保存为标准格式。

嗯,这一章的内容就到这里。深度和RAW是Camera Framework里比较「底层」的部分,但也是最能体现技术深度的地方。如果你能把这两块吃透,那Android相机这块基本就没有能难住你的了。


公众号:蓝海资料掘金营,微信deep3321