單元 11 · 白平衡與色彩(AWB/CCM)

感測器端白平衡

AWB 原理

不同色溫光源讓白色偏色。AWB 對 R/G/B 套不同 gains 把白校正回白。OV5640 內建 AWB(register 0x5180 起)。

透過 V4L2 控制

V4L2 AWB
v4l2-ctl -d /dev/v4l-subdev0 -c auto_n_white_balance=0   # 關 AWB
v4l2-ctl -d /dev/v4l-subdev0 -c red_balance=128          # R gain
v4l2-ctl -d /dev/v4l-subdev0 -c blue_balance=128         # B gain

實作:灰卡校正

  1. 拍灰卡(固定光源/曝光)。
  2. 關 AWB,手動調 red/blue balance 直到灰卡中性(R=G=B)。
  3. 記錄該光源下的 gains → 建立色溫→gains 表。
調適陷阱:感測器端 AWB 對混合色溫不準。要精準色彩,關閉感測器 AWB,改平台端做(若有 ISP)。

11.6 深入:白平衡實作

控制 AWB
v4l2-ctl -d /dev/v4l-subdev0 -c auto_n_white_balance=0
  1. 拍灰卡,手動調 gains 直到 R=G=B。
  2. 記錄該色溫的 gains。
  3. 不同色溫重複 → 建表。
陷阱:感測器端 AWB 對混合色溫不準;要精準色彩,關閉感測器 AWB、改平台端做。

11.8 深入原理:色溫、色彩向量與 CCM

AWB 只解決「白平衡」——讓灰卡變灰。真正讓顏色「準」還需要 CCM(色彩校正矩陣):把感測器響應換算到目標色彩空間。

概念意義OV5640
色溫(K)光源偏藍(高)/偏黃(低)AWB 依此估 gains
AWB gainsR/G、B/G 比值0x5180 帶
CCM3×3 矩陣校正色偏0x4750 帶(部分公開)
灰卡白點中性參考校正基準

灰卡校正為什麼可靠:灰卡(如 18% gray)在可見光譜中性反射。把「灰卡在影像中 R=G=B」當條件解出 gains,就得到該光源的 AWB。CCM 則在固定光源下用色卡(如 ColorChecker)解矩陣。

感測器端 vs 平台端:感測器內 AWB 只有 gains、CCM 欄位多半不公開——所以「精準色彩」在 Orange Pi 上通常要後處理補 CCM。

11.9 完整 Worked Example:灰卡校正 SOP

步驟 1|固定光源與曝光
# 關自動曝光與 AWB,鎖定曝光,避免灰卡亮度跑掉
v4l2-ctl -d /dev/v4l-subdev0 -c auto_exposure=0
v4l2-ctl -d /dev/v4l-subdev0 -c exposure=2000
v4l2-ctl -d /dev/v4l-subdev0 -c auto_n_white_balance=0
步驟 2|拍灰卡
v4l2-ctl -d /dev/video0 --set-fmt-video=width=1280,height=720,pixelformat=YUYV \
  --stream-mmap=3 --stream-count=1 --stream-to=gray.yuv
步驟 3|量灰卡區的 RGB
python3 - <<'EOF'
import numpy as np
yuv = np.fromfile('gray.yuv', dtype=np.uint8).reshape(720,1280,2)
y  = yuv[...,0].astype(float)
u  = (yuv[...,1]-128).astype(float)
v  = (yuv[...,1]-128).astype(float)   # 簡化:用中央區
roi = y[300:420, 500:780]
print('Y mean =', roi.mean())        # 曝光基準參考
EOF
步驟 4|手動調 gains 讓灰卡中性
v4l2-ctl -d /dev/v4l-subdev0 -c red_balance=140
v4l2-ctl -d /dev/v4l-subdev0 -c blue_balance=110
v4l2-ctl -d /dev/v4l-subdev0 -C red_balance -C blue_balance
完成標準:在不同色溫(日光燈/白熾燈/陰天窗邊)各做一次,建立「色溫 → red/blue gains」查表。這張表就是你後續固定色溫場景的起點。

11.10 疑難排解決策樹:色彩問題

現象方向
整體偏黃/偏藍AWB 沒收斂或混合色溫 → 灰卡校正
灰卡上 R≠Bgains 沒調平 → 重做步驟 4
白色正常但彩色彩偏CCM 問題 → 後處理矩陣校正
特定光源下突然色偏AWB 演算法誤判 → 鎖定手動 gains
色偏只在角落LSC 色差 → 回單元 12
常見錯誤與陷阱:① 用螢幕/手機當「中性參考」——不是灰卡;② 灰卡沒佔滿中央就量——用 ROI 只取中央;③ 忘了關 auto AWB 就手動設 gains——被覆寫;④ 在不同光源間複用同一組 gains——每光源一組。

11.11 練習

  1. 完成灰卡校正 SOP,建立至少兩組色溫的 gains 表。
  2. 對比「開 AWB」與「關 AWB 手動」在同場景的 RGB 統計。
  3. 說明為什麼 CCM 在感測器端可能受限,並列出後處理補 CCM 的做法。
  4. 在混合色溫場景記錄 AWB 誤判的證據(照片 + RGB 值)。

11.12 深入:後處理 CCM 的矩陣實作

OpenCV 套 CCM
python3 - <<'EOF'
import cv2, numpy as np
img = cv2.imread('shot.png').astype(float)/255.0
b,g,r = cv2.split(img)                     # BGR 順序
ccm = np.array([[1.6,-0.4,-0.2],
                [-0.2,1.4,-0.2],
                [-0.1,-0.2,1.3]])
rgb = np.stack([r,g,b],axis=-1) @ ccm.T
r,g,b = cv2.split(rgb.clip(0,1))
out = cv2.merge([b,g,r])*255
cv2.imwrite('shot_ccm.png', out.astype(np.uint8))
EOF
提醒:CCM 讓顏色「準」但不讓「白」——白平衡先做好,矩陣才有意義。順序:AWB → CCM。

11.13 回顧練習

  1. 灰卡校正一組 gains。
  2. 對比「開 AWB」與「關 AWB 手動」的結果。

11.14 進階真實情境 Worked Example:混合色溫場景下的 AWB 分層策略

場景:Orange Pi 5 接 OV5640,在室內日光燈 + 窗邊日光的混合色溫場景下,AWB 嚴重偏色。需要用分層策略校正。

步驟 1|拍混合色溫場景
v4l2-ctl -d /dev/v4l-subdev0 -c auto_exposure=0 -c exposure=2000
v4l2-ctl -d /dev/video0 --set-fmt-video=width=1280,height=720,pixelformat=YUYV \
  --stream-mmap=3 --stream-count=1 --stream-to=mixed.yuv
步驟 2|分區域量測 RGB
python3 - <<'EOF'
import numpy as np
yuv = np.fromfile('mixed.yuv', dtype=np.uint8).reshape(720,1280,2)
# 假設 left=日光燈區, right=窗邊日光區
left_roi  = yuv[:, 0:640, :]    # 日光燈偏綠
right_roi = yuv[:, 640:1280, :] # 日光偏暖
print('左區 Y mean:', left_roi[...,0].mean())
print('右區 Y mean:', right_roi[...,0].mean())
EOF
步驟 3|用後處理 CCM 分別校正兩區
python3 - <<'EOF'
import cv2, numpy as np
img = cv2.imread('mixed.png').astype(float)/255.0
# 左半用冷色 CCM,右半用暖色 CCM
ccm_cool = np.array([[1.2,-0.1,-0.1],[-0.1,1.1,-0.0],[-0.1,-0.1,1.2]])
ccm_warm = np.array([[1.0,-0.2,0.2],[0.0,1.0,0.0],[0.0,-0.1,1.1]])
h, w = img.shape[:2]
left  = img[:, :w//2] @ ccm_cool.T
right = img[:, w//2:] @ ccm_warm.T
out = np.clip(np.concatenate([left, right], axis=1), 0, 1) * 255
cv2.imwrite('mixed_ccm.png', out.astype(np.uint8))
EOF
設計決策:感測器端 AWB 只能給一組全局 gains,無法處理混合色溫。後處理可分區域套不同 CCM,但非即時。RK3588 的 ISP3 有 zone-based AWB,可部分緩解但仍有極限。

11.15 深入原理擴充:RK3588 ISP3 的 AWB 演算法 vs OV5640 內建 AWB

項目OV5640 內建 AWBRK3588 ISP3 AWB
zone 數1(全局)15×15 或 17×13
色溫範圍固定(2800K–6500K)可調(依 tuning)
混合色溫處理❌ 無法⚠️ zone-based 可部分處理
gains 精度8-bit(0–255)12-bit(更高精度)
CCM 支援固定矩陣(不公開)可調 3×3 + 分色溫

容易忽略的邊界案例:OV5640 的 AWB gains 是 R/G 和 B/G 的比值,而不是 R/B 的絕對值——所以「red_balance=140」在不同 OV 感測器上可能代表不同的絕對色彩。跨感測器比較時必須用色溫 K 值而非 gains 值。

11.16 診斷式疑難排解表

症狀可能原因解決方案
灰卡校正後在另一光源仍偏色每光源需一組 gains;不能複用為每種光源各做一次灰卡校正,建立查表
AWB gains 設定後被覆寫auto AWB 仍在跑先關 auto_n_white_balance=0
CCM 後白色仍偏色AWB 未先做好,CCM 無法補白平衡順序:AWB → CCM
混合色溫場景 AWB 抖動zone-based AWB 在邊緣震盪增 zone 數量或調整 zone 權重
OV5640 red_balance 設 128 但偏紅128 不是中性值(因 OV 的 gains 定義)用灰卡實測中性值,非假設 128

11.17 進階挑戰題

  1. 在混合色溫場景下,用後處理實作 zone-based AWB:把畫面分成 4×4 zone,每個 zone 獨立算 gains,再混合。比較與全局 AWB 的色偏差異。
  2. 建立一個「色溫 → gains」查表:在 5 種不同光源(日光燈/白熾燈/LED/陰天/黃昏)各做一次灰卡校正,記錄 gains,寫成 CSV。
  3. 在 RK3588 上,用 ISP3 的 zone-based AWB 與 OV5640 內建 AWB 分別拍混合色溫場景,量化兩者的 Delta E 差異。

11.18 專案級 Worked Example:多光源色彩校正資料庫專案

把單元 11 的色彩知識做成完整的色彩校正資料庫專案:為多種光源建立「色溫 → AWB gains → CCM」查表,並驗證不同光源下的色準。這是產品「色彩一致性」的根基。

階段 1|定義光源清單
# 日光燈(4000K)、白熾燈(2700K)、陰天(6500K)、LED(5000K)、黃昏(3000K)
declare -A K=( [fluo]=4000 [tungsten]=2700 [cloudy]=6500 [led]=5000 [dusk]=3000 )
階段 2|逐光源灰卡校正(單元 11.9)
#!/bin/bash
for src in fluo tungsten cloudy led dusk; do
  v4l2-ctl -d /dev/v4l-subdev0 -c auto_exposure=0 -c exposure=2000
  v4l2-ctl -d /dev/v4l-subdev0 -c auto_n_white_balance=0
  v4l2-ctl -d /dev/video0 --set-fmt-video=pixelformat=YUYV \
    --stream-mmap=3 --stream-count=1 --stream-to=gray_$src.yuv
  # 手動調 red/blue_balance 直到灰卡 R≈G≈B
  echo "$src: 記錄 red_balance / blue_balance"
done
階段 3|建立查表(CSV)
python3 - <<'EOF'
import csv
rows = [('fluo',4000,135,105), ('tungsten',2700,160,85),
        ('cloudy',6500,120,135), ('led',5000,128,112), ('dusk',3000,150,95)]
with open('awb_lut.csv','w',newline='') as f:
    w = csv.writer(f)
    w.writerow(['light','kelvin','red_balance','blue_balance'])
    w.writerows(rows)
print('已寫入 awb_lut.csv')
EOF
階段 4|驗證色準(色卡 + 後處理 CCM)
python3 - <<'EOF'
# 用 ColorChecker 的 patch 平均色比對目標值,算 Delta E
import numpy as np
# measured = 讀色卡 patch 的 RGB;target = 標準值
# Delta E 計算(簡化):對每個 patch 比對 LAB 距離
print('目標:所有 patch 平均 Delta E < 8(Consumer)、< 5(工控)')
EOF
專案完成標準:你有一份 awb_lut.csv 查表 + 各光源的 Delta E 報告。產品需要換光源或換場景時,直接查表套用,不必重新摸索——這是色彩一致性的核心資產。

11.19 量測/驗證 SOP:色彩準確度驗證

步驟作法通過判據
1. 灰卡校正固定光源/曝光,調 gains 到 R=G=B灰卡中性
2. 色卡量測拍 ColorChecker,比對 24 patches計算平均 Delta E
3. AWB 一致性同一光源重複 5 次校正gains 變異 < 5%
4. 跨光源驗證每光源套用各自查表白平衡保持中性
5. CCM 收尾固定光源解 CCM 矩陣彩色彩差收斂
SOP 紀律:色彩驗證最忌「用螢幕/手機當參考」——不是灰卡就是錯的。灰卡 + 色卡 + 固定光源 + 量化 Delta E,才是可信的色彩流程。

11.20 平台間對照:色彩校正

面向Orange PiRPi5Orin NanoThor
AWB感測器端 / 手動 gainslibcamera AWBNVIDIA AWBNVIDIA AWB
CCM後處理 OpenCVlibcamera tuningNVIDIA tuningNVIDIA tuning
gains 表示red_balance/blue_balance(整數)ColourGains(浮點比值)Argus 內部Argus 內部
混合色溫後處理分區 CCMlibcamera 全局NVIDIA 分區 AWBNVIDIA 分區 AWB
色準工具自寫 Python Delta Elibcamera 工具NVIDIA 工具NVIDIA 工具
核心領悟:AWB/CCM 的數學四平台相同,差異在「誰幫你算」與「精度」。Orange Pi 的 gains 是整數(128=1.0×),RPi5 用浮點比值(1.4)——換算時注意定義,別直接搬數值。

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

checklist
- [ ] 我能完成多光源校正專案並產出 awb_lut.csv。
- [ ] 我能用灰卡校正並量化 Delta E。
- [ ] 我理解「AWB 讓白變白,CCM 讓彩色變準」的差異。
- [ ] 我已建立至少兩組色溫的 gains 查表。
- [ ] 我理解 Orange Pi 整數 gains 與 RPi5 浮點 gains 的換算。
- [ ] 我能處理混合色溫場景(分區 CCM 或接受限制)。

11.22 Register 位元級完整工作流

白平衡(AWB)與色彩校正(CCM)的「讀→改→寫→驗證」是色彩準確性的基礎:

步驟暫存器/位址位元欄位操作預期值
1. 開啟 AWB0x5183[0] AWB enablei2cset -y 3 0x3c 0x5183 0x01AWB 開啟
2. 設定 red gain0x5185[7:0] red balancei2cset -y 3 0x3c 0x5185 0x801.0×(128=1.0×)
3. 設定 blue gain0x5187[7:0] blue balancei2cset -y 3 0x3c 0x5187 0x801.0×
4. 回讀驗證0x5185 / 0x5187[7:0]i2cget ... 0x5185; i2cget ... 0x51870x80 + 0x80
5. 開關 LSC0x5800[0]i2cset ... 0x5800 0x00LSC off(測色彩)
6. 取幀驗證v4l2-ctlv4l2-ctl --stream-mmap=1 --stream-count=1色彩平衡正確
位元級關鍵: OV5640 的 AWB gains 是整數格式:128 = 1.0×。RPi5 的 ColourGains 是浮點比值(1.4)。跨平台移植時,千萬不要直接搬數值——必須先換算定義。

11.23 多層疑難排解決策樹

決策樹 A:白平衡偏色

決策樹 A
影像偏色?
├─ 偏暖(偏紅/橙)→ red gain 太高
│  ├─ 減少 0x5185 值
│  └─ 或增加 0x5187(blue gain)
├─ 偏冷(偏藍)→ blue gain 太高
│  ├─ 減少 0x5187 值
│  └─ 或增加 0x5185(red gain)
├─ 混合色溫 → AWB 無法單一值修正
│  └─ 接受限制或用分區 CCM
└─ AWB 完全失效 → 0x5183=0x00
   └─ 檢查 AWB enable 狀態

決策樹 B:色彩不準(Delta E 太大)

決策樹 B
Delta E > 5?
├─ AWB 正常但色彩不準 → CCM 問題
│  ├─ 校正 CCM(用灰卡 + 色卡)
│  └─ 檢查 0x5800~0x5830 的 CCM 係數
├─ 特定色偏 → 該色域 CCM 係數錯誤
│  └─ 調整對應 CCM 係數
└─ 全域色偏 → AWB + CCM 都需校正
   └─ 先校正 AWB(白變白),再校正 CCM(色變準)

11.24 量測驗證完整 SOP

步驟指令預期輸出判讀標準
1. 用灰卡校正拍攝 18% 灰卡灰卡影像RGB 均值接近
2. 設定 AWBi2cset ... 0x5185 0x80; i2cset ... 0x5187 0x801.0× gains灰卡偏中性
3. 量測 Delta EPython scriptDelta E 值< 3(良好)
4. 調整 gains遞增/遞減 0x5185/0x5187色彩變化最小化 Delta E
5. 建 LUT多光源測試awb_lut.csv至少兩組色溫
6. 驗證混合色溫拍攝混合光源場景影像記錄局限

11.25 四平台終極對照

面向Orange PiRPi5Orin NanoThor推薦
AWB 控制0x5180~0x5190libcamera AWBNVIDIA AWBNVIDIA AWB底層→OV5640
gains 表示整數(128=1.0×)浮點比值NVIDIA 內部NVIDIA 內部換算時注意定義
CCM 控制0x5800~0x5830ColourCorrectionNVIDIA CCMNVIDIA CCM學習→OV5640
混合色溫後處理分區libcamera 全局NVIDIA 分區NVIDIA 分區接受局限
色準工具自寫 Pythonlibcamera 工具NVIDIA 工具NVIDIA 工具學習→Python

11.26 完整 Bring-up 專案 Checklist

checklist
- [ ] 完成「多光源校正專案」並產出 awb_lut.csv
- [ ] 用灰卡校正並量化 Delta E
- [ ] 理解「AWB 讓白變白,CCM 讓彩色變準」
- [ ] 建立至少兩組色溫的 gains 查表
- [ ] 換算 Orange Pi 整數 gains(128=1.0×)與 RPi5 浮點 gains
- [ ] 處理混合色溫場景(分區 CCM 或接受限制)
- [ ] 產出「色彩校正報告」(含 Delta E 數據)
- [ ] 驗證 AWB 收斂後色彩穩定
- [ ] 用色卡(X-Rite)驗證校正效果
- [ ] 產出「AWB tuning log」(含 gains/Delta E/光源)
看完這單元你應該能說出:
  • AWB 原理與 gains。
  • OV5640 AWB 與 V4L2 控制。
  • 灰卡校正流程。
  • 混合色溫的局限與分層策略。

延伸閱讀