單元 2 · 影像感測器基礎

光電物理、Bayer、快門、OV5647 內部

2.1 光電轉換的完整物理模型

光子photodiode(p-n 接面空乏區)累積電荷 Q = η(λ)·P·TexpSource Follower 轉電壓Column ADCRAW
核心數學(shot noise):訊號電子數 N,shot noise σ = √N。 SNR = N / √N = √N。訊號增 4 倍 → SNR 增 2 倍(+6 dB)。 這直接解釋調校鐵律:「加曝光」增加真實訊號、SNR 提升;「加增益」把訊號與雜訊等比放大、SNR 不變。因此能加曝光就別加增益。

2.2 Bayer CFA:為什麼只有 1/4 彩度

RGrRGr
GbBGbB
RGrRGr
GbBGbB
Bayer order 的後果:OV5647 是 RGGB。若平台以錯誤 order 解讀(如當成 BGGR),色彩會全錯(紅↔藍、綠誤配)。單元 8 教你用 RAW 驗證。

2.3 快門時序:Rolling 流水線與 Global

Rolling shutter:逐列「曝光→讀出」,且曝光與讀出流水線重疊(前一行還在讀、後一行已開始曝光)。因此每一列的曝光時間起點不同:

型態時序特性OV5647典型問題
Rolling逐列曝光+讀出(有時間差)高速物體扭曲、banding
Global全像素同時曝光→同時讀出電路複雜、成本高

banding:若曝光時間不是「光源頻率週期的整數倍」,各列累積的交流成分不同 → 明暗條紋。50Hz 光源 → 曝光須為 10ms 整數倍;60Hz → 8.33ms(單元 10 深入)。

2.4 OV5647 內部方塊圖

Pixel Array 2592×1944Column ADC(10-bit)數位處理(增益/DPC)MIPI TX(2-lane)

周邊區塊:Timing Generator(列/幀同步)、PLL(由 XCLK 產生內部時脈)、I2C 介面(register 控制)、上電管理(PWDN/RESET)。

2.5 OV5647 完整規格表(公開 datasheet)

項目規格工程意義
解析度2592×1944全解析輸出
像素尺寸1.4 µm決定感光面積與滿阱
光學格式1/4"鏡頭像圈匹配
快門Rolling
ADC10-bitRAW 位元深
SNR max~36 dB低光性能指標
動態範圍~68 dB
類比增益2× 內建可再數位延伸
MIPI2-lane CSI-2RPi5 接法
I2C 位址0x36(7-bit)單元 3 實作
XCLK6–27 MHz(典型 24)單元 6 PLL
「RAW 天花板」原則:雜訊底、動態範圍、色彩靈敏度都在感測器層定型。ISP 只能「校正逼近」,不能「無中生有」。調校的極限由此決定——理解它,才知道「調不上去」是感測器問題還是流程問題。

2.5 深入:曝光、增益與 SNR 的完整數學

一張圖的訊號鏈:

N = η·P·Texp(電子)V = N·CG(電荷→電壓)G_ana(類比增益)ADC
Worked Example:增益對 SNR 的影響
# 場景:低光,訊號只有 N=400 e-,read noise σr=8
σ_shot = √400 = 20
σ = √(400 + 64) ≈ 21.5
SNR = 20·log10(400/21.5) ≈ 25.4 dB

# 若把類比增益×4(訊號也×4,雜訊也×4)
# SNR 不變:25.4 dB(雜訊被等比放大)

# 若改曝光×4(真實訊號 N=1600)
σ_shot = √1600 = 40;σ = √(1600+64) ≈ 40.8
SNR = 20·log10(1600/40.8) ≈ 31.9 dB  # +6.5 dB!
# 結論:曝光勝過增益
工程口訣:「能加曝光就別加增益」不是口號,是 SNR 數學的必然結果。

2.6 練習:Bayer 觀察

  1. 固定參數拍 RAW(rpicam-still --raw -o t.dng)。
  2. 用 rawpy 讀四通道均值,確認 RGGB 位置。
  3. 對紅/綠/藍三色光各拍一張,觀察哪個通道最亮。
看完這單元你應該能說出:
  • 光電轉換數學與「加曝光優於加增益」。
  • Bayer 物理、Gr/Gb 差異、demosaic 需求。
  • Rolling 流水線與 banding 成因。
  • OV5647 內部方塊與完整規格。

進階真實情境 Worked Example:量測 OV5647 在不同增益下的實際 SNR

場景:你正在為一個低光監控專案決定 OV5647 的增益上限。需要量化「gain 到多少時 SNR 降到不可接受」。

SNR vs Gain 量測腳本
#!/bin/bash
# 用固定曝光、不同增益拍 RAW,量 σ
for GAIN in 1 2 4 6 8 10 12 16; do
  rpicam-still --raw --gain $GAIN --shutter 33000 \
    --awb off --awbgains 1,1 -o snr_g${GAIN}.dng 2>/dev/null
done

python3 - <<'EOF'
import rawpy, numpy as np
for g in [1,2,4,6,8,10,12,16]:
    raw = rawpy.imread(f'snr_g{g}.dng').raw_image.astype(float)
    roi = raw[600:1400, 600:1400]
    signal = roi.mean()
    noise = roi.std()
    snr_db = 20 * np.log10(signal / noise) if noise > 0 else 0
    print(f"gain={g:2d}  signal={signal:7.1f}  noise={noise:5.1f}  SNR={snr_db:.1f}dB")
EOF
為什麼選這條路徑:用 rpicam-still --raw 而非串流是為了確保每張圖的曝光完全一致(無 AE 浮動)。用 numpy 直接從 RAW Bayer 資料算 σ,跳過 ISP 處理,量到的是感測器本體的雜訊特性。這個方法比依賴 ISP 輸出更能精準定位「硬體極限在哪」。

深入原理擴充:OV5647 的滿阱容量與動態範圍限制

OV5647 的 1.4µm 像素在 1/4" 光學格式下,滿阱容量(Full Well Capacity)約為 4800 e-。這直接限制了動態範圍:

大家以為沒問題但其實是陷阱:很多人以為「Bayer order 驗證過了就沒問題」,但 OV5647 在不同解析度下(2592×1944 vs 1296×972)的 pixel timing 與 PLL 配置不同——同一顆感測器在不同 mode 下的 SNR 特性可能差異顯著。你不能只在全解析度下做 SNR 量測就下結論。

診斷式疑難排解表

症狀可能原因解決方案
低光下色彩明顯偏綠Gr/Gb 不平衡——Bayer pattern 中綠色像素佔 50%,低光 shot noise 對綠色通道影響最大量測 Gr vs Gb 均值差;若差異 >5% 需調整 ISP 的 Gr/Gb 平衡校正
增益加到 8× 以上畫面充滿彩色斑點shot noise σ = √N,低訊號下 σ/Signal 比值暴增限制增益上限(建議 ≤8×);改用物理補光增加真實訊號
長曝光(>1s)暗部出現固定亮點hot pixel + dark current 累積啟用 DPC(感測器端缺陷像素校正);長曝降溫或限制曝光
切換解析度後色彩偏移不同 mode 的 PLL/timing 設定差異導致增益映射偏移切換解析度後重新量測黑位與 AWB gains
灰卡量測 R/G/B 不等(已關 AWB)光源色溫非中性 或 感測器光譜響應偏差先確認光源色溫(用色溫計);若光源正確,需校正 CCM

進階挑戰題

  1. 推導 OV5647 在 2592×1944 模式下的 frame rate 與曝光行數上限的數學關係(假設 PLL 輸出 pixel clock 為 96MHz)。若曝光行數設為 frame length 的 1.5 倍會發生什麼?
  2. 設計一個實驗:用 OV5647 在 3000K(白熾燈)與 6500K(日光)下各拍一張 RAW,分析兩個色溫下 Gr/Gb 的差異,解釋差異的物理原因。
  3. 若要將 OV5647 的 SNR 量測結果與 IMX708 做公平比較,需要控制哪些變數?寫出完整的實驗設計。

延伸閱讀

專案級端到端 Worked Example:感測器「真規格」驗證專案

場景:datasheet 宣稱 OV5647 SNR max ≈ 36 dB、DR ≈ 68 dB。你為監控專案需要一份「實測規格表」,用來設定調校目標(gain 上限、曝光策略)。本專案整合單元 2(物理)、5(拍 RAW)、7(黑位)、13(雜訊)知識。

建立實測規格表
# 階段 1:量測黑位與飽和(決定動態範圍)
# 遮鏡頭拍黑位;對強光源拍飽和
rpicam-still --raw --shutter 20000 --gain 1 -o black.dng
rpicam-still --raw --shutter 2000  --gain 1 -o sat.dng

# 階段 2:量測 SNR vs gain(固定曝光、不同增益)
for GAIN in 1 2 4 8 16; do
  rpicam-still --raw --gain $GAIN --shutter 33000 --awb off --awbgains 1,1 -o spec_g${GAIN}.dng 2>/dev/null
done

# 階段 3:計算並產出規格表
python3 - <<'EOF'
import rawpy, numpy as np
def roi_stats(f):
    raw = rawpy.imread(f).raw_image.astype(float)[600:1400, 600:1400]
    return raw.mean(), raw.std()
blk_m, _ = roi_stats('black.dng')
sat_m, _ = roi_stats('sat.dng')
print(f"BlackLevel = {blk_m:.0f}  Saturation = {sat_m:.0f}")
print(f"動態範圍 = {20*np.log10((sat_m-blk_m)/1):.1f} dB")
for g in [1,2,4,8,16]:
    m, s = roi_stats(f'spec_g{g}.dng')
    print(f"gain={g:2d}  signal={m:7.1f}  σ={s:5.1f}  SNR={20*np.log10(m/s):.1f} dB")
EOF
# 預期輸出:DR 接近 datasheet(~65-68 dB);SNR 隨 gain 上升而下滑
專案規模與跨單元整合:把「資料表上的規格」變成「你板上實測的規格」是調校的第一步。SNR 用單元 2 的數學、黑位用單元 7 的量測、σ 用單元 13 的方法。這份實測表往後會是你的「調校目標來源」——例如「gain≤8 才能維持 SNR≥30dB」直接寫進 AE 限制。

量測/驗證 SOP:SNR 與動態範圍量測

  1. 固定鏡頭、光源、拍攝距離;用 --shutter 33000 --gain 1 --awb off 拍基準 RAW。
  2. 遮鏡頭拍 black.dng,取中央 ROI 均值 = 黑位。
  3. 對強均勻光源拍 sat.dng,取中央 ROI 均值 = 飽和值。
  4. 依 gain = 1/2/4/8/16 各拍一張(固定曝光),計算 SNR。
  5. 畫出 σ vs gain 曲線,標出「σ 暴增拐點」= 增益上限。
  6. 把結果寫入規格表並存檔(供回歸比較)。
判讀指標:黑位四通道差異 <20 階為正常;SNR 在 gain≤8 應維持 >30dB;DR 量測值與 datasheet 差 >5dB 時,檢查黑位是否被 ISP 錯誤扣除。

平台間對照:感測器規格驗證

面向RPi5Orange PiOrin NanoThor
RAW 取得rpicam-still --raw(DNG)v4l2-ctl + 自行解包argus raw(.nvraw)argus / Holoscan raw
黑位 metadataDNG 內嵌 BlackLevel無(需自行量測)ISP 統計可查ISP 統計可查
量測工具rawpy/numpy 全開源Python 自寫NVIDIA 工具(部分封閉)NVIDIA 工具
DNG 黑位可能被誤寫10-bit packing 要手解raw 格式私有生態新、工具少

互動式檢核清單


第 3 輪深度加深

① Register 位元級完整工作流:OV5647 暫存器讀取驗證

步驟指令位元級細節
1. 讀取 Chip IDi2cget -y 1 0x3C 0x300A wOV5647 chip ID 高位=0x56, 低位=0x47 → 回讀 0x5647
2. 讀取位元深度i2cget -y 1 0x3C 0x302E wbit[1:0] 決定 RAW 輸出位元:0b00=10-bit, 0b01=8-bit
3. 讀取曝光行數i2cget -y 1 0x3C 0x3500 w + 0x3501 w + 0x3502 w3-byte 曝光值:bit[19:16] | bit[15:8] | bit[7:0],行數 = value >> 4
4. 驗證 PLLi2cget -y 1 0x3C 0x3034 wbit[7:6]=PLL root divider, bit[5:4]=PLL pre-divider, bit[2:0]=sys divider
重要:所有 OV5647 暫存器為 8-bit 寬度,以 16-bit 地址存取。i2cgetw flag 讀取 16-bit,實際只用低 8-bit。

② 多層疑難排解決策樹

決策樹 A:Chip ID 讀取失敗(回讀 0xFF 或 0x00)

Chip ID 異常
├─ 檢查 A:i2cget 本身是否成功(有無 "Error")
│  ├─ Error: errno=5 → I2C 匯流排被佔用 → 移除 libcamera/cam 佔用
│  └─ 正常回覆但值異常 → 繼續
├─ 檢查 B:A09 GPIO 是否為 High(供電使能)
│  ├─ Low → 供電 IC 未啟用 → 檢查 Raspberry Pi 電源供應(5V/2.5A+)
│  └─ High → 繼續
├─ 檢查 C:PWRDN 排線時序
│  ├─ Reset GPIO 持 Low 超過 10ms → sensor 未脫離 reset
│  └─ 時序正確 → 繼續
└─ 檢查 D:嘗試 0x3C 與 0x78 兩個地址
   ├─ 兩者都無回應 → 排線問題 / 感測器損壞
   └─ 有一個回應 → 用該地址

③ 量測驗證完整 SOP:動態範圍量測

  1. 環境:標準光源箱 5000K、灰卡(18% 反射率)、三腳架固定
  2. 黑位量測:遮光蓋住鏡頭,短曝光(1ms),ISO 100 → 記錄 mean_raw = 黑位
  3. 飽和點量測:對準白色反射板,逐步增加曝光直至 pixel 值不再上升 → 記錄 sat_raw
  4. 計算:DR(dB)= 20 × log₁₀((sat_raw − black_raw) / read_noise_std)
  5. 重複:每增益級(ISO 100/200/400/800)各量 5 次,取平均
  6. 判讀:OV5647 預期 DR ≈ 60dB (10-bit);<55dB 需檢查黑位排除
  7. 常見偏差:未遮光量黑位 → 偏高;非 T¹/₃ 衰減器 → 飽和點偏移

④ 四平台終極對照

面向RPi5Orange PiOrin NanoThor推薦
可選感測器OV5647/5640/9281OV5640/IMX219 等IMX219/477/577 等IMX 系列 + 自有Orin 感測器支援最廣
位元深度10-bit RAW8/10-bit10/12-bit12/14-bitThor 位元深度最高
像素技術BSI (OV5647)BSIStacked BSIStacked BSIOrin/Thor 最先進
支援 ROI / 窗口支援 binning部分支援完整 ROI完整 ROI + HDROrin/Thor 最靈活
開源驅動✓ mainline部分✓ kernel✓ kernelRPi5 + Orin 開源最好

⑤ 完整 Bring-up 專案 Checklist