Vision & Inspection

[LensCal] 텔레센트릭 렌즈에도 왜곡 측정이 필요한 이유 — 광학 품질 3종 세트

PixelMechanic 2026. 4. 13. 17:42

광학

시리즈 5/7. 같은 체커보드 이미지에서 렌즈의 광학적 품질까지 뽑아냅니다. 그런데 첫 번째 질문은 이겁니다. "텔레센트릭 렌즈는 왜곡이 0 이라고 하는데, 굳이 측정해야 할까요?"

1. 기하 지표와 광학 지표의 차이

앞 편에서 다룬 기하학적 지표 (재투영 오차, 직교성) 는 "격자가 반듯한가" 를 봅니다. 그런데 이 지표들로는 답하지 못하는 질문이 여전히 있습니다.

  • 이 렌즈의 왜곡 특성이 스펙 범위 안에 들어오는가?
  • 이미지 전체에 조명이 균일하게 들어왔는가?
  • 포커스가 영역별로 균일하게 맞았는가?

이건 격자의 기하학적 반듯함이 아니라, 렌즈 자체의 물리적/광학적 특성입니다. 이번 편에서 다룰 세 지표가 정확히 이 영역을 담당합니다.

  1. 방사 왜곡 계수 (Radial Distortion k1, k2)
  2. 영역별 콘트라스트 (Michelson Contrast)
  3. 에지 샤프니스 (Sobel-based Sharpness)

2. 방사 왜곡 계수 — "0 인 걸 알면서 왜 재나요?"

2.1. 텔레센트릭 렌즈에 왜곡 측정이 왜 필요한가

우리 장비는 텔레센트릭 렌즈를 사용합니다. 이론적으로 텔레센트릭 렌즈의 왜곡 계수는 ≈ 0 이어야 합니다. 그러면 "어차피 0 인 걸 아는데, 왜 굳이 측정하나요?" 라는 질문이 자연스럽게 나옵니다.

저도 이 질문을 스스로에게 던져 봤고, 결론은 이랬습니다.

"이론상 0" 이라는 전제 자체를 실측으로 검증하는 것이 캘리브레이션의 본질이다.

텔레센트릭 렌즈도 제조 편차가 있고, 오래 쓰면 미세하게 틀어집니다. 충격을 받거나 온도 변화가 큰 환경에서는 광학 요소의 정렬이 바뀔 수 있습니다. k1 이 기준값을 벗어나는 순간 이 그 렌즈를 의심해야 할 시점입니다.

즉 이 지표는 "값이 얼마인가" 가 아니라 "임계값을 넘었는가" 를 판정하기 위해 존재합니다. 대부분의 경우 측정값은 1e-9 수준이고 사람이 직접 볼 필요도 없습니다. 그러다 어느 날 1e-5 가 찍히면, 그때 이 지표가 밥값을 합니다. 캘리브레이션 도구의 많은 부분이 "안 쓰이다가 결정적인 순간에 한 번 쓰이는" 식으로 동작합니다. 평소에 값이 0 이라는 것 자체가 도구가 잘 만들어졌다는 증거입니다.

2.2. 계산 과정

1) 이미지 중심 (cx, cy) 기준 각 코너의 반경
   r_detected[i] = sqrt((x[i] - cx)² + (y[i] - cy)²)

2) 재투영 오차에서 구한 projected 점들의 반경
   r_ideal[i]    = sqrt((px[i] - cx)² + (py[i] - cy)²)

3) 반경 방향 편차
   dr[i] = r_detected[i] - r_ideal[i]

4) 최소자승법으로 모델 피팅
   dr = k1 · r³ + k2 · r⁵
   → cv::solve(A, b, x, DECOMP_SVD) 로 [k1, k2] 추정

5) 최대 왜곡량
   dMaxRadialDistortion = max(|dr[i]|)  (pixels)

2.3. 왜 3차, 5차 항만 쓰는가

Brown–Conrady 왜곡 모델의 방사 성분은 이렇게 생겼습니다.

dr = k1·r³ + k2·r⁵ + k3·r⁷ + ...

일반 렌즈 캘리브레이션에서는 k3 까지 쓰기도 합니다. 그런데 텔레센트릭 렌즈에서는 k1, k2 만으로 충분합니다.

기대값이 거의 0 인 영역에서 고차항을 넣으면 오히려 노이즈에 피팅되어 해석이 어려워집니다. 모델의 자유도를 필요 이상으로 늘리면 "아무것도 없는 곳에서 패턴을 찾아내는" 현상이 벌어집니다. 모델은 목적에 맞게 가볍게 유지하는 편이 좋습니다.

2.4. 판정 기준

|k1| < 1e-7   : 왜곡 없음 (정상 텔레센트릭)
|k1| > 1e-5   : 비텔레센트릭 의심 — 렌즈 점검 필요

2.5. 구조체

// STEP 9: 왜곡
double dDistortionK1;
double dDistortionK2;
double dMaxRadialDistortion;  // pixels

3. 영역별 콘트라스트 — Michelson

3.1. 무엇을 보는가

조명과 노출이 9 영역에 균일하게 들어왔는지를 봅니다. 체커보드의 흑/백 타일이 각 영역에서 얼마나 명확하게 구분되는가로 정량화합니다.

3.2. Michelson 콘트라스트

contrast = (V_max - V_min) / (V_max + V_min)

두 밝기 값의 차이를 합으로 정규화한 값입니다. 0 ~ 1 사이로 나오고, 1 에 가까울수록 흑과 백이 명확하게 구분된다 는 뜻입니다. 체커보드는 이 계산에 딱 어울리는 패턴입니다. 각 코너 주변에는 흑/백 타일이 대각선 배치로 놓여 있어서, 작은 패치만 샘플링해도 V_min / V_max 를 뽑아낼 수 있습니다.

3.3. 계산 방식

각 코너 주변 NxN 패치 (예: 20×20 px):

  코너를 중심으로 4분면으로 나눔
  ┌─────┬─────┐
  │  A  │  B  │   체커보드 패턴상
  ├─────┼─────┤   A, D = black,  B, C = white
  │  C  │  D  │   (또는 그 반대)
  └─────┴─────┘

  V_white = (B + C) 평균
  V_black = (A + D) 평균
  contrast = (V_white - V_black) / (V_white + V_black)

영역 인덱스 (r, c) 로 9 영역 소속 판정 → dRegionContrast[9] 평균

3.4. 왜 Michelson 인가

콘트라스트 정의는 여러 가지가 있습니다.

  • Weber: (V_max - V_min) / V_min — 배경이 균일할 때
  • Michelson: (V_max - V_min) / (V_max + V_min) — 주기 패턴에 적합
  • RMS: 픽셀 강도의 표준편차 — 전역 이미지 통계용

체커보드는 주기적인 흑/백 패턴 이므로 Michelson 이 표준입니다. 또한 평균 밝기에 의존하지 않고 정규화되어 있어서, 노출 조건이 다른 이미지끼리도 비교 할 수 있다는 장점이 있습니다. 이 점이 산업 현장에서 특히 중요합니다. 조명 세팅이 미묘하게 바뀌더라도 같은 기준으로 비교할 수 있어야 하거든요.

3.5. 구조체

// STEP 7: 콘트라스트
double dRegionContrast[9];   // 영역별 0 ~ 1
double dContrastMin;
double dContrastMax;

4. 에지 샤프니스 — "포커스는 잘 맞았는가"

4.1. 계산

각 코너 주변의 작은 crop 에서 Sobel 그래디언트의 평균 크기 를 계산합니다.

// 각 코너 위치에서 31 × 31 crop
cv::Sobel(crop, sobelX, CV_64F, 1, 0);
cv::Sobel(crop, sobelY, CV_64F, 0, 1);
cv::magnitude(sobelX, sobelY, gradient);
double sharpness = cv::mean(gradient)[0];

// 영역별 평균 → dRegionSharpness[9]

4.2. 절대값이 아니라 상대값

샤프니스는 mean(|gradient|) 이기 때문에 절대 수치 자체는 조명/노출에 따라 달라집니다. 그래서 우리가 실제로 보는 건 영역 간 상대 편차입니다.

double dSharpnessMin;
double dSharpnessMax;
// 사용 예: (max - min) / avg 가 크면 영역별 포커스 불균일

4.3. 왜 유용한가 — "가장 흔한 보이지 않는 불량"

포커스 문제는 렌즈 캘리브레이션에서 가장 흔한 "보이지 않는 불량" 입니다. 현장 운용자는 이미지가 살짝 흐릿해도 검출이 성공하면 그냥 진행해 버리는 경우가 많습니다. 검출률만 체크하는 시스템이라면 이 상태로 계속 측정이 돌고, 어느 순간 측정값이 누적으로 틀어지기 시작합니다.

샤프니스 지표가 있으면 "검출은 성공했지만 포커스가 일부 영역에서 나쁘다" 는 상황을 잡을 수 있습니다. 유용한 조합 예시는 이런 것들입니다.

  • 중심 샤프니스 대비 모서리가 50% 이하 → 필드 커버리지 문제 (곡률 수차 의심)
  • 전체 샤프니스가 기준값 이하 → 포커스 재조정 필요
  • 특정 변만 샤프니스 급락 → 렌즈 한쪽 오염 또는 기울어진 설치

4.4. 구조체

// STEP 8: 샤프니스
double dRegionSharpness[9];
double dSharpnessMin;
double dSharpnessMax;

5. 전체 12 개 지표 총정리

이 편까지 소개한 지표를 한 표로 정리하면 이렇습니다.

분류 지표 필드
기본 해상도 H / V dScaleH, dScaleV
  격자 회전 각도 dGridAngle
  검출 코너 수 iDetectedCount
균일도 9 영역 스케일 dRegionScaleAvg[9]
  등방성 dIsotropyRatio
  중심-주변 편차 dCenterEdgeDevMax, dCenterEdgeDevAvg
기하 정밀도 재투영 오차 dReprojErrorRMS, dReprojErrorMax
  직교성 dOrthogonalityMean, dOrthogonalityMax
광학 특성 방사 왜곡 k1, k2 dDistortionK1, dDistortionK2
  최대 왜곡량 dMaxRadialDistortion
  영역별 콘트라스트 dRegionContrast[9]
  영역별 샤프니스 dRegionSharpness[9]

체커보드 한 장에서 이 정도를 뽑아낼 수 있다는 건, 결국 코너 좌표에 들어있는 정보 밀도가 그만큼 높다 는 뜻입니다. 우리가 지금까지 그 대부분을 버리고 있었던 거죠.

6. 한 줄 교훈

측정의 목적은 "값을 알기 위해서" 가 아니라 "임계값을 넘었는지 판정하기 위해서" 다.

이 관점이 잡히고 나니, 앞으로 추가할 지표들의 우선순위가 훨씬 명확해졌습니다. "값을 보여주기 위한" 지표가 아니라 "판정을 내리기 위한" 지표 를 먼저 쌓는 거죠.

그런데 이렇게 열심히 지표를 뽑아내도, "어떻게 보여주느냐" 에서 실패하면 현장에서 안 쓰이게 됩니다. 다음 편은 이 12 개 지표를 운용자가 1 초 안에 해석할 수 있도록 만든 대시보드 설계 이야기 입니다.

반응형