20、Camera 深度与RAW:Depth16/DepthPointCloud格式、RAW10/RAW16输出、RAW处理管线、DNG保存
这一章我们来聊聊两个比较「硬核」的话题——深度数据和RAW原始数据。说实话,这两个东西在Android相机框架里属于「高阶玩法」。大部分应用开发者可能一辈子都用不到,但一旦你需要做AR、3D扫描、专业摄影或者计算摄影,你就绕不开它们。
我个人习惯把深度和RAW比作相机的「底牌」。JPEG是洗好的照片,RAW是底片,深度数据则是场景的「三维地图」。今天我们就来翻翻这些底牌。
深度数据:Depth16与DepthPointCloud
先说说深度。Android从API 23开始支持深度相机,但真正成熟是在API 26之后。深度数据有两种主要格式:Depth16 和 DepthPointCloud。
Depth16:紧凑的深度图
Depth16,顾名思义,每个像素用16位来存储深度值。单位是毫米,范围0~65535毫米,也就是大约65米。这个精度对于手机上的AR应用来说,基本够用。
我在项目中遇到过一个问题:Depth16的数据是大端序的。嗯,这里要注意,Android的Depth16强制使用大端(Big Endian),不管你底层传感器是什么字节序。如果你直接按小端去读,出来的深度图会像「雪花屏」一样乱。
Depth16的置信度信息也藏在里面。每个像素的高4位其实是置信度,低12位才是深度值。但说实话,大部分设备这个置信度字段都是填0,表示「我信我自己」。
DepthPointCloud:稀疏的点云
DepthPointCloud就不一样了。它不是一张图,而是一组三维点。每个点包含x、y、z三个float32坐标,单位也是毫米。说白了,这就是一个稀疏的点云。
你想想看,Depth16是稠密的——每个像素都有深度值。但DepthPointCloud是稀疏的——只有那些「特征点」才有深度。为什么会有这种区别?
因为有些深度传感器(比如ToF)输出的是整张深度图,而有些(比如双目立体视觉)只能算出特征点的深度。Android为了兼容这两种情况,就提供了两种格式。
RAW数据:RAW10与RAW16输出
聊完深度,我们来说RAW。RAW是传感器最原始的数据,没有经过ISP处理。它保留了最多的动态范围和色彩信息。
Android支持两种RAW格式:RAW10 和 RAW16。注意,这里的数字不是位深,而是存储格式。
| 格式 | 实际位深 | 存储方式 | 典型场景 |
|---|---|---|---|
| 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。
RAW处理管线
RAW数据拿到手之后,怎么变成我们能看的照片?这就涉及到RAW处理管线了。Android的Camera2 API提供了两种方式:
- 方式一:用系统ISP处理。你只需要设置好输出格式为YUV或JPEG,剩下的交给系统。
- 方式二:自己处理RAW。你拿到RAW数据,自己做去马赛克、白平衡、色彩校正、伽马校正等。
方式一简单,但灵活性差。方式二复杂,但你能控制每一个环节。我个人的经验是:如果你做专业摄影App,必须走方式二。因为系统ISP的处理策略是「通用优化」,不会针对你的场景做特殊调优。
RAW处理管线的基本流程如下:
- 黑电平校正:减去传感器的暗电流偏移
- 去马赛克:把Bayer格式插值成RGB
- 白平衡:调整R/G/B增益,让白色看起来是白色
- 色彩校正矩阵:把传感器色彩空间映射到标准色彩空间
- 伽马校正:把线性数据映射到人眼感知的非线性空间
- 色调映射:压缩动态范围,适应显示设备
这里面每一步都有坑。比如黑电平校正,不同传感器的黑电平值不一样。我见过有人直接用固定值去减,结果暗部细节全丢了。
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需要你传入CameraCharacteristics和CaptureResult。这两个对象里包含了传感器校准数据、色彩矩阵、黑电平等信息。如果你传错了,生成的DNG文件在Lightroom里打开颜色会偏得离谱。
INFO_SUPPORTED_HARDWARE_LEVEL是否为FULL或LEVEL_3。另外,DNG文件体积很大——一张1200万像素的RAW,大约25~40MB。存储空间要留够。
知识体系总览
下面这张图总结了本章的核心内容,从深度数据到RAW数据,再到DNG保存的完整链路:
从这张图可以看得很清楚:深度数据和RAW数据是两条并行的技术路线,最终都汇聚到「处理管线」这一层。深度数据经过处理变成点云或深度图,RAW数据经过处理变成可用的图像,最后都可以保存为标准格式。
嗯,这一章的内容就到这里。深度和RAW是Camera Framework里比较「底层」的部分,但也是最能体现技术深度的地方。如果你能把这两块吃透,那Android相机这块基本就没有能难住你的了。