카메라 교정
카메라 왜곡
- 카메라 렌즈의 효과로 생기는 왜곡: 볼록하면 배럴(barrel), 오목하면 핀쿠션(pincushion)
- 교정 방법
- 교정 패턴(Calibration pattern): 다양한 관점에서 알려진 차원의 패턴의 이미지를 여러 장 캡처 (체커보드 패턴, 원형 패턴 등)
- 기하학적 단서(Geometric clues): 직선과 소실점과 같은 기하학적 단서를 사용
- 딥러닝 기반: 다양한 렌즈의 이미지를 딥러닝으로 학습시켜 보정 (한 장만 있어도 사용할 수 있음, 학습된 모델이 필요)
체커보드 패턴 다운로드 받기
체커보드 패턴의 교차점 찾기
src = cv.imread('fisheye01.jpg')
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
patternSize = 7, 10
flags = (cv.CALIB_CB_ADAPTIVE_THRESH | cv.CALIB_CB_FAST_CHECK |
cv.CALIB_CB_NORMALIZE_IMAGE)
retval, corners = cv.findChessboardCorners(gray, patternSize, flags=flags)
교차점을 더 정확하게
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)
교정 준비
import glob
filenames = glob.glob('fisheye*.jpg')
objpoints = []
imgpoints = []
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)
corners2 = cv.cornerSubPix(gray, corners, (11,11),(-1,-1), criteria)
imgpoints.append(corners2)
카메라 교정
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)
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
scale = 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)