RAW、bayer、Argus raw
| 格式 | 說明 |
|---|---|
NV12 / YUV | ISP 處理後 |
SRGGB10 / Bayer RAW | 感測器原始(調校分析) |
argus_camera --mode 0 --raw-file out.raw --duration 1
argus_camera --mode 0 --raw-file raw.raw --duration 1
RAW 是影像品質分析的根本:雜訊、黑位、Bayer order 都回 RAW。
| 錯誤 | 症狀 | 檢查 |
|---|---|---|
| Bayer order 錯 | 色彩全亂 | 拍純色卡判別四通道 |
| bit depth 錯讀 | 暗部階調斷裂 | 確認 RAW 位元深 |
| packing 錯 | 條紋狀假影 | 用對應位元深工具解析 |
10-bit RAW 每像素 10 bits,但記憶體以 byte 為單位,所以需要 packing 規則:
| 布局 | 每像素位元 | Orin/感測器常見用法 |
|---|---|---|
| Unpacked(左對齊) | 16-bit 存 10-bit 值 | 分析軟體易讀 |
| MIPI packed | 4 像素塞 5 bytes | CSI-2 傳輸實際格式 |
| 8-bit 截斷 | 只留高位 8-bit | 快速預覽 |
用錯誤布局讀 RAW 的典型症狀:條紋、鋸齒、階調斷裂——不是「糊掉」,而是「pattern 錯」。解析 RAW 前先確認:位元深 × packing × Bayer order × 尺寸。
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))
EOF1. 有條紋/鋸齒? ├─ 是 ─→ packing 或位元深錯 └─ 否 ┐ 2. 色彩全亂(灰卡變彩色)? ├─ 是 ─→ Bayer order 錯(試其他 3 種起點) └─ 否 ┐ 3. 暗部階調斷裂? ├─ 是 ─→ 位元深讀錯 / 黑位扣太多 └─ 否 ┐ 4. 四角明顯暗於中央? ├─ 是 → 正常 lens shading(進 LSC) └─ 否 → 均勻 → ✅
RAW 是品質分析的共同語言。四個必做的基礎統計:
| 指標 | 怎麼看 | 異常代表 |
|---|---|---|
| 黑位 | 全遮後 RAW min/mean | 偏高 → 暗部浮灰 |
| 飽和點 | 亮部 RAW max | 常達滿位 → 過曝風險 |
| 雜訊 σ | 平坦區標準差 | 過高 → 增益/曝光問題 |
| 缺陷像素 | 固定位置異常亮點 | 需 DPC(單元 13) |
□ 解析度:width × height(含裁切?) □ 位元深:8 / 10 / 12 □ Packing:unpacked / MIPI packed □ Bayer order:RGGB / BGGR / GRBG / GBRG □ 黑位:全遮量測值
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場景:你需要同時從 OV9281(global shutter, 10-bit RAW)和 OV5640(rolling shutter, 10-bit RAW)取 RAW,做 blind image quality comparison。
# 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 調校前的「感測器本質」指標。
RAW 資料從感測器到 CPU memory 的路徑中,經歷了 DMA 傳輸。cache line 對齊和memory stride會影響分析速度。
| 概念 | 說明 | 對 RAW 分析的影響 |
|---|---|---|
| stride | 每列在 memory 中的實際位元組數(可能大於 width×bytes/pixel) | reshape 時若沒考慮 stride,影像會「斜切」 |
| cache line | CPU cache 的最小傳輸單位(64 bytes on ARM) | 非對齊的 RAW 讀取會造成 cache miss,分析速度降數倍 |
| DMA alignment | 硬體 DMA 要求的位址對齊(通常 128 bytes) | V4L2 buffer 分配時自動處理,但自行 malloc 可能不對齊 |
ls -l raw.raw 確認檔案大小是否 = width × height × 2。| 症狀 | 可能原因 | 解決方案 |
|---|---|---|
| RAW 檔案大小不符合預期(width × height × bytes/pixel) | stride padding 或 packing 格式不符 | 計算 stride = 檔案大小 / height / bytes_per_pixel;確認 packing 格式 |
| numpy reshape 後影像斜切/錯位 | stride ≠ width × bytes/pixel | 用 np.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(缺陷像素校正)處理;記錄缺陷位置供後續參數化 |
場景:每次要分析 RAW 都重新寫腳本,格式假設容易錯。專案目標:建立一套RAW 分析工具鏈:自動偵測格式(解析度 / 位元深 / packing / Bayer)、輸出黑位 / 雜訊 / 缺陷像素統計,作為團隊共用的品質分析工具。
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(兩幀 / 兩顆感測器比較)。工具鏈一次建立、全團隊共用。
| 面向 | Orin Nano | RPi5 | Orange Pi | Thor |
|---|---|---|---|---|
| 取 RAW 指令 | argus --raw-file | rpicam --raw | v4l2-ctl --stream-to | argus / Holoscan |
| RAW 格式 | 同感測器輸出相同(SRGGB10…) | |||
| Packing 差異 | 依感測器 / 驅動,與平台無關 | |||
| 分析工具 | Python / numpy / rawpy 全平台共用 | |||
| 取樣品質 | 可能受驅動 init table 影響(回顧 9.10) | 同左 | 同左 | 同左 |
| 步驟 | Command | 驗證目標 | 預期結果 |
|---|---|---|---|
| 1. 取 RAW | v4l2-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 |
#!/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")
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 → 設非零增益
1. high_bytes = data >> 8 → mean() = 0? ├─ 是 ┐ │ 2. sensor 在 8-bit 模式? │ ├─ 是 → 應該是全 0(正確) │ │ 改 10-bit 模式重取 │ └─ 否 ┐ │ 3. 寄存器 set 未生效? │ → 回歸單元 6:Write → Read → Verify └─ 否 → packing 正常
v4l2-ctl --stream-mmap=1 --stream-count=1 --stream-to=test.raw。ls -l test.raw → 預期 W × H × 2 bytes。np.fromfile("test.raw", dtype=np.uint16) → 確認無 error。data.max() → 1023 = 10-bit,255 = 8-bit,4095 = 12-bit。(data >> 8).mean() → 若 > 0 = 10-bit packed 正常。data.min() → 與 datasheet 黑位比對。| 面向 | Orin Nano | RPi5 | Orange Pi | Thor | 推薦 |
|---|---|---|---|---|---|
| RAW 取得工具 | argus --file-type raw | rpicam --raw | v4l2-ctl --stream-to | argus / Holoscan | 各有首選 |
| 原生 RAW 格式 | SRGGB10 packed | SRGGB10 packed | 依感測器 | SRGGB10 packed | 10-bit 為主流 |
| Packing 解析 | Python numpy 全平台共用 | 平台無關 | |||
| Bayer 驗證 | 雷射亮點法全平台共用 | 平台無關 | |||
| 位元深判定 | max value 判定全平台共用 | 平台無關 | |||
| RAW 資料報告 | 格式相同(bit depth/packing/Bayer/黑位) | 可跨平台攜帶 | |||
| 擷取速度 | ~30 fps | ~30 fps | ~15 fps | >30 fps | 依感測器上限 |