← 返回课程

ISP管线

Camera全栈开发(Qcom Camx) 第 17 / 30 节

第 15 章:ISP 管线详解 — IFE/BPS/IPE/JPEG ⭐⭐⭐


本章导读

对照这张图读本章

第 1 章讲了 RAW → YUV → JPEG 的概念。现在要讲真正的硬件实现——Qualcomm Spectra ISP(在 xxx 内部代号 xxx)是怎么把这些处理步骤组织成 IFE、BPS、IPE 三个硬件流水级的。

这章的重要性:不理解 ISP 管线,你就看不懂 chromatix tuning 文件、不知道 ISP overflow 是怎么回事、不知道为什么某些分辨率/帧率组合不支持。


15.1 ISP 管线的全貌——两条路径

关键认知:Preview 和 Snapshot 走的是不同的 ISP 路径。不是所有帧都经过 BPS。

xxx Spectra 480 的 IFE 硬件可以同时输出两路:

IFE 输出 1: Bayer RAW → BPS(全质量路径,Snapshot)
IFE 输出 2: YUV420   → IPE(快速路径,Preview/Video)

Preview / Video 路径(不过 BPS)

Sensor → IFE → IPE → Display/MediaCodec
               ↘
            Stats Node

IFE 内部集成了轻量级的 Bayer→YUV 转换硬件,可以直接输出 YUV420,跳过 BPS。这条路径延迟最低,适合预览和录像。

Snapshot 路径(过 BPS)

Sensor → IFE → BPS → IPE → JPEG
               ↘
            Stats Node

BPS 做完整的 Bayer 域处理(Demosaic、高级降噪、CCM),质量最高但耗时更长。这条路径只在拍照时使用。

各模块速查

模块 输入 做什么 预览用? 拍照用? 源码位置
IFE MIPI RAW 预处理 + Stats camx/src/hwl/ife/
BPS Bayer RAW Demosaic + 降噪 + CCM camx/src/hwl/bps/
IPE YUV/RGB 后处理 + 缩放 camx/src/hwl/ipe/
JPEG YUV 编码 camx/src/hwl/jpeg/

💡 从源码验证:Preview 管线的 Link 定义(g_pipelines.h)只有 IFE → IPE,没有 BPS 节点。BPS(Node ID 65539)只出现在 Snapshot/Reprocess 管线中。


15.2 IFE(Image Front-End)——吃 RAW,吐 RAW

IFE 是 ISP 管线的第一站,直接接收 Sensor 通过 MIPI 送来的 RAW 数据。

IFE 的每一道工序

MIPI CSI 输入(RAW10 / RAW12 / RAW14)
    │
① Black Level Subtraction(黑电平校正)
    │  传感器即使全黑也会输出非零值(暗电流)
    │  黑电平 = 镜头完全遮住时像素输出的平均值
    │  RAW_corrected = RAW_original - black_level
    │  如果不做校正 → 图像发灰(黑色不黑)
    ↓
② Linearization(线性化)
    │  某些 sensor 的输出不是严格线性的
    │  通过查表(LUT)补偿非线性
    │  IFE 内部有一个硬件 LUT 模块
    ↓
③ Bad Pixel Correction(坏点校正)
    │  sensor 上某些像素可能永久损坏(静态坏点)或间歇异常(动态坏点)
    │  静态坏点: 生产时检测 → 写入 OTP → IFE 从 OTP 读取坏点表 → 校正
    │  动态坏点: 实时检测异常亮度像素 → 用邻居插值替代
    ↓
④ Lens Shading Correction(镜头阴影校正)
    │  镜头边缘进光量少于中心 → 边缘偏暗(暗角)
    │  LSC = 为每个像素位置乘一个增益系数
    │  增益系数从 chromatix LSC table 加载(17×13 网格)
    │  中心增益 ≈ 1.0,边缘增益 ≈ 1.2~1.5
    ↓
⑤ Stats 提取(AE/AWB/AF 统计数据采集)
    │  IFE 硬件从画面中提取统计信息
    │  不是全图:只采集预定义的 N×M 个 ROI 区域
    │  AE Stats: 每个区域的亮度直方图
    │  AWB Stats: 每个区域的 R/G、B/G 比例
    │  AF Stats: 对比度值或相位差(PDAF 像素)
    │  → 发给 Stats Node → 3A 算法使用
    ↓
Bayer RAW 输出 → 送往 BPS

IFE 的关键参数

// camx/src/core/camxnode.h
BufferCountForIFE = 4;  // IFE 是入口,需要更多 Buffer

为什么是 4?
  → BPS 在用一个,Stats Node 在用另一个
  → Sensor 在写入第三个,第四个作为备用
  → 少于 4 个,流水线容易堵

IFE 源码中的体现

camx/src/hwl/ife/          → IFE Node 的主实现
camx/src/hwl/xxx/     → xxx ISP 的寄存器定义
  (xxx 是 xxx 上 ISP 的内部代号)

IFE Node 的核心工作:
  1. 接收 MIPI 数据 → 通过 CSL 从 Kernel 获取 Buffer
  2. 配置 IFE 硬件模块 → 写 ISP 寄存器(BLS/BPC/LSC 参数来自 chromatix)
  3. 触发硬件处理 → 等待 Fence 信号
  4. 提取 Stats → 发给 Stats Node

15.3 BPS(Bayer Processing Segment)——只在拍照时走的高质量路径

BPS 做的是 ISP 中计算量最大的工作——只在拍照时启用。预览不走 BPS。

BPS 的每一道工序

IFE → Bayer RAW
    │
① Demosaic(去马赛克)
    │  核心: 把 Bayer 排列的单色像素插值为完整 RGB
    │  第 1 章讲过概念,这里讲实现:
    │  BPS 的 Demosaic 硬件做了三件事:
    │    a) 边缘方向检测 — 判断这个像素处于水平/垂直/对角边缘
    │    b) 沿边缘插值 — 避免跨边缘产生模糊
    │    c) 伪彩色抑制 — 防止彩色边缘出现假色(如紫边)
    ↓
② Spatial Denoise(空域去噪)
    │  在当前帧内部做降噪
    │  对于平坦区域(如天空)→ 多平滑(降噪)
    │  对于边缘区域(如建筑边缘)→ 少平滑(保留细节)
    │  强度由 chromatix 中的 denoise 参数控制
    ↓
③ Temporal Denoise(时域去噪,可选)
    │  利用前一帧的信息帮助当前帧降噪
    │  需要 motion detection — 检测画面是否运动
    │  静止区域 → 多帧平均降噪
    │  运动区域 → 只用空域降噪(避免鬼影)
    ↓
④ Color Correction Matrix(颜色校正)
    │  sensor 的 RGB 响应 ≠ 人眼看到的 RGB
    │  CCM 是一个 3×3 矩阵:
    │  [R_out]   [M11 M12 M13] [R_in]
    │  [G_out] = [M21 M22 M23] [G_in]
    │  [B_out]   [M31 M32 M33] [B_in]
    │  CCM 参数来自 chromatix,针对不同色温有不同的矩阵
    ↓
⑤ Gamma 校正
    │  传感器的 RAW 是线性的(光强 ×2 → RAW 值 ×2)
    │  但人眼对暗部更敏感(非线性感知)
    │  Gamma = 把线性值映射到非线性空间
    │  典型 Gamma 曲线: output = input^(1/2.2)
    │  这一步为后续的 YUV 处理做准备
    ↓
RGB/YUV 输出 → 送往 IPE

BPS 源码位置

camx/src/hwl/bps/camxchinodebps.cpp  → BPS Node 实现

15.4 IPE(Image Processing Engine)——YUV 的精修

IPE 负责 YUV 域的最终处理。

IPE 的每一道工序

BPS → RGB/YUV
    │
① RGB→YUV 转换(如果 BPS 输出的是 RGB)
    │  Y  = 0.299R + 0.587G + 0.114B  (亮度)
    │  U  = -0.169R - 0.331G + 0.5B   (蓝色差)
    │  V  = 0.5R - 0.419G - 0.081B    (红色差)
    │  这是 BT.601 标准,硬件固定实现
    ↓
② Chroma Denoise(色度降噪)
    │  色度噪声比亮度噪声更显眼(人眼敏感)
    │  对 U/V 通道做额外的降噪
    │  CNR 强度由 chromatix 控制
    ↓
③ Sharpen(锐化)
    │  增强边缘对比度
    │  原理: 提取高频部分 → 乘以增益 → 加回原图
    │  可控参数: sharpening strength, radius, threshold
    │  过度锐化 → 边缘出现白色光晕(halo)
    ↓
④ 缩放 / 裁切
    │  把 ISP 输出的分辨率缩放到 App 请求的尺寸
    │  如 sensor 输出 4000×3000 → 缩放到 1920×1080(预览)
    │  缩放算法: 双线性 / 双三次(由 ISP 硬件固定实现)
    │  裁切: 对应 App 的 SCALER_CROP_REGION
    ↓
YUV420(NV12)输出 → Display / JPEG / MediaCodec

IPE 的可配置参数(来自 App 的 CaptureRequest)

// 锐化强度
builder.set(CaptureRequest.EDGE_MODE, EDGE_MODE_HIGH);

// 降噪强度
builder.set(CaptureRequest.NOISE_REDUCTION_MODE, NR_MODE_HIGH);

// 颜色模式
builder.set(CaptureRequest.COLOR_CORRECTION_MODE, CC_MODE_FAST);

// 裁切区域
builder.set(CaptureRequest.SCALER_CROP_REGION, cropRect);

这些参数到了 HAL 层,最终变成写入 IPE 寄存器的值。

IPE 源码位置

camx/src/hwl/ipe/camxchinodeipe.cpp  → IPE Node 实现

15.5 ISP 管线与 chromatix 的关系

第 21 章会详细讲 AE/AWB/AF,这里先建立 ISP 和 chromatix 的联系:

每个 ISP 模块的"调整旋钮"都来自 chromatix 参数:

IFE:
  - 黑电平值        → BLS calibration data
  - 坏点表          → BPC map
  - LSC table       → Lens shading gain grid

BPS:
  - Denoise 强度    → Spatial/Temporal NR strength
  - CCM 3×3 矩阵    → Color Correction Matrix
  - Gamma 曲线      → Gamma LUT (256 points)

IPE:
  - Sharpen 强度    → Edge enhancement strength
  - CNR 强度        → Chroma denoise strength
  - Tuning 参数     → 其他可调的 ISP 寄存器

所有这些参数都从 /vendor/etc/camera/chromatix/{sensor_name}/ 加载。

💡 chromatix 文件就是 ISP 的"调音台"。每个模块都有一组旋钮(参数),调好了图像就好,调不好就偏色/噪点多/锐化过度。这就是 Tuning 工程师的日常工作——不停地调这些参数。


15.6 具体例子——Preview 和 Snapshot 的不同路径

以 12MP sensor(4000×3000)为例,对比同一帧在 Preview 和 Snapshot 下的不同路径。

Preview 路径(IFE → IPE,不过 BPS)

① Sensor 输出:
   RAW10, 4000×3000, 10bit/pixel, ~15MB/帧

② IFE 接收:
   MIPI CSI → CSIPHY/CSID 解码 → IFE 输入 Buffer
   IFE 处理: BLS → Linearization → BPC → LSC
   处理速度: ~2ms
   输出 1: YUV420 → IPE(内置轻量 Bayer→YUV 转换,不走 BPS)
   输出 2: Stats → Stats Node
   同时: 提取 Stats → AE 算曝光 / AWB 算色温 / AF 算焦点

③ IPE 接收(直接来自 IFE):
   IFE 输出 YUV ←→ IPE 输入 (跳过 BPS)
   IPE 处理: CNR → 锐化 → 缩放到 1920×1080
   处理速度: ~2ms
   输出: YUV420 NV12 (1920×1080) ≈ 3MB

④ 输出到 Display:
   IPE 输出 Buffer → queueBuffer → BufferQueue → SurfaceFlinger

总 ISP 延迟: ~4ms(远小于 30fps 的 33ms 帧间隔)

Snapshot 路径(IFE → BPS → IPE,走全流程)

① Sensor 输出: 同上

② IFE 接收: 同上
   IFE 输出 1: Bayer RAW → BPS(全质量路径,不是 YUV420)
   IFE 输出 2: Stats → Stats Node

③ BPS 接收(拍照时才激活):
   IFE 输出 Bayer RAW → BPS 输入
   BPS 处理: Demosaic → 去噪 → CCM → Gamma
   处理速度: ~3.5ms(比 IFE 的轻量转换慢,但质量更高)
   输出: RGB 8bit×3 (4000×3000) ≈ 36MB

④ IPE 接收(来自 BPS):
   BPS 输出 RGB → IPE 输入
   IPE 处理: RGB→YUV → CNR → 锐化(非缩放,保持全分辨率)
   输出: YUV420 (4000×3000 或按需缩放)

⑤ JPEG 编码:
   IPE 输出 YUV → JPEG Node → JPEG 文件

总 ISP 延迟: ~8ms + JPEG 编码 ~80ms

两条路径的对比

              Preview 路径       Snapshot 路径
              ───────────       ────────────
流经模块:      IFE → IPE          IFE → BPS → IPE
BPS:          跳过               启用
Demosaic:     IFE 内置轻量版     BPS 完整版(质量最好)
延迟:         ~4ms               ~8ms + JPEG
适用场景:     预览/录像           拍照

15.7 本章总结

IFE(图像前端): RAW → RAW
  黑电平校正 → 线性化 → 坏点校正 → LSC → Stats 提取
  源码: camx/src/hwl/ife/

BPS(Bayer 处理): Bayer → RGB/YUV
  Demosaic → 空域/时域去噪 → CCM → Gamma
  源码: camx/src/hwl/bps/

IPE(图像后处理): RGB/YUV → YUV
  RGB→YUV 转换 → 色度降噪 → 锐化 → 缩放
  源码: camx/src/hwl/ipe/

JPEG(编码): YUV → JPEG
  源码: camx/src/hwl/jpeg/

关键联系:
  chromatix = ISP 的参数文件
  每个 ISP 模块的"旋钮"都在 chromatix 中

动手验证

  1. 查看你的设备上 chromatix 文件:

    adb shell ls /vendor/etc/camera/chromatix/
    
  2. 开启 Node 处理时间分析,确认各 ISP 模块的耗时:

    echo "dumpNodeProcessingInfo=1" >> camxoverridesettings.txt
    # logcat 中搜 "NodeProcessing"
    
  3. 在你的源码中找到 IFE/BPS/IPE 的 Node 实现文件,确认路径和本章描述一致。

常见误解