單元 13 · 雜訊與降噪

量測與 NR 調校

13.1 雜訊物理(四種來源)

雜訊來源模型抑制方式
shot noise光子隨機性σ = √(訊號)加曝光(真實訊號)
read noise讀出電路固定底噪(e-)硬體限制
dark current高溫/長曝光隨溫度指數上升散熱、減曝光
hot pixel缺陷像素固定亮點DPC 校正

13.2 量測雜訊(σ vs 增益)

不同增益下量 σ(決定增益上限)
python3 - <<'EOF'
import rawpy, numpy as np, subprocess
for g in [1, 2, 4, 8]:
    subprocess.run(['rpicam-still','--raw','--gain',str(g),
                    '--shutter','20000','-o',f'g{g}.dng'])
    raw = rawpy.imread(f'g{g}.dng').raw_image
    roi = raw[500:1500,500:1500].astype(float)
    print(f'gain={g}  mean={roi.mean():.1f}  std={roi.std():.1f}')
EOF
判讀:σ 隨增益的曲線——若 gain=8 的 σ 暴增,表示該增益區間不可接受 → 設定增益上限、改用曝光補。

13.3 libcamera 降噪調校

tuning 檔 Denoise 區塊(示意)
{ "Denoise": { "strength": 0.3 } }
降噪類型效果調校建議
亮度降噪(luma)影響整體細節弱,避免塑膠感
彩色降噪(chroma)抑制色斑可稍強
時間降噪(temporal)多幀平均需多幀,換幀率
鐵律:NR 與細節是平衡——NR 太強畫面變「塑膠感」。先確認增益沒拉太高(源頭),再讓 NR 收尾。

13.4 感測器端 DPC(缺陷像素校正)

OV5647 內建 DPC,可標定並替換缺陷像素(hot pixel)。

檢查缺陷像素
python3 - <<'EOF'
import rawpy, numpy as np
raw = rawpy.imread('black.dng').raw_image
hot = raw > raw.mean() + 10 * raw.std()
print('hot pixel 數量:', hot.sum())
print('最亮:', raw.max())
EOF

13.5 降噪調校流程

  1. 測量各增益下的 σ(決定增益上限)。
  2. 確認 DPC 生效(hot pixel 已修)。
  3. 從弱 NR 開始,逐步增強;每次對比「細節區」與「平坦區」。
  4. 找到「平坦區乾淨、細節區不糊」的平衡點。

13.6 雜訊預算(Worked Example)

判斷「該補光還是接受雜訊」
# 假設:目標 SNR >= 30dB(σ/S 比值 < 3.2%)
# 量測:gain=8 時 σ=20, 訊號=400
SNR = 20 * log10(400/20) = 26 dB   # 低於目標

# 選項 A:曝光加倍(真實訊號)→ 訊號 800, σ≈28 → SNR 29 dB
# 選項 B:改回 gain=4(σ 較小)但補光 → 訊號 800, σ≈18 → SNR 33 dB ✓
# 結論:補光 + 降增益 優於 硬拉增益
工程判斷:用「SNR 預算」量化——先定可接受的 SNR,再決定「曝光/增益/補光」組合。這比「看起來乾淨」可重現。

13.7 DPC 與 NR 的順序

DPC 前後對比
python3 - <<'EOF'
import rawpy, numpy as np
for name in ['before.dng','after.dng']:
    raw = rawpy.imread(name).raw_image
    hot = (raw > raw.mean() + 10*raw.std()).sum()
    print(name, 'hot pixels:', hot, 'max:', raw.max())
EOF

13.8 深入:雜訊測量的標準方法

分離 dark current
python3 - <<'EOF'
import rawpy, numpy as np
for ms in [1000, 5000, 20000]:
    raw = rawpy.imread(f'black_{ms}.dng').raw_image
    print(f'曝光{ms}µs: 黑位均值={raw.mean():.1f}')
    # 均值隨曝光線性增 → slope 即 dark current
EOF

13.9 練習:建立「增益上限」決策

  1. 量測 gain=1/2/4/8 的 σ。
  2. 畫出 σ vs gain 曲線。
  3. 決定「可接受的增益上限」並記錄。
看完這單元你應該能說出:
  • 四種雜訊物理與模型。
  • σ vs 增益量測與增益上限。
  • libcamera NR 三類型與平衡。
  • DPC 與 NR 分工。
  • 降噪調校流程。

進階真實情境 Worked Example:用遮光板量測 OV5647 的 read noise floor

場景:你要確定 OV5647 的 read noise 底線,以便設定增益上限和 NR 強度。

read noise 精確量測
#!/bin/bash
# 完全遮光 + 短曝光(最小化 dark current)
# 短曝光讓 dark current 影響最小,量到的是純 read noise
for GAIN in 1 2 4 8; do
  rpicam-still --raw --gain $GAIN --shutter 1000 \
    --awb off --awbgains 1,1 -o rn_g${GAIN}.dng 2>/dev/null
done

python3 - <<'EOF'
import rawpy, numpy as np
print("gain | read_noise(e-) | dark_current_offset")
print("-----|---------------|-------------------")
for g in [1,2,4,8]:
    raw = rawpy.imread(f'rn_g{g}.dng').raw_image.astype(float)
    roi = raw[600:1400, 600:1400]
    std = roi.std()
    mean = roi.mean()
    # 假設 10-bit ADC, 1 LSB ≈ 1 e- (簡化)
    print(f"  {g}  |     {std:5.1f}     |       {mean:.1f}")
    
    # 量測不同區域的雜訊一致性
    zones = {
        'top-left':     raw[100:500, 100:500].std(),
        'center':       roi.std(),
        'bottom-right': raw[1500:1900, 2100:2500].std()
    }
    for zone, s in zones.items():
        print(f"    {zone}: σ={s:.1f}")
EOF
為什麼選這條路徑:用 1000µs(1ms)的極短曝光,dark current 的影響 ≈ 0.5e-/s × 0.001s = 0.0005 e-,可忽略。量到的 σ 就是純 read noise。分區域量測是為了確認雜訊是否空間均勻——若角落 σ 比中心大,可能是鏡頭漏光或 LSC 增益放大了角落雜訊。

深入原理擴充:OV5647 的四種雜訊源的溫度與增益依賴性

OV5647 的雜訊不是常數——它隨溫度、增益、曝光時間變化:

大家以為沒问题但其實是陷阱:很多人以為「增益加倍 = 雜訊加倍」,但 OV5647 的類比放大器在高增益時read noise 也會增加(因為放大器自身的雜訊被放大)。所以 gain=16 時的總雜訊不是 gain=1 的 16 倍,而是更多——這在決定增益上限時很重要。

診斷式疑難排解表

症狀可能原因解決方案
遮光 RAW 中有固定亮點(hot pixel)感測器缺陷像素,OV5647 的正常現象啟用 DPC(Defect Pixel Correction);標定 hot pixel 位置
不同增益下 σ 的增加不成比例類比放大器的非線性 + read noise 隨增益增加量測完整 σ vs gain 曲線,找出「σ 暴增」的拐點作為增益上限
長曝光(>5s)暗部充滿雜訊dark current 累積,尤其高溫環境散熱(加散熱片);限制長曝光時間;用 dark frame subtraction
NR 開到最強仍無法消除低光雜訊源頭雜訊過高(增益太高),NR 無法「無中生有」回到源頭:降增益、加曝光、補光;NR 只能收尾
平坦區 σ 低但紋理區 σ 高紋理區的 shot noise 隨訊號增加(正常的 Poisson 特性)這是物理限制,NR 強度需在「平坦區乾淨」與「紋理區保留」間取捨

進階挑戰題

  1. 量測 OV5647 在 25°C 和 50°C 下的 dark current 差異(用不同曝光時間的遮光 RAW,畫出 dark_current vs temperature 圖)。
  2. 設計一個「自動增益上限決定」腳本:從 gain=1 開始遞增,量測每級的 σ,當 σ 增加斜率 > 閾值時停止,自動輸出建議增益上限。
  3. 研究 OV5647 的 DPC 暫存器(0x4000-0x400C),分析其工作原理與限制。

延伸閱讀

專案級端到端 Worked Example:感測器雜訊特性資料庫專案

場景:要建立每顆感測器型號的「雜訊特性曲線庫」(σ vs gain、dark current vs 曝光、hot pixel 地圖),供演算法與調校決策使用。整合單元 13(雜訊)、2(物理)、7(黑位)、5(拍攝)知識。

noise_db.sh:雜訊資料庫
#!/bin/bash
# 1. read noise:遮光 + 短曝光(1ms)不同增益
for G in 1 2 4 8 16; do
  rpicam-still --raw --gain $G --shutter 1000 \
    --awb off --awbgains 1,1 -o rn_g$G.dng 2>/dev/null
done

# 2. dark current:遮光 + 長曝光不同時長
for T in 1000 10000 50000 100000; do
  rpicam-still --raw --gain 1 --shutter $T \
    --awb off --awbgains 1,1 -o dc_t$T.dng 2>/dev/null
done

python3 - <<'EOF'
import rawpy, numpy as np
print("== read noise(短曝 1ms,σ 即 read noise)==")
for g in [1,2,4,8,16]:
    roi = rawpy.imread(f'rn_g{g}.dng').raw_image[600:1400,600:1400]
    print(f" gain={g:2d}  σ={roi.std():5.2f}")

print("== dark current(黑位均值 vs 曝光,slope=暗電流)==")
means = []
for t in [1000,10000,50000,100000]:
    m = rawpy.imread(f'dc_t{t}.dng').raw_image[600:1400,600:1400].mean()
    means.append((t,m))
    print(f" {t:6d}µs  mean={m:6.1f}")
slope = (means[-1][1]-means[0][1])/(means[-1][0]-means[0][0])
print(f" 暗電流斜率 ≈ {slope*1e6:.2f} e-/s(10-bit 單位換算)")
EOF
專案規模與跨單元整合:這份資料庫是「調校決策的依據」:σ vs gain 決定增益上限(單元 13.2)、dark current 決定長曝光限制(單元 2)、hot pixel 地圖交給 DPC(單元 13.4)。所有量測用單元 5 的標準管線,黑位用單元 7 方法排除。資料庫隨模組批次更新,比較批次品質。

量測/驗證 SOP:雜訊量測

  1. 遮光 + 短曝光(1ms)量 read noise(各增益)。
  2. 遮光 + 變長曝光量 dark current(畫黑位 vs 曝光斜率)。
  3. 灰卡量總雜訊(shot + read)與 SNR。
  4. 建立 hot pixel 地圖(遮光 RAW 的 >10σ 位置)。
  5. 畫出 σ vs gain 曲線,標出增益上限拐點。
  6. 存資料庫並納入版本管理。
判讀指標:gain 每級 σ 增幅平穩,出現「暴增」拐點即增益上限;dark current 斜率隨溫度上升;hot pixel 數 >0.1% 需啟用 DPC。

平台間對照:雜訊與降噪

面向RPi5Orange PiOrin NanoThor
NR 控制tuning Denoise(luma/chroma)後處理NVIDIA 自動(kernel 可調)AI 驅動
DPC感測器端(OV5647 內建)感測器端NVIDIA 處理NVIDIA 處理
雜訊量測rawpy 全開源手動解 RAWNVIDIA 工具Holoscan
NR 過強塑膠感無 NR 介面黑箱難深調生態新

互動式檢核清單


第 3 輪深度加深

① Register 位元級完整工作流:OV5647 DPC + NR 控制

步驟暫存器位元級說明
1. 啟用 DPC0x50800x01bit[0]=1 DPC on
2. 設定 DPC threshold0x50810x0Abit[7:0] = 雜訊閾值(越大越保守)
3. 啟用 NR (luma)0x52900x04bit[3:2]=01 Luma NR strength medium
4. 啟用 NR (chroma)0x52910x08bit[3:2]=10 Chroma NR strength high
5. 驗證read-back確認所有值正確

② 多層疑難排解決策樹

決策樹 A:NR 過強導致細節模糊

NR 過強
├─ 檢查 A:NR strength 設定值
│  ├─ 過高 → 降低 strength level
│  └─ 適中 → 繼續
├─ 檢查 B:NR threshold 是否針對當前 gain
│  ├─ 不是 → 不同 gain 需要不同 threshold
│  └─ 是 → 繼續
├─ 檢查 C:是否同時有 spatial + temporal NR
│  ├─ 是 → 可能疊加過強 → 分別調整
│  └─ 否 → 繼續
└─ 檢查 D:後處理 pipeline 是否有額外 NR
   ├─ 有 → 去除多餘 NR 階段
   └─ 無 → 調整 NR 演算法參數

③ 量測驗證完整 SOP:SNR 量測(ISO Sweep)

  1. 工具:灰卡 + 標準光源 + rawpy + numpy
  2. 步驟:
    a. 固定曝光,改變 ISO(100/200/400/800/1600)
    b. 每個 ISO 拍 10 幀
    c. 計算每幀灰卡 ROI 的 mean 和 std
    d. SNR = 20 × log₁₀(mean / std)
  3. 畫圖:SNR vs ISO 曲線
  4. 判讀:ISO 100 SNR >40dB = 優良;ISO 800 SNR >25dB = 可接受
  5. 啟用 NR 後重測:比較 NR on/off 的 SNR 差異
  6. 常見偏差:灰卡不平 → mean 偏差;環境光洩入 → 黑位偏高

④ 四平台終極對照

面向RPi5Orange PiOrin NanoThor推薦
NR 類型Spatial NR基本 NRSpatial + TemporalSpatial + Temporal + AIThor 最先進
DPC感測器端感測器端ISP 端ISP + AIThor 最強
NR 強度可調✓ tuning有限✓ 完整✓ AI 自適應Orin/Thor 最靈活
低光 SNR中等較差優異Orin/Thor 最佳
功耗RPi5 功耗最低

⑤ 完整 Bring-up 專案 Checklist