Skip to main content

카메라 교정

카메라 왜곡

  • 카메라 렌즈의 효과로 생기는 왜곡: 볼록하면 배럴(barrel), 오목하면 핀쿠션(pincushion)
  • 교정 방법
    • 교정 패턴(Calibration pattern): 다양한 관점에서 알려진 차원의 패턴의 이미지를 여러 장 캡처 (체커보드 패턴, 원형 패턴 등)
    • 기하학적 단서(Geometric clues): 직선과 소실점과 같은 기하학적 단서를 사용
    • 딥러닝 기반: 다양한 렌즈의 이미지를 딥러닝으로 학습시켜 보정 (한 장만 있어도 사용할 수 있음, 학습된 모델이 필요)

체커보드 패턴 다운로드 받기

체커보드 패턴의 교차점 찾기

src = cv.imread('fisheye01.jpg')
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) # 흑백으로
patternSize = 7, 10 # + 모양 교차점의 개수(7행 10열)
flags = (cv.CALIB_CB_ADAPTIVE_THRESH | cv.CALIB_CB_FAST_CHECK |
cv.CALIB_CB_NORMALIZE_IMAGE)
# 교차 찾기(찾는데 성공하면 retval == True)
retval, corners = cv.findChessboardCorners(gray, patternSize, flags=flags)

교차점을 더 정확하게

# 11x11 범위에서 더 정확하게 찾기. (-1, -1)은 제외하는 범위가 없다는 뜻.
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)
corners2 = cv.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria)
# 찾은 교차점을 원본 이미지에 추가
img_corner = cv.drawChessboardCorners(src, patternSize, corners2, retval)

교정 준비

# fisheye 로 시작하는 jpg 파일 모두 찾기 (*은 "아무 글자들"을 뜻함)
import glob
filenames = glob.glob('fisheye*.jpg')

# 좌표들
objpoints = [] # 3차원 좌표
imgpoints = [] # 2차원 좌표
objp = np.zeros((1, patternSize[0] * patternSize[1], 3), np.float32)
objp[0,:,:2] = np.mgrid[0:patternSize[0], 0:patternSize[1]].T.reshape(-1, 2)

모든 이미지에서 교차점 좌표 찾기

for fname in filenames: # 모든 파일에 대해
gray = cv.imread(fname, cv.IMREAD_GRAYSCALE)
retval, corners = cv.findChessboardCorners(gray, patternSize,flags=flags)
if retval: # 교차점을 찾았으면
objpoints.append(objp) # 3차원 좌표 추가
corners2 = cv.cornerSubPix(gray, corners, (11,11),(-1,-1), criteria)
imgpoints.append(corners2) # 2차원 좌표 추가

카메라 교정

ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(
objpoints, imgpoints, gray.shape[::-1], None, None)
  • mtx: 카메라에서 3차원 이미지를 2차원으로 만드는 행렬
  • dist: 렌즈의 왜곡
  • rvecs: 카메라의 회전
  • tvecs: 카메라의 수평이동

이미지에서 렌즈 왜곡 교정

src = cv.imread('fisheye01.jpg') # 이미지 열기
# 왜곡 제거를 위한 교정치 얻기
h,w = gray.shape[:2]
newcameramtx, roi = cv.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))
dst = cv.undistort(src, mtx, dist, None, newcameramtx) # 교정치 적용

참고 자료

리매핑 Remapping

  • 원래 이미지의 점들을 다른 좌표로 옮겨주는 것
    src = cv.imread('basketball.webp')
    height, width = src.shape[:2] # 이미지의 높이, 너비
    mapy, mapx = np.indices((height, width),dtype=np.float32) # 원래의 좌표
    mapx = width - mapx - 1 # 좌우 대칭
    mapy = height - mapy - 1 # 상하 대칭
    dst = cv.remap(src, mapx, mapy, cv.INTER_LINEAR) # 점들의 좌표를 바꿈
    # 보간 방법은 앞 장을 참고

극좌표계로 변환

mapy, mapx = np.indices((height, width), dtype=np.float32) # 원래의 좌표
# -1~1로 정규화된 좌표로 변경
center_x, center_y = width // 2, height // 2 # 중심 좌표 설정
norm_mapx = (mapx - center_x) / (width // 2)
norm_mapy = (mapy - center_y) / (height // 2)
r, theta = cv.cartToPolar(norm_mapx, norm_mapy) # 극좌표계로 변환

볼록 오목 변환

exp = 1.5 # 볼록, 오목 지수 (오목 < 1 < 볼록)
scale = 1 # 적용 영역 반지름 (0 ~ 1)
r[r< scale] = r[r<scale] ** exp # 왜곡 영역만 중심확대/축소 지수 적용
norm_mapx, norm_mapy = cv.polarToCart(r, theta) # 직교좌표계로 변환
# 정규화된 좌표를 이미지 좌표로 변경
mapx = norm_mapx * (width // 2) + center_x
mapy = norm_mapy * (height // 2) + center_y
dst = cv.remap(src, mapx, mapy, cv.INTER_LINEAR)