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

RAW、bayer、Argus raw

RAW 與處理後格式

格式說明
NV12 / YUVISP 處理後
SRGGB10 / Bayer RAW感測器原始(調校分析)

Argus 取 RAW

argus raw(示意)
argus_camera --mode 0 --raw-file out.raw --duration 1
陷阱:Bayer order 設錯 → 全色錯;位元深與後處理格式不符 → 暗部階調損失。設定 RAW 前先確認 datasheet 的 Bayer 起點。

8.6 RAW 擷取實作(本平台)

argus 取 RAW(--raw-file)
argus_camera --mode 0 --raw-file raw.raw --duration 1

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

8.7 常見 RAW 格式錯誤

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

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

重要:不同解析度/ binning 下 Bayer order 與 shading 不同。調校與回歸必須鎖定單一模式(固定 size + format)。
看完這單元你應該能說出:
  • RAW vs NV12 用途。
  • Argus 取 RAW。
  • Bayer order 陷阱。
  • RAW 在調校分析角色。

8.9 深入原理:RAW 位元布局與 packing

10-bit RAW 每像素 10 bits,但記憶體以 byte 為單位,所以需要 packing 規則:

布局每像素位元Orin/感測器常見用法
Unpacked(左對齊)16-bit 存 10-bit 值分析軟體易讀
MIPI packed4 像素塞 5 bytesCSI-2 傳輸實際格式
8-bit 截斷只留高位 8-bit快速預覽

用錯誤布局讀 RAW 的典型症狀:條紋、鋸齒、階調斷裂——不是「糊掉」,而是「pattern 錯」。解析 RAW 前先確認:位元深 × packing × Bayer order × 尺寸。

8.10 Worked Example:用 Python 檢查 RAW 黑位與 Bayer

分析一幀 OV9281 RAW
python3 - <<'EOF'
import numpy as np
# 假設 10-bit packed RAW,1280x800,BGGR
# 這只是骨架:正式解析要用 rawpy / libcamera 格式工具
w, h = 1280, 800
raw = np.fromfile('raw.raw', dtype=np.uint16).reshape(h, w)
print('黑位(全遮)約:', raw.min())      # 應接近 datasheet 黑位
print('亮部峰值  :', raw.max())        # 接近 1023 → 過曝風險
print('中央亮度  :', raw[h//4:3*h//4, w//4:3*w//4].mean().round(1))
EOF
驗證順序:① 全遮鏡頭:黑位應穩定(不正常 → 電源/黑位設定)② 開鏡拍灰卡:中央 vs 四角差別應符合 lens shading ③ 拍純色卡:確認 Bayer 四通道。

8.11 疑難排解決策樹:RAW 看起來怪

決策樹
1. 有條紋/鋸齒?
   ├─ 是 ─→ packing 或位元深錯
   └─ 否 ┐
2. 色彩全亂(灰卡變彩色)?
   ├─ 是 ─→ Bayer order 錯(試其他 3 種起點)
   └─ 否 ┐
3. 暗部階調斷裂?
   ├─ 是 ─→ 位元深讀錯 / 黑位扣太多
   └─ 否 ┐
4. 四角明顯暗於中央?
   ├─ 是 → 正常 lens shading(進 LSC)
   └─ 否 → 均勻 → ✅

8.12 常見錯誤 / 陷阱

陷阱 ①:拿 RAW 的「最大值」判斷過曝——要先扣黑位再換算,否則黑位高時誤判。
陷阱 ②:不同 mode(binning/裁剪)下 Bayer order 與 shading 不同——分析前先鎖定 mode(回顧 8.8)。
陷阱 ③:RAW 分析必須「逐顆感測器」獨立做——兩顆相同型號間仍有個體差異。

8.13 練習

  1. 取一幀 RAW,確認:位元深、packing、Bayer order。
  2. 全遮鏡頭測黑位,記錄數值。
  3. 寫一小段 script 輸出 RAW 的 min/max/mean。

8.14 進階:RAW 統計與影像品質指標

RAW 是品質分析的共同語言。四個必做的基礎統計:

指標怎麼看異常代表
黑位全遮後 RAW min/mean偏高 → 暗部浮灰
飽和點亮部 RAW max常達滿位 → 過曝風險
雜訊 σ平坦區標準差過高 → 增益/曝光問題
缺陷像素固定位置異常亮點需 DPC(單元 13)
為什麼要會:這些指標與 ISP 參數無關、純粹描述感測器本身——換 ISP、換平台都能用同一套分析。

8.15 快速參考:RAW 驗證清單

解析 RAW 前的 5 個確認
□ 解析度:width × height(含裁切?)
□ 位元深:8 / 10 / 12
□ Packing:unpacked / MIPI packed
□ Bayer order:RGGB / BGGR / GRBG / GBRG
□ 黑位:全遮量測值

8.16 看完本單元該記住的三件事

RAW 是品質分析的根本,NV12 是已處理結果。
Bayer / 位元深 / packing 錯 → 不同花樣壞法,各有判讀。
鎖定單一 mode 再分析,調校與回歸才可比。

8.17 Worked Example:確認 Bayer order 的方法

拍一個亮點,看四通道哪個最亮
python3 - <<'EOF'
import numpy as np
# raw: 用雷射筆/強光點照在畫面上拍的 RAW
h, w = raw.shape
# 依假設的 2x2 起點取樣四通道
ch = {
  'RGGB': (raw[0::2,0::2].max(), raw[0::2,1::2].max(),
           raw[1::2,0::2].max(), raw[1::2,1::2].max()),
  # 對每種起點算:哪個通道抓到亮點
}
# 亮點所在通道(R/Gr/Gb/B 中最亮者)與「該起點該通道應是 R」
# 一致的就是正確 Bayer order
EOF
原理:雷射光是純色——只有對應的 Bayer 通道會很亮。對照亮點位置,就知道哪個 2×2 起點把該位置標成 R/G/B,進而推出真實 order。

8.18 看完本單元該記住的三件事

RAW 是品質分析的根本,格式錯花樣多但可判讀。
位元深 × packing × Bayer × 黑位,解析前五確認。
分析前鎖定 mode,比較才公平。

延伸閱讀

8.19 進階真實情境 Worked Example:OV9281 + OV5640 雙感測器 RAW 同步擷取與品質比較

場景:你需要同時從 OV9281(global shutter, 10-bit RAW)和 OV5640(rolling shutter, 10-bit RAW)取 RAW,做 blind image quality comparison。

雙感測器 RAW 同步擷取
# 1. 兩路同時串流取 RAW
v4l2-ctl -d /dev/video0 --set-fmt-video=pixelformat=SRGGB10,width=1280,height=800 \
  --stream-mmap=1 --stream-count=1 --stream-to=ov9281.raw
v4l2-ctl -d /dev/video1 --set-fmt-video=pixelformat=SRGGB10,width=2592,height=1944 \
  --stream-mmap=1 --stream-count=1 --stream-to=ov5640.raw
# 2. 分析腳本
python3 - <<'EOF'
import numpy as np

def analyze_raw(path, w, h, bit_depth=10, bayer='RGGB'):
    raw = np.fromfile(path, dtype=np.uint16).reshape(h, w)
    black = raw.min()
    sat = (raw == (1 << bit_depth) - 1).sum()
    flat_region = raw[h//3:2*h//3, w//3:2*w//3]
    noise = flat_region.std()
    return {
        'black_level': int(black),
        'saturated_pixels': int(sat),
        'noise_sigma': round(float(noise), 2),
        'dynamic_range': round(float(20 * np.log10(raw.max() / max(noise, 1))), 1)
    }

ov9281 = analyze_raw('ov9281.raw', 1280, 800)
ov5640 = analyze_raw('ov5640.raw', 2592, 1944)
print('OV9281:', ov9281)
print('OV5640:', ov5640)
EOF

設計決策:OV9281 低解析度但高幀率(適合高速場景),OV5640 高解析度但色彩更好。RAW 分析量化了兩者的黑位、雜訊、動態範圍差異——這些是 ISP 調校前的「感測器本質」指標。

8.20 深入原理擴充:RAW 資料的 memory layout 與 cache 行為

RAW 資料從感測器到 CPU memory 的路徑中,經歷了 DMA 傳輸。cache line 對齊memory stride會影響分析速度。

概念說明對 RAW 分析的影響
stride每列在 memory 中的實際位元組數(可能大於 width×bytes/pixel)reshape 時若沒考慮 stride,影像會「斜切」
cache lineCPU cache 的最小傳輸單位(64 bytes on ARM)非對齊的 RAW 讀取會造成 cache miss,分析速度降數倍
DMA alignment硬體 DMA 要求的位址對齊(通常 128 bytes)V4L2 buffer 分配時自動處理,但自行 malloc 可能不對齊
陷阱:「用 numpy reshape(800, 1280) 讀 RAW 檔案」——若 stride ≠ width×2(10-bit unpacked 用 16-bit 存),影像會扭曲。先用 ls -l raw.raw 確認檔案大小是否 = width × height × 2。

8.21 診斷式疑難排解表

症狀可能原因解決方案
RAW 檔案大小不符合預期(width × height × bytes/pixel)stride padding 或 packing 格式不符計算 stride = 檔案大小 / height / bytes_per_pixel;確認 packing 格式
numpy reshape 後影像斜切/錯位stride ≠ width × bytes/pixelnp.frombuffer(raw_bytes, dtype=...).reshape(-1, stride_bytes) 再裁切
RAW 分析腳本跑很久大量 cache miss(非對齊記憶體存取)np.ascontiguousarray() 確保連續記憶體;或用 mmap 隨機存取
全遮黑位值高於 datasheet 標稱黑位扣除(black level subtraction)未做在分析前扣除黑位值;或用 ISP 的 BLC 模組預處理
RAW 中出現固定 pattern 的亮點缺陷像素(dead/hot pixel)用 DPC(缺陷像素校正)處理;記錄缺陷位置供後續參數化

8.22 進階挑戰題

  1. 撰寫一個 RAW 格式自動偵測腳本:給定任意 RAW 檔案,自動判斷(1)解析度、(2)位元深、(3)packing 格式、(4)Bayer order。提示:嘗試不同假設,看哪個產生最「合理」的統計值。
  2. 比較 packed vs unpacked RAW 在分析速度上的差異:取同一幀 10-bit RAW 的兩種格式,計時 min/max/mean 計算。
  3. 若你的 RAW 是從多感測器同步擷取的,設計一個「時間戳對齊」腳本:從 V4L2 buffer 的 metadata 中提取每幀的時間戳,計算兩路之間的偏移量。

8.23 專案級端到端 Worked Example:RAW 分析工具鏈專案

場景:每次要分析 RAW 都重新寫腳本,格式假設容易錯。專案目標:建立一套RAW 分析工具鏈:自動偵測格式(解析度 / 位元深 / packing / Bayer)、輸出黑位 / 雜訊 / 缺陷像素統計,作為團隊共用的品質分析工具。

rawstat.py(工具鏈核心)
python3 - <<'EOF'
import numpy as np, sys, glob, os

def detect_resolution(path):
    size = os.path.getsize(path)
    cands = [(1280,800),(2592,1944),(3280,2464)]
    for w,h in cands:
        if size == w*h*2: return w,h        # 16-bit unpacked
    raise ValueError('unknown size')

def analyze(path):
    w,h = detect_resolution(path)
    raw = np.fromfile(path, dtype=np.uint16).reshape(h,w)
    roi = raw[h//3:2*h//3, w//3:2*w//3]
    return {
        'size': f'{w}x{h}',
        'black': int(raw.min()),
        'sat_px': int((raw==1023).sum()),
        'noise_sigma': round(float(roi.std()),2),
        'mean': round(float(raw.mean()),1),
    }

for f in sorted(glob.glob('*.raw')):
    print(f, analyze(f))
EOF

專案輸出:rawstat.py(統計)+ rawdetect.py(格式自動偵測,呼應 8.22 挑戰 1)+ diff.py(兩幀 / 兩顆感測器比較)。工具鏈一次建立、全團隊共用。

8.24 量測/驗證 SOP:RAW 品質驗證

  1. 解析前 5 確認:解析度、位元深、packing、Bayer order、黑位(回顧 8.15)。
  2. 檔案大小:確認 = width × height × bytes/pixel(無 stride 陷阱)。
  3. 黑位:全遮鏡頭量 min / mean,與 datasheet 標稱 ±10%。
  4. 飽和點:亮部峰值是否常達滿位(過曝風險)。
  5. 雜訊:平坦區 σ,記錄 σ vs gain 曲線。
  6. 缺陷像素:黑框下統計固定亮點,需要時進 DPC(單元 13)。
紀律:分析前鎖定 mode(解析度 / 格式),跨幀比較才公平(回顧 8.8)。

8.25 平台間對照:RAW 取樣

面向Orin NanoRPi5Orange PiThor
取 RAW 指令argus --raw-filerpicam --rawv4l2-ctl --stream-toargus / Holoscan
RAW 格式同感測器輸出相同(SRGGB10…)
Packing 差異依感測器 / 驅動,與平台無關
分析工具Python / numpy / rawpy 全平台共用
取樣品質可能受驅動 init table 影響(回顧 9.10)同左同左同左
重點:RAW 是「平台無關」的共通語言——分析腳本與判讀標準可直接跨平台搬;差異只在「取 RAW 的指令」與「驅動 init table 造成的底色差」。

8.26 互動式檢核清單:RAW 驗收

8.18 Register 位元級完整工作流:RAW 資料位元深驗證

步驟Command驗證目標預期結果
1. 取 RAWv4l2-ctl --stream-mmap=1 --stream-count=1 --stream-to=test.raw取得一幀test.raw 存在
2. 檔案大小ls -l test.raw大小正確W × H × 2 bytes(10-bit unpacked)
3. uint16 讀取numpy fromfile uint16讀取成功無 error
4. 最大值data.max()位元深~1023(10-bit)或 ~255(8-bit)
5. 分布檢查data >> 8 計數packing高 8-bit 非全 0
RAW 位元深完整驗證腳本
#!/usr/bin/env python3
# raw_verify.py — 驗證 RAW 位元深與 packing
import numpy as np, sys

f = sys.argv[1] if len(sys.argv) > 1 else "test.raw"
data = np.fromfile(f, dtype=np.uint16)

print(f"Total pixels: {len(data)}")
print(f"Max value: {data.max()}")
print(f"Mean value: {data.mean():.1f}")

# 位元深判定
max_val = data.max()
if max_val <= 255:
    bits = 8
elif max_val <= 1023:
    bits = 10
elif max_val <= 4095:
    bits = 12
elif max_val <= 65535:
    bits = 16
else:
    bits = "未知"
print(f"Detected bit depth: {bits}-bit")

# 檢查是否 packed(高 8-bit 是否有數據)
high_bits = data >> 8
low_bits = data & 0xFF
print(f"High byte mean: {high_bits.mean():.2f} (should be > 0 for 10-bit)")
print(f"Low byte mean: {low_bits.mean():.2f}")

# 驗證檔案大小
import os
file_size = os.path.getsize(f)
expected = len(data) * 2
print(f"File size: {file_size} bytes (expected {expected})")
print("SIZE: OK" if file_size == expected else "SIZE: MISMATCH")

# 視覺化(可選)
try:
    import matplotlib.pyplot as plt
    fig, axes = plt.subplots(1, 3, figsize=(12, 4))
    axes[0].hist(data, bins=256, color='blue', alpha=0.7)
    axes[0].set_title(f'RAW Histogram ({bits}-bit)')
    axes[1].imshow(data.reshape(800, 1280), cmap='gray')
    axes[1].set_title('RAW Image')
    axes[2].hist(high_bits, bins=256, color='green', alpha=0.7)
    axes[2].set_title('High Byte Distribution')
    plt.tight_layout()
    plt.savefig('raw_analysis.png')
    print("Saved: raw_analysis.png")
except ImportError:
    print("matplotlib not available, skipping visualization")

8.19 多層疑難排解決策樹

決策樹 A:RAW 檔案異常(大小不對 / 全 0 / 全黑)
1. 檔案大小 != W×H×2?
   ├─ 是 → 四件事缺一:
   │   ├─ 缺 padding → stream-mmap buffer 設定
   │   ├─ 缺 stride → v4l2-ctl --list-formats-ext 確認 bytesperline
   │   ├─ 缺 interleaving → 0x8002 0x0000 設定
   │   └─ 缺 frame 標頭 → 檔案大小 + 16 bytes
   └─ 否 ┐
2. 檔案存在但全 0?
   ├─ 感測器沒輸出 → I2C 通訊 / 供電 / MCLK
   └─ DMA buffer 問題 → 改 stream-count 重試
3. 檔案有數據但全黑?
   ├─ 曝光 0 → 設非零曝光
   └─ 增益 0 → 設非零增益
決策樹 B:10-bit 數據高 8-bit 全 0(packing 錯誤)
1. high_bytes = data >> 8 → mean() = 0?
   ├─ 是 ┐
   │   2. sensor 在 8-bit 模式?
   │      ├─ 是 → 應該是全 0(正確)
   │      │       改 10-bit 模式重取
   │      └─ 否 ┐
   │      3. 寄存器 set 未生效?
   │         → 回歸單元 6:Write → Read → Verify
   └─ 否 → packing 正常

8.20 量測驗證完整 SOP

  1. 取 RAWv4l2-ctl --stream-mmap=1 --stream-count=1 --stream-to=test.raw
  2. 大小驗證ls -l test.raw → 預期 W × H × 2 bytes。
  3. uint16 讀取np.fromfile("test.raw", dtype=np.uint16) → 確認無 error。
  4. 位元深判定data.max() → 1023 = 10-bit,255 = 8-bit,4095 = 12-bit。
  5. Packing 確認(data >> 8).mean() → 若 > 0 = 10-bit packed 正常。
  6. 黑位量測data.min() → 與 datasheet 黑位比對。
  7. 視覺化:直方圖 + 影像顯示 → 確認分佈合理。
判讀標準:大小正確 + max ≈ 1023 + high bytes mean > 0 + 黑位在標稱範圍 = RAW 資料完整。

8.21 四平台终极對照

面向Orin NanoRPi5Orange PiThor推薦
RAW 取得工具argus --file-type rawrpicam --rawv4l2-ctl --stream-toargus / Holoscan各有首選
原生 RAW 格式SRGGB10 packedSRGGB10 packed依感測器SRGGB10 packed10-bit 為主流
Packing 解析Python numpy 全平台共用平台無關
Bayer 驗證雷射亮點法全平台共用平台無關
位元深判定max value 判定全平台共用平台無關
RAW 資料報告格式相同(bit depth/packing/Bayer/黑位)可跨平台攜帶
擷取速度~30 fps~30 fps~15 fps>30 fps依感測器上限

8.22 完整 Bring-up 專案 Checklist