0biglife.

[자율주행] 카메라 이미지의 실제 — 왜곡 이미지, 보정, 그리고 잃는 것들

자율주행/센서

· 2026-09-02

[자율주행] 카메라 이미지의 실제 — 왜곡 이미지, 보정, 그리고 잃는 것들

본문의 점선 밑줄 친 전문용어를 누르면 설명이 열립니다.

센서 캘리브레이션에서 를 다루며 "렌즈가 직선을 휘게 만드는 왜곡 계수"를 한 줄로 지나갔다. 실무에서 그 한 줄이 만드는 사고가 꽤 많아서, 여기서 제대로 판다.

출발점은 이 문장이다. 자율주행의 이미지 한 장은 세상의 투명한 기록이 아니다. 렌즈가 휘게 만들고, 가 여러 번 가공하고, 센서가 줄 단위로 시간차를 두고 읽고, 코덱이 눌러서 만든 결과물이다. 이 사슬의 각 단계가 좌표와 픽셀값에 흔적을 남긴다.

이미지가 만들어지는 사슬

빛 → 렌즈(왜곡) → 이미지 센서(베이어·롤링셔터) → ISP → 인코더 → 파일
      ↑ 기하가 휜다        ↑ 시간이 어긋난다      ↑ 색·톤이 바뀐다  ↑ 질감이 눌린다

각 화살표가 데이터 문제를 하나씩 만든다. 순서대로 본다.

핀홀 모델과 K

이상적인 카메라는 3D 점을 이렇게 픽셀로 옮긴다.

[u]     [fx  0  cx] [X/Z]
[v]  =  [ 0 fy  cy] [Y/Z]
[1]     [ 0  0   1] [ 1 ]

fx, fy 는 초점거리, cx, cy 는 주점(광축이 센서를 뚫는 지점)이다. 이 3×3 이 카메라 행렬 K 다.

여기서 자주 놓치는 것 하나. K 는 특정 해상도에 묶여 있다. 1920×1080 에서 잰 K 를 960×540 이미지에 그대로 쓰면 좌표가 두 배로 틀린다. 학습 전처리에서 리사이즈·크롭을 넣고 K 를 안 고치는 건 흔한 버그다. 그래서 camera_info 에는 K 가 유효한 width, height 가 반드시 함께 들어간다.

왜곡 — 직선이 휜다

실제 렌즈는 직선을 직선으로 찍지 않는다. 두 종류가 섞인다.

  • 방사 왜곡(radial) — 중심에서 멀수록 휜다. 바깥으로 부풀면 배럴(barrel), 안으로 오므라들면 핀쿠션(pincushion). 계수 k1, k2, k3.
  • 접선 왜곡(tangential) — 렌즈와 이미지 센서가 완벽히 평행하지 않아 생긴다. 계수 p1, p2.
   격자를 찍으면
   ┌──────────┐        ╭──────────╮
   │ ┼ ┼ ┼ ┼  │        │╭ ┼ ┼ ┼ ╮│
   │ ┼ ┼ ┼ ┼  │   →    ││ ┼ ┼ ┼ ││     바깥으로 부푼다 = 배럴 왜곡
   │ ┼ ┼ ┼ ┼  │        │╰ ┼ ┼ ┼ ╯│     (광각일수록 심하다)
   └──────────┘        ╰──────────╯
     이상적              실제

모델은 화각에 따라 갈린다.

모델계수쓰는 곳
plumb_bob / rad-tank1,k2,p1,p2,k3일반 렌즈 (~90°)
rational_polynomial항이 더 많음광각에서 정밀도를 더 낼 때
equidistant / Kannala-Brandtk1~k4 (120°~200°)
double sphere, MEI별도 파라미터초광각·전방위

화각이 120°를 넘어가면 핀홀 모델로는 표현이 안 된다. 수학적으로도 그렇다 — 180° 어안을 평면에 펴려면 가장자리가 무한대로 늘어난다.

왜곡 보정이 공짜가 아닌 이유

"그럼 다 펴서 쓰면 되지 않나"가 자연스러운 반응이다. 대가가 셋 있다.

하나. 화각을 잃거나 검은 여백이 생긴다. 휜 상을 펴면 직사각형 밖으로 나가는 영역이 생긴다. 잘라내면 시야가 좁아지고, 채우면 검은 테두리가 학습 데이터에 들어온다.

둘. 리샘플링 손실. 보정은 픽셀을 새 위치로 옮기며 보간하는 일이라 미세 질감이 뭉개진다. 원거리 소형 객체 — 100m 밖 신호등, 작은 표지판 — 에서 이게 직접 성능으로 온다.

셋. 은 애초에 못 편다. AVM 처럼 어안 4개를 쓰는 파이프라인에서는 "핀홀로 편 이미지"라는 선택지 자체가 없다. 대신 지면 평면으로 투영 를 만든다.

그래서 팀에 따라 선택이 갈린다.

그대로보정 후 사용
화질원본 유지보간 손실
화각전부 유지잘리거나 여백
기하 계산매번 K, D 를 다 써야K 만으로 단순
사람 검수가장자리가 휘어 어색직관적
어안유일한 선택불가능

정답은 없다. 다만 일관성이 정확도보다 중요하다. 학습·라벨링·추론이 같은 이미지 공간에 있어야 한다. 배치마다 다르면 조용히 섞인다.

조용히 틀리는 지점

가장 자주 나오는 사고 둘.

라이다 투영에서 D 를 빼먹는다

라이다 점을 이미지 위에 겹쳐 그릴 때, K 만 적용하고 왜곡 계수 D 를 안 쓰면 화면 중앙은 맞고 가장자리만 밀린다. 그리고 사람은 보통 중앙을 보고 " 잘 맞네" 하고 넘어간다.

1import cv2, numpy as np
2
3pts_cam = (T_lidar_to_cam @ pts_h.T).T[:, :3]     # 카메라 좌표계로
4pts_cam = pts_cam[pts_cam[:, 2] > 0.1]            # 뒤쪽 점 제거 (필수)
5
6# 왜곡 이미지 위에 투영: rvec/tvec 은 0 (이미 카메라 좌표계),
7# K 와 D 를 함께 넘겨야 가장자리가 맞는다
8uv, _ = cv2.projectPoints(
9    pts_cam, np.zeros(3), np.zeros(3), K, D
10)
11
12# 보정된 이미지 위에 투영할 거라면 D 는 0 으로,
13# 대신 이미지를 undistort 해 둔 상태여야 한다
14# uv, _ = cv2.projectPoints(pts_cam, np.zeros(3), np.zeros(3), K_new, None)

z > 0 필터도 빼먹기 쉽다. 카메라 뒤쪽 점을 투영하면 화면 안에 유령처럼 찍힌다.

라벨이 어느 이미지 공간인지 안 적혀 있다

위에 그린 2D 박스와 보정 이미지 위에 그린 박스는 좌표가 다르다. 라벨링 도구를 바꿨거나, 업체가 달라졌거나, 전처리에 리사이즈가 추가되면 두 종류가 한 테이블에 섞인다. 모델은 에러 없이 그냥 조금 나빠진다.

어노테이션 레코드에 명시적으로 적는다.

image_space:      distorted | undistorted
camera_info_ref:  calib_v7        # 어떤 K, D 기준
resolution:       [1920, 1080]
frame_id:         camera_front

정류(rectification)는 다른 일이다

과 자주 혼동되지만 다르다.

  • 왜곡 보정 — 렌즈의 휨을 편다. 카메라 한 대의 문제.
  • — 스테레오 두 카메라를 같은 평면에 정렬해, 같은 물체가 양쪽 이미지의 같은 가로줄에 오게 만든다. 두 대의 관계 문제.

정류를 하면 스테레오 매칭이 2차원 탐색에서 1차원 탐색으로 줄어 깊이 추정이 훨씬 싸진다. ROS CameraInfoR(정류 회전)과 P(정류 후 투영 행렬)가 이 정보를 담는다. 모노 카메라라면 R 은 단위행렬이고 PK 에 0 열을 붙인 형태다.

롤링 셔터 — 한 장이 한 순간이 아니다

대부분의 CMOS 센서는 이미지를 한 번에 찍지 않고 위에서 아래로 줄 단위로 읽는다. 즉 한 장의 이미지가 여러 시각에 걸쳐 만들어진다. 회전 라이다의 과 정확히 같은 문제다.

결과:

  • 고속에서 세로 기둥이 기울어 보인다.
  • 라이다 점을 투영하면 화면 위쪽과 아래쪽이 서로 다른 만큼 어긋난다. 전체를 한 시각으로 가정한 융합은 여기서 잔차를 남긴다.

정밀한 융합에서는 줄별 시각을 고려한 보정이 필요하다. 글로벌 셔터 센서는 이 문제가 없지만 비싸다. 어느 쪽인지 로그 메타데이터에 적어둬야 나중에 오차의 출처를 설명할 수 있다.

ISP — 같은 세상이 다른 픽셀로

파일에 담긴 픽셀은 센서가 낸 raw 신호가 아니다. 그 사이에 (Image Signal Processor) 사슬이 있다.

베이어 raw → 디모자이킹 → 화이트밸런스 → 톤매핑/HDR → 노이즈 제거 → 샤프닝 → 인코딩

데이터를 다루는 사람에게 중요한 건 하나다. ISP 설정이 바뀌면 같은 세상이 다른 픽셀로 찍힌다. 차량 소프트웨어 업데이트로 톤매핑이 바뀌면, 그 전후 데이터로 학습한 모델의 성능이 설명 없이 갈린다. 그리고 이건 코드 diff 에도 데이터 스키마에도 안 나타난다.

그래서 ISP 버전과 주요 설정을 로그 메타데이터에 박는다. 버전과 같은 성격의 정보다.

관련해서 자율주행 특유의 문제 둘.

  • HDR — 터널 출구는 명암비가 극단적이다. 어디를 날리고 어디를 살릴지가 ISP 설정에서 정해지고, 그 결정이 인지 성능을 좌우한다.
  • LED 플리커 — LED 신호등과 전광판은 사람 눈에 안 보이는 주기로 깜빡인다. 노출이 짧으면 꺼진 순간이 찍힌다. 그래서 신호등 인지용 카메라는 노출을 따로 잡거나 플리커 완화 센서를 쓴다.

로그에 영상을 어떻게 담을 것인가

카메라 8대 × 30fps × 2MP 를 raw 로 담으면 시간당 수 TB 다. 현실적인 선택은 셋이다.

용량임의 프레임 접근쓰는 곳
Raw쉬움저조도 연구, 검증
JPEG10~20× 작음쉬움 (프레임 독립)라벨링, 학습
/26550~100× 작음비쌈 (부터 디코딩)장기 아카이브

실무 절충은 대개 이렇다. 아카이브는 H.264, 학습·라벨링용으로 뽑은 키프레임은 JPEG. 그리고 압축 아티팩트가 성능에 미치는 영향을 한 번은 측정해두는 게 좋다 — 특히 원거리 신호등처럼 몇 픽셀짜리 객체에서.

MCAP 에 담을 때 챙길 것:

  • 이미 압축된 페이로드에 압축을 또 거는 건 이득이 거의 없다.
  • 이미지 옆에 camera_info 를 반드시 함께 기록한다. 정적인 값이라도 로그마다 한 번은. 이미지만 있는 로그는 3년 뒤에 기하 정보를 못 살린다.
  • 이미지의 캡처 시각이 페이로드 안에 있어야 한다. 으로 융합하면 어긋난다.

데이터 엔지니어의 시선

  • KD해상도·전처리와 한 몸이다. 리사이즈하면 같이 고친다.
  • 라벨에는 이미지 공간과 버전을 적는다. 안 적으면 배치가 섞였을 때 알 방법이 없다.
  • camera_info 없는 이미지 로그는 반쪽짜리다.
  • ISP·셔터 방식·노출 정책은 메타데이터로 남긴다. 성능 변화의 원인이 여기 있는 경우가 생각보다 많다.
  • 압축 정책은 아카이브와 학습을 나눠 정한다.

정리

  • 이미지는 렌즈·센서·ISP·코덱을 거친 가공물이다. 각 단계가 기하와 픽셀값에 흔적을 남긴다.
  • 왜곡에는 방사·접선 두 종류가 있고, 화각이 커지면 핀홀 모델로는 표현이 안 된다. 은 별도 모델(등거리·Kannala-Brandt)을 쓴다.
  • 은 공짜가 아니다 — 화각·질감을 잃고, 어안은 아예 못 편다. 무엇을 고르든 일관성이 우선이다.
  • 라이다 투영에서 D 를 빼먹으면 가장자리만 조용히 밀린다.
  • 는 왜곡 보정과 다른 일이다 — 스테레오 두 이미지의 정렬.
  • 때문에 한 장이 한 순간이 아니다. ISP 변경은 같은 세상을 다른 픽셀로 만든다. 둘 다 메타데이터로 남긴다.

이어서 볼 글: 좌표계와 변환의 근간은 센서 캘리브레이션, 이 이미지들이 어느 태스크에 쓰이는지는 인지 태스크 지도, 로그에 담는 규약은 MCAP 완전 정복에서 이어진다.

Index

이미지가 만들어지는 사슬핀홀 모델과 K왜곡 — 직선이 휜다왜곡 보정이 공짜가 아닌 이유조용히 틀리는 지점라이다 투영에서 D 를 빼먹는다라벨이 어느 이미지 공간인지 안 적혀 있다정류(rectification)는 다른 일이다롤링 셔터 — 한 장이 한 순간이 아니다ISP — 같은 세상이 다른 픽셀로로그에 영상을 어떻게 담을 것인가데이터 엔지니어의 시선정리