單元 12 · 鏡頭陰影校正(LSC)

shading 校正

Lens shading 成因

鏡頭邊緣光量低於中心(cos⁴),造成四周變暗;廣角鏡頭常角落偏色

Orange Pi 的 LSC 三路線

路線做法可行度
平台 ISP(RK3588)Rockchip LSC 網格✅(需 Rockchip 工具)
感測器端OV5640 內建 shading(有限)⚠️ 範圍小
後處理OpenCV 增益圖✅ 通用非即時

實作:後處理 LSC(最務實)

OpenCV 增益圖
python3 - <<'EOF'
import cv2, numpy as np
gray = cv2.imread('graycard.png', 0)      # 均勻灰
gain = gray.mean() / (gray + 1e-6)        # 逐像素增益(需平滑)
# 套用到影像 frame_out = frame * gain
EOF
關鍵:LSC 是「鏡頭 + 感測器」組合特有——換鏡頭就要重新校正。

12.6 LSC 網格結構(本平台)

12.7 手算 LSC 增益圖(無工具時)

從均勻灰 RAW 推算
python3 - <<'EOF'
import numpy as np
# raw = 讀取的 RAW(分通道 R/Gr/Gb/B)
# center = 中央區均值;gain = center / cell(四周>1)
# 需分通道各自計算;對純灰卡才準
EOF
注意:手算只為理解;量產請用工具校正。換鏡頭後暗角比變化 >5% → 重新校正。

12.8 深入原理:cos⁴ law 與 shading 的量化模型

鏡頭邊緣變暗不是「故障」而是 光學定律。點光源經鏡頭成像,照度隨離軸角 θ 依 cos⁴θ 衰減:

中心照度 E₀×cos⁴θ=角落照度
為什麼要分通道:亮度 shading 補亮度,色差 shading 補色彩。若只用單一增益,角落會「變亮但仍偏色」——這是初學者最常漏的。

12.9 完整 Worked Example:完整後處理 LSC 流程

步驟 1|拍均勻亮場(diffuse 光源 + 灰/白卡)
# 固定曝光避免過曝,關 AWB 讓四通道可比
v4l2-ctl -d /dev/video0 --set-fmt-video=width=1280,height=720,pixelformat=SRGGB10 \
  --stream-mmap=3 --stream-count=1 --stream-to=flat.raw
步驟 2|分通道算增益圖
python3 - <<'EOF'
import numpy as np
raw = np.fromfile('flat.raw', dtype=np.uint16).reshape(720,1280)
chans = {'R':raw[0::2,0::2], 'Gr':raw[0::2,1::2],
         'Gb':raw[1::2,0::2], 'B':raw[1::2,1::2]}
for name, c in chans.items():
    center = c[100:260, 200:440].mean()
    gain = center / (c + 1e-6)          # 逐像素增益
    np.save(f'gain_{name}.npy', gain)
    print(name, 'corner gain ~', np.percentile(gain, 5))
EOF
步驟 3|套用到實拍幀
python3 - <<'EOF'
import numpy as np, cv2
raw = np.fromfile('shot.raw', dtype=np.uint16).reshape(720,1280)
out = raw.copy().astype(float)
for name, sl in {'R':(slice(0,None,2),slice(0,None,2)),
                 'Gr':(slice(0,None,2),slice(1,None,2)),
                 'Gb':(slice(1,None,2),slice(0,None,2)),
                 'B':(slice(1,None,2),slice(1,None,2))}.items():
    g = np.load(f'gain_{name}.npy')
    out[sl] = out[sl] * g
out = np.clip(out, 0, 1023).astype(np.uint16)
out.tofile('shot_lsc.raw')
EOF
紀律:亮場拍攝必須「不飽和且不靠近黑位」——用曝光把峰值壓在 ~80% 滿量程,再對齊中央。增益圖要做平滑(blur)否則會放大雜訊。

12.10 疑難排解決策樹:LSC 效果不好

現象方向
角落仍暗增益圖範圍不足或亮場不均 → 換更 diffuse 光源
角落偏色只用單通道增益 → 分 R/Gr/Gb/B 各自算
套用後雜訊暴增增益圖沒平滑 → blur 增益圖
中心出現亮斑亮場過曝 saturate → 降曝光重拍
換鏡頭後失效LSC 是鏡頭+感測器組合特有 → 重新校正
常見錯誤與陷阱:① 用不均勻光源當「亮場」——整張都是假的 shading;② 把 YUV 當 RAW 算增益——必須在 RAW 做;③ 忘了平滑增益圖;④ 想「一組 LSC 走天下」——不同解析度/binning/鏡頭都要重做。

12.11 練習

  1. 完成完整後處理 LSC 流程,比較「校正前/後」角落亮度。
  2. 分別用「單增益」與「分通道增益」跑一次,記錄偏色差異。
  3. 說明為什麼必須在 RAW 上做 LSC,而不是 YUV。
  4. 在你板卡上記錄「不同解析度的 LSC 是否要分開」。

12.12 進階真實情境 Worked Example:RK3588 ISP3 LSC 網格 vs Python 增益圖的品質對比

場景:Orange Pi 5 Plus(RK3588)接 OV5640,用 17×13 網格(ISP3)與逐像素增益圖(Python)分別做 LSC,比較角落亮度提升與色差改善。

步驟 1|ISP3 網格 LSC(需 Rockchip tuning 工具)
# 假設已有 tuning 工具設定 17×13 網格
rkisp_tuning -d /dev/media0 --set-lsc grid_17x13
v4l2-ctl -d /dev/video0 --stream-mmap=3 --stream-count=1 --stream-to=isp_lsc.yuv
步驟 2|Python 逐像素增益圖
python3 - <<'EOF'
import numpy as np, cv2
raw = np.fromfile('flat.raw', dtype=np.uint16).reshape(720, 1280)
center = raw[300:420, 500:780].mean()
gain = center / (raw.astype(float) + 1e-6)
gain_smooth = cv2.GaussianBlur(gain, (21, 21), 0)  # 平滑避免雜訊放大
np.save('gain_smooth.npy', gain_smooth)
EOF
設計決策:ISP3 網格 LSC 是硬體即時處理(無 CPU 負擔),但網格固定 17×13、tuning 封閉;Python 增益圖可做任意大小且可平滑,但非即時。選擇取決於「即時性需求 vs 調校精準度」。

12.13 深入原理擴充:Allwinner H618 的 LSC 限制

Allwinner H618 的 CIF 完全沒有 LSC 硬體模組,所有 lens shading 校正只能靠後處理:

項目Allwinner H618RK3588
LSC 硬體❌ 無✅ ISP3 網格
網格大小—(後處理任意)17×13(固定)
分通道後處理可分 R/Gr/Gb/BISP3 內建分通道
即時性⚠️ CPU 敏感✅ 硬體管線
色差 shading後處理可處理ISP3 內建

容易忽略的邊界案例:H618 的 OV5640 雖然有內建 shading 校正(0x4600 帶),但公開的 register 極少、網格極小——不能依賴它做深度校正。在 H618 上,LSC 後處理是唯一的務實路線。

12.14 診斷式疑難排解表

症狀可能原因解決方案
LSC 後角落仍暗亮場不均勻或增益圖沒涵蓋角落用更 diffuse 光源(陰天窗邊)重拍亮場
LSC 後角落偏色只用單通道增益做 LSC分 R/Gr/Gb/B 各自算增益圖
增益圖套用後雜訊暴增增益圖沒平滑用 GaussianBlur 平滑增益圖
換解析度後 LSC 失效LSC 是鏡頭+感測器+解析度 組合特有每次換組合都要重新校正
H618 無法用 ISP LSCCIF 無 LSC 硬體接受後處理;或升級到 RK3588

12.15 進階挑戰題

  1. 在 RK3588 上,用 ISP3 17×13 網格 LSC 與 Python 逐像素增益圖各做一次 LSC,用 PSNR 量化兩者的品質差異。
  2. 實作「自適應 LSC」:根據亮場的統計量自動決定增益圖的平滑半徑(σ),在雜訊抑制與校正精度之間取最佳平衡。
  3. 在 H618 上,設計一個即時 LSC 管線:OV5640 出 RAW → Python 做增益圖(blur 後)→ 套用到下一幀。測量端到端延遲。

12.16 專案級 Worked Example:多鏡頭 LSC 校正資料庫專案

把單元 12 的 LSC 知識做成多鏡頭校正資料庫專案:為每支鏡頭(模組)建立專屬的 LSC 增益圖,並產出「鏡頭 → 增益圖」的對照管理。這是量產一致性的關鍵——每支鏡頭的光學特性都不完全一樣。

階段 1|定義鏡頭清單與命名
# lens_A(廣角)、lens_B(標準)、lens_C(窄角)
mkdir -p lsc_db && cd lsc_db
階段 2|逐鏡頭拍亮場(單元 12.9)
#!/bin/bash
for lens in A B C; do
  # 換上 lens_$lens,固定曝光拍均勻亮場
  v4l2-ctl -d /dev/video0 --set-fmt-video=pixelformat=SRGGB10 \
    --stream-mmap=3 --stream-count=1 --stream-to=flat_$lens.raw
done
階段 3|逐鏡頭算增益圖
python3 - <<'EOF'
import numpy as np, cv2
for lens in 'ABC':
    raw = np.fromfile(f'flat_{lens}.raw', dtype=np.uint16).reshape(720,1280)
    for name, sl in {'R':(slice(0,None,2),slice(0,None,2)),
                     'Gr':(slice(0,None,2),slice(1,None,2)),
                     'Gb':(slice(1,None,2),slice(0,None,2)),
                     'B':(slice(1,None,2),slice(1,None,2))}.items():
        c = raw[sl].astype(float)
        center = c[100:260, 200:440].mean()
        gain = cv2.GaussianBlur(center/(c+1e-6), (21,21), 0)
        np.save(f'lsc_db/{lens}_{name}.npy', gain)
print('已產生 12 個增益圖(3 鏡頭 × 4 通道)')
EOF
階段 4|套用與回歸驗證
python3 - <<'EOF'
import numpy as np
raw = np.fromfile('shot_B.raw', dtype=np.uint16).reshape(720,1280)
out = raw.copy().astype(float)
for name, sl in {'R':(slice(0,None,2),slice(0,None,2)),
                 'Gr':(slice(0,None,2),slice(1,None,2)),
                 'Gb':(slice(1,None,2),slice(0,None,2)),
                 'B':(slice(1,None,2),slice(1,None,2))}.items():
    out[sl] = out[sl] * np.load(f'lsc_db/B_{name}.npy')
out = np.clip(out,0,1023).astype(np.uint16)
out.tofile('shot_B_lsc.raw')
c0 = raw[300:420,500:780].mean(); c1 = raw[10:80,10:80].mean()
o0 = out[300:420,500:780].mean(); o1 = out[10:80,10:80].mean()
print(f'校正前 corner/center = {c1/c0:.3f} → 校正後 = {o1/o0:.3f}')
EOF
專案完成標準:你有一份 lsc_db/ 資料庫(每鏡頭每通道的增益圖)+ 套用腳本。換鏡頭、換解析度都能快速切換對應增益圖——這是多樣化產品的標準做法。

12.17 量測/驗證 SOP:LSC 效果驗收

步驟作法通過判據
1. 拍均勻亮場diffuse 光源 + 灰/白卡峰值壓在 ~80% 滿量程
2. 量校正前 shadingcorner/center 比值記錄基準(如 0.56)
3. 算增益圖分通道 + 平滑增益圖平滑無爆點
4. 套用後重測corner/center 比值接近 1.0(±5%)
5. 色差檢查角落 R/B 比值無明顯色偏(色差 shading 已處理)
6. 換模式回歸換解析度/binning 重測必要時重新校正
SOP 紀律:「角落變亮」≠「LSC 完成」——還要檢查色差(分通道)與雜訊(平滑)。且 LSC 是鏡頭+感測器+解析度組合特有,換任何一項都要重做。

12.18 平台間對照:鏡頭陰影校正

面向Orange PiRPi5Orin NanoThor
硬體 LSCRK3588 有 / H618 無硬體 ISP 內建NVIDIA ISP 內建NVIDIA ISP 內建
網格RK 17×13 / 後處理任意libcamera tuningNVIDIA 網格NVIDIA 網格
分通道後處理可做ISP 內建ISP 內建ISP 內建
校正工具自寫 Pythonlibcamera tuning 工具NVIDIA 工具NVIDIA 工具
即時性⚠️ 後處理 CPU✅ 硬體✅ 硬體✅ 硬體
核心領悟:LSC 的「數學」四平台相同(中心照度 / 各點照度),差異在「誰做」——Orange Pi 沒硬體時用後處理(非即時),有硬體時用封閉工具。用 Python 先學會原理,任何平台都能對應。

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

checklist
- [ ] 我能完成多鏡頭 LSC 資料庫專案並產出增益圖。
- [ ] 我能量測校正前後的 corner/center 比值。
- [ ] 我能分通道做 LSC 並避免色差殘留。
- [ ] 我理解為什麼 LSC 必須在 RAW 上做而非 YUV。
- [ ] 我已平滑增益圖避免放大雜訊。
- [ ] 我知道 H618 沒有硬體 LSC,後處理是唯一路線。

12.20 Register 位元級完整工作流

鏡頭陰影校正(LSC)的「讀→改→寫→驗證」是影像均勻性的基礎:

步驟暫存器/位址位元欄位操作預期值
1. 開啟 LSC0x5800[0] LSC enablei2cset -y 3 0x3c 0x5800 0x01LSC 開啟
2. 設定中心增益0x5801[7:0] center gaini2cset ... 0x5801 0x401.0×(64=1.0×)
3. 設定角落增益0x5802~0x5806[7:0] corner gainsi2cset ... 0x5802 0x802.0×(補償暗角)
4. 回讀驗證0x5800~0x5806[7:0]i2cget ... 0x5800所有值正確
5. 關閉 LSC0x5800[0]i2cset ... 0x5800 0x00LSC off(對比用)
6. 取幀對比v4l2-ctl拍兩幀(LSC on/off)角落亮度改善
位元級關鍵: OV5640 的 LSC 增益是整數格式:64 = 1.0×。角落需要更高的增益(如 128 = 2.0×)來補償 cos⁴ law 的亮度衰減。必須在 RAW 上做,不能在 YUV 上做。

12.21 多層疑難排解決策樹

決策樹 A:LSC 效果不明顯

決策樹 A
LSC on/off 差異不大?
├─ 鏡頭品質好(暗角小)
│  └─ 正常!高品質鏡頭不需要 LSC
├─ LSC 增益設定太小
│  └─ 增加角落增益(0x5802~0x5806)
└─ LSC 未生效
   └─ 檢查 0x5800[0] 是否為 1

決策樹 B:LSC 後角落雜訊放大

決策樹 B
LSC 後角落雜訊增加?
├─ 增益太高 → 減少角落增益
├─ 雜訊基底高 → 先降噪再 LSC
└─ 正常(物理限制)
   └─ 記錄「LSC 增益 vs 雜訊」的取捨

12.22 量測驗證完整 SOP

步驟指令預期輸出判讀標準
1. 關閉 LSCi2cset ... 0x5800 0x00RAW 無校正角落偏暗
2. 取 RAWv4l2-ctl --stream-mmap=1 --stream-count=1RAW 檔有暗角
3. 開啟 LSCi2cset ... 0x5800 0x01LSC on角落亮度改善
4. 取校正後 RAWv4l2-ctl --stream-mmap=1 --stream-count=1校正後 RAW均勻性改善
5. 量測 corner/centerPython script亮度比值校正後比值接近1
6. 分通道驗證R/G/B 分別量測各通道比值避免色差殘留

12.23 四平台終極對照

面向Orange PiRPi5Orin NanoThor推薦
LSC 硬體OV5640 內建(有限)ISP 內建ISP 內建ISP 內建學習→OV5640
後處理 LSC自寫 Pythonlibcamera 後期可選可選底層→Python
即時性⚠️ 後處理 CPU✅ 硬體✅ 硬體✅ 硬體效能→硬體
分通道後處理可做ISP 內建ISP 內建ISP 內建學習→分通道
校正工具自寫 Pythonlibcamera tuningNVIDIA 工具NVIDIA 工具學習→Python

12.24 完整 Bring-up 專案 Checklist

checklist
- [ ] 完成「多鏡頭 LSC 資料庫」專案並產出增益圖
- [ ] 量測校正前後的 corner/center 比值
- [ ] 分通道做 LSC 並避免色差殘留
- [ ] 驗證「LSC 必須在 RAW 上做」(YUV 上做會失敗)
- [ ] 平滑增益圖避免放大雜訊
- [ ] 確認 H618 沒有硬體 LSC(後處理是唯一路線)
- [ ] 產出「LSC 校正報告」(含增益圖/corner-center 比值)
- [ ] 測試不同鏡頭的 LSC 差異
- [ ] 驗證 LSC 隨組合(感測器+鏡頭)需重新校正
- [ ] 產出「lens shading database」
看完這單元你應該能說出:
  • lens shading 成因。
  • Orange Pi 三條 LSC 路線。
  • 後處理 LSC 實作。
  • LSC 需隨組合重新校正。
  • cos⁴ law 與分通道增益的必要性。
  • 完整後處理 LSC 的 Worked Example。

延伸閱讀