六區塊演算法 + 調校參數
| 區塊 | 演算法/作用 | 調校點 | 設錯症狀 |
|---|---|---|---|
| Black Level | 扣除暗電流造成的黑位 offset | 黑位 offset(R/Gr/Gb/B 各自) | 暗部偏灰/偏黑、暗部色彩偏移 |
| Lens Shading | 網格增益補償鏡頭暗角(cos⁴) | LSC 網格 | 四周暗、角落偏色 |
| Demosaic | Bayer→全彩(方向性插值 + 假色抑制) | 假色抑制強度 | 鋸齒、彩色摩爾紋 |
| 降噪 | 空間/時間降噪(亮度+彩色) | NR strength | 細節糊、塑膠感 |
| CCM | 3×3 色彩校正(分色溫) | CCM 矩陣 | 色彩偏差、過飽和 |
| Gamma | 亮度對比曲線(tone mapping) | gamma/對比 | 階調異常、暗部死黑 |
demosaic 用方向性插值重建全彩:先沿邊緣方向插值,再抑制假色。常見品質問題:
rpicam-still --info -o /dev/null
# 輸出含 ExposureTime, AnalogueGain, ColourGains, 統計摘要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)。
黑位不是常數——隨增益與溫度改變。實務:在不同增益下各測一次黑位,確認偏移是否顯著;顯著則 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黑位設錯不只是「暗部偏灰」——它會污染 AE/AWB 統計:
# 遮鏡頭:黑位 = 102(10-bit 值) # 開鏡頭對均勻亮:飽和值 = 1023 # 可用動態範圍 = 1023 - 102 = 921 階 # 若黑位校正設錯(如扣到 200)→ 暗部 200~1023 被壓縮 # → 暗部階調損失
場景:你正在製作 tuning 檔的 BlackLevel 區塊,需要確認 OV5647 在不同增益下黑位的穩定性。
#!/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}")
EOFRPi5 的 BCM2712 ISP 使用的 demosaic 演算法,比基礎的雙線性插值複雜得多:
| 症狀 | 可能原因 | 解決方案 |
|---|---|---|
| 暗部偏灰(不夠黑) | 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 統計是否合理 |
場景:你要為整批 OV5647 模組建立黑位資料庫(每顆模組黑位不同)。本專案建一個「遮光量測工作站」:自動拍黑框 → 算四通道黑位 → 判斷是否需分通道校正 → 產出 BlackLevel 設定建議。整合單元 7(黑位)、2(物理)、5(拍攝)、13(雜訊)知識。
#!/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}")
EOFgain | 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)
| 面向 | RPi5 | Orange Pi | Orin Nano | Thor |
|---|---|---|---|---|
| 黑位設定 | tuning BlackLevel 分通道 | 感測器驅動控制 | NVIDIA tuning(自動) | NVIDIA tuning |
| 統計可見性 | rpicam-still --info / IPA log | 有限(V4L2) | ISP 統計(部分封閉) | Holoscan telemetry |
| 黑位驗證 | rawpy 讀 DNG BlackLevel | 手動解 RAW | NVIDIA 工具 | NVIDIA 工具 |
| 坑 | DNG BlackLevel 可能誤寫 | 無 metadata | 黑箱難驗證 | 生態新 |
| 步驟 | 暫存器 | 操作 | 位元級說明 |
|---|---|---|---|
| 1. 讀取 ISP 設定 | 0x7e802000 (ISP control) | devmem2 0x7e802000 w | bit[0]=ISP_EN, bit[1]=BAYER_EN, bit[2]=JPEG_EN |
| 2. 啟用 ISP | 同上 | devmem2 0x7e802000 w 0x00000003 | bit[0]=1(ISP_ON) + bit[1]=1(BAYER_ON) |
| 3. 設定輸出格式 | 0x7e802008 | 寫入格式控制 | bit[3:0]=0b0010 (RGB888), 0b0100 (YUV420) |
| 4. 驗證 | 0x7e802000 | read-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 硬體問題
rpicam-hello -k --nopreview -t 10000 --raw --info-text| 面向 | RPi5 | Orange Pi | Orin Nano | Thor | 推薦 |
|---|---|---|---|---|---|
| ISP 類型 | GPU-based ISP | 硬體 ISP (部分 SoC) | 專用 ISP 硬體 | 專用 ISP + DLA | Orin/Thor 效能最佳 |
| 處理能力 | ~4K@30 | ~1080p@30 | ~4K@60 | ~8K@30 | Thor 規格最高 |
| Latency | 10-20ms | 15-30ms | 5-10ms | 3-8ms | Thor 最低 |
| 功耗 | ~5W total | ~4W total | ~10W | ~15W | RPi5 功耗最低 |
| 可調性 | 中等(tuning tool) | 低 | 高(NV tuning) | 高(Holoscan) | Orin/Thor 可調性最高 |
vcgencmd get_throttled)vcgencmd measure_clock arm)