RAW、bayer、HSB 資料
| 格式 | 說明 |
|---|---|
NV12 / YUV | ISP 處理後 |
SRGGB10 / Bayer RAW | 感測器原始(調校分析) |
HSB 把感測器資料封包走乙太網路,host 端解包可選 RAW 或處理後。RAW 分析仍是品質調校基礎。
argus_camera / Holoscan 取 RAW
RAW 是影像品質分析的根本:雜訊、黑位、Bayer order 都回 RAW。
| 錯誤 | 症狀 | 檢查 |
|---|---|---|
| Bayer order 錯 | 色彩全亂 | 拍純色卡判別四通道 |
| bit depth 錯讀 | 暗部階調斷裂 | 確認 RAW 位元深 |
| packing 錯 | 條紋狀假影 | 用對應位元深工具解析 |
感測器輸出 10-bit 像素,但儲存/傳輸常包成 RAW10(4 bytes 裝 5 pixels)節省頻寬。解析 RAW 前必須先 unpack,否則像素值全錯。
5 個 10-bit 像素 = 50 bits pack 成 4 bytes(32 bits):前 8 個 pixel[7:0] 高位, 再放 2 個「整組 extra」bit 集合 # 以 Python 讀取:raw10 bytes → 像素 array def unpack_raw10(buf): out = [] for i in range(0, len(buf), 5): b0,b1,b2,b3,b4 = buf[i:i+5] out.append((b0<<2) | (b4>>0 & 0x03)) out.append((b1<<2) | (b4>>2 & 0x03)) out.append((b2<<2) | (b4>>4 & 0x03)) out.append((b3<<2) | (b4>>6 & 0x03)) out.append(b4>>... ) # 依 MIPI 規範實作 return out
python3 - <<'EOF'
import rawpy, numpy as np
raw = rawpy.imread('frame.dng').raw_image
print(raw.shape) # (H, W),1 channel(Bayer)
bl = raw[0:20, 0:20].mean() # 黑框區應接近黑位
print('black level ~', bl)
b = raw[::2, ::2]; g1 = raw[::2, 1::2]
g2 = raw[1::2, ::2]; r = raw[1::2, 1::2]
print('R/Gr/Gb/B means:', r.mean(), g1.mean(), g2.mean(), b.mean())
EOF1. 有彩色條紋? ├─ packing 錯(unit-08 陷阱) └─ 或 Bayer order 錯 2. 全圖偏某一色? └─ Bayer order 判別(拍白卡) 3. 有固定直條紋? └─ 位元深/packing 錯、或 stride 算錯 4. 有亮點/黑點? └─ 缺陷像素(unit-13 DPC) 5. 曝光不對? └─ 回 unit-10 曝光控制
解析 RAW 時要把三個概念分開:有效像素是感測器真正輸出的 W×H,payload 是 packing 後的位元組,stride/pitch 則是 buffer 每列實際跳過的距離。MIPI packet 的 word count 只描述 payload,不一定等於 driver buffer 的 pitch;DMA 對齊也可能在每列尾端加入 padding。
| 層級 | 要回答的問題 | 驗證 |
|---|---|---|
| CSI packet | DT、word count、ECC/CRC 是否正確? | dmesg、CSI counter |
| buffer layout | 每列從哪個 byte 開始? | bytesperline、plane length |
| pixel decode | 每 10/12 bit 如何還原? | 灰階分布、黑位、白位 |
有效像素:1280 × 800 = 1,024,000 pixels這個例子說明「檔案大小正確」仍不代表畫面正確:若讀檔程式忽略 64 bytes padding,第二列起就會斜移。先從 V4L2 query 回讀
bytesperline與sizeimage,再決定 decoder 的 row stride。8.16 疑難排解決策樹:RAW 條紋從哪一層來
逐層縮小範圍1. CSI/HSB 有 CRC、drop 或 sequence error? ├─ 有 → 先查 lane rate、線材、封包/網路統計 └─ 無 ─┐ 2. 每列起點是否依 bytesperline 前進? ├─ 否 → 修 stride/pitch 與 plane offset └─ 是 ─┐ 3. 5-pixel/4-byte(或 sensor 指定)邊界出現規律條紋? ├─ 是 → 修 RAW packing / bit shift └─ 否 ─┐ 4. 白卡四通道是否只是顏色順序錯? ├─ 是 → 修 Bayer order └─ 否 → 查曝光、黑位、DPC 與 ISP8.17 常見錯誤與陷阱
陷阱 1:把 CSI bit rate 當成檔案 bytes。CSI 還有 packet/header、lane 分配與對齊;頻寬估算與 buffer layout 必須分開。陷阱 2:忽略有效位元所在 bit。有些 10-bit 16-bit container 左移 6 bit,有些右對齊;直接轉成 uint16 會讓亮度差 64 倍。陷阱 3:用 ISP 後的 YUV 判斷 RAW。YUV 已經過黑位、demosaic、gamma;格式問題要回到 RAW 或未處理 capture 驗證。8.18 練習
- 讀出平台的
bytesperline、sizeimage、fourcc,寫下三者關係。- 用同一幀資料分別套用正確與錯誤 stride,描述兩者的視覺特徵。
- 用白卡與黑框各做一次 Bayer、packing、黑位檢查,將結果存入回歸紀錄。
看完這單元你應該能說出:
- RAW vs NV12 用途。
- HSB 資料封包的 RAW 分析。
- Bayer order 陷阱。
- RAW 在調校分析角色。
延伸閱讀
場景:Thor T5000 透過 HSB 接 OV9281(1280×800 RAW10 @ 120 fps),Holoscan 解包後取 RAW。取到的 RAW 有規律的彩色條紋,但同一顆感測器走 CSI 直接取流時正常。
# 問題定位: # CSI 直取 RAW 正常 → 感測器輸出正確 # HSB 取 RAW 有條紋 → 解包/封包層問題 # 分析步驟: 1. 比較 CSI 與 HSB 兩路 RAW 的前 10 bytes → HSB 多了 4 bytes 的 packet header → 原始解包未剝離 header → 第一列像素錯位 4 bytes 2. 驗證 stride: CSI:bytesperline = 1280 × 10/8 = 1600 HSB:bytesperline = 1600 + 4(header)= 1604 # 若用 CSI 的 stride 讀 HSB 的 RAW → 每列累積錯位 3. 修正方案: # 在 Holoscan SensorSourceOp 中設定 header_offset = 4 # 或用 V4L2 的 V4L2_BUF_DATA_FORMAT 設定 header size # 設計決策: # 1. HSB 與 CSI 的 RAW 格式差異在封包 header——內容相同、包裝不同 # 2. 解包時必須先剝離 header,再用 sensor mode 的 stride 進行解析 # 3. 標準做法:用 V4L2 query 回讀 bytesperline,而非自行假設
RAW10 是 4 bytes 裝 5 pixels,RAW12 則是 3 bytes 裝 2 pixels(或 2 bytes 裝 1 pixel + padding)。在多感測器系統中,若 4 顆感測器使用不同的 RAW bit depth(例如前視 RAW12 + 側視 RAW10),每路的 stride 與 packing 規則不同,Holoscan 的 buffer pool 必須為每路維護獨立的 unpack 設定。
| 症狀 | 可能原因 | 解決方案 |
|---|---|---|
| RAW 第一列正常、第二列起逐漸錯位 | stride/pitch 設定不包含 padding bytes | 回讀 V4L2 的 bytesperline,確保 decoder 使用正確的 row stride |
| HSB RAW 有 5-pixel 為一組的規律條紋 | RAW10 packing 未正確解包 | 確認 unpack 函式符合 MIPI CSI-2 的 RAW10 packing 規範;驗證黑位應為固定常數 |
| 同一感測器 CSI 取 RAW 正常但 HSB 取 RAW 有彩色條紋 | HSB 封包 header 未剝離 | 在 Holoscan SensorSourceOp 設定 header_offset;或用 V4L2 query 回讀 |
| RAW 位元深搞混(10-bit 當 16-bit 讀)導致亮度差 64 倍 | container 大小 vs 有效位元所在 bit 未確認 | 確認有效位元在 container 的哪個 bit(左移 6 bit 或右對齊);用黑位值驗證 |
| 多感測器中某路的 RAW packing 設定被其他路覆蓋 | Holoscan buffer pool 未為每路維護獨立設定 | 為每路感測器建立獨立的 unpack 設定;避免共用全域 buffer pool 設定 |
場景:調校團隊需要一套「可重複使用的 RAW 分析工具鏈」,支援多感測器(RAW10/RAW12 混用)、CSI 與 HSB 雙路徑。
M1 單張解包 ├─ 寫 unpack_raw10 / unpack_raw12 函式 ├─ 用已知黑位驗證 └─ 通過:黑位 = 固定常數 M2 stride 處理 ├─ 從 V4L2 query 回讀 bytesperline/sizeimage ├─ decoder 依 bytesperline 前進 └─ 通過:多列連續無錯位 M3 雙路徑支援 ├─ CSI 與 HSB 各建載入器 ├─ HSB 剝離 packet header └─ 通過:兩路 RAW 內容一致 M4 自動化檢查 ├─ Bayer order / 黑位 / histogram 自動輸出 └─ 通過:一鍵產出 RAW 健康報告 M5 多感測器 ├─ 每路獨立 unpack 設定 └─ 通過:RAW10/RAW12 混用各自正確 M6 整合 repo └─ 通過:工具鏈 + 測試檔 + README 進 repo
bytesperline 永遠從 device query 回讀,不要假設。Step 1 查 layout v4l2-ctl -d /dev/video0 --get-fmt-video # 記錄 bytesperline、sizeimage、fourcc Step 2 取 RAW v4l2-ctl -d /dev/video0 --set-fmt-video=pixelformat=SRGGB10 --stream-mmap=3 --stream-count=1 --stream-to=r.raw Step 3 檔案大小 ls -l r.raw # ≈ W×H×10/8(±padding) Step 4 解包驗證 python 解包 → 檢查黑位(固定常數) Step 5 Bayer order 拍白卡,四通道平均,Gr≈Gb Step 6 HSB 對照(若有) 剝 header 後與 CSI 的 RAW 比較 判讀指標: 條紋規律 → packing/stride 錯 全圖偏色 → Bayer order 錯 黑位飄移 → 感測器/解包錯
| 面向 | Thor T5000 | RPi5 | Orange Pi | Orin Nano |
|---|---|---|---|---|
| RAW fourcc | SRGGB10/12(V4L2) | rpicam --raw(DNG) | V4L2 RAW | SRGGB10/12(V4L2) |
| RAW 容器 | raw buffer | DNG(含 metadata) | raw buffer | raw buffer |
| bytesperline 取得 | V4L2 query | libcamera metadata | V4L2 query | V4L2 query |
| HSB 封包 header | ✅ 需剝離 | — | — | — |
| RAW10 packing | MIPI 4B/5P | 同 MIPI(DNG 解包) | 同 MIPI | 同 MIPI |
本單元涉及的關鍵 register,以及「讀→改→寫→驗證」的完整位元級操作序列:
| Register | 位址 | 功能 | Bit Field 說明 |
|---|---|---|---|
CSI_PIXEL_FORMAT | 0x0c0a0008 | CSI 像素格式設定 | bit[5:0]=DT (0x2B=RAW10, 0x2C=RAW12, 0x1E=YUV422) |
VI_FORMAT | 0x0c800008 | VI 輸入格式 | bit[5:0]=format code, bit[9:8]=BPP |
# Step 1: 讀取目前值 $ devmem2 0x0c0a0008 w # 記錄 current_value # Step 2: 計算新值(設定 bit[0]=1) $ new_value=$((current_value | 0x0001)) # Step 3: 寫入 $ devmem2 0x0c0a0008 w $new_value # Step 4: 驗證 $ devmem2 0x0c0a0008 w # 確認 bit[0] = 1,其餘 bit 不變 # Step 5: 進階 — bitmask 操作 $ read_val=$(devmem2 0x0c0a0008 w | grep "Read" | awk '{print $NF}') $ mask=0x0001 $ expected=0x0001 $ [ $(($read_val & $mask)) -eq $expected ] && echo "PASS" || echo "FAIL: bit[0] not set"
1. 用 hexdump 看 raw 檔,data 看起來隨機? ├─ 格式不對 → 確認 DT 設的 data type(0x2B=RAW10, 0x2C=RAW12) └─ 資料對但 display 畸形 → 解析度設定不符(stride 不對) 2. frame 間有 shift? ├─ sensor line length 不符 → 檢查 sensor datasheet 的 line_length └─ V4L2 bytesperline 設錯 → 修改 format callback
1. HSB stream 的 raw 與 CSI raw 格式不同? ├─ HSB 封包有額外 header → 用 Holoscan 的 packet parser └─ byte order 不同(little vs big endian)→ 設定 swap
RAW 資料驗證完整 SOP:
| 步驟 | 動作 | 指令/方法 | 預期輸出 |
|---|---|---|---|
| Step 1 | 確定格式 | `v4l2-ctl -d /dev/video0 --list-formats-ext` | 確認 RAW10/12 |
| Step 2 | 抓一幀 raw | `v4l2-ctl --stream-mmap --stream-count=1 --stream-to=frame.raw` | 檔案大小 = W×H×BPP |
| Step 3 | 驗證 header | `xxd frame.raw | head` | DT byte = 0x2B (RAW10) 或 0x2C (RAW12) |
| Step 4 | 驗證解析度 | file 大小 / (W×H×BPP) 應 = 1(±0.1%) | stride 正確 |
| Step 5 | Bayer pattern 確認 | 裁切 2×2 block 看 R/Gr/Gb/B 分佈 | Bayer order 正確 |
| Step 6 | 連續幀穩定 | 抓 10 幀比較 hash | 相同(固定場景) |
| 面向 | Thor T5000 | RPi5 | Orange Pi | Orin Nano |
|---|---|---|---|---|
| 支援 RAW 格式 | RAW8–16, YUV, PDAF | RAW10, YUV | RAW10, YUV | RAW10–14, YUV |
| RAW 傳輸方式 | MIPI CSI-2 + HSB | MIPI CSI-2 | MIPI CSI-2 | MIPI CSI-2 + GMSL |
| BPP 處理 | 硬體 unpack(CSI→ISP) | 軟體 unpack | 軟體 unpack | 硬體 unpack |
| HSB 封包格式 | Ethernet frame + header | N/A | N/A | N/A |
| PDAF 資料分離 | ✅ | ❌ | ❌ | ✅ |
| Frame metadata 支援 | ✅ | ❌ | ❌ | ✅ |
針對「RAW 擷取與資料格式」主題的完整 bring-up 步驟清單: