單元 8 · RAW 擷取與資料格式

Bayer format、V4L2 格式

V4L2 pixel format

fourcc意義
YUYV / UYVYYUV422(感測器內 ISP 出圖,最常見)
SRGGB10RAW Bayer(10-bit)
NV12平台 ISP/後處理輸出
查支援格式
v4l2-ctl -d /dev/video0 --list-formats-ext

Bayer order 與位元深

調適紀律:固定「同一格式 + 同一解析度」比較,避免 binning/格式切換造成的假性差異。

8.6 RAW 擷取實作(本平台)

v4l2-ctl 取 RAW(SRGGB10 等 fourcc)
v4l2-ctl -d /dev/video0 --set-fmt-video=width=1280,height=720,pixelformat=SRGGB10

RAW 是影像品質分析的根本:雜訊、黑位、Bayer order 都回 RAW。

8.7 常見 RAW 格式錯誤

錯誤症狀檢查
Bayer order 錯色彩全亂拍純色卡判別四通道
bit depth 錯讀暗部階調斷裂確認 RAW 位元深
packing 錯條紋狀假影用對應位元深工具解析

8.8 多模式一致性(調校前必修)

重要:不同解析度/ binning 下 Bayer order 與 shading 不同。調校與回歸必須鎖定單一模式(固定 size + format)。

8.9 深入原理:RAW packing 與 bytesperline

RAW 不是「每個像素一個 16-bit 值」那麼單純——V4L2 用 pixelformat 的位元描述 packing:

fourcc每像素位元packing
SRGGB88每 byte 1 像素
SRGGB101016-bit 容器存 10-bit(低 6 bit 補 0)
SRGGB10P10packed,跨 byte 連續
SRGGB121216-bit 容器存 12-bit

bytesperline 是「一列影像在記憶體佔多少 byte」,常因硬體對齊而大於「寬 × 每像素 bytes」。用 v4l2-ctl --get-fmt-video 可看到 bytesperlinesizeimage,程式必須用這兩個值而非「寬×高×bpp」去讀 buffer。

實務後果:如果程式假設「每列剛好 寬×bpp」,碰到對齊的 bytesperline 會整張圖斜掉或出現條紋——這是 RAW 除錯最經典的 bug 之一。

8.10 完整 Worked Example:正確讀取 RAW buffer

步驟 1|取 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
步驟 2|用 bytesperline 重組
v4l2-ctl -d /dev/video0 --get-fmt-video   # 讀 bytesperline / sizeimage
python3 - <<'EOF'
import numpy as np, struct
# 假設 bytesperline=2560, height=720, bpp=2
raw = np.fromfile('raw.bin', dtype=np.uint16).reshape(720, 1280)
print('R/Gr/Gb/B =', raw[0::2,0::2].mean(), raw[0::2,1::2].mean(),
      raw[1::2,0::2].mean(), raw[1::2,1::2].mean())
EOF
紀律:永遠用 --get-fmt-video 回傳的 bytesperline/sizeimage 配置讀取,不要手算。記錄你板卡的實際對齊值,寫進 team SOP。

8.11 疑難排解決策樹:RAW 影像異常

現象原因檢查
整圖斜掉/鋸齒bytesperline 對齊沒算改用 get-fmt 回傳值
色彩全亂Bayer order 錯拍色卡驗四通道
暗部斷階/條紋位元深讀錯確認 SRGGB10P vs SRGGB10
畫面偏綠沒做黑位校正就看RAW 本來就該偏色——先校正再看
尺寸不對sizeimage 含 padding以 sizeimage 為準
常見錯誤與陷阱:① 用 JPEG/PNG 轉出的「偽 RAW」分析——有損壓縮毀掉統計;② SRGGB10SRGGB10P 搞混——padded 與 packed 的位元佈局不同;③ 不同解析度/binning 下 Bayer order 可能不同——每次換模式都要重新確認。

8.12 深入原理:YUV 與 NV12 的布局

除了 RAW,Orange Pi 也可能輸出 YUV(感測器內 ISP)或 NV12(平台後處理)。布局完全不同:

格式布局每像素平均
YUYV / UYVY交錯 Y0 U0 Y1 V02 byte
NV12全部 Y 平面 + 交錯 UV 平面1.5 byte
NV21全部 Y 平面 + 交錯 VU 平面1.5 byte

1280×720 的 YUYV 大小 = 1280×720×2 = 1,843,200 byte;NV12 則是 1280×720×1.5 = 1,382,400 byte。兩者「同解析度但 sizeimage 不同」——所以永遠以 fourcc + get-fmt 的 sizeimage 為準

判斷檔案是 YUYV 還是 NV12
# 比較實際檔案大小與兩種理論值
ls -l frame.bin
python3 - <<'EOF'
W,H = 1280,720
import os
n = os.path.getsize('frame.bin')
print('YUV422 =', W*H*2, ' NV12 =', W*H*3//2, ' actual =', n)
EOF
為什麼要懂布局:後處理(OpenCV/GStreamer)需要正確的 caps;解析器(如 FFmpeg)會用布局假設去解資料。布局猜錯 → 畫面斜掉或綠紫分離。

8.13 練習:格式驗證總複習

  1. 取 SRGGB10 幀,用 --get-fmt-video 的 bytesperline 正確重組並印四通道均值。
  2. 分別取 YUYV 與 RAW 幀,用 sizeimage 驗證每種格式的理論大小。
  3. 比較 SRGGB10 vs SRGGB8 的 sizeimage,解釋差異。
  4. 故意用「寬×bpp」誤讀一次,記錄出現什麼現象。
  5. 寫一支小程式:讀 YUYV 檔,輸出 Y、U、V 三平面各自大小。
  6. 在不同解析度各取一幀,驗證 Bayer order 是否一致。

8.14 進階真實情境 Worked Example:RK3588 上 SRGGB10 與 SRGGB10P 的差異實測

場景:Orange Pi 5 Plus(RK3588)接 OV5640,同一解析度下分別取 SRGGB10(16-bit 容器)與 SRGGB10P(packed),比較兩者的檔案大小與解析品質。

步驟 1|分別取兩種格式
v4l2-ctl -d /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=SRGGB10 \
  --stream-mmap=3 --stream-count=1 --stream-to=raw10.bin
v4l2-ctl -d /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=SRGGB10P \
  --stream-mmap=3 --stream-count=1 --stream-to=raw10p.bin
步驟 2|比較大小與計算
ls -l raw10.bin raw10p.bin
python3 - <<'EOF'
import os
for f in ['raw10.bin', 'raw10p.bin']:
    sz = os.path.getsize(f)
    print(f'{f}: {sz} bytes  (理論 SRGGB10={1920*1080*2}  SRGGB10P≈{1920*1080*10//8+1})')
EOF
設計決策:SRGGB10P 比 SRGGB10 小約 23%(10/16),但解析需要 bit unpacking——增加 CPU 負擔。若後續做 ISP 或編碼,通常 SRGGB10 的 16-bit 對齊更有效率。

8.15 深入原理擴充:Allwinner H618 的 MIPI CSI-2 接收器限制

Allwinner H618 的 CIF(Camera Interface)與 RK3588 的 MIPI CSI-2 接收器在 RAW 處理上有關鍵差異:

項目Allwinner CIFRK3588 CSI-2
支援 RAW packing僅 8-bit / 10-bit(無 packed)支援 SRGGB10P(packed)
最大 lane 數2 lanes4 lanes per receiver
Virtual channel不支援支援 VC0-VC3
Bytesperline 對齊通常 16-byte 對齊通常 64-byte 對齊

容易忽略的邊界案例:H618 的 CIF 不支援 SRGGB10P(packed 10-bit),所以在 Allwinner 板卡上只能用 SRGGB10(16-bit 容器)。如果你的 Python 腳本在 RK3588 上用 SRGGB10P 測試通過,搬到 H618 上會直接失敗——format 不被支援。

8.16 診斷式疑難排解表

症狀可能原因解決方案
RAW 檔案 size 是預期的 2 倍用 uint16 讀 8-bit RAW,多讀了一倍確認 fourcc 對應的 dtype:SRGGB8=uint8, SRGGB10=uint16
SRGGB10P 讀出全是 0H618 不支援 packed 格式改用 SRGGB10(16-bit 容器)
RAW 四通道均值差距大Bayer order 假設錯誤或場景非均勻拍灰卡驗證;或用自動推斷腳本
條紋狀假影bytesperline 對齊未處理--get-fmt-video 的 bytesperline 重組
NV12 檔案 size 是 YUYV 的 1.5 倍(而非預期)sizeimage 含 padding--get-fmt-video 的 sizeimage 為準

8.17 進階挑戰題

  1. 在 RK3588 上,用 Python 實作 SRGGB10P 的 bit unpacking,將 packed RAW 轉為 16-bit 容器格式,並比較與直接取 SRGGB10 的時間差異。
  2. 寫一支腳本,自動偵測 ls -l 的檔案大小,推斷是 YUYV、NV12、SRGGB10 還是 SRGGB8,印出格式與解析度。
  3. 在 H618 上,確認 v4l2-ctl --list-formats-ext 不支援 SRGGB10P,然後用 SRGGB10 取一幀,計算實際 bytesperline 與理論值的差異(padding 量)。

8.18 專案級 Worked Example:RAW 採集與格式自動驗證專案

把單元 8 的格式知識做成自動化 RAW 採集與驗證專案:自動抓取所有支援的 fourcc,驗證大小、bytesperline、Bayer order 一致性,產出一份「格式能力表」。這是後續所有調校與跨模式比較的基礎設施。

階段 1|列全部支援格式
v4l2-ctl -d /dev/video0 --list-formats-ext
階段 2|逐格式採集與驗證
#!/bin/bash
for fmt in SRGGB8 SRGGB10 SRGGB10P YUYV NV12; do
  v4l2-ctl -d /dev/video0 --set-fmt-video=width=1280,height=720,pixelformat=$fmt \
    --stream-mmap=3 --stream-count=1 --stream-to=fmt_$fmt.bin 2>/dev/null \
    && echo "$fmt 成功: $(stat -c%s fmt_$fmt.bin) bytes" \
    || echo "$fmt 不支援"
done
階段 3|驗證 bytesperline 對齊
v4l2-ctl -d /dev/video0 --get-fmt-video
python3 - <<'EOF'
import os
for fmt in ['SRGGB8','SRGGB10','YUYV','NV12']:
    f=f'fmt_{fmt}.bin'
    if os.path.exists(f):
        print(fmt, 'actual =', os.path.getsize(f), '理論(1280x720) =', {'SRGGB8':1280*720,'SRGGB10':1280*720*2,'YUYV':1280*720*2,'NV12':1280*720*3//2}[fmt])
EOF
階段 4|產出格式能力表
# 輸出格式:
# 格式      支援    sizeimage     bytesperline    Bayer order
# SRGGB10   ✅     1,843,200     2560           RGGB(驗證)
# SRGGB10P  ❌     —             —              —(H618 不支援)
# YUYV      ✅     1,843,200     2560           —
# NV12      ✅     1,382,400     1280           —
專案完成標準:你有一支 format_capture.sh 可一鍵產出「格式能力表」。這張表是跨平台移植(單元 15)與調校 SOP 的第一份參考文件。

8.19 量測/驗證 SOP:RAW 正確性驗收

步驟作法通過判據
1. sizeimage 驗證比對檔案大小與理論值匹配(含 padding 判斷)
2. bytesperline 重組用 get-fmt 的 bytesperline 讀取影像不斜不鋸齒
3. Bayer order 驗證拍純色卡看四通道RGGB 對應正確
4. 位元深驗證亮場峰值對照位元深10-bit → 峰值近 1023
5. packing 驗證SRGGB10 vs 10P 檔案大小10P 約為 10 的 5/8
SOP 紀律:「格式能取」≠「格式正確」。每次採集都跑一遍五步驗證,尤其換解析度 / 換模式後——binning 可能改變 Bayer order 與 padding。

8.20 平台間對照:RAW 格式

面向Orange PiRPi5Orin NanoThor
常見 RAW 輸出SRGGB10(16-bit 容器)RPi RAW(SBGGR10)RAW10(packed)RAW10/12
打包格式支援 10P / 不支援依 SoC依 libcamera rawpacked 為主packed 為主
bytesperline 對齊16/64 byte16 byte 常見NVIDIA 對齊NVIDIA 對齊
取 RAW 方式v4l2-ctl pixelformatrpicam --rawargus / nvgstcaptureHoloscan capture
格式工具V4L2 fourcc(標準)libcamera 描述NVIDIA 專屬NVIDIA 專屬
核心領悟:四平台的 RAW 本質相同(Bayer + 位元深),差別在「打包方式與工具名」。用「位元深 + Bayer order」描述格式(如 SRGGB10)是跨平台唯一的共同語言。

8.21 互動式檢核清單:本單元進階驗收

checklist
- [ ] 我能跑 format_capture.sh 產出格式能力表。
- [ ] 我能用 bytesperline 正確重組 RAW 而不斜圖。
- [ ] 我能區分 SRGGB10 與 SRGGB10P 的檔案大小差異。
- [ ] 我明白 YUYV / NV12 / RAW 的 sizeimage 計算。
- [ ] 我已驗證本平台在主要解析度的 Bayer order 一致性。
- [ ] 我能用「位元深 + Bayer order」描述任何平台的 RAW 格式。

8.22 Register 位元級完整工作流

RAW 格式的「讀→改→寫→驗證」是影像品質分析的基礎。以下是完整的位元級序列:

步驟暫存器/位址位元欄位操作預期值
1. 讀輸出格式0x3808~0x380B[15:0] width, [15:0] heighti2cget ... 0x3808; i2cget ... 0x3809依解析度
2. 設定 RAW 格式v4l2-ctl pixelformatv4l2-ctl --set-fmt-video=pixelformat=SRGGB10格式設定成功
3. 驗證 sizeimagefile sizels -l stream.raw= width × height × 1.25
4. 驗證 bytesperlineV4L2 formatv4l2-ctl --list-formats-ext依解析度對齊
5. 讀 Bayer order0x4300[3:0] formati2cget ... 0x4300SRGGB10
6. 驗證 RAW 資料Python scriptraw_verify.py stream.rawBayer 資料正確
位元級關鍵: sizeimage 的計算:SRGGB10 = 10bit → 1.25 bytes/pixel。1920×1080×1.25 = 2,592,000 bytes。若檔案大小不符,代表格式設定錯誤或 bytesperline 對齊異常。

8.23 多層疑難排解決策樹

決策樹 A:RAW 檔案大小異常

決策樹 A
ls -l stream.raw
├─ 太大 → format 錯誤
│  ├─ 檢查 pixelformat 設定
│  └─ 換 SRGGB10P(packed)vs SRGGB10(unpacked)
├─ 太小 → format 錯誤
│  └─ 可能誤設 YUYV(4 bytes/pixel)
└─ 正確 → 格式正確
   └─ 繼續驗證 Bayer 資料

決策樹 B:RAW 斜圖或色偏

決策樹 B
RAW 圖像異常?
├─ 斜圖 → bytesperline 對齊問題
│  ├─ 用 Python 確認 bytesperline
│  └─ 調整 width 對齊(16/64 byte)
├─ 色偏 → Bayer order 錯誤
│  ├─ 嘗試四種 order(RGGB/GRBG/GBRG/BGGR)
│  └─ 用四通道均值法驗證
└─ 正常 → 格式正確
   └─ 進階:用 RAW 做 OB/黑位分析

8.24 量測驗證完整 SOP

步驟指令預期輸出判讀標準
1. 列出格式v4l2-ctl --list-formats-extSRGGB10/8/12依 sensor 能力
2. 設定格式v4l2-ctl --set-fmt-video=pixelformat=SRGGB10格式設定成功match sensor
3. 取 RAWv4l2-ctl --stream-mmap=1 --stream-count=1RAW 檔sizeimage 正確
4. 驗證大小ls -l stream.raw= W × H × 1.25SRGGB10 計算正確
5. 驗證 Bayerpython3 raw_check.py stream.rawBayer 資料正確四通道均值合理
6. 測 bytesperlinev4l2-ctl --list-formats-ext | grep bytesperline依解析度16/64 byte 對齊

8.25 四平台終極對照

面向Orange PiRPi5Orin NanoThor推薦
RAW 位元深8/10/1210/1210/12/1410/12/14動態範圍→14-bit
取 RAW 方式v4l2-ctl pixelformatrpicam --rawargus / nvgstHoloscan底層→v4l2-ctl
format 工具V4L2 fourcclibcamera 描述NVIDIA 專屬NVIDIA 專屬通用→fourcc
bytesperline16/64 byte16 byteNVIDIA 對齊NVIDIA 對齊依解析度
Bayer 驗證自寫 PythonlibcameraNVIDIA 工具Holoscan學習→Python

8.26 完整 Bring-up 專案 Checklist

checklist
- [ ] 用 v4l2-ctl --list-formats-ext 列出所有支援格式
- [ ] 設定 SRGGB10 格式取 RAW
- [ ] 驗證 sizeimage = width × height × 1.25
- [ ] 用 Python 確認 bytesperline 對齊
- [ ] 用四通道均值法驗證 Bayer order
- [ ] 測試 SRGGB10 vs SRGGB10P 的檔案大小差異
- [ ] 測試 YUYV / NV12 / RAW 的 sizeimage 計算
- [ ] 產出「格式能力表」(format/size/bytesperline)
- [ ] 驗證本平台在主要解析度的 Bayer order 一致性
- [ ] 用「位元深 + Bayer order」描述任何平台的 RAW 格式
看完這單元你應該能說出:
  • V4L2 fourcc 與 RAW/YUV 路線。
  • 查感測器支援格式。
  • Bayer order 陷阱。
  • 固定格式/解析度調校。
  • RAW packing 與 bytesperline 對齊。
  • 正確讀取 RAW buffer 的 Worked Example。
  • YUV/NV12 布局與 sizeimage 驗證。

延伸閱讀