NVIDIA NR
| 雜訊 | 來源 |
|---|---|
| shot noise | 光子隨機性 |
| read noise | 讀出電路 |
| dark current | 高溫/長曝光 |
| hot pixel | 缺陷像素 |
# 目標 SNR >= 30dB(σ/S < 3.2%) # gain 高時量測:σ=20, 訊號=400 SNR = 20*log10(400/20) = 26 dB # 不達標 # 補光 + 降增益:訊號 800, σ≈18 → SNR 33 dB ✓ # 結論:補光 + 降增益 優於 硬拉增益
python3 - <<'EOF'
import rawpy, numpy as np, subprocess
for g in [1, 2, 4, 8]:
# 用平台指令取 RAW(OV9281)
raw = rawpy.imread(f'g{g}.dng').raw_image
roi = raw[500:1500,500:1500].astype(float)
print(f'gain={g} σ={roi.std():.1f}')
EOF影像雜訊以 SNR(dB) 度量:SNR = 20·log10(訊號/σ)。訊號隨曝光與增益上升;shot noise 隨訊號平方根上升——所以「增益補曝光」會讓 SNR 惡化,而「加光」讓訊號與雜訊一起升、SNR 反而變好。
# 方案 A:增益 ×4 補曝光(訊號 4x,σ 約 2x) SNR_A = 20*log10(400/20) ≈ 26 dB # 方案 B:補光讓光多 4x、增益不變(訊號 4x,σ 約 2x 同 shot noise) # 但 B 的 σ 起點低,且不放大 read noise: SNR_B ≈ 20*log10(1600/32) ≈ 34 dB # 高出 8 dB # 結論:光源 → 曝光 → 類比增益 → 數位增益,順序不可反
缺陷像素(hot/dead pixel)是「確定性」異常,統計性 NR 會把它擴散成亮斑;因此管線必須先 DPC(defect pixel correction)修缺陷、再 NR 降統計雜訊。
| 處理 | 處理什麼 | 放哪裡 |
|---|---|---|
| DPC | 固定缺陷像素 | NR 之前 |
| 空間 NR | 隨機亮點 | DPC 之後 |
| 時間 NR | 跨幀穩定雜訊 | 最後(動態場景小心鬼影) |
1. 增益是否過高? ├─ 是 → 補光 / 降增益 / 長曝光(先檢查 banding) └─ 否 ┐ 2. 是固定亮點還是一般顆粒? ├─ 固定亮點 → DPC 沒處理 / 缺陷表 └─ 一般顆粒 ┐ 3. NR 已開? ├─ 未開 → 開 NR(空間+時間) └─ 已開仍粗 ┐ 4. 動態場景有鬼影? ├─ 是 → 時間 NR 過強:調降 └─ 否 → 檢查 NR 與 sharpening 平衡(單元 14)
類比增益(AGC)放大「訊號+讀出雜訊」,數位增益只放大訊號數字——所以同一總增益下,類比優先通常比數位放大乾淨。但類比增益太高仍會放大熱雜訊;實際每顆感測器各有甜蜜點。
# 固定曝光,逐級類比增益量 σ
for g in 1 1.5 2 3 4 6 8; do
v4l2-ctl -d /dev/v4l-subdev0 -c analogue_gain=$g
argus_camera --mode 0 --capture-auto 1 --raw-file g$g.raw --duration 1
done
# σ 突增點 = 此後增益不可用 → 設為上限| 症狀 | 先查 |
|---|---|
| 整片顆粒 | 增益過高 → 補光/曝光 |
| 固定亮點 | DPC / 缺陷表 |
| 暗處彩色雜訊 | 暗部增益過高 + NR |
| 長曝光亮點變多 | dark current → 降溫/減曝光 |
| 動態鬼影 | 時間 NR 過強 |
# 靜態場景:空間+時間 NR 都有效,畫質最好 # 放一個緩慢移動物體: # - 有鬼影/拖影 → 時間 NR 過強(試降時間強度) # - 移動體顆粒仍在 → 空間 NR 不足(試升空間強度) # 高速運動:直接關時間 NR,只留空間 NR
場景:OV9281 在昏暗工廠環境(~50 lux)下做缺陷檢測。降噪太強會抹掉缺陷細節,降噪太弱雜訊淹沒信號。
# 1. 固定曝光(排除 AE 干擾) argus_camera --mode 0 --ae-manual --exposure-time 20000 --gain 4.0 # 2. 關閉所有 NR,拍 baseline argus_camera --mode 0 --nr-off --duration 2 --raw-file baseline.raw # 3. 逐步開啟 2D NR,觀察 SNR 改善 for level in 1 2 3 4 5; do argus_camera --mode 0 --nr-2d $level --duration 2 --raw-file nr2d_L${level}.raw done # 4. 在 baseline 上分析 SNR python3 - <<'EOF' import numpy as np raw = np.fromfile('baseline.raw', dtype=np.uint16).reshape(800, 1280) flat = raw[300:500, 500:800] print(f'Baseline SNR: {20*np.log10(flat.mean()/flat.std()):.1f} dB') for level in range(1, 6): nr = np.fromfile(f'nr2d_L{level}.raw', dtype=np.uint16).reshape(800, 1280) nf = nr[300:500, 500:800] print(f'NR 2D level {level}: SNR={20*np.log10(nf.mean()/nf.std()):.1f} dB, σ={nf.std():.1f}') EOF # 找到 SNR 改善最大但 σ 仍然合理的 level
設計決策:2D NR 處理空間雜訊(同幀內的 spatial noise),3D NR 處理時間雜訊(跨幀 temporal noise)。低光場景先開 2D NR,若畫面有動態再開 3D NR。NR level 選擇的原則:SNR 提升 ≥ 3dB 且缺陷細節不模糊。
NR 和 DPC 有交互作用:NR 會「模糊」缺陷像素的特徵,使 DPC 更難偵測。
| 處理順序 | 效果 | 建議 |
|---|---|---|
| DPC → NR | 先修缺陷再降噪(推薦) | DPC 能清楚看到缺陷位置 |
| NR → DPC | NR 模糊了缺陷,DPC 漏檢 | 避免此順序 |
| DPC + NR 同時 | 取決於 ISP 內部 pipeline 順序 | 確認 Argus 的 pipeline 順序(DPC 通常在 NR 之前) |
| 症狀 | 可能原因 | 解決方案 |
|---|---|---|
| NR 開啟後缺陷更明顯(而非消失) | DPC 在 NR 之後處理(pipeline 順序錯) | 確認 Argus pipeline:DPC 應在 NR 之前;調整 pipeline 順序 |
| NR 過強導致邊緣模糊 | 2D NR 的 spatial kernel 太大 | 降低 2D NR level;或只對低頻區域開 NR(edge-aware NR) |
| 3D NR 在快速運動時產生殘影 | 3D NR 的 temporal kernel 跨幀對齊失敗 | 在有運動的場景關閉 3D NR;或降低 temporal 強度 |
| NR 在低光下幾乎無效 | 雜訊太大,NR 的 threshold 被超過 | 先提高 analog gain 以下的信號(降低 gain + 延長曝光) |
| NR 開啟後色彩偏移 | NR 對 R/G/B 通道的處理不均 | 確認 NR 是 per-channel 處理;或在 RGB 空間做 NR 而非 YUV |
場景:低光缺陷檢測(回顧 13.12 場景)需要「雜訊可接受」與「缺陷細節保留」的平衡。專案目標:建立雜訊預算,從量測感測器本質 → 定增益上限 → 調 NR level,全流程量化。
# Phase 1 · 感測器本質量測(回顧 13.6) for g in 1 2 4 8; do v4l2-ctl -d /dev/v4l-subdev0 -c analogue_gain=$g argus_camera --mode 0 --capture-auto 1 --raw-file g$g.raw --duration 1 done # 畫 σ vs gain → 找 σ 暴增點 → 定增益上限(例:max gain 4) # Phase 2 · 定雜訊預算:目標 SNR ≥ 30dB(σ/S < 3.2%) # Phase 3 · 補光 / 曝光調整,讓預算在正常場景達標 # Phase 4 · NR 階梯(回顧 13.12 方法) for level in 0 1 2 3 4 5; do argus_camera --mode 0 --nr-2d $level --duration 2 --raw-file n$level.raw done # 找「SNR 提升 ≥ 3dB 且缺陷細節不模糊」的 level # Phase 5 · DPC 驗證:黑框 RAW 的 max 應被壓制 # Phase 6 · 動態場景確認無鬼影(時間 NR 慎用)
專案輸出:σ vs gain 曲線、增益上限決策、NR level 選擇、DPC 驗證報告。這份「雜訊預算書」是影像品質的總依據。
20·log10(訊號/σ),對照目標預算。| 面向 | Orin Nano | RPi5 | Orange Pi | Thor |
|---|---|---|---|---|
| 空間 NR | ✅ ISP 內建 | ✅ libcamera | ⚠️ 感測器 | ✅ ISP 內建 |
| 時間 NR | ✅(可調強度) | ✅ | ⚠️ | ✅ + AI 降噪 |
| DPC | ✅ 內建 | ✅ | ⚠️ | ✅ |
| NR 調校介面 | level 參數(有限) | tuning file(完整) | 有限 | level + AI |
| 量測方法 | RAW σ / SNR 全平台共用 | |||
| 步驟 | Command | 量測目標 | 預期 |
|---|---|---|---|
| 1. 增益 1x 出圖 | argus --gain-value 1.0 | 基線 σ | σ ≈ 2-5 |
| 2. 增益 2x 出圖 | argus --gain-value 2.0 | σ 增加 | σ ≈ 4-10 |
| 3. 增益 4x 出圖 | argus --gain-value 4.0 | σ 增加 | σ ≈ 8-20 |
| 4. 增益 8x 出圖 | argus --gain-value 8.0 | σ 顯著增加 | σ ≈ 16-40 |
| 5. σ vs gain 曲線 | matplotlib plot | 線性關係 | σ ∝ gain(理想) |
#!/usr/bin/env python3 # noise_vs_gain.py — 雜訊 vs 增益完整量測 import numpy as np, matplotlib.pyplot as plt, subprocess, glob gains = [1.0, 2.0, 4.0, 8.0, 16.0] results = [] for gain in gains: subprocess.run([ "argus_camera", "--camera-id", "0", "--mode", "0", "--exposure-value", "0.016", "--gain-value", str(gain), "--capture-auto", "0", "--duration", "1", "--output-dir", f"/tmp/noise_g{int(gain)}" ], capture_output=True) raw = glob.glob(f"/tmp/noise_g{int(gain)}/*.raw") if raw: data = np.fromfile(raw[0], dtype=np.uint16).reshape(800, 1280) flat_region = data[350:450, 550:700] sigma = flat_region.std() mean = flat_region.mean() snr = mean / sigma if sigma > 0 else 0 results.append({"gain": gain, "mean": mean, "sigma": sigma, "snr": snr}) print(f"Gain {gain:.0f}x: mean={mean:.1f} sigma={sigma:.2f} SNR={snr:.1f}") # Plot gains_r = [r["gain"] for r in results] sigmas = [r["sigma"] for r in results] snrs = [r["snr"] for r in results] fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4)) ax1.plot(gains_r, sigmas, 'bo-', label='Measured') ax1.plot(gains_r, [sigmas[0]*g for g in gains_r], 'r--', label='Ideal (σ ∝ gain)') ax1.set_xlabel('Gain'); ax1.set_ylabel('σ (DN)'); ax1.set_title('Noise vs Gain') ax1.legend(); ax1.grid(True, alpha=0.3) ax2.plot(gains_r, snrs, 'go-') ax2.set_xlabel('Gain'); ax2.set_ylabel('SNR'); ax2.set_title('SNR vs Gain') ax2.grid(True, alpha=0.3) plt.tight_layout(); plt.savefig('noise_analysis.png') print("Saved: noise_analysis.png") # Verdict ideal_sigma = sigmas[0] * gains_r[-1] actual_sigma = sigmas[-1] deviation = abs(actual_sigma - ideal_sigma) / ideal_sigma * 100 print(f"\nIdeal σ at {gains_r[-1]}x: {ideal_sigma:.1f}") print(f"Actual σ at {gains_r[-1]}x: {actual_sigma:.1f}") print(f"Deviation: {deviation:.1f}%") print(f"VERDICT: {'PASS' if deviation < 30 else 'CHECK SENSOR'}")
1. σ 隨 gain 增加但不成正比? ├─ 是 → 感測器固定雜訊佔比高 │ → 降曝光時間(短曝 + 高 gain 更糟) └─ 否 ┐ 2. σ 在高 gain 暴增(非線性跳躍)? ├─ 是 → 感測器接近最大增益限制 │ → 限制增益上限(如 8x → 16x 不建議) └─ 否 ┐ 3. σ 隨 gain 不變? ├─ 是 → ISP 降噪已介入(或 gain 未生效) └─ 否 → 正常線性關係
1. 降噪後細節嚴重流失? ├─ 2D 降噪強度過高 → 降 spatial strength │ 或改用 temporal(時域)降噪 ├─ 是 ┐ │ 2. Temporal 降噪可用? │ ├─ 是 → 用 temporal(保細節更好) │ └─ 否 → 降 spatial strength + 開 edge preservation └─ 否 → 正常 tradeoff
| 面向 | Orin Nano | RPi5 | Orange Pi | Thor | 推薦 |
|---|---|---|---|---|---|
| 降噪方式 | ISP 2D/3D 降噪 | libcamera 簡化降噪 | 感測器內建 | ISP + AI 降噪 | 各有方案 |
| AI 降噪 | ✅ TensorRT | 有限 | ❌ | ✅ 更強 | 高品質 → Orin/Thor |
| 降噪 vs 細節 | 可調平衡 | 固定預設 | 無控制 | AI 最佳平衡 | 量產 → Orin |
| 3D Temporal | ✅(需穩定場景) | 有限 | ❌ | ✅ | 監控 → 3D |
| Edge Preservation | ✅ 可調 | 自動 | ❌ | ✅ + AI | 邊緣重要 → Orin |
| 雜訊模型 | shot + read + FPN | shot + read | 依感測器 | 完整模型 | 全平台共通 |
| 降噪學習成本 | 中高 | 低 | 不需 | 高 | 入門 → RPi5 |