Bayer、快門型態、OV5640 規格
| R | Gr | R | Gr |
| Gb | B | Gb | B |
| 型態 | 特性 | OV5640 |
|---|---|---|
| Rolling | 逐列曝光,高速扭曲 | ✅ 預設 |
| Global | 全像素同時曝光 | —(OV9281) |
| 項目 | 規格 |
|---|---|
| 解析度 | 5 MP(2592×1944) |
| 像素 | 1.4 µm |
| 輸出 | MIPI CSI-2(2-lane)/ DVP |
| 內建功能 | AEC/AGC/AWB、DPC、內建 ISP |
| I2C 位址 | 0x3C(寫)/ 0x3D(讀),可切 0x21 |
| 感測器 ID | 0x300A=0x56 / 0x300B=0x40 |
感測器輸出的 RAW 不是「照片」,而是一串 依 Bayer order 排列的單色強度。理解這層,之後的 RAW 驗證(單元 8)才有基礎。
SRGGB10 的「10」就是這個。光看規格表說 RGGB 不夠——實際「拍出來的 RAW 是 RGGB」要驗證。最可靠的方法:拍單色色卡,再檢查 RAW 四通道的強度分佈。
v4l2-ctl -d /dev/video0 --set-fmt-video=width=1280,height=720,pixelformat=SRGGB10 \ --stream-mmap=3 --stream-count=1 --stream-to=raw.bin
python3 - <<'EOF'
import numpy as np
raw = np.fromfile('raw.bin', dtype=np.uint16).reshape(720, 1280)
r = raw[0::2, 0::2] # R 位置(假設 RGGB 起點)
gr = raw[0::2, 1::2] # Gr
gb = raw[1::2, 0::2] # Gb
b = raw[1::2, 1::2] # B
print('R Gr Gb B =', r.mean(), gr.mean(), gb.mean(), b.mean())
EOFR 應最高;拍純藍時 B 最高;拍灰卡時四者應接近。若 R/B 對調,Bayer order 就是 BGGR(或設定錯)。| 現象 | 可能原因 | 檢查 |
|---|---|---|
| 四通道全接近 0 | 鏡頭蓋著/曝光太低/黑位 | 開蓋、開自動曝光、確認有光 |
| R 與 B 對調 | Bayer order 假設錯 | 換成 BGGR 重算 |
| Gr 與 Gb 明顯不同 | 灰卡不均或 shading | 拍中央小區塊、之後做 LSC(單元 12) |
| 數值跳動、非整數步階 | 10-bit 被當 8-bit 讀 | 確認 packing 與 dtype |
SRGGB10 用 np.uint8 讀——packing 完全不同;② 鏡頭蓋著就開始驗證 Bayer——先確認曝光正常;③ 用 JPEG/PNG 來判斷 Bayer order——有損壓縮會污染資料,必須用 RAW。v4l2-ctl --list-formats-ext 記錄你的板卡支援哪些 RAW fourcc 與位元深。場景:在 Orange Pi 5 Plus(RK3588)上接 OV9281(960p IR 感測器),驗證其 Bayer order 與 OV5640 不同的影響。
v4l2-ctl -d /dev/video0 --set-fmt-video=width=1280,height=720,pixelformat=SRGGB8 v4l2-ctl -d /dev/video0 --stream-mmap=3 --stream-count=1 --stream-to=ov9281.raw
python3 - <<'EOF'
import numpy as np
raw_9281 = np.fromfile('ov9281.raw', dtype=np.uint8).reshape(720, 1280)
raw_5640 = np.fromfile('ov5640.raw', dtype=np.uint16).reshape(720, 1280)
print('OV9281 8-bit max:', raw_9281.max(), ' OV5640 10-bit max:', raw_5640.max())
print('OV9281 R:', raw_9281[0::2,0::2].mean(), ' B:', raw_9281[1::2,1::2].mean())
EOFRK3588 的 ISP3 對 RAW 資料的處理與感測器內 ISP 完全不同:
容易忽略的邊界案例:RK3588 ISP3 在 demosaic 階段支援「自適應邊緣感知」(edge-aware interpolation),但預設值對高紋理場景(如磚牆)容易產生 false color——需要手動調 demosaic_th 參數。這在 Allwinner 平台上完全不存在,因為 CIF 沒有 demosaic 模組。
| 症狀 | 可能原因 | 解決方案 |
|---|---|---|
| 拍灰卡時 Gr ≠ Gb(差距 >5%) | 亮場不均勻或 LSC 未校正 | 用更 diffuse 的光源(如陰天窗邊)重拍;或先套 LSC 增益圖再驗證 |
| 10-bit RAW 用 uint8 讀,出現規律條紋 | SRGGB10 與 SRGGB10P 的 packing 不同 | 確認 fourcc:SRGGB10 是 16-bit 容器存 10-bit;SRGGB10P 是 packed 連續位元 |
| 換了解析度後 Bayer order 變了 | binning 模式改變了 pixel 排列 | 每次換解析度/binning 都要重新驗證 Bayer order |
| OV9281 8-bit RAW 暗部全 0 | 黑位偏高或曝光不足 | 先量測黑位(OB),確認暗部有足夠值域;必要時補光 |
| RAW 檔案 size 不符預期 | bytesperline 對齊 padding | 用 v4l2-ctl --get-fmt-video 取得實際 bytesperline 與 sizeimage |
把單元 2、8、12、13 串成一個感測器品質檢驗專案:對同一顆 OV5640 完整驗證「Bayer order、位元深、黑位、雜訊、shading」五項規格。這是任何調校開始前必做的「感測器體檢」。
v4l2-ctl -d /dev/video0 --set-fmt-video=width=1280,height=720,pixelformat=SRGGB10 \ --stream-mmap=3 --stream-count=1 --stream-to=dark.raw # 蓋鏡頭 v4l2-ctl -d /dev/video0 --stream-count=1 --stream-to=flat.raw # 均勻亮場 v4l2-ctl -d /dev/video0 --stream-count=1 --stream-to=color.raw # 色卡
python3 - <<'EOF'
import numpy as np
def analyze(fn):
raw = np.fromfile(fn, dtype=np.uint16).reshape(720,1280)
return dict(R=raw[0::2,0::2], Gr=raw[0::2,1::2],
Gb=raw[1::2,0::2], B=raw[1::2,1::2])
dark = analyze('dark.raw'); flat = analyze('flat.raw'); color = analyze('color.raw')
ob = np.median(dark['R'])
print('黑位 OB =', ob)
print('亮場 corner/center ratio:',
round(flat['R'][350:420,500:780].mean()/flat['R'][10:80,10:80].mean(),3))
print('色卡四通道:', {k: round(v.mean(),1) for k,v in color.items()})
EOF# 判定標準: # Bayer order: 拍純紅/純藍時 R/B 通道應分別最高 # 位元深: 亮場峰值應接近 1023(10-bit 滿量程) # 黑位: dark 中位數應落在 0–64 之間(>64 可能黑位偏高) # 雜訊: 灰卡暗部 σ 應在合理範圍(單元 13 詳細量)
| 步驟 | 作法 | 通過判據 |
|---|---|---|
| 1. 驗證 Bayer order | 拍純紅/純藍色卡,比較四通道均值 | 純紅時 R 最高、純藍時 B 最高 |
| 2. 驗證位元深 | 拍亮場看峰值 | 10-bit 峰值 ≈ 1023;用對應 dtype 讀 |
| 3. 量黑位 | 蓋鏡頭拍暗幀,取中位數 | OB 落在 0–64(10-bit)合理範圍 |
| 4. 量雜訊基底 | 灰卡中低光 ROI 的 σ | σ 隨增益上升而上升(單元 13) |
| 5. 量 shading | 亮場 corner/center 比值 | 比值 < 1,記錄供單元 12 用 |
| 面向 | Orange Pi | RPi5 | Orin Nano | Thor |
|---|---|---|---|---|
| 取得 RAW | v4l2-ctl SRGGB10 | rpicam --raw | argus raw capture | Holoscan capture |
| 常見感測器 | OV5640 / OV9281 | IMX219 / OV5647 | IMX219 / OV9281 | IMX 系列高階 |
| RAW 位元深 | 8/10/12 依 fourcc | 10/12 | 10/12/14 | 10/12/14 |
| Bayer 驗證工具 | 自行寫 Python | libcamera + rpicam-apps | NVIDIA 工具 + Argus | Holoscan 範例 |
| 感測器內 ISP | OV5640 內建 | IMX 內建(有限) | 感測器直出 RAW | 感測器直出 RAW |
- [ ] 我能用四通道均值驗證 OV5640 的 RGGB order。 - [ ] 我能說明 SRGGB10 與 SRGGB8 的位元深差異與讀取方式。 - [ ] 我已量測並記錄本平台的黑位 OB 值。 - [ ] 我能解釋 rolling 與 global shutter 的取捨。 - [ ] 我已跑完 2.13 專案並產出「感測器體檢表」。 - [ ] 我能指出「RAW 驗證」為何是四平台通用的第一鐵律。
感測器的核心秘密在 register。以下是以 OV5640 為例的「讀→改→寫→驗證」位元級完整序列:
| 步驟 | 暫存器/位址 | 位元欄位 | 操作 | 預期值 |
|---|---|---|---|---|
| 1. 讀感測器 ID | 0x300A | [7:0] chip ID high | i2cget -y 3 0x3c 0x300a | 0x56 |
| 2. 讀感測器 ID | 0x300B | [7:0] chip ID low | i2cget -y 3 0x3c 0x300b | 0x40 |
| 3. 讀 SCCL | 0x3040 | [7:0] sccb mode | i2cget ... 0x3040 | 0x00(標準 I2C) |
| 4. 驗證 OV 輸出格式 | 0x3808~0x380B | [15:0] width, [15:0] height | i2cget ... 0x3808; i2cget ... 0x3809 | 依 init 設定 |
| 5. 讀 OB 值 | 0x3B07 | [7:0] black level | i2cget ... 0x3b07 | ~0x10(黑位偏移) |
| 6. 驗證 PLL | 0x3035 | [7:4] PLL prediv | i2cget ... 0x3035 | 依 target fps |
決策樹 A:感測器 ID 讀不到
i2cget 0x300a 回什麼? ├─ 0xFF → I2C 無回應 │ ├─ i2cdetect -y 3 → 0x3c 出現嗎? │ │ ├─ 不出現 → I2C bus 問題(上拉/時序) │ │ └─ 出現 → 位址正確但 register 讀失敗 → 檢查 power-on 時序 │ └─ 嘗試 i2cget -y 3 0x3c 0x300a(其他位址) ├─ 0x00 → 感測器 reset 未釋放 → 檢查 reset-gpios ├─ 0x30/0x31 → 不同感測器(如 OV9281)→ 確認模組型號 └─ 0x56 → 正常!繼續讀 0x300B 確認低 byte
決策樹 B:感測器 ID 正確但無 RAW 輸出
stream-on 後有資料嗎? ├─ 無 → DPHY 訊號問題 │ ├─ 檢查 dmesg csi 錯誤 → 時序/頻寬 │ └─ 嘗試降低解析度 → 是否能輸出 ├─ 有但全黑 → sensor 無曝光 │ ├─ 讀 0x3500/0x3501/0x3502 → 驗證 AEC 行為 │ └─ 手動設 0x3503=0x08 → 覆蓋自動曝光 └─ 有但全白 → sensor 過曝 ├─ 減少 analog gain:0x350a → 降低值 └─ 減少 exposure:0x3500~0x3502 → 減少值
| 步驟 | 指令 | 預期輸出 | 判讀標準 |
|---|---|---|---|
| 1. 讀感測器 ID | i2cget -y 3 0x3c 0x300a | 0x56 | OV5640 正常回應 |
| 2. 讀 0x300B | i2cget -y 3 0x3c 0x300b | 0x40 | 完整 ID = 0x5640 |
| 3. 輸出格式 | i2cget -y 3 0x3c 0x3808 | 依解析度 | width 高 byte |
| 4. 黑位 OB | i2cget -y 3 0x3c 0x3b07 | ~0x10 | 感測器基準黑位 |
| 5. PLL 設定 | i2cget -y 3 0x3c 0x3035 | 依 fps | PLL 與 clock 一致 |
| 6. 嘗試手動曝光 | i2cset -y 3 0x3c 0x3503 0x08 | 覆蓋自動曝光 | 可手動控制 |
| 面向 | Orange Pi | RPi5 | Orin Nano | Thor | 推薦 |
|---|---|---|---|---|---|
| 感測器支援 | OV/OV5640 為主 | IMX/OV 全系列 | IMX 系列高階 | IMX 系列高階 | 畫質→IMX |
| RAW 位元深 | 8/10/12 | 10/12 | 10/12/14 | 10/12/14 | 動態範圍→14-bit |
| 感測器內 ISP | OV5640 內建 | IMX 內建(有限) | 感測器直出 RAW | 感測器直出 RAW | 畫質→直出 |
| Bayer 驗證工具 | 自寫 Python | libcamera + rpicam | NVIDIA 工具 | Holoscan 範例 | 學習→Python |
| I2C 位址 | 0x3c | 0x3c | 0x36 | 0x36 | 跨平台先 i2cdetect |
- [ ] 確認感測器型號(OV5640 / OV9281 / IMX219) - [ ] 讀取 0x300A + 0x300B 確認完整 ID - [ ] 記錄感測器支援的四種 Bayer 位元深 - [ ] 產出「感測器規格表」(型號/ID/格式/最大解析度) - [ ] 確認 I2C 位址(0x3c vs 0x36)並記錄 - [ ] 測量黑位 OB 值並記錄 - [ ] 產出「感測器體檢表」(含所有關鍵 register 值) - [ ] 驗證 Bayer order(RGGB / GRBG / GBRG / BGGR) - [ ] 確認 rolling vs global shutter 行為 - [ ] 用四通道均值法驗證 Bayer order