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

NVIDIA LSC

Lens shading 成因

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

NVIDIA 的 LSC

關鍵:LSC 是「鏡頭 + 感測器」組合特有——換鏡頭或感測器板就要重新校正;多感測器每顆獨立 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% → 重新校正。
看完這單元你應該能說出:
  • lens shading 成因。
  • NVIDIA LSC 網格。
  • LSC 需隨組合重新校正。
  • 多感測器獨立 LSC。

12.8 深入原理:cos⁴ 定律與網格插值

鏡頭邊緣光量依視角的 cos⁴ 衰減——視角越大,四角越暗。若感測器各通道(R/Gr/Gb/B)衰減速度不同,還伴隨「角落偏色」。LSC 用一張增益網格補償:

均勻灰卡 RAW分通道取樣網格計算各節點增益節點間雙線性插值逐像素套用
重點:網格越密越能貼合真實 shading,但節點過多會放大雜訊。工具會平衡「平滑度」與「擬合度」。

12.9 Worked Example:量化 lens shading

拍均勻灰卡並量四角
python3 - <<'EOF'
import numpy as np
# raw: 均勻灰卡 RAW(已確認 Bayer 與黑位)
h, w = raw.shape
def block(cx, cy):
    return raw[cy-50:cy+50, cx-50:cx+50].mean()
corners = [block(w//4,h//4), block(3*w//4,h//4),
           block(w//4,3*h//4), block(3*w//4,3*h//4)]
center  = block(w//2, h//2)
print('中央 vs 角落比:', [round(c/center,3) for c in corners])
# 例:[0.78, 0.80, 0.79, 0.81] → 需 ~1.27x 角落增益
EOF
判讀規則:角落/中央比「越接近 1」越好。若 < 0.85(需 >1.18x 增益)屬明顯 shading;換鏡頭後此比值改變 >5% → 重新校正。

12.10 疑難排解決策樹:暗角或角落偏色

決策樹
1. 四角均暗、中央正常?
   ├─ 是 → 需要 LSC:拍灰卡生成網格
   └─ 否 ┐
2. 角落偏色(如紅角)?
   ├─ 是 → 分通道 shading 不同:分通道 LSC
   └─ 否 ┐
3. LSC 後仍不均?
   ├─ 是 → 網格太疏 / 灰卡不均勻 / 光源不均
   └─ 否 ─→ 均勻 → ✅

12.11 常見錯誤 / 陷阱

陷阱 ①:灰卡本身不均勻或光源不均 → 校正「正確」地補了一個錯的 shading——用積分球或均勻光源。
陷阱 ②:換鏡頭 / 換感測器板後沿用舊 LSC——shading 是「鏡頭+感測器」組合特有,必須重校。
陷阱 ③:多感測器共用一份 LSC——每顆獨立校正(個體差異)。

12.12 練習

  1. 拍一張均勻灰卡,量化中央 vs 角落比。
  2. 做一次 LSC 校正並比較前後 RAW。
  3. 說明「為什麼換鏡頭要重校」。

12.13 進階:分通道 shading 與偏色

鏡頭光學衰減對不同波長不同——所以四角的「暗」其實伴隨「偏色」(例如角落偏紅)。LSC 必須分通道(R/Gr/Gb/B)給不同增益,才能同時消除暗角與偏色。

通道角色校正重點
R / B紅藍通道色彩 shading(偏色)
Gr / Gb綠色雙通道亮度 shading(暗角)
驗證:LSC 後拍灰卡,四角與中央的「各通道比」都應接近 1;只亮均勻但角落仍偏色 = 分通道沒做好。

12.14 快速參考:LSC 小抄

時機動作
換鏡頭重校(shading 是組合特有)
換感測器板重校
換模式(binning)檢查並重校
多感測器每顆獨立
邊緣比值變 >5%重校

12.15 看完本單元該記住的三件事

shading 是「鏡頭+感測器」組合特有。
LSC 要分通道,同時解暗角與偏色。
灰卡與光源要均勻,否則校正「把錯的當對的補」。

12.16 深入原理:為什麼 Gr/Gb 也要分開

Bayer 的兩個綠色通道(Gr 在 R 列、Gb 在 B 列)理論上相同,但感測器製程與微透鏡會讓兩者產生細微差異——不均勻時畫面出現「棋盤格」亮暗紋(maze pattern)。

檢查 Gr vs Gb
python3 - <<'EOF'
import numpy as np
# raw: 均勻灰卡 RAW(BGGR 範例)
# 抽 Gr / Gb 兩通道均值
gr = raw[0::2, 1::2].mean()   # 依 Bayer 起點調整
gb = raw[1::2, 0::2].mean()
print('Gr/Gb 差:', round((gr-gb)/gb*100, 2), '%')   # >2% → 需分開校正
EOF
除錯:看到「整片細微棋盤格」別先怪 NR——先檢查 Gr/Gb 平衡;這是 LSC/黑位分通道要處理的。

12.17 看完本單元該記住的三件事

cos⁴ 造成暗角,分通道造成偏色——LSC 兩者都要解。
LSC 是鏡頭+感測器組合特有,換了就重校。
多感測器逐顆獨立校正。

延伸閱讀

12.9 進階真實情境 Worked Example:OV9281 + 廣角鏡頭的 LSC 校正

場景:OV9281 搭配 120° 廣角鏡頭,邊緣失光嚴重(中心亮度是邊緣的 3 倍)。需要做 LSC 校正使全畫面亮度均勻。

LSC 校正流程
# 1. 拍均勻白牆(確保光線均勻、無陰影)
argus_camera --mode 0 --raw-file whitewall.raw --duration 1
# 2. 分析中心 vs 邊緣亮度比
python3 - <<'EOF'
import numpy as np
raw = np.fromfile('whitewall.raw', dtype=np.uint16).reshape(800, 1280)
center = raw[300:500, 500:800].mean()
edge_top = raw[0:100, 500:800].mean()
edge_left = raw[300:500, 0:200].mean()
print(f'中心: {center:.0f}, 上邊緣: {edge_top:.0f}, 左邊緣: {edge_left:.0f}')
print(f'邊緣/中心比: 上={edge_top/center:.2f}, 左={edge_left/center:.2f}')
EOF
# 3. 用 NVIDIA tuning tool 產生 LSC 校正表
#    產生 33×33 的增益表(每個 grid cell 的亮度補償倍數)
# 4. 載入 Argus ISP
argus_camera --mode 0 --lsc-file lsc_table.bin --duration 5

設計決策:120° 廣角鏡頭的光學特性決定 LSC 必須做——邊緣失光是物理定律(cos⁴ law),不是缺陷。LSC 校正的關鍵是「拍攝條件要均勻」:白牆、正對、無反光。

12.10 深入原理擴充:LSC 校正表的 grid 結構

LSC 校正表把畫面分成 grid(例如 33×33),每個 grid cell 有一個增益值(1.0 = 不調整,>1.0 = 補亮)。

Grid 大小精細度記憶體適用場景
17×17鏡頭 shading 平滑(簡單鏡頭)
33×33多數工業鏡頭
65×65非對稱 shading(例如側面補光)
陷阱:LSC 校正表是「空間域」校正——它根據 pixel 在畫面中的位置補償亮度。若光源不均勻(例如一面牆有陰影),LSC 會「錯誤補償」:把陰影區當作鏡頭 shading 來補亮,導致該區域過曝。

12.11 診斷式疑難排解表

症狀可能原因解決方案
LSC 校正後邊緣反而更暗校正表的增益方向搞反(<1.0 而非 >1.0)確認增益表中「邊緣 cell」的值 > 1.0;翻轉表值
LSC 校正後四角過亮校正表過度補償(增益值過大)降低邊緣 cell 的增益值;確認拍攝時光線真的均勻
LSC 校正後中心與邊緣亮度一致但色彩不均LSC 只補償亮度,不補償色彩(色溫差異)LSC 是 per-channel 校正(RGB 各自獨立);確認 LSC 校正表是 per-channel
換鏡頭後 LSC 校正表失效不同鏡頭的 shading 特性不同每顆鏡頭獨立做 LSC 校正;不可共用
LSC 校正表在低光下效果差低光下雜訊放大,LSC 補亮的同時也放大雜訊在低光下降低 LSC 補償上限;或搭配 NR 同時調校

12.12 進階挑戰題

  1. 為你的 OV9281 + 廣角鏡頭拍攝 LSC 校正圖(白牆),用 Python 計算並視覺化 33×33 的增益表(用 heatmap 顯示)。
  2. 比較「同一顆 OV9281」搭配「標準鏡頭」和「廣角鏡頭」的 LSC 校正表差異,畫出兩者的增益曲線。
  3. 設計一個自動化 LSC 校正流程:腳本自動拍白牆、自動分析、自動生成校正表、自動載入 ISP。

12.13 專案級端到端 Worked Example:LSC 校正產線化專案

場景:量產時每台設備的「鏡頭 + 感測器」組合都有個體差異,需要逐台做 LSC。專案目標:把單元 12 的 LSC 校正流程自動化:一鍵「拍白牆 → 分析 → 生成校正表 → 驗證」,產出每台設備專屬的 LSC 檔。

lsc_auto.sh(自動化骨架)
#!/bin/bash
set -e
echo "== [1/4] 拍白牆 =="
argus_camera --mode 0 --raw-file white.raw --duration 1
echo "== [2/4] 分析並生成校正表 =="
python3 - <<'EOF'
import numpy as np
raw = np.fromfile('white.raw', dtype=np.uint16).reshape(800, 1280)
gx, gy = 32, 32                       # 33x33 grid
h, w = raw.shape
grid = np.zeros((gx, gy))
for i in range(gx):
    for j in range(gy):
        ys = slice(i*h//gx, (i+1)*h//gx)
        xs = slice(j*w//gy, (j+1)*w//gy)
        grid[i, j] = raw[ys, xs].mean()
center = grid[gx//2, gy//2]
lsc = center / np.maximum(grid, 1e-6)   # 增益表:角落 > 1
np.save('lsc_table.npy', lsc)
print('生成 33x33 增益表,範圍', round(lsc.min(),2), '-', round(lsc.max(),2))
EOF
echo "== [3/4] 驗證:套用後重拍,角落比應接近 1 =="
# (載入 LSC 後重拍白牆,四角/中央比 < 1.05)
echo "== [4/4] 存檔 =="
mv lsc_table.npy lsc_$(date +%Y%m%d_%H%M%S).npy

專案輸出:自動化腳本 + 每台設備的 LSC 檔 + 驗證報告。產線 QA 只要跑一鍵流程。

12.14 量測/驗證 SOP:LSC 驗證

  1. 拍攝條件:白牆 / 積分球、光線均勻、正對、無反光。
  2. 量測:拍均勻灰 RAW,量中央 vs 四角比。
  3. 分通道:R / Gr / Gb / B 各自量,確認無分通道偏色。
  4. 生成:依組合(鏡頭 + 感測器)生成校正表。
  5. 驗證:套用後重拍,四角 / 中央比應 < 1.05。
  6. 回歸時機:換鏡頭、換感測器板、換 mode 都要重校。
驗收指標:套用後角落 / 中央比 < 1.05、四角無殘留偏色、Gr / Gb 差 < 2%。

12.15 平台間對照:LSC

面向Orin NanoRPi5Orange PiThor
LSC 支援✅ ISP 內建網格✅ libcamera tuning⚠️ 依感測器✅ ISP 內建
校正表格式NVIDIA 工具格式tuning file(JSON)感測器 / 驅動NVIDIA 格式
生成工具NVIDIA tuning tool開源工具 / 自寫自寫NVIDIA tuning tool
網格密度依工具(17-65)可自訂有限依工具
校正方法白牆 / 積分球 + 分通道增益,全平台相同
重點:「組合特有 + 分通道 + 重校時機」是平台無關的鐵律;差異只在校正表的格式與生成工具。

12.16 互動式檢核清單:LSC 驗收

12.11 Register 位元級完整工作流:LSC 校正——白牆拍攝 + 增益表

步驟Command / 量測預期
1. 白牆/白紙出圖argus + 均勻白光源中心與角落亮度一致
2. 暗角量測中心 mean / 角落 mean比值 < 1.2
3. 色偏量測角落 R/G/B ratio vs 中心偏離 < 5%
4. 增益表視覺化plt.imshow(gain_map)中心 1.0,邊緣 > 1.0
LSC 完整驗證腳本
#!/usr/bin/env python3
# lsc_verify.py — 驗證鏡頭陰影校正
import numpy as np, matplotlib.pyplot as plt

# 假設已有白牆 RAW(mode 0, SRGGB10)
raw = np.fromfile("white_wall.raw", dtype=np.uint16).reshape(800, 1280)

# 四通道分離
R = raw[0::2, 0::2].astype(float)
Gr = raw[0::2, 1::2].astype(float)
Gb = raw[1::2, 0::2].astype(float)
B = raw[1::2, 1::2].astype(float)

# 亮度圖(四通道平均)
luma = (R + Gr + Gb + B) / 4

# 暗角量測
center = luma[350:450, 550:700].mean()
corner_tl = luma[0:100, 0:100].mean()
corner_tr = luma[0:100, -100:].mean()
corner_bl = luma[-100:, 0:100].mean()
corner_br = luma[-100:, -100:].mean()
corners = np.mean([corner_tl, corner_tr, corner_bl, corner_br])

vignetting = corners / center
print(f"Center: {center:.1f}")
print(f"Avg corner: {corners:.1f}")
print(f"Vignetting ratio: {vignetting:.3f} ({(1-vignetting)*100:.1f}% loss)")
print(f"VERDICT: {'PASS' if vignetting > 0.85 else 'NEEDS LSC'}")

# 色偏
rg_center = R[350:450, 550:700].mean() / Gr[350:450, 550:700].mean()
bg_center = B[350:450, 550:700].mean() / Gb[350:450, 550:700].mean()
rg_corner = R[0:100, 0:100].mean() / Gr[0:100, 0:100].mean()
bg_corner = B[0:100, 0:100].mean() / Gb[0:100, 0:100].mean()
print(f"Center R/G: {rg_center:.3f}, Corner R/G: {rg_corner:.3f}")
print(f"Center B/G: {bg_center:.3f}, Corner B/G: {bg_corner:.3f}")
color_shift = abs(rg_center - rg_corner) + abs(bg_center - bg_corner)
print(f"Color shift: {color_shift:.3f} ({'PASS' if color_shift < 0.05 else 'NEEDS CORRECTION'})")

# Gain map
gain_map = center / np.where(luma > 10, luma, 1)
plt.figure(figsize=(10, 4))
plt.subplot(121); plt.imshow(luma, cmap='gray'); plt.title('Luma (uncorrected)')
plt.subplot(122); plt.imshow(gain_map, cmap='hot', vmin=1, vmax=2); plt.title('LSC Gain Map')
plt.colorbar(); plt.tight_layout(); plt.savefig('lsc_analysis.png')
print("Saved: lsc_analysis.png")

12.12 多層疑難排解決策樹

決策樹 A:白牆中心與角落亮度差異大
1. 暗角比值 < 0.85(> 15% loss)?
   ├─ 是 ┐
   │   2. 用 f/4.0 重拍?暗角改善?
   │      ├─ 是 → 純光學暗角(正常)
   │      │       → 用 LSC 補償
   │      └─ 否 → 感測器微透鏡問題
   │              → 需要光機調整(鏡頭位置)
   └─ 否 ┐
2. 中心與角落色偏 > 5%?
   ├─ 是 → LSC 未啟用或增益表有誤
   └─ 否 → 無暗角問題
決策樹 B:LSC 增益表方向錯誤
1. LSC 後角落更暗?
   ├─ 增益表方向反轉 → 中心值 / 角落值 改為 角落值 / 中心值
   └─ 正常 → ✅

12.13 量測驗證完整 SOP

  1. 白牆拍攝:均勻打光白牆/白紙,取 RAW。
  2. 暗角量測:中心 100×100 vs 四角 100×100 → 比值。
  3. 判定:比值 > 0.85 → 無需 LSC;< 0.85 → 需要 LSC。
  4. 增益表計算:gain(x,y) = center_mean / pixel(x,y)。
  5. 視覺化:畫 gain map → 確認中心 ~1.0、邊緣 > 1.0。
  6. LSC 應用:原始 RAW × gain map = LSC corrected RAW。
  7. 驗證:LSC 後暗角比值 > 0.95 = 成功。
判讀標準:LSC 前暗角 > 15% → LSC 後暗角 < 5% = LSC 校正成功。

12.14 四平台终极對照

面向Orin NanoRPi5Orange PiThor推薦
LSC 實作tegracam tuninglibcamera tuningtegracam + Holoscan各有格式
增益表來源校正工具生成自動偵測自動 + AI校正 → 最準
即時 LSC✅ 硬體加速全平台可選
校正需求每顆鏡頭獨立每顆鏡頭獨立不支援每顆鏡頭獨立鏡頭 ↔ 感測器配對
暗角容忍度< 5%(量產)< 10%(原型)N/A< 3%(高階)依應用
LSC 增益表格式16-bit gridJSON 陣列16-bit grid平台專屬
全平台共通知識暗角原理 + 增益表計算 + 中心/角落比值原理最重要

12.15 完整 Bring-up 專案 Checklist