感測器內 ISP vs 平台
與 RPi5(硬體 ISP)不同,Orange Pi 的校正多半發生在感測器內部(OV5640 內建 AEC/AGC/AWB/DPC)。
| SoC | 平台 ISP | 調校空間 |
|---|---|---|
| RK3588 | Rockchip ISP | 較大(需 Rockchip 工具) |
| H618/H616 | 多無完整 ISP | 小(靠感測器) |
| Register 群 | 功能 |
|---|---|
| 0x3500–0x350F | AEC/AGC(曝光與增益) |
| 0x5180–0x51A5 | AWB(白平衡) |
| 0x5000–0x5003 | ISP 功能開關(色彩/降噪) |
無論感測器內 ISP 還是平台 ISP,校正幾乎都走同一條「經典管線」:
OV5640 內建 ISP 把整條管線做在感測器內部,用 0x5000 一帶的開關控制各階段。每個階段的「校正空間」由對應 register 決定:
| 階段 | OV5640 register | 性質 |
|---|---|---|
| 黑位/OB | 0x4000 帶 | 固定 offset 減除 |
| LSC | 0x4600 帶 | 有限的 shading 表 |
| DPC | 0x4800 帶 | 缺陷像素校正 |
| AWB | 0x5180–0x51A5 | R/G/B gains |
| 色彩矩陣 | 0x4750 帶 | CCM |
for r in 0x5000 0x5001 0x5002 0x5003; do
echo -n "$r: "; sudo i2ctransfer -y 3 w2@0x3c ${r:2:2} ${r:4:2} r1
donesudo i2ctransfer -y 3 w3@0x3c 0x50 0x00 0x00 # 0x5000 bit0 控制 ISP 總開關v4l2-ctl -d /dev/video0 --set-fmt-video=width=1280,height=720,pixelformat=YUYV \ --stream-mmap=3 --stream-count=1 --stream-to=isp_off.yuv
| 現象 | 方向 |
|---|---|
| 關 ISP 後全綠/全洋紅 | 正常——RAW 沒 demosaic,顏色當然錯(別慌,這是「證明 ISP 在工作」的訊號) |
| 色彩過飽和/色偏 | AWB/CCM 問題 → 回單元 11 |
| 角落暗 | LSC 沒開或表太小 → 單元 12 |
| 雜訊明顯 | NR 階段弱 → 單元 13 |
| 全無差別 | 0x5000 開關對 YUYV 路徑無效 / 需重啟串流才生效 |
「能調什麼」比「有沒有 ISP」更實用。用同一個校正任務比較感測器內 ISP 與平台 ISP:
| 任務 | 感測器內(OV5640) | 平台 ISP(RK3588) | 後處理(OpenCV) |
|---|---|---|---|
| 黑位 | 0x4000 帶(固定 offset) | OB 參數可分段 | 減常數 |
| LSC | 表極小、公開欄位少 | 完整網格(如 17×13) | 任意大小增益圖 ✅ |
| AWB | 0x5180 gains | 3A 引擎 + 分色溫策略 | 後處理 gains ✅ |
| CCM | 欄位多不公開 | 完整矩陣 + 分區域 | OpenCV 矩陣 ✅ |
| NR/Sharpen | 0x5000 帶(弱) | 硬體模組強 | 演算法任意 ✅ |
| 即時性 | ✅ 感測器內 | ✅ 硬體管線 | ⚠️ 視算力 |
0x5000–0x5003 開關實驗(7.4)驗證「哪一段由感測器做」,備份原始值並取幀對比。場景:在 Orange Pi 5 Plus(RK3588)上,用 OV5640 拍均勻亮場,分別用 ISP3 內建 LSC 與後處理 Python 增益圖做 lens shading 校正,比較效果。
# 若有 rkisp tuning 工具: rkisp_tuning -d /dev/media0 --set-lsc grid_17x13 # 用 ISP3 出圖 v4l2-ctl -d /dev/video0 --stream-mmap=3 --stream-count=1 --stream-to=isp_lsc.yuv
python3 - <<'EOF'
import numpy as np
raw = np.fromfile('flat.raw', dtype=np.uint16).reshape(720, 1280)
center = raw[300:420, 500:780].mean()
gain = center / (raw.astype(float) + 1e-6)
np.save('gain.npy', gain)
corrected = (raw * gain).clip(0, 1023).astype(np.uint16)
corrected.tofile('post_lsc.raw')
EOFRK3588 的 RGA 可以做 ISP 出圖後的即時處理,但它不在 media graph 內:
| 功能 | ISP3 做? | RGA 做? | 後處理做? |
|---|---|---|---|
| RAW → YUV(demosaic) | ✅ 硬體 | ❌ 不做 | ⚠️ CPU 慢 |
| 旋轉 90°/270° | ❌ 不做 | ✅ 硬體即時 | ⚠️ CPU 慢 |
| 縮放 | ❌ 不做 | ✅ 硬體即時 | ⚠️ CPU 慢 |
| YUV → NV12 | ✅ 可設定 | ✅ 硬體即時 | ⚠️ CPU 慢 |
容易忽略的邊界案例:RGA 的旋轉操作會改變圖像的 stride(bytesperline),但不會更新 V4L2 的 format metadata——所以旋轉後的 buffer 必須在 userspace 手動重新計算 layout,否則會出現條紋。
| 症狀 | 可能原因 | 解決方案 |
|---|---|---|
| 關 ISP 後出圖仍是 YUV 而非 RAW | OV5640 的 ISP 開關只影響色彩處理,不切換輸出格式 | 用 0x3008 切到 raw output mode;或用 V4L2 format 設 SRGGB10 |
| RK3588 ISP3 LSC 效果不如後處理 | ISP3 網格太粗或未 tuning | 用 Rockchip tuning 工具調整網格大小;或接受後處理更精準 |
| OV5640 0x5000 設定後無變化 | 需重啟 streaming 才套用 | STREAMOFF → 設定 → STREAMON |
| 色彩過飽和但 AWB 看起來正常 | CCM 問題(非 AWB) | 用 ColorChecker 量 CCM;後處理補矩陣 |
| ISP3 出圖延遲明顯 | ISP pipe 塞住或 DDR 頻寬不足 | 降低解析度測試;確認 DDR 頻寬 DT 設定 |
0x5000 的 bit 開關逐個開啟/關閉 OV5640 的 ISP 模組(DPC/AWB/CCM),對同一場景取幀,記錄每開關對畫質的量化影響。把單元 7 的 ISP 知識做成架構評估專案:對你的應用需求,實測「感測器內 ISP」與「平台 ISP / 後處理」各能提供什麼,最後產出「誰來做什麼」的分工決定文件。
v4l2-ctl -d /dev/v4l-subdev0 --list-ctrls # 看有哪些感測器端校正
for r in 0x5000 0x5180 0x4600; do
echo -n "$r: "; sudo i2ctransfer -y 3 w2@0x3c ${r:2:2} ${r:4:2} r1
donemedia-ctl -p -d /dev/media0 | grep -iE "isp|cif" # 有無平台 ISP v4l2-ctl -d /dev/v4l-subdev0 --list-ctrls | wc -l # controls 數量 = 調校深度
python3 -c "import cv2; print('OpenCV', cv2.__version__)"
# 量後處理 LSC/NR 的延遲(單元 12 增益圖、單元 13 NR)
python3 - <<'EOF'
import cv2, time, numpy as np
img = np.random.randint(0,255,(720,1280,3), dtype=np.uint8)
t=time.time(); cv2.fastNlMeansDenoisingColored(img,None,10,10,7,21)
print(f'OpenCV NR 延遲: {(time.time()-t)*1000:.0f} ms')
EOF# 範例輸出: # 任務 感測器 平台ISP 後處理 決定 # 黑位 ✅0x4000 ✅OB ✅ → 感測器 # LSC ⚠️小表 ✅網格 ✅ → 平台/後處理 # AWB ✅ ⚠️ ✅ → 感測器(簡單場景) # CCM ❌ ✅ ✅ → 後處理 # NR ⚠️弱 ✅ ✅ → 後處理
| 步驟 | 作法 | 通過判據 |
|---|---|---|
| 1. 備份原始值 | 讀 0x5000–0x5003 全部欄位 | 記錄原始值 |
| 2. 關單一功能 | 改 0x5000 的對應 bit | register 變更成功 |
| 3. 重啟串流 | STREAMOFF → STREAMON | 新設定套用(7.5 提到需重啟) |
| 4. 取幀比較 | 開/關各取一幀 | 肉眼與數值可見差異 |
| 5. 復原並確認 | 寫回原始值 | 回到基準狀態 |
| 面向 | Orange Pi | RPi5 | Orin Nano | Thor |
|---|---|---|---|---|
| 平台 ISP | RK3588 有 / H618 無 | 硬體 ISP | NVIDIA ISP | NVIDIA ISP(新) |
| 3A 引擎 | 感測器內建為主 | libcamera IPA | NVIDIA 3A | NVIDIA 3A |
| LSC 網格 | RK 工具 / 後處理 | libcamera tuning | NVIDIA tuning | NVIDIA tuning |
| 調校文件 | BSP 資料(少) | 開源(完整) | 封閉(需簽約) | 封閉(新) |
| 自訂彈性 | 高(register 可直控) | 中(IPA 可改) | 低(封閉) | 低(封閉) |
- [ ] 我能完成 ISP 分工評估專案並產出分工表。 - [ ] 我能用 0x5000 開關實驗證明「哪一段由感測器做」。 - [ ] 我明白「感測器有 LSC」與「可深度調 LSC」的差別。 - [ ] 我已建立自己的「黑位/LSC/AWB/CCM/NR 誰來做」表。 - [ ] 我能說明 RPi5(開源)vs NVIDIA(封閉)的調校路徑差異。 - [ ] 我遵守「單變數實驗法」並保留每次實驗的備份。
ISP 管線的各階段都由 register 控制。以下是 OV5640 內建 ISP 的位元級操作序列:
| 步驟 | 暫存器/位址 | 位元欄位 | 操作 | 預期值 |
|---|---|---|---|---|
| 1. 開啟 ISP | 0x501F | [0] ISP enable | i2cset -y 3 0x3c 0x501f 0x01 | ISP 開啟 |
| 2. 開關 LSC | 0x5800 | [0] LSC enable | i2cset -y 3 0x3c 0x5800 0x01/0x00 | LSC on/off |
| 3. 開關 AWB | 0x5180~0x5190 | [0] AWB enable | i2cset -y 3 0x3c 0x5183 0x01 | AWB on |
| 4. 設定 AEC | 0x3503 | [0] AEC manual | i2cset -y 3 0x3c 0x3503 0x08 | 手動 AEC |
| 5. 讀 AEC 結果 | 0x3500~0x3502 | [19:8] exposure | i2cget ... 0x3500 | 依場景 |
| 6. 驗證 ISP 輸出 | v4l2-ctl | — | v4l2-ctl --stream-mmap=1 --stream-count=1 | 成功取流 |
決策樹 A:ISP 開關實驗失敗
i2cset 0x501F=0x01 後影像異常? ├─ 影像不變 → ISP 未生效 │ ├─ 檢查 0x501F 回讀值 → 是否寫入成功 │ └─ 檢查 sensor format → ISP 只在特定格式下運作 ├─ 影像變差 → ISP 設定錯誤 │ ├─ 檢查 LSC/AWB/CCM 開關 │ └─ 嘗試只開 ISP 但關閉其他模組 └─ 影像變好 → 正常!記錄差異 └─ 截圖對比:ISP on vs off
決策樹 B:感測器內 ISP vs 平台 ISP 混亂
OV5640 的 ISP 幹什麼? ├─ 內建:AEC/AGC/AWB/LSC(0x501F 控制) ├─ 平台:RK3588 的 ISP(若有) └─ 後處理:你的 Python 腳本(任意) └─ 關鍵:先在 RAW 上驗證(關閉 0x501F)
| 步驟 | 指令 | 預期輸出 | 判讀標準 |
|---|---|---|---|
| 1. 關閉 ISP | i2cset -y 3 0x3c 0x501f 0x00 | RAW 輸出 | 感測器直出 RAW |
| 2. 取 RAW | v4l2-ctl --stream-mmap=1 --stream-count=1 | RAW 檔 | Bayer 格式正確 |
| 3. 開啟 ISP | i2cset -y 3 0x3c 0x501f 0x01 | ISP 管線 | 影像經過處理 |
| 4. 取 ISP 輸出 | v4l2-ctl --stream-mmap=1 --stream-count=1 | 處理後影像 | 對比 RAW 差異 |
| 5. 開關 LSC | i2cset ... 0x5800 0x00 | LSC 關閉 | 角落暗角出現 |
| 6. 驗證 AEC | i2cset ... 0x3503 0x08; i2cset ... 0x3500 0x00 | 手動曝光 | 可控制亮度 |
| 面向 | Orange Pi | RPi5 | Orin Nano | Thor | 推薦 |
|---|---|---|---|---|---|
| ISP 硬體 | RK3588 有 / H618 無 | 硬體 ISP | NVIDIA ISP | Blackwell ISP | 畫質→NVIDIA |
| ISP 開關 | 0x501F(OV5640) | libcamera tuning | 封閉 | 封閉 | 學習→0x501F |
| 調校彈性 | 最高(register 層) | 高(IPA 開源) | 低(封閉) | 低(封閉) | 學習→Orange Pi |
| 校正路線 | 感測器內建 | libcamera 3A | NVIDIA 3A | NVIDIA 3A | 底層→感測器 |
| LSC/AWB/CCM | OV5640 內建 | ISP 內建 | ISP 內建 | ISP 內建 | 學習→OV5640 |
- [ ] 產出「ISP 分工表」(哪一段由感測器/平台/後處理做) - [ ] 用 0x501F 開關實驗證明「哪一段由感測器做」 - [ ] 用 0x5800 開關 LSC 並截圖對比 - [ ] 驗證 AEC 手動控制(0x3503=0x08) - [ ] 關閉 ISP 取 RAW,開啟 ISP 取處理後影像 - [ ] 產出「三層校正能力表」(sensor/platform/post) - [ ] 確認 OV5640 內建 AEC/AGC/AWB/LSC 的 register 群 - [ ] 比較 RK3588 與 H618 的 ISP 能力差異 - [ ] 遵守「單變數實驗法」並保留每次實驗備份 - [ ] 產出「ISP 管線實驗報告」