首页影像百科
2026年06月25日

高通平台 RAW 成像链路分析与画质问题定位

在某项目中系统排查 4K 视频画质模糊问题,从 App 层逐层下钻到 CamX HAL 层,通过 MIPI RAW10 解包、节点对比分析和 Dump 配置,最终定位根因并推动修复。

在 某项目 中,发现 4K 模式录制的视频和拍摄的照片画质模糊。清晰度只有1080p的水平,怀疑相关配置出错,因此需要看raw排查问题。

  • 硬件平台:Qualcomm QRB5165

  • 底层架构:Qualcomm CamX

方法一:App 端抓取与分析

该项目有两种拍raw的方式:

  1. 使用配套的app中,将拍照格式设为raw,进行拍照,然后adb导出raw文件。

这种方式拍摄的raw,无法直接用aya打开(画面异常),且体积异常。

推测:该设备基于 Qualcomm (QRB5165) 平台,高通平台默认输出通常为 MIPI RAW10 (Packed) 格式。

  • 这种格式采用"5 字节存储 4 个像素"的紧凑排布(10-bit)。

  • 普通的 PC 查看工具(如 AYA、Photoshop)默认按 1 字节(8-bit)或 2 字节(16-bit)为一个单位读取。

  • 如果不经过格式转换直接打开,像素的位偏移会导致画面完全花屏或无法成像。

  • 由于 MIPI RAW10 无法直视,必须使用解包脚本将其转换为 Plain 16-bit 格式。

  • 转换逻辑

    1. 脚本读取连续的 5 个字节。

    2. 按照 MIPI 协议将其拆解成 4 个独立的 10-bit 数值。

    3. 将这些 10-bit 数值放入 16-bit 的容器中(补零对齐)。

    预测解包后的文件在 AYA 中设置 16-bitLittle Endian 以及 Bit shift: +6 后,方可呈现出正常的图像内容。

    Python 脚本解包后得到的是 10-bit LSB aligned(低位对齐) 保存在 16-bit 容器中的 RAW 数据。

在 App 中设定的拍照分辨率为 4K (3840 x 2160)。如果 4K 设置生效,按 MIPI RAW10 格式计算,文件体积理论上应为:3840×2160×1.25=10,368,000 字节 (约 9.8 MB)

[raw转换脚本.py]

由于文件名没有具体分辨率信息,推测两个可能性,1.sensor输出了8192 x 6144全尺寸的raw,原先4k setting不生效导致raw体积异常增大。2.原先4k setting生效,其他原因导致raw体积异常增大。

使用脚本转换,手动输入8192 x 6144进行转换,使用aya打开,依旧异常。

直接使用手动设定3840 x 2160,发现图像正常显示,但是重复两张。

继续尝试1920 x 1080,图像正常显示。

得出结论:app中拍摄raw使用的setting为1920*1080,不是4k,因此解析力差,和现象对齐。

重新使用脚本将原raw文件转换为1080p,得出单个文件大小为3.95mb。

原始文件是 MIPI RAW10 (Packed) 格式,每像素占 1.25 Byte

单帧 1080p 大小 = 1920×1080×1.25=2,592,000

62,914,560÷2,592,000≈24.27帧。

结论:原始文件可能是24帧的raw数据流

脚本转换后的文件是 Plain 16-bit (Unpacked) 格式,每像素占 2 Byte。

单帧 1080p 大小 = 1920×1080×2=4,147,200字节。

转换为 MiB = 4,147,200÷1024÷1024≈3.955 MB

结论:这符合 AYA 能够直接读取的标准 16bit 格式大小。

输入 (60MB):是 24 帧的"压缩"数据(RAW10)。

输出 (约 95MB):是 24 帧的"解压"数据(16bit)。

2÷1.25=1.6 倍

60MB×1.6=96MB,这正好对应 24 帧解压后的总大小。

综上结论:app中拍摄的raw,虽然设置了4k,但是拍摄出来是1080p的图片。相关配置出错了。

方法二:camx-hal3-test 底层抓取

由于 App 端的结论证实了分辨率缩减问题,因此切换到更底层的 camx-hal3-test 链路。这种方式直接调用 Qualcomm CamX HAL3 接口,绕过 App 层的处理干扰,获取真正 4K 全尺寸 RAW 来进行验证排查。

  • camx-hal3-testHAL层(硬件抽象层) 的测试工具。

如果在这里手动下发 4K 指令抓出来的 RAW 依然模糊,那就可以直接定性为问题出在 Sensor 驱动(寄存器配置不对) 或者 硬件(镜头没对焦、模组坏了) 身上,底层驱动配置或 Sensor 硬件问题,不需要再在 App 层面浪费时间。

执行命令后,在 /data/vendor/camera 目录下会生成一系列带"节点标签"的 .RAWMIPI10 文件。通过分析文件名中的 IFE(预览路径节点)和 BPS(拍照路径节点)对应的 RAW 数据,我们可以精准定位画质损失究竟发生在流水线的哪个环节。

  • IFE (Image Front End): 相当于"前哨站"。主要负责预览、实时处理。它的 RAW 比较"原始"。

  • BPS (Bayer Processing Segment): 相当于"精修站"。专门处理高画质拍照(Snapshot)的节点。

  • 为什么要对比它们: 如果 IFE 清晰但 BPS 模糊,说明"精修"环节把图搞坏了;如果两个都模糊,说明"前哨站"拿到的原始数据就有问题。

在实际操作中,这一步远比 App 拍照复杂,涉及到了底层服务的停启和权限的博弈。

环境冲突与盲拍

为了腾出系统资源并避免系统相机服务占用摄像头,往往需要停掉或删除 App 服务。

  • 代价: 删除系统相机服务后,热点会消失

  • 后果: 没了热点就没了 RTSP 预览,整个过程变成了"盲拍"。无法通过屏幕确认对焦是否成功、画面是否过曝,只能靠 adb 盲操作,效率极低。这就是为什么在这个项目里,抓 RAW 和正常评测存在不可调和的矛盾。

    1、刷机首次执行

    adb root
    adb remount
    adb disable-verity
    adb reboot
    

    2、开机每次执行

    adb root
    adb remount
    adb shell
    mount -o remount,rw /
    systemctl stop gst-camera-app.service
    

核心交互指令

进入 /data/misc/camera 目录后,执行 camx-hal3-test 进入交互界面。此时必须按顺序输入:

配置流 (A指令): A:id=0,psize=1920x1080,pformat=yuv420,ssize=3840x2160,sformat=jpeg,zsl=1

告诉相机,我要开 0 号摄像头,预览设为 1080p,但拍照(ssize)我要 4K 的 JPEG 路径。

触发抓图 (S指令): S:1(拍 1 张)。

退出工具 (Q指令): Q

初次拍摄失败。根据 log.txt 显示,在 shell 中启动 camx-hal3-test 时,程序瞬间报错:

[log.txt]

  • 现象: =========>>>catch signal 6 <<< from tid:1519======

  • 定位: 堆栈信息显示崩溃发生在 CamX::HwEnvironment::Initialize。这说明工具在尝试初始化硬件环境时遭到了拒绝。在下方尝试输入的 A:id=0... 报错 not found,是因为程序已经崩了,其实是在向普通 Linux Shell 发送指令。

  • 建议 仅仅 stop 服务是不够的,因为系统相机服务可能存在拉起机制或残留进程占用 /dev/video* 节点。"删除 usr/bin/gst-camera-app" 确保没有任何 App 层的代码能干扰到 HAL 层的初始化。

删除**usr/bin/gst-camera-app**后,

使用指令拍摄,却发现 /data/misc/camera 里只有 YUV 和 JPG 。执行完指令后 /data/vendor/camera/ 目录为空,说明底层 Dump 开关未开启。必须将该文件推送到 /vendor/etc/camera/ 路径以强行覆盖系统默认行为。

  • 原因: camx-hal3-test 默认只输出指令要求的格式(如 sformat=jpeg)。要拿到流水线中途的原始 RAW,必须强行开启底层 Dump 开关

为了强行接管 CamX 的行为,建议把下面camxoverridesettings文件推到/vendor/etc/camera/目录下

[camxoverridesettings.txt]

  • 核心配置项: autoImageDump=TRUE。这是开启底层 Dump 的总开关,不开启则不会产出 RAW 文件。

  • Mask 掩码逻辑: 配置文件中的 autoImageDumpMask=0x87 以及 IFE/IPE/BPS 的各个 outputPortMask 被设置为高位值(如 0x3FFBFFF)。 这意味着系统会无差别地导出镜像流水线中所有节点的数据。它不再依赖 App 的设置,而是由底层 CamX 直接向硬盘写入数据。

潜在风险与副作用(事后总结)

  • 磁盘空间报警: 由于 Mask 掩码设置极其激进,系统会全量导出 4K 模式下的每一帧中间数据。

  • 系统崩溃: 这解释了为什么实验过程中 /data 分区(约 94G)会在几分钟内被打满。磁盘写满后会导致服务无法启动、热点消失,设备进入不可视的异常状态。

  • 操作建议: 抓取完成后必须立即将 autoImageDump 设为 FALSE 或删除该配置文件,并及时清理 /data/vendor/camera/ 下的残留文件。

开启全量 Dump 后,每次触发拍照,/data/vendor/camera 目录下会瞬间生成几十个不同节点的文件。必须通过文件名的特定标签进行筛选,才能定位到真正需要的 4K RAW。

  1. 文件命名与筛选

一个典型的底层 RAW 文件名格式如下: p[RealTimeFeature...]_req[1]_IFE[0]_w[3840]_h[2160]_..._b[10]_...RAWMIPI10

  • p[...]:代表该文件所属的 Pipeline 流程。

  • req[x]:请求 ID,同一拍产生的多个文件,其 ID 是一致的。

  • w[xxxx]_h[xxxx]最核心识别指标。必须在这里看到 3840_2160(4K)或 4096_3072(4:3),才能证明抓到了全分辨率。

  • RAWMIPI10:代表这是 10-bit 的 MIPI 原始数据。

在几十个文件中,重点关注以下两类:

  • IFE 标签文件p[RealTimeFeatureZSLPreviewRaw]..._IFE[...]...

    • 这是前端输入节点,反映了 Sensor 吐出来的最原始状态,主要用于排查对焦和基础清晰度。
  • BPS 标签文件p[ZSLSnapshotYUVHAL]..._BPS[...]...

    • 这是拍照路径的中间节点。如果 BPS 出来的图比 IFE 差,说明处理算法或增益分配在 BPS 环节出了问题。
  1. 过滤无效文件
  • 忽略后缀: 忽略所有 .BLOB.PD10.UBWCTP10 等后缀。这些是元数据、相位数据或高通内部压缩格式,常规工具无法解析。

[p[ZSLSnapshotYUVHAL]_req[1]_batch[0]_BPS[0]_port[0]_w[3840]_h[2160]_19700101_003949.RAW]

[p[RealTimeFeatureZSLPreviewRaw]_req[9]_batch[0]_IFE[0]_port[9]_w[3840]_h[2160]_19700101_003949.RAW]

两张raw解析力均正常。但是BPS文件p[ZSLSnapshotYUVHAL]_req[1]_batch[0]_BPS[0]_port[0]_w[3840]_h[2160]_19700101_003949_3840x2160_f01_u16的亮度异常,需要增加72xgain,整体亮度才和IFE文件类似。

通过对比 App 端和底层 CamX 端的 RAW 数据,得出以下差异:

  1. App 抓取路径(基于某通信协议)
  • 现象:设置 4K 但产出 1080p 连帧 RAW(约 24 帧)。

  • 推断:该路径受 App 通信协议消息集控制。虽然控制端下发了 4K 拍照指令,但最终 dump 的是预览路径(Preview Path)的 Buffer,而非真正的单帧快照流(Snapshot Path)。

  1. 底层抓取路径(camx-hal3-test 直连)
  • 现象:手动下发 ssize=3840x2160 指令后,成功在 BPS 节点获取 4K 全分辨率单帧 RAW。

  • 推断:此结果证明了传感器模组、底层驱动及 HAL 层的 4K Snapshot 链路是打通的。

  1. 节点数据特征差异(IFE vs BPS)
  • IFE 节点(前端):RAW 画面解析力正常,亮度正常。数据带有基础的 Preview Gain,反映了 Sensor 吐出的原始预览状态。

  • BPS 节点(后端):RAW 画面解析力正常,但亮度极暗(需 72x Gain 还原)。这符合高通 BPS 节点截获线性原始数据(Linear RAW)的特征,证明该节点抓取的是未经 ISP 数字增益处理的、真正的快照原始底图。

在完成 4K (16:9) RAW 抓取后,尝试抓取 4096 x 3072 分辨率的 RAW 数据以进行全像素分析。在执行过程中,经历了三次指令调整:

第一次尝试:内存溢出

  • 指令A:id=0,psize=640*480,pformat=yuv420,ssize=4096*3072,sformat=jpeg,zsl=1

  • 现象:程序报错 Cannot allocate memory 并显示 Killed

  • 原因分析

    • 该错误由 msmgbm_bo_create 触发,说明系统在尝试为 4096 x 3072 分辨率分配图形缓冲区(GBM)时失败。

    • ZSL 模式占用:由于开启了 zsl=1,CamX 会在后台预留多帧高分辨率缓冲区。推测4:3 全分辨率(约 12MP)的数据量极大,超出了当前系统分配给 camx-hal3-test 的可用内存上限,导致 OOM(内存溢出)被系统强杀。

第二次尝试:无文件输出

  • 指令A:id=0,psize=1920x1080,pformat=yuv420,ssize=2160x1536,sformat=jpeg,zsl=1

  • 现象:指令运行看似成功(显示 Exiting application!),但 /data/vendor/camera 目录下没有生成任何 RAW 文件。

  • 原因分析

    • 虽然程序未崩溃,但推测该特定分辨率(2160x1536)可能不在系统预设的静态流配置表中,导致 Pipeline 虽然初始化,但未能成功触发 Dump 逻辑

第三次尝试:成功

  • 指令A:id=0,psize=1920x1080,pformat=yuv420,ssize=2048*1536,sformat=jpeg,zsl=1

  • 结果:成功产出文件。经核对,生成的 RAW 文件名中 w[4096]_h[3072],即达到了 4:3 全尺寸要求。

  • 关键推断

    • ssize 触发机制:在 camx-hal3-test 中,ssize 参数并不直接决定 RAW 的大小,而是决定了系统调用哪种 Sensor Mode

    • 逻辑链路:输入 2048*1536 成功触发了 Sensor 的 Full Size 模式(不进行 Binning 或裁切)。由于底层 Dump(autoImageDump)是在数据流经缩放器(Resizer)之前截获的,因此即便指令要求的是 2048x1536,我们拿到的仍然是该模式下最原始的 4096 x 3072 图像数据。

结论

抓取底层 RAW 时,指令中的 ssize 需参考提供的"触发分辨率"。只要 Sensor 进入了对应的全尺寸工作模式,底层 Dump 出来的 RAW 就会保持该模式下的最高物理分辨率,不受后续缩放指令影响。

4:3下,BPS和IFE均正常。

后续:反馈解析力不足可能是由于4k60fps那边的数据应该没有用上,看日志是用到了2k60fps的数据。

提供新的配置文件后,4k60p的解析力正常。