後處理 NR
| 雜訊 | 來源 |
|---|---|
| shot noise | 光子隨機性 |
| read noise | 讀出電路 |
| dark current | 高溫/長曝光 |
| hot pixel | 缺陷像素 |
OV5640 內建缺陷像素校正(DPC),先修 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(OV5640)
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雜訊不是「隨機骯髒」——它可被數學模型預測,這是雜訊預算的基礎:
| 雜訊項 | 模型 | 特性 |
|---|---|---|
| Shot noise | σ_shot ∝ √(S) | 光子隨機性,與訊號綁定 |
| Read noise | σ_read ≈ 常數 | 讀出電路,低光主導 |
| Dark current | σ_dark ∝ t × T | 長曝光/高溫上升 |
| Fixed pattern | 常數 offset | 固定圖樣,黑位校正移除 |
SNR 公式:SNR = 20·log₁₀(S / σ_total)。增益放大「訊號與雜訊一起」——所以「拉增益」不會改善 SNR,只會讓影像看起來更亮、雜訊也更明顯。要提升 SNR,只能:增加光子(補光/加長曝光)或降低 read noise。
v4l2-ctl -d /dev/v4l-subdev0 -c auto_exposure=0 -c auto_gain=0 for g in 64 128 256 512 1024; do v4l2-ctl -d /dev/v4l-subdev0 -c gain=$g v4l2-ctl -d /dev/video0 --stream-mmap=3 --stream-count=1 --stream-to=g$g.raw done
python3 - <<'EOF'
import numpy as np
for g in [64,128,256,512,1024]:
raw = np.fromfile(f'g{g}.raw', dtype=np.uint16).reshape(720,1280)
roi = raw[500:1500,500:1500].astype(float)
print(f'gain={g:5d} σ={roi.std():6.1f} mean={roi.mean():6.1f}')
EOF# 對暗部:SNR = 20*log10(mean/σ)
python3 - <<'EOF'
import numpy as np
for g in [64,128,256,512,1024]:
raw = np.fromfile(f'g{g}.raw', dtype=np.uint16).reshape(720,1280)
roi = raw[500:1500,500:1500].astype(float)
snr = 20*np.log10(roi.mean()/max(roi.std(),1e-6))
print(f'gain={g:5d} SNR={snr:5.1f} dB')
EOF| 現象 | 方向 |
|---|---|
| 整片顆粒感 | 增益太高 → 降增益、加曝光/補光 |
| 固定亮點(hot pixel) | 缺陷像素 → DPC 先做 |
| 長曝光才出現亮點 | dark current → 縮短曝光或降溫 |
| 條紋狀雜訊 | read noise / 電源干擾 → 查電源、時序 |
| NR 後糊掉 | NR 過強 → 取捨細節 |
場景:Orange Pi 5 Plus(RK3588)接 OV5640,在低光場景下比較 ISP3 硬體 NR 與 OpenCV 後處理 NR 的效果與延遲。
# 啟用 ISP3 的 Bayer denoise 模組
media-ctl -d /dev/media0 -V "'rkisp-isp':0[nr_strength=128]"
v4l2-ctl -d /dev/video0 --stream-mmap=3 --stream-count=1 --stream-to=isp_nr.yuvpython3 - <<'EOF'
import cv2, time
img = cv2.imread('noisy.png')
t0 = time.time()
nr = cv2.fastNlMeansDenoisingColored(img, None, 10, 10, 7, 21)
dt = time.time() - t0
print(f'OpenCV NR: {dt*1000:.0f}ms for {img.shape[1]}x{img.shape[0]}')
cv2.imwrite('opencv_nr.png', nr)
EOF| 項目 | OV5640 DPC | RK3588 ISP3 DPCC |
|---|---|---|
| 校正方式 | 固定 threshold | 自適應(根據統計動態調) |
| 支援像素類型 | hot pixel(亮) | hot + cold pixel(亮+暗) |
| 校正強度 | 有限(register 公開少) | 可調(tuning 工具) |
| 副作用 | 過強會抹掉細節 | 過強同樣會模糊 |
容易忽略的邊界案例:OV5640 的 DPC 與 ISP 的 NR 是獨立模組,但它們的順序很重要——DPC 必須在 NR 之前做,否則 NR 會把 hot pixel 擴散成亮斑。在 RK3588 上,ISP3 的 DPCC 與 Bayer denoise 也在管線中有固定順序(DPCC → Denoise),但如果你用後處理,必須手動確保這個順序。
| 症狀 | 可能原因 | 解決方案 |
|---|---|---|
| 低光場景 SNR 低但拉增益無效 | 增益同時放大訊號與雜訊 | 補光 + 降增益;或加長曝光(若無 motion blur) |
| hot pixel 在 NR 後變成亮斑 | DPC 沒先做,NR 擴散了缺陷像素 | 先 DPC(修 hot pixel)再 NR |
| 長曝光暗部有固定亮點 | dark current 累積 | 縮短曝光或降溫(若可能) |
| NR 後畫面「塑膠感」 | NR 強度過高 | 降低 NR 強度;接受適度雜訊換回細節 |
| ISP3 NR 效果不如預期 | NR tuning 參數未調整 | 用 Rockchip tuning 工具調整 Bayer denoise 參數 |
把單元 13 的雜訊知識做成完整雜訊管理專案:建立「場景照度 → 增益上限 → SNR → 降噪策略」的決策鏈。這是低光產品(監控、門鈴、車載)畫質控制的基礎。
v4l2-ctl -d /dev/v4l-subdev0 -c auto_exposure=0 -c auto_gain=0 for g in 64 128 256 512 1024; do v4l2-ctl -d /dev/v4l-subdev0 -c gain=$g v4l2-ctl -d /dev/video0 --stream-mmap=3 --stream-count=1 --stream-to=g$g.raw done
python3 - <<'EOF'
import numpy as np
for g in [64,128,256,512,1024]:
raw = np.fromfile(f'g{g}.raw', dtype=np.uint16).reshape(720,1280)
roi = raw[300:420,500:780].astype(float)
snr = 20*np.log10(roi.mean()/max(roi.std(),1e-6))
flag = 'OK' if snr>=30 else '超上限'
print(f'gain={g:5d} SNR={snr:5.1f} dB {flag}')
EOF# 依 SNR 分級: # SNR ≥ 40 dB → 不需 NR(保留細節) # 30–40 dB → 輕度時域平均(2 幀) # < 30 dB → 空間 NR + 補光建議 python3 - <<'EOF' # 時域平均(多幀降噪)範例: import numpy as np frames = [np.fromfile(f'seq_{i}.raw', dtype=np.uint16).reshape(720,1280) for i in range(1,5)] avg = np.mean(frames, axis=0).astype(np.uint16) avg.tofile('seq_avg.raw') print('4 幀時域平均完成(SNR 約提升 6 dB)') EOF
# 輸出範例: # 場景照度 建議曝光 增益上限 SNR 降噪策略 # 日間(>500lx) 2000 128 ≥45 無 # 黃昏(50lx) 8000 256 35 時域平均 x2 # 夜間(5lx) 20000 512 28 空間 NR + 補光
| 步驟 | 作法 | 通過判據 |
|---|---|---|
| 1. 鎖定條件 | 關 AEC/AGC,固定曝光 | 條件穩定可控 |
| 2. 逐級增益取樣 | 每級取 RAW | 每級皆有效資料 |
| 3. 量 ROI σ | 避開飽和與黑位區 | σ 合理(無飽和失真) |
| 4. 算 SNR | 20·log10(mean/σ) | 記錄每級 SNR |
| 5. 訂上限 | SNR 掉到目標值以下 | 訂出增益上限 |
| 6. 驗證 DPC 順序 | 黑框 RAW 的 max | DPC 後亮點被壓制 |
| 面向 | Orange Pi | RPi5 | Orin Nano | Thor |
|---|---|---|---|---|
| 硬體 NR | RK3588 有 / H618 無 | ISP 內建 NR | NVIDIA ISP NR | NVIDIA ISP NR |
| DPC | 感測器 DPC | ISP + libcamera | NVIDIA DPCC | NVIDIA DPCC |
| 後處理 NR | OpenCV fastNlMeans | libcamera 後期 | 可選 | Holoscan 元件 |
| 時域平均 | 自寫 Python | libcamera 支援 | 可選 | 可選 |
| NR 控制 | 參數開放(可調) | 開源可改 | 封閉 | 封閉 |
- [ ] 我能完成雜訊管理專案並產出「雜訊管理表」。 - [ ] 我能量測各增益的 SNR 並訂出增益上限。 - [ ] 我理解「拉增益不改善 SNR」的數學原理。 - [ ] 我遵守「DPC 先、NR 後」的順序。 - [ ] 我已用時域平均驗證 SNR 提升(多幀 +6dB)。 - [ ] 我知道如何在四平台對應 NR 策略(硬體/後處理)。
雜訊管理的「讀→改→寫→驗證」是影像品質的關鍵。以下是 OV5640 的位元級操作序列:
| 步驟 | 暫存器/位址 | 位元欄位 | 操作 | 預期值 |
|---|---|---|---|---|
| 1. 開啟 DPC | 0x5080 | [0] DPC enable | i2cset -y 3 0x3c 0x5080 0x01 | DPC 開啟 |
| 2. 設定 DPC 閾值 | 0x5081 | [7:0] threshold | i2cset ... 0x5081 0x10 | 依雜訊程度 |
| 3. 開啟 NR | 0x5300 | [0] NR enable | i2cset ... 0x5300 0x01 | NR 開啟 |
| 4. 設定 NR 強度 | 0x5301 | [7:0] strength | i2cset ... 0x5301 0x20 | 依需求 |
| 5. 回讀驗證 | 0x5080 / 0x5300 | [0] | i2cget ... 0x5080; i2cget ... 0x5300 | 兩者皆 = 0x01 |
| 6. 取幀對比 | v4l2-ctl | — | 拍兩幀(NR on/off) | 雜訊減少 |
決策樹 A:雜訊太重
影像雜訊太重? ├─ 增益太高 → 減少 analog gain │ └─ 測試 gain=1/2/4/8 的 SNR ├─ 暗場 → 增加曝光(拉亮場景) │ └─ 或用時域平均(多幀堆疊) ├─ NR 強度不足 → 增加 0x5301 值 │ └─ 但注意細節會被抹掉 └─ 有壞點 → 先開 DPC └─ 0x5080=0x01, 0x5081 調閾值
決策樹 B:NR 抹掉細節
NR 後細節模糊? ├─ NR 強度太高 → 減少 0x5301 值 ├─ 改用時域平均(不傷細節) │ └─ 多幀堆疊:SNR 提升 6dB/倍數 ├─ 改用空間 NR 的低強度 │ └─ 取捨:雜訊 vs 細節 └─ 接受限制 └─ 記錄「NR 強度 vs 細節」的取捨
| 步驟 | 指令 | 預期輸出 | 判讀標準 |
|---|---|---|---|
| 1. 量測 SNR | Python script(signal/noise) | SNR 值(dB) | 依增益 |
| 2. 關閉 NR | i2cset ... 0x5300 0x00 | 無降噪 | 基準雜訊 |
| 3. 開啟 NR | i2cset ... 0x5300 0x01; i2cset ... 0x5301 0x20 | 降噪 | 雜訊減少 |
| 4. 量測後 SNR | Python script | SNR 值 | 提升 > 3dB |
| 5. 測 DPC | i2cset ... 0x5080 0x01 | 壞點移除 | 孤立壞點消失 |
| 6. 時域平均 | 多幀堆疊 Python | SNR 提升 | +6dB/倍數 |
| 面向 | Orange Pi | RPi5 | Orin Nano | Thor | 推薦 |
|---|---|---|---|---|---|
| DPC | OV5640 內建 | ISP 內建 | ISP 內建 | ISP 內建 | 底層→OV5640 |
| 空間 NR | OV5640 內建 | ISP 內建 | NVIDIA NR | NVIDIA NR | 學習→OV5640 |
| 後處理 NR | OpenCV fastNlMeans | libcamera 後期 | 可選 | Holoscan | 底層→OpenCV |
| 時域平均 | 自寫 Python | libcamera 支援 | 可選 | 可選 | 學習→Python |
| NR 控制 | 參數開放 | 開源可改 | 封閉 | 封閉 | 學習→Orange Pi |
- [ ] 完成「雜訊管理專案」並產出雜訊管理表 - [ ] 量測各增益的 SNR 並訂出增益上限 - [ ] 驗證「拉增益不改善 SNR」的數學原理 - [ ] 遵守「DPC 先、NR 後」的順序 - [ ] 用時域平均驗證 SNR 提升(多幀 +6dB) - [ ] 在四平台對應 NR 策略(硬體/後處理) - [ ] 產出「NR tuning report」(含 SNR/增益/細節取捨) - [ ] 測試 DPC 閾值對壞點移除的影響 - [ ] 測試 NR 強度對細節的影響 - [ ] 產出「雜訊管理表」(SNR/增益/NR/quality)