Bayer format、bit depth、RAW 分析
| fourcc | 排列起點 | OV5647 |
|---|---|---|
SRGGB10 | R 起點、10-bit | ✅ RAW(實際) |
SGBRG10 | Gb 起點 | CSI 傳輸層呈現 |
YUYV/NV12 | YUV(ISP 輸出) | ✅ |
SGBRG10_CSI2P 是 CSI 傳輸層排列;OV5647 實際 RGGB。驅動處理轉換;但你手動解析 RAW 檔時以 RGGB 為準——這是新手最常搞混的點。python3 - <<'EOF'
import rawpy, numpy as np
raw = rawpy.imread('t.dng').raw_image
h, w = raw.shape
roi = raw[h//4:3*h//4, w//4:3*w//4]
cells = [roi[0::2,0::2].mean(), roi[0::2,1::2].mean(),
roi[1::2,0::2].mean(), roi[1::2,1::2].mean()]
print('R=%.1f Gr=%.1f Gb=%.1f B=%.1f' % tuple(cells))
EOF判別法:對純紅色光源/卡片拍 RAW——R 位置(若 order 正確)應最亮。若 B 位置最亮 → order 是反的。
| 位元 | 灰階數 | 意義 |
|---|---|---|
| 8 | 256 | 常見輸出 |
| 10 | 1024 | OV5647 RAW |
| 12 | 4096 | 高動態需要 |
rpicam-still --width 1296 --height 972 -o half.jpg
DNG 是 Adobe 的 RAW 封裝(TIFF 基底)。調校時透過 rawpy/dcraw 讀,但理解結構有助除錯:
| DNG 欄位 | 意義 |
|---|---|
CFAPattern / raw_pattern | Bayer order(驗證用) |
BlackLevel | 各通道黑位 |
WhiteLevel | 飽和值(10-bit → 1023) |
AsShotNeutral | AWB gains |
ColorMatrix | 色彩矩陣 |
python3 - <<'EOF'
import rawpy
raw = rawpy.imread('t.dng')
print('Bayer pattern:', raw.raw_pattern) # 驗證 order
print('BlackLevel:', raw.black_level_per_channel)
print('WhiteLevel:', raw.white_level)
EOF| 錯誤 | 症狀 | 檢查 |
|---|---|---|
| Bayer order 錯 | 色彩全亂 | raw_pattern + 純色卡 |
| bit depth 錯讀 | 暗部階調斷裂/過曝 | white_level 是否 1023 |
| ROI 錯 | 影像偏移/錯位 | width/height 與 mode 一致 |
| packing 錯(10-bit 當 8-bit) | 條紋狀假影 | 用支援 10-bit 的解碼器 |
rpicam-hello --list-cameras # 記錄要調校的 mode rpicam-still --width 2592 --height 1944 --raw -o full.dng # 固定全解析
10-bit 資料在記憶體中的 packing 方式影響解碼:
| packing | 說明 |
|---|---|
| Unpacked(16-bit) | 每像素 2 byte,低 6 bit 無效 |
| Packed(MIPI CSI-2) | 5 byte 塞 4 像素(4×10=40 bit) |
python3 - <<'EOF'
import rawpy
r = rawpy.imread('t.dng')
print('raw_pattern:')
for row in r.raw_pattern:
print(' ', row) # 0=R,1=G,2=B(依 libraw 慣例)
EOF比對 datasheet 的 RGGB 起點,確認平台設定一致。
場景:你要驗證「rpicam-still 的 DNG 是否真的包含原始 Bayer 資料」,需要從 V4L2 層直接擷取。
# 1. 確認 V4L2 裝置與 format
v4l2-ctl -d /dev/video0 --list-formats-ext
# 找到 SGBRG10_1X10 或 SRGGB10_1X10
# 2. 設定 format 並單次擷取
v4l2-ctl -d /dev/video0 \
--set-fmt-video=width=2592,height=1944,pixelformat=SBGGR10 \
--stream-mmap --stream-count=1 --stream-to=raw_v4l2.bin
# 3. 用 rawpy 驗證
python3 - <<'EOF'
import rawpy, numpy as np
# 注意:V4L2 輸出可能是 packed 10-bit,rawpy 可能無法直接讀
# 需要先確認 packing 格式
data = np.fromfile('raw_v4l2.bin', dtype=np.uint8)
print(f"原始資料大小: {len(data)} bytes")
print(f"期望大小 (2592*1944*10/8): {2592*1944*10//8}")
EOFOV5647 輸出 10-bit RAW,但 MIPI CSI-2 傳輸層以 byte 為單位,packing 方式影響資料解讀:
| 症狀 | 可能原因 | 解決方案 |
|---|---|---|
| DNG 的 raw_pattern 顯示的 order 與預期不符 | libcamera metadata 寫入錯誤,或感測器 init table 的 order 設定有誤 | 對照 v4l2-ctl 直接擷取的原始資料確認;查 ov5647.c 的 format 設定 |
| 用 rawpy 讀 DNG 時色彩通道全是灰色 | bit depth 設定錯誤(10-bit 當 8-bit 讀)或 black_level 設太高 | 確認 raw.white_level 是否為 1023;調整 raw.black_level_per_channel |
| V4L2 擷取的 raw 檔大小與預期不符 | packing 格式不匹配(packed vs unpacked) | 確認 V4L2 format 的 pixelformat;計算期望大小 vs 實際大小 |
| 不同解析度下 DNG 的 Bayer order 不同 | binning 模式改變了 effective pixel order | 鎖定單一解析度做調校;不同 mode 分別驗證 Bayer order |
| rawpy 讀出的通道均值全是 0 | DNG 的 black_level 設為 0,導致扣除後全負 | 手動修正 DNG metadata 的 black_level;或用 dcraw 原始解碼 |
場景:每次拍攝後都要確認 RAW 是否「乾淨」:Bayer order 正確、bit depth 正確、黑位合理、無缺陷像素。本專案建一個 raw_report.py 一鍵產出 RAW 健康報告,整合單元 8(格式)、7(黑位)、13(缺陷像素)、5(拍攝)知識。
#!/usr/bin/env python3
import sys, rawpy, numpy as np
def report(path):
raw = rawpy.imread(path)
img = raw.raw_image
roi = img[img.shape[0]//4:3*img.shape[0]//4,
img.shape[1]//4:3*img.shape[1]//4].astype(float)
print(f"檔案: {path}")
print(f"shape: {img.shape} white_level: {raw.white_level}")
print(f"Bayer pattern:\n{raw.raw_pattern}")
print(f"BlackLevel/ch: {raw.black_level_per_channel}")
cells = [roi[0::2,0::2].mean(), roi[0::2,1::2].mean(),
roi[1::2,0::2].mean(), roi[1::2,1::2].mean()]
print(f"通道均值: R={cells[0]:.0f} Gr={cells[1]:.0f} "
f"Gb={cells[2]:.0f} B={cells[3]:.0f}")
hot = (img > roi.mean() + 10*roi.std()).sum()
print(f"hot pixels (>10σ): {hot} max: {img.max()}")
report(sys.argv[1])./raw_report.py first.dng # 預期: # shape: (1944, 2592) white_level: 1023 # Bayer pattern 顯示 0/1/2 排列(驗證 RGGB) # BlackLevel/ch 四通道合理(~100) # 通道均值差異不大、hot pixels 少 # 判讀:任一異常 → 回對應單元診斷
--raw 拍攝基準 RAW(固定參數)。| 面向 | RPi5 | Orange Pi | Orin Nano | Thor |
|---|---|---|---|---|
| RAW 封裝 | DNG(含 metadata) | 裸 RAW(V4L2) | .nvraw(私有) | .nvraw / Holoscan |
| 解析工具 | rawpy / dcraw | numpy 手解 packing | NVIDIA 專用轉換 | NVIDIA 專用轉換 |
| Bayer order 驗證 | raw_pattern 欄位 | 自行判斷(純色卡) | ISP 設定宣告 | ISP 設定宣告 |
| 坑 | metadata 可能誤寫 | 10-bit packing 要手解 | 格式不公開 | 工具少 |
| 步驟 | 暫存器 | 值 | 位元級說明 |
|---|---|---|---|
| 1. 設定格式 | 0x4300 | 0xF0 | bit[7:4]=1111 → RAW Bayer, bit[3:0]=0000 → 10-bit |
| 2. 設定 Bayer 順序 | 0x4301 | 0x00 | bit[1:0]=00 → GR/BG (RGGB), 01=BG/GR (BGGR) |
| 3. 設定輸出 byte packing | 0x4302 | 0x00 | bit[0]=0 → 10-bit packed (4 byte/4 pixel) |
| 4. 驗證 | dump 0x4300-0x4302 | — | 確認值與設定一致 |
決策樹 A:RAW 檔解碼後全黑或全白
RAW 解碼異常 ├─ 檢查 A:Bayer 順序是否正確 │ ├─ 不確定 → 依序嘗試 RGGB/BGGR/GRBG/GBRG,看哪個畫面正確 │ └─ 正確 → 繼續 ├─ 檢查 B:位元深度是否匹配 │ ├─ 10-bit 用 8-bit 解讀 → 發暗 → 確認 unpack 設定 │ └─ 匹配 → 繼續 ├─ 檢查 C:stride / padding 是否正確 │ ├─ stride 不對 → 行對齊錯誤 → 調整 bytes_per_line │ └─ 正確 → 繼續 └─ 檢查 D:是否存在 data offset ├─ 有 → RAW data 前有 metadata header → 跳過 header └─ 無 → 格式設定問題
raw = rawpy.imread('test.raw')print(raw.shape, raw.dtype, raw.min(), raw.max(), raw.mean())| 面向 | RPi5 | Orange Pi | Orin Nano | Thor | 推薦 |
|---|---|---|---|---|---|
| RAW 格式 | 10-bit packed Bayer | 8/10-bit Bayer | 10/12-bit packed | 12/14-bit packed | 各有不同 |
| RAW 擷取工具 | rpicam-apps --raw | v4l2-ctl --capture | NV nvraw tools | Holoscan pipeline | RPi5 最簡單 |
| RAW 格式解析 | rawpy (Python) | rawpy / dcraw | NV tools | NV + Holoscan | rawpy 最通用 |
| Bayer 順序可調 | Device tree | Device tree | NV tuning | Holoscan config | 各有方式 |