[자율주행] 카메라 이미지의 실제 — 왜곡 이미지, 보정, 그리고 잃는 것들
자율주행/센서
· 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-tan | k1,k2,p1,p2,k3 | 일반 렌즈 (~90°) |
rational_polynomial | 항이 더 많음 | 광각에서 정밀도를 더 낼 때 |
equidistant / Kannala-Brandt | k1~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 CameraInfo 의 R(정류 회전)과 P(정류 후 투영 행렬)가 이 정보를 담는다. 모노 카메라라면 R 은 단위행렬이고 P 는 K 에 0 열을 붙인 형태다.
롤링 셔터 — 한 장이 한 순간이 아니다
대부분의 CMOS 센서는 이미지를 한 번에 찍지 않고 위에서 아래로 줄 단위로 읽는다. 즉 한 장의 이미지가 여러 시각에 걸쳐 만들어진다. 회전 라이다의 과 정확히 같은 문제다.
결과:
- 고속에서 세로 기둥이 기울어 보인다.
- 라이다 점을 투영하면 화면 위쪽과 아래쪽이 서로 다른 만큼 어긋난다. 전체를 한 시각으로 가정한 융합은 여기서 잔차를 남긴다.
정밀한 융합에서는 줄별 시각을 고려한 보정이 필요하다. 글로벌 셔터 센서는 이 문제가 없지만 비싸다. 어느 쪽인지 로그 메타데이터에 적어둬야 나중에 오차의 출처를 설명할 수 있다.
ISP — 같은 세상이 다른 픽셀로
파일에 담긴 픽셀은 센서가 낸 raw 신호가 아니다. 그 사이에 (Image Signal Processor) 사슬이 있다.
베이어 raw → 디모자이킹 → 화이트밸런스 → 톤매핑/HDR → 노이즈 제거 → 샤프닝 → 인코딩
데이터를 다루는 사람에게 중요한 건 하나다. ISP 설정이 바뀌면 같은 세상이 다른 픽셀로 찍힌다. 차량 소프트웨어 업데이트로 톤매핑이 바뀌면, 그 전후 데이터로 학습한 모델의 성능이 설명 없이 갈린다. 그리고 이건 코드 diff 에도 데이터 스키마에도 안 나타난다.
그래서 ISP 버전과 주요 설정을 로그 메타데이터에 박는다. 버전과 같은 성격의 정보다.
관련해서 자율주행 특유의 문제 둘.
- HDR — 터널 출구는 명암비가 극단적이다. 어디를 날리고 어디를 살릴지가 ISP 설정에서 정해지고, 그 결정이 인지 성능을 좌우한다.
- LED 플리커 — LED 신호등과 전광판은 사람 눈에 안 보이는 주기로 깜빡인다. 노출이 짧으면 꺼진 순간이 찍힌다. 그래서 신호등 인지용 카메라는 노출을 따로 잡거나 플리커 완화 센서를 쓴다.
로그에 영상을 어떻게 담을 것인가
카메라 8대 × 30fps × 2MP 를 raw 로 담으면 시간당 수 TB 다. 현실적인 선택은 셋이다.
| 용량 | 임의 프레임 접근 | 쓰는 곳 | |
|---|---|---|---|
| Raw | 1× | 쉬움 | 저조도 연구, 검증 |
| JPEG | 10~20× 작음 | 쉬움 (프레임 독립) | 라벨링, 학습 |
| /265 | 50~100× 작음 | 비쌈 (부터 디코딩) | 장기 아카이브 |
실무 절충은 대개 이렇다. 아카이브는 H.264, 학습·라벨링용으로 뽑은 키프레임은 JPEG. 그리고 압축 아티팩트가 성능에 미치는 영향을 한 번은 측정해두는 게 좋다 — 특히 원거리 신호등처럼 몇 픽셀짜리 객체에서.
MCAP 에 담을 때 챙길 것:
- 이미 압축된 페이로드에 압축을 또 거는 건 이득이 거의 없다.
- 이미지 옆에
camera_info를 반드시 함께 기록한다. 정적인 값이라도 로그마다 한 번은. 이미지만 있는 로그는 3년 뒤에 기하 정보를 못 살린다. - 이미지의 캡처 시각이 페이로드 안에 있어야 한다. 으로 융합하면 어긋난다.
데이터 엔지니어의 시선
K와D는 해상도·전처리와 한 몸이다. 리사이즈하면 같이 고친다.- 라벨에는 이미지 공간과 버전을 적는다. 안 적으면 배치가 섞였을 때 알 방법이 없다.
camera_info없는 이미지 로그는 반쪽짜리다.- ISP·셔터 방식·노출 정책은 메타데이터로 남긴다. 성능 변화의 원인이 여기 있는 경우가 생각보다 많다.
- 압축 정책은 아카이브와 학습을 나눠 정한다.
정리
- 이미지는 렌즈·센서·ISP·코덱을 거친 가공물이다. 각 단계가 기하와 픽셀값에 흔적을 남긴다.
- 왜곡에는 방사·접선 두 종류가 있고, 화각이 커지면 핀홀 모델로는 표현이 안 된다. 은 별도 모델(등거리·Kannala-Brandt)을 쓴다.
- 은 공짜가 아니다 — 화각·질감을 잃고, 어안은 아예 못 편다. 무엇을 고르든 일관성이 우선이다.
- 라이다 투영에서
D를 빼먹으면 가장자리만 조용히 밀린다. - 는 왜곡 보정과 다른 일이다 — 스테레오 두 이미지의 정렬.
- 때문에 한 장이 한 순간이 아니다. ISP 변경은 같은 세상을 다른 픽셀로 만든다. 둘 다 메타데이터로 남긴다.
이어서 볼 글: 좌표계와 변환의 근간은 센서 캘리브레이션, 이 이미지들이 어느 태스크에 쓰이는지는 인지 태스크 지도, 로그에 담는 규약은 MCAP 완전 정복에서 이어진다.