소스 뷰어
영상 위핑¶
- 영상 위핑은 하나의 영상에서 비선형적인 특정한 규칙에 따라 입력 영상을 재추출(resampling)하여 영상의 형태를 변형시키는 기술이다. 이기술은 NASA에서 인공위성이나 우주선으로부터 전송된 영상이 렌즈의 변형이나 신호의 왜곡 등으로 인해 일그러지는 경우가 많아서 이를 복원하는 용도로 처음 사용되었다.
- 위핑은 영상을 여러 다른 방향으로 늘이거나 크기를 조절하는 기법으로 순수한 스케일링과 달리 크기 변화의 정도가 영상 전체에 대해 균일하지 않는 것이 특징이다. 특히 고무관 위에 영상이 있는 것과 같이 임의의 형태로 늘이는 것과 같은 효과를 낸다는 의미에서 고무 시트 변환(Rubber sheet Transform)이라고도 한다
- 영상 위핑 기술은 랜즈 왜곡 보정, 스테레오 영상 정합, 파노라마 영상 합셩 등에 사용될수 있다
- OpenCV에서 제공하는 함수 중에 카메라 렌즈 등에 의해 발생하는 방사 왜곡이나 핀쿠션 왜곡을 보정하기 위한 cv2.calibrateCamera(), cv2.initUndistortReactifyMap() 등의 함수가 있다.
- 일반적으로 카메라로 찍은 영상은 여러가지 이유에 의해서 왜곡된다. 여기서 왜곡되는 요인은 카메라 외부 파라미터와 내부 파라미터로 구분할 수 있다.
- 카메라는 3차원 실세계 영상을 2차원 평면 영상으로 맺히게 하기 떄문에 기하학적인 왜곡이 발생하게 된다. 이것은 카메라 외부 파라미터에 의한 왜곡에 해당하며, 대표적으로 원근 투시 왜곡이 있다. 또한, 갭쳐된 영상은 렌즈, 초점거리, 렌즈와 이미지 센서가 이루는 각도 등과 같은 카메라 내부의 기구적인 부분에 의해서 상당한 영향을 받는다. 이러한 요인을 내부 파라미터 요인이라 한다.
- 영상 좌표로부터 실세계의 3차원 좌표를 계산하거나 실세계의 3차원 좌표를 영상에 투영된 위치로 계산해야 하는 경우가 이다. 이때 카메라 내부 요인을 제거해야만 보다 정확한 좌표의 계산이 가능하다. 여기서 내부 요인의 파라미터 값으 구하는 과정을 카메라 캘리브레이션(camear calibration)이라 한다
캘리브레이션을 수행하는 카메라의 왜곡을 보정¶
import numpy as np, cv2, pickle
def findCorners(image, bSize):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCorners(gray, bSize) # 코너 검출
# 코너가 검출 될 때
if ret: # 부화소(subpixel) 위치를 찾아서 코너 좌표 개선
criteria = (cv2.TermCriteria_MAX_ITER + cv2.TermCriteria_EPS, 30, 0.1)
cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
return ret, np.array(corners, np.float32), image
def show_image(file, bSize, result):
cv2.drawChessboardCorners(result[2], bSize, result[1], result[0]) # 코너 표시
cv2.imshow(file, result[2])
# 캘러브레이션 수행 및 보정
def calibrate_correct(objectPoints, imagePoints, image):
size = image.shape[1::-1] # 형태와 크기는 역순
ret = cv2.calibrateCamera(objectPoints, imagePoints, size, None, None)
newSacle, roi = cv2.getOptimalNewCameraMatrix(ret[1], ret[2], size, 1) # 재수정
undistorted = cv2.undistort(image, ret[1], ret[2], None, newSacle) # 왜곡 부정 수행
x, y, w, h = roi
return ret, undistorted, undistorted[y:y + h, x:x + w] # 왜곡 영역 제거
bSize = (8, 7) # 체스판 코너 개수(가로, 세로)
points = [(x, y, 0) for y in range(bSize[1]) for x in range(bSize[0])]
points = np.array(points, np.float32) # 실수형 변경
- 영상의 코너 좌표에 대응되는 실세계의 3차원 좌표들(points)을 bsize 개수(8x7)만큼 3 원소를 갖는다. 이중 반복문을 순회하며 56행 3열의 행렬을 구성
코너 좌표 및 실세계 3차원 좌표¶
files = ["chessboard_01", "chessboard_02", "chessboard_03"] # 체스보드 파일명
images = [cv2.imread('img/%s.jpg' % file, 1) for file in files]
results = [findCorners(image, bSize) for image in images]
imagePoints = [result[1] for result in results if result[0]]
objectPoints = [points] * len(imagePoints)
[show_image(f, bSize, result) for f, result in zip(files, results) if result[0]]
image = cv2.imread("img/chessboard_05.jpg", cv2.IMREAD_COLOR)
if image is None: raise Exception("영상 파일 읽기 에러")
ret, undistorted, correct_img = calibrate_correct(objectPoints, imagePoints, image)
print("RMS error reported by cv2.calibrateCamera:", ret[0])
print("cameraMatrix =\n%s" % ret[1])
print("distCoeffs =\n%s" % ret[2])
print("rvecs =\n%s" % np.reshape(ret[3], (3,-1)))
print("tvecs =\n%s" % np.reshape(ret[4], (3,-1)))
with open('camera_calibration.txt', 'wb') as f:
pickle.dump(ret, f)
cv2.imshow("original", image)
cv2.imshow("undistorted", undistorted)
cv2.imshow("cropUndistorted", correct_img)
cv2.waitKey(0)
- 실행 결과에서 files 리스트의 원소 개수만큼 체스보드 영상을 입력받아서 코너 좌표들을 찾는다. 찾은 코너 좌표들과 이에 대응하는 3차원 실세계 좌표를 지정한다. 그런 후에 카메라 캘리브레이션을 수행해 왜곡 영상을 보정한다