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

成因、網格、校正實作

12.1 Lens shading 成因(物理)

鏡頭邊緣光量低於中心,主要因 cos⁴ law:亮度 ∝ cos⁴(入射角)。廣角鏡頭還伴隨角落偏色(R/G/B 各自衰減不同,因濾鏡與入射角)。

原始:四周暗LSC 網格增益亮度均勻

12.2 LSC 網格機制

重要:LSC 是「鏡頭 + 感測器 + 模組」特有的——換鏡頭、換感測器板、甚至換模組距離就要重新校正

12.3 libcamera 產生 LSC(實作流程)

拍均勻灰(積分球/均勻光源)
# 固定曝光、AWB off,多拍幾張不同亮度
rpicam-still --shutter 20000 --gain 1 --awb off -o lsc1.png
rpicam-still --shutter 40000 --gain 1 --awb off -o lsc2.png
# 用 libcamera / 社群 calibration 工具產生網格 → tuning

社群工具:rpicam-apps 或 libcamera tuning 範例的 LSC 產生腳本。

12.4 手動檢查 LSC 成效

量測四角 vs 中央亮度
python3 - <<'EOF'
import cv2, numpy as np
img = cv2.imread('lsc_check.png', 0)   # 均勻灰拍出的圖
h, w = img.shape
c = img[h//2, w//2]
corners = [img[50,50], img[50,w-50], img[h-50,50], img[h-50,w-50]]
print('center:', c)
print('corners:', corners)
print('暗角比:', min(corners)/c)     # 目標 >0.9(均勻)
EOF

12.5 調校檢查表

症狀檢查
四周暗角LSC 增益偏低、網格未涵蓋全畫面
角落偏色R/G/B 分通道 LSC 是否獨立設定
換鏡頭又暗LSC 是組合特有 → 重做
網格邊界不連續網格節點過疏、需加密

12.6 LSC 網格檔結構(libcamera meshes)

LensShading 區塊(概念)
{ "LensShading": {
    "meshes": [
      { "size": [17, 13],          # 網格維度
        "type": "sector",          # 資料型態
        "reference": [...],        # 參考網格
        "r": [...], "gr": [...], "gb": [...], "b": [...] }
    ] } }

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

從均勻灰 RAW 推算
python3 - <<'EOF'
import rawpy, numpy as np
raw = rawpy.imread('lsc.dng').raw_image.astype(float)
h, w = raw.shape
# 以中心為基準,算每一格對中心的比例 → 倒數為增益
center = raw[h//2 - 50:h//2+50, w//2-50:w//2+50].mean()
grid = 13
gh, gw = h//grid, w//grid
print('增益圖(中心=1,四周>1):')
for gy in range(grid):
    row = []
    for gx in range(grid):
        cell = raw[gy*gh:(gy+1)*gh, gx*gw:(gx+1)*gw].mean()
        row.append(f'{center/cell:.2f}')
    print(' '.join(row))
EOF
注意:手算增益圖要分通道(R/Gr/Gb/B 各自),且需對「純灰卡」才準——這只是理解用;量產請用工具校正。

12.8 LSC 與鏡頭更換的驗證

  1. 換鏡頭後,重拍均勻灰,量測四角/中心暗角比(單元 12.4)。
  2. 暗角比變化 >5% → 必須重新校正 LSC。
  3. 同鏡頭不同光圈/焦距(若變焦)→ shading 曲線不同 → 各拍一組。

12.9 深入:shading 的色偏機制

鏡頭不只亮度衰減,R/G/B 因微透鏡與濾鏡的入射角響應不同,角落偏色是獨立於亮度的問題——這就是為什麼 LSC 要 R/Gr/Gb/B 分通道。

判斷:角落「又暗又偏紫/綠」→ 需要分通道 LSC;只有暗不偏色 → 亮度補償即可。

12.10 Worked Example:暗角比的量測與目標

量測
# 均勻灰拍圖
center = 中央區平均
corner = 四角平均
暗角比 = corner / center
# 目標:校正後 > 0.9(原可能 0.7 甚至更低)

12.11 練習

  1. 拍均勻灰,量測校正前暗角比。
  2. 若可用,跑 LSC 校正工具。
  3. 再量一次,確認提升。
看完這單元你應該能說出:
  • lens shading 物理成因(cos⁴ 與偏色)。
  • LSC 網格與插值機制。
  • libcamera LSC 校正實作。
  • 量測暗角比驗證。
  • LSC 組合特有、需重做。

進階真實情境 Worked Example:用 Python 自動產生 LSC 校正網格

場景:你沒有積分球,但有一張用 OV5647 拍攝的均勻白牆照片。需要從這張照片推算 LSC 網格增益。

從白牆照片推算 LSC 網格
python3 - <<'EOF'
import rawpy, numpy as np
raw = rawpy.imread('white_wall.dng').raw_image.astype(float)
h, w = raw.shape
grid_h, grid_w = 13, 17  # libcamera 標準網格
cell_h, cell_w = h // grid_h, w // grid_w
center_roi = raw[h//2-50:h//2+50, w//2-50:w//2+50]
center_val = center_roi.mean()

print(f"{'':>8}", end="")
for gx in range(grid_w):
    print(f"col{gx:2d}", end=" ")
print()

for gy in range(grid_h):
    print(f"row{gy:2d}: ", end="")
    for gx in range(grid_w):
        cell = raw[gy*cell_h:(gy+1)*cell_h, gx*cell_w:(gx+1)*cell_w].mean()
        gain = center_val / cell if cell > 0 else 99
        print(f"{gain:5.2f}", end=" ")
    print()

print(f"\n中心值: {center_val:.1f}")
print(f"最暗角落增益: {center_val / raw[20:80, 20:80].mean():.2f}")
EOF
為什麼選這條路徑:白牆是「免費的均勻灰源」——雖然不如積分球完美,但在沒有專業設備時是最佳替代。用 rawpy 直接分析 RAW(而非 JPEG/PNG),可以精確量測每個像素通道的亮度,避免 JPEG 壓縮和色彩處理的干擾。產生的網格增益可以直接填入 libcamera tuning JSON 的 LensShading 區塊。

深入原理擴充:cos⁴ law 的精確數學與鏡頭特性

cos⁴ law 是 LSC 的物理基礎,但實務中鏡頭的 shading 比純 cos⁴ 更複雜:

大家以為沒问题但其實是陷阱:很多人以為「LSC 校正一次就永久有效」,但 OV5647 的微透鏡陣列在溫度變化 ±15°C 後,shading 曲線會改變 3-5%——這是因為塑膠鏡筒的熱膨脹改變了 lens-to-sensor 的距離。在戶外應用(溫差大)中,可能需要根據溫度動態調整 LSC。

診斷式疑難排解表

症狀可能原因解決方案
LSC 校正後暗角改善但四角偏紫LSC 只校正了亮度,未分 R/Gr/Gb/B 通道確認 LSC 網格的四通道各自獨立;偏紫是 R/B 衰減比例不同
換鏡頭後暗角比從 0.7 變成 0.5新鏡頭的 shading 特性不同(LSC 是鏡頭+感測器組合特有)必須重新校正 LSC;不能沿用舊鏡頭的參數
LSC 網格邊界處出現微弱網格狀紋路網格節點過疏,雙線性插值無法平滑過渡加密網格節點(如 17×13→33×25);或用更高階插值
均勻灰拍攝的四角亮度不一致(已校正 LSC)LSC 校正用的拍攝環境不夠均勻(有陰影或反射)用更均勻的光源重做校正;或增加曝光範圍取平均
低光下 LSC 效果比高光下差低光下暗角區域的雜訊被 LSC 增益放大在低光下降低 LSC 增益上限;或接受暗角(不校正)

進階挑戰題

  1. 用 rawpy 分析 OV5647 在不同光圈(若有可變光圈鏡頭)下的 LSC 差異,畫出「光圈 vs 暗角比」的關係圖。
  2. 設計一個「LSC 網格密度優化」實驗:分別用 13×17、25×19、33×25 的網格做 LSC,量化比較暗角比改善程度與 tuning 檔大小的 trade-off。
  3. 若 OV5647 的 IR 濾鏡在高溫下截止波長漂移了 5nm,LSC 的 R 通道增益需要如何調整?推導修正公式。

延伸閱讀

專案級端到端 Worked Example:LSC 校正自動化專案

場景:鏡頭/模組批次更換頻繁,每次手動算 LSC 太慢。本專案建立「LSC 校正流水線」:拍均勻灰 → 分通道算網格增益 → 驗證暗角比 → 輸出 tuning 網格。整合單元 12(LSC)、7(黑位)、8(RAW)、5(拍攝)知識。

lsc_pipeline.sh:LSC 校正流水線
#!/bin/bash
# 1. 拍均勻灰(白牆/積分球)固定參數
rpicam-still --raw --shutter 20000 --gain 1 \
  --awb off --awbgains 1,1 -o lsc_src.dng

# 2. 分通道算網格增益(以中央為 1.0)
python3 - <<'EOF'
import rawpy, numpy as np
raw = rawpy.imread('lsc_src.dng').raw_image.astype(float)
h, w = raw.shape
gh, gw = 13, 17                      # libcamera 標準網格
ch, cw = h//gh, w//gw
center = raw[h//2-50:h//2+50, w//2-50:w//2+50].mean()
print("角落/中心 暗角比 =", round(raw[20:80,20:80].mean()/center, 3))
# 對每格輸出增益(此處以整體亮度示意,實作需分 RGGB)
for gy in range(gh):
    row = []
    for gx in range(gw):
        cell = raw[gy*ch:(gy+1)*ch, gx*cw:(gx+1)*cw].mean()
        row.append(f"{center/cell:.3f}")
    print(" ".join(row))
EOF
# 3. 將網格填入 tuning JSON 的 LensShading 區塊 → v2_lsc.json
驗證 LSC 成效
# 套用新 tuning 重拍均勻灰
rpicam-still --raw --tuning-file v2_lsc.json --shutter 20000 \
  --gain 1 --awb off --awbgains 1,1 -o lsc_after.dng
python3 - <<'EOF'
import rawpy, numpy as np
raw = rawpy.imread('lsc_after.dng').raw_image.astype(float)
c = raw[800:1200, 1100:1500].mean()
corn = [raw[50,50],raw[50,-51],raw[-51,50],raw[-51,-51]]
print("中央:", round(c,1), " 四角:", [round(x,1) for x in corn])
print("暗角比:", round(min(corn)/c, 3), "(目標 >0.9)")
EOF
專案規模與跨單元整合:LSC 是「鏡頭+感測器+模組」組合特有(單元 12):校正前先確認黑位已對(單元 7,否則網格被污染)、用 RAW 而非 JPEG(單元 8)、套用後用暗角比驗收(單元 12.4)。流水線產出的網格直接進 tuning(單元 14),換鏡頭時只需重跑一輪。

量測/驗證 SOP:LSC 校正驗證

  1. 用均勻光源(積分球最佳、白牆替代)拍 RAW。
  2. 計算校正前暗角比(四角/中央)。
  3. 分通道(RGGB)算網格增益。
  4. 寫入 tuning 後重拍,量測校正後暗角比。
  5. 檢查角落偏色是否消除(分通道效果)。
  6. 換鏡頭/模組後重跑(LSC 組合特有)。
判讀指標:校正後暗角比 >0.9 為達標;角落偏色殘留 = 分通道網格未生效;低光下 LSC 放大角落雜訊 = 需降增益上限或接受暗角。

平台間對照:LSC 校正

面向RPi5Orange PiOrin NanoThor
LSC 機制tuning JSON 網格(17×13 等)有限/後處理NVIDIA 工具自動NVIDIA 工具
網格可調性節點數/分通道全可設可自訂(工具)可自訂
校正輸入均勻灰 RAW(自拍)手動NVIDIA 校正流程NVIDIA 校正流程
白牆不均勻會污染網格無標準工具流程封閉生態新

互動式檢核清單


第 3 輪深度加深

① Register 位元級完整工作流:RPi5 LSC 表格寫入

步驟操作位元級說明
1. 啟用 LSC 模組ISP control register bit[3]=10x7e80200C |= 0x00000008
2. 設定表格地址寫入 LSC RAM base address通常在 ISP SRAM 內固定地址
3. 寫入 gain 表依序寫入 R/Gr/Gb/B 四通道的 gain 值每通道 17×13 = 221 個 1.8 fixed-point gain 值
4. 驗證read-back LSC table與計算值比對

② 多層疑難排解決策樹

決策樹 A:LSC 後暗角仍然存在

暗角未消除
├─ 檢查 A:LSC 是否已啟用
│  ├─ 未啟用 → 啟用 LSC register
│  └─ 已啟用 → 繼續
├─ 檢查 B:LSC table 是否針對當前 lens
│  ├─ 不是 → 重新生成 LSC table(用 flat field 拍攝)
│  └─ 是 → 繼續
├─ 檢查 C:lens mount 是否正確
│  ├─ 偏心 → 重新對準 lens
│  └─ 正確 → 繼續
└─ 檢查 D:zoom level 是否改變
   ├─ 是 → 不同 zoom 需要不同的 LSC table
   └─ 否 → LSC 補償範圍不足

③ 量測驗證完整 SOP:LSC 效果量測

  1. 工具:Flat white target + 5000K 均勻光源
  2. 步驟:
    a. 拍攝 flat white(曝光固定)
    b. 計算中心 10% 區域平均亮度 = center_ref
    c. 計算四角各 5% 區域平均亮度 = corner_avg
    d. 暗角比 = corner_avg / center_ref
  3. 校正前:暗角比 <0.7 代表嚴重暗角
  4. 校正後:暗角比 >0.9 = LSC 成功;0.8-0.9 = 可接受
  5. 重複:三種 zoom level 各量一次
  6. 常見偏差:光源不均勻 → 誤判暗角;lens 未裝好 → 非對稱暗角

④ 四平台終極對照

面向RPi5Orange PiOrin NanoThor推薦
LSC 控制libcamera tuning手動為主NV 自動 + 手動NV + AI各有方式
LSC table 生成flat field 拍攝手動計算NV camera toolsHoloscan 自動RPi5 最透明
多 lens 支援✓(不同 tuning file)有限✓(多 profile)✓(自動偵測)Thor 最智慧
精準度中等較差優異Orin/Thor 最佳

⑤ 完整 Bring-up 專案 Checklist