單元 7 · ISP 管線深入

六區塊演算法 + 調校參數

7.1 管線全圖

Black LevelLens ShadingDemosaic降噪CCMGamma輸出

7.2 六區塊逐一拆解(演算法 + 調校 + 設錯症狀)

區塊演算法/作用調校點設錯症狀
Black Level扣除暗電流造成的黑位 offset黑位 offset(R/Gr/Gb/B 各自)暗部偏灰/偏黑、暗部色彩偏移
Lens Shading網格增益補償鏡頭暗角(cos⁴)LSC 網格四周暗、角落偏色
DemosaicBayer→全彩(方向性插值 + 假色抑制)假色抑制強度鋸齒、彩色摩爾紋
降噪空間/時間降噪(亮度+彩色)NR strength細節糊、塑膠感
CCM3×3 色彩校正(分色溫)CCM 矩陣色彩偏差、過飽和
Gamma亮度對比曲線(tone mapping)gamma/對比階調異常、暗部死黑
調校依序原則:黑位 → LSC → CCM → gamma。跳著調會互相干擾(例:黑位沒對,LSC 網格就失真)。

7.3 Demosaic 深入(最常被忽略)

demosaic 用方向性插值重建全彩:先沿邊緣方向插值,再抑制假色。常見品質問題:

判斷:看到「彩色摩爾紋」先別改 CCM——那是 demosaic/光學問題,不是色彩矩陣問題。用細網格測試圖驗證。

7.4 ISP 統計回饋機制

RAWISP統計(亮度直方圖/色彩平均/AE 值)IPA(AE/AWB 演算法)下幀控制
看控制與統計
rpicam-still --info -o /dev/null
# 輸出含 ExposureTime, AnalogueGain, ColourGains, 統計摘要
調校核心:AE/AWB 演算法依「統計」調整下一幀。統計被黑位錯誤污染 → 自動模式全歪。所以「先校正靜態區塊,再談自動」。

7.5 黑位量測實作(完整)

遮鏡頭拍 RAW 統計
rpicam-still --raw --shutter 20000 --gain 1 -o black.dng
python3 - <<'EOF'
import rawpy, numpy as np
raw = rawpy.imread('black.dng').raw_image
roi = raw[400:1600, 400:1600]   # 中央 ROI,避開四周漏光
print('R : %.1f' % roi[0::2,0::2].mean())
print('Gr: %.1f' % roi[0::2,1::2].mean())
print('Gb: %.1f' % roi[1::2,0::2].mean())
print('B : %.1f' % roi[1::2,1::2].mean())
print('max: %d (hot pixel?)' % roi.max())
EOF

四通道均值應接近(都是黑位)。若 R/Gr/Gb/B 差異明顯 → 需分通道黑位校正(tuning 的 BlackLevel 區塊)。max 異常大 → 缺陷像素(單元 13 DPC)。

7.6 黑位與增益的關係(進階)

黑位不是常數——隨增益與溫度改變。實務:在不同增益下各測一次黑位,確認偏移是否顯著;顯著則 tuning 需分段。

不同增益的黑位漂移
for g in 1 2 4 8; do
  rpicam-still --raw --gain $g --shutter 20000 -o b$g.dng
  python3 -c "import rawpy,numpy as np;r=rawpy.imread('b$g.dng').raw_image;print('g=$g 黑位=%.1f'%r[400:1600,400:1600].mean())"
done

7.7 深入:Black Level 校正對統計的影響

黑位設錯不只是「暗部偏灰」——它會污染 AE/AWB 統計

調校鐵律:黑位是「統計的地基」。地基歪,所有自動演算法都歪。所以單元 7 的黑位量測是調校的第一步。

7.8 Worked Example:黑位與動態範圍

量測與計算
# 遮鏡頭:黑位 = 102(10-bit 值)
# 開鏡頭對均勻亮:飽和值 = 1023
# 可用動態範圍 = 1023 - 102 = 921 階
# 若黑位校正設錯(如扣到 200)→ 暗部 200~1023 被壓縮
# → 暗部階調損失

7.9 練習

  1. 遮鏡頭拍 RAW,量測黑位四通道。
  2. 檢查 max(hot pixel 候選)。
  3. 把黑位值寫進「預備寫入 tuning 的清單」。
看完這單元你應該能說出:
  • ISP 六區塊演算法與調校點。
  • 每區塊設錯的症狀。
  • 統計回饋給 AE/AWB 的機制。
  • 黑位量測與分通道校正。

進階真實情境 Worked Example:用遮光板量測各增益下的黑位漂移

場景:你正在製作 tuning 檔的 BlackLevel 區塊,需要確認 OV5647 在不同增益下黑位的穩定性。

黑位 vs 增益完整量測
#!/bin/bash
# 確保鏡頭完全遮光(用遮光蓋或黑布)
echo "gain,black_r,black_gr,black_gb,black_b,max_value"
for G in 1 2 4 8 12 16; do
  rpicam-still --raw --gain $G --shutter 33000 \
    --awb off --awbgains 1,1 -o black_g${G}.dng 2>/dev/null
done

python3 - <<'EOF'
import rawpy, numpy as np
for g in [1,2,4,8,12,16]:
    raw = rawpy.imread(f'black_g{g}.dng').raw_image
    roi = raw[500:1500, 500:1500]
    r  = roi[0::2,0::2].mean()
    gr = roi[0::2,1::2].mean()
    gb = roi[1::2,0::2].mean()
    b  = roi[1::2,1::2].mean()
    mx = roi.max()
    print(f"gain={g:2d}  R={r:.1f}  Gr={gr:.1f}  Gb={gb:.1f}  B={b:.1f}  max={mx}")
EOF
為什麼選這條路徑:遮光板量測排除了所有外部光源干擾,量到的是感測器的「純黑位」——包含 read noise、dark current 和 amplifier offset。分增益量測是因為 OV5647 的類比放大器在不同增益下 offset 不同(gain=1 時 offset≈100,gain=16 時可能到 120+),若只用一個增益的黑位值,其他增益下會有 10-20 階的偏差。

深入原理擴充:BCM2712 ISP 的 demosaic 演算法與假色抑制

RPi5 的 BCM2712 ISP 使用的 demosaic 演算法,比基礎的雙線性插值複雜得多:

大家以為沒问题但其實是陷阱:很多人以為「LSC 增益調好就均勻了」,但 LSC 的網格節點是雙線性插值的——在網格邊界處,增益可能出現不連續(尤其在畫面邊角的高增益區域)。症状是「LSC 後的均勻灰圖出現微弱的網格狀紋路」。解決方案是加密網格節點(如從 17×13 改為 33×25),但會增加 tuning 檔大小。

診斷式疑難排解表

症狀可能原因解決方案
暗部偏灰(不夠黑)BlackLevel 設太低,扣除了不足夠的黑位 offset遮光量測實際黑位值;更新 tuning 檔的 BlackLevel R/Gr/Gb/B
LSC 校正後暗角改善但角落偏紫LSC 只校正了亮度,未分通道校正 R/G/B確認 LSC 網格的 R/Gr/Gb/B 四通道各自獨立;角落偏色需分通道增益
demosaic 後出現彩色摩爾紋被攝物有細密重複紋理(如衣物纖維),超過 Nyquist 頻率降低 ISP 的假色抑制強度無效;需光學低通或改變拍攝距離
黑位量測的 R/Gr/Gb/B 差異 > 20 階感測器的四通道 amplifier offset 不一致在 tuning 檔中分通道設 BlackLevel(不能用統一值)
ISP 統計的亮度與實際畫面不符BlackLevel 污染了 ISP 的亮度統計先校正 BlackLevel,再觀察 AE 統計是否合理

進階挑戰題

  1. 設計一個實驗,量化 OV5647 在不同溫度(25°C vs 45°C)下的 dark current 差異。如何在不拆機殼的情況下改變感測器溫度?
  2. 若 LSC 網格從 17×13 改為 33×25,tuning 檔大小約增加多少?畫質改善是否值得?寫出量化比較方案。
  3. 從 ISP 管線圖推導:若把 BlackLevel 校正移到 LSC 之後(違反校正順序),會產生什麼具體問題?

延伸閱讀

專案級端到端 Worked Example:黑位量測工作站專案

場景:你要為整批 OV5647 模組建立黑位資料庫(每顆模組黑位不同)。本專案建一個「遮光量測工作站」:自動拍黑框 → 算四通道黑位 → 判斷是否需分通道校正 → 產出 BlackLevel 設定建議。整合單元 7(黑位)、2(物理)、5(拍攝)、13(雜訊)知識。

black_level_lab.sh:黑位工作站
#!/bin/bash
# 遮光箱/鏡頭蓋確保全黑
for G in 1 2 4 8 16; do
  rpicam-still --raw --gain $G --shutter 33000 \
    --awb off --awbgains 1,1 -o black_g$G.dng 2>/dev/null
done

python3 - <<'EOF'
import rawpy, numpy as np
print("gain |   R   |  Gr   |  Gb   |   B   | max | hot>10σ")
for g in [1,2,4,8,16]:
    raw = rawpy.imread(f'black_g{g}.dng').raw_image
    roi = raw[500:1500, 500:1500].astype(float)
    r  = roi[0::2,0::2].mean(); gr = roi[0::2,1::2].mean()
    gb = roi[1::2,0::2].mean(); b  = roi[1::2,1::2].mean()
    hot = (roi > roi.mean() + 10*roi.std()).sum()
    print(f" {g:2d} | {r:6.1f} | {gr:5.1f} | {gb:5.1f} | {b:6.1f} | {roi.max():4d} | {hot}")
EOF
預期輸出與判讀
gain |   R   |  Gr   |  Gb   |   B   | max | hot>10σ
  1  |  100.2| 102.1| 101.8|  99.7 | 1080 | 3
 16  |  118.5| 120.0| 119.3| 117.2 | 1080 | 3
# 判讀:四通道差 <20 階 → 可統一 BlackLevel;差 >20 → 分通道
# 黑位隨 gain 上升(100→118)→ 需在 tuning 分段或接受誤差
# hot 固定幾顆 → 啟用 DPC(單元 13)
專案規模與跨單元整合:黑位是 ISP 統計的地基(單元 7):黑位錯 → AE/AWB 全歪。這個工作站把「量測 → 判讀 → 設定」自動化:量測用單元 2 的物理、拍攝用單元 5 的管線、判讀黑位漂移用單元 13 的雜訊知識。產出的 BlackLevel 直接寫進 tuning(單元 14)。

量測/驗證 SOP:黑位校正驗證

  1. 遮光拍攝(鏡頭蓋 + 黑布,避免漏光)。
  2. 在 gain=1/2/4/8/16 各拍黑框 RAW。
  3. 取中央 ROI(避開四周漏光)計算四通道均值。
  4. 檢查 max 與 hot pixel 數量(>10σ)。
  5. 寫入 tuning 的 BlackLevel 區塊後,重拍黑框確認四通道均值被校正到接近 0。
  6. 記錄黑位-增益曲線,供不同曝光設定使用。
判讀指標:校正後黑框均值應 <1 階;四通道殘差 >2 階 = 校正值有誤;黑位隨增益漂移 >15 階 = 需分段設定或接受(實務上 gain≤8 內偏移可忽略)。

平台間對照:黑位與統計

面向RPi5Orange PiOrin NanoThor
黑位設定tuning BlackLevel 分通道感測器驅動控制NVIDIA tuning(自動)NVIDIA tuning
統計可見性rpicam-still --info / IPA log有限(V4L2)ISP 統計(部分封閉)Holoscan telemetry
黑位驗證rawpy 讀 DNG BlackLevel手動解 RAWNVIDIA 工具NVIDIA 工具
DNG BlackLevel 可能誤寫無 metadata黑箱難驗證生態新

互動式檢核清單


第 3 輪深度加深

① Register 位元級完整工作流:RPi5 ISP 模組啟用控制

步驟暫存器操作位元級說明
1. 讀取 ISP 設定0x7e802000 (ISP control)devmem2 0x7e802000 wbit[0]=ISP_EN, bit[1]=BAYER_EN, bit[2]=JPEG_EN
2. 啟用 ISP同上devmem2 0x7e802000 w 0x00000003bit[0]=1(ISP_ON) + bit[1]=1(BAYER_ON)
3. 設定輸出格式0x7e802008寫入格式控制bit[3:0]=0b0010 (RGB888), 0b0100 (YUV420)
4. 驗證0x7e802000read-back確認 bit[1:0] = 0b11

② 多層疑難排解決策樹

決策樹 A:ISP 模組初始化失敗

ISP 初始化失敗
├─ 檢查 A:RPi5 GPU firmware 版本
│  ├─ 過舊 → sudo rpi-update 或重裝韌體
│  └─ 最新 → 繼續
├─ 檢查 B:ISP clock 是否啟用
│  ├─ 未啟用 → 設定 clock divider register
│  └─ 已啟用 → 繼續
├─ 檢查 C:ISP 記憶體分配是否成功
│  ├─ 失敗 → CMA pool 不足 → config.txt 設定 dtoverlay=cma,cma=256M
│  └─ 成功 → 繼續
└─ 檢查 D:ISP pipeline 設定是否有 timing violation
   ├─ 有 → 調整 ISP clock 或 frame size
   └─ 無 → ISP 硬體問題

③ 量測驗證完整 SOP:ISP Pipeline 延遲量測

  1. 方法:比較 RAW frame 與 ISP 輸出 frame 的 metadata timestamp
  2. 步驟:
    a. rpicam-hello -k --nopreview -t 10000 --raw --info-text
    b. 同時擷取 RAW + ISP output frames
    c. 比對對應幀的 timestamp 差值
  3. 量測條件:三種解析度(640×480 / 1280×720 / 1920×1080)
  4. 統計:每條件 100 幀,計算 mean / std / p99
  5. 判讀:mean < 15ms = 正常;std > 3ms = pipeline 有 jitter
  6. 常見偏差:GPU 負載高時延遲增加;CMA 分配延遲造成首幀 spike

④ 四平台終極對照

面向RPi5Orange PiOrin NanoThor推薦
ISP 類型GPU-based ISP硬體 ISP (部分 SoC)專用 ISP 硬體專用 ISP + DLAOrin/Thor 效能最佳
處理能力~4K@30~1080p@30~4K@60~8K@30Thor 規格最高
Latency10-20ms15-30ms5-10ms3-8msThor 最低
功耗~5W total~4W total~10W~15WRPi5 功耗最低
可調性中等(tuning tool)高(NV tuning)高(Holoscan)Orin/Thor 可調性最高

⑤ 完整 Bring-up 專案 Checklist