[OpenCV] Camera Calibration 2 (카메라 구멍 없이 왜곡 펴기)

사용 환경 : Ubuntu Linux 18.04 LTS, Python 3.7.7
OpenCV Version = 4.2.0.34

참고 블로그 : https://darkpgmr.tistory.com/31

 

카메라 왜곡보정 - 이론 및 실제

저번 카메라 캘리브레이션에 대한 포스팅에 이어 오늘은 카메라 렌즈 왜곡 보정에 대해 이론에서 실제까지 전반적인 내용을 포스팅합니다. 카메라 캘리브레이션 및 왜곡 보정은 영상처리 분야�

darkpgmr.tistory.com

위 블로그의 수학적 모델을 참고해서 코드를 작성했습니다.

좀 더 이해하기 쉽도록 그림으로 그려보겠습니다.

먼저, 미리 보정된 이미지가 들어갈 검은색 이미지를 생성합니다.

distortion image                                           undistortion image

만약 보정된 이미지가 검은 이미지에 들어간다면 아래의 그림과 같이 가장자리가 늘어난 이미지가 생성될 것입니다.

수학적 모델을 참고하여 카메라 Parameter 값을 넣고 계산하면 오른쪽 보정된 이미지의 (xpu, ypu)에 왼쪽 왜곡된 이미지의 어떤 점(xpd, ypd)이 뱉어지고, 그 점에 대한 pixel값을 오른쪽 이미지의 (xpu, ypu)에 넣는 식으로 하다 보면 보정된 이미지가 완성됩니다.

카메라 Parameter (fx, fy, cx, cy, k1, k2, p1, p2)는 아래 프로그램에서 얻은 값들입니다.

1. 프로그램 다운로드
https://darkpgmr.tistory.com/attachment/cfile24.uf@1505523D510E9CBF09DBC3.exe

2. 이미지 촬영
체스 보드 패턴을 인쇄한 후 왜곡을 펴고 싶은 카메라 화각으로 패턴을 여러 각도에서 촬영합니다.

3.
프로그램을 실행한 후 File - New Project에서 프로젝트를 새로 생성하고 패턴의 가로, 세로 개수를 입력합니다.
(참고로 이 프로그램에서는 가로/세로 패턴 개수가 각각 짝수/홀수 이거나 홀수/짝수인 이미지만 가능합니다.)

4.
Object Detection - Add Image에서 촬영한 이미지 파일들을 추가한 후 Object Detection - Detect All을 누르면 각 사진마다 체스 보드 패턴을 찾아 줍니다.

5.
적어도 세 개의 이미지 이상에서 패턴 찾기를 성공했다면 Calibration - calibrate 을 누르면 아래와 같은 값을 얻을 수 있습니다.

여기에서 fx, fy = Focal length의 첫번째, 두번째 값, cx, cy = Principal point의 첫번째, 두번째 값, k1, k2, p1, p2 = Distortion, k3 == skew_c == 0입니다.

import cv2
import numpy as np
import matplotlib.pyplot as plt
from copy import deepcopy

# 프로그램에서 얻은 camera parameters
fx = 459.732
fy = 455.991
cx = 638.651
cy = 446.503
k1 = -0.160827
k2 = 0.020127
k3 = 0
p1 = 0.002496
p2 = -0.001515
skew_c = 0

file = 'C:\\Users\\bit\\Desktop\\chessboard\\chessboard309.jpg'

img_d = cv2.imread(file, cv2.IMREAD_GRAYSCALE)  ##1280-720
h, w = (img_d.shape[0], img_d.shape[1])
# cv2.imshow('ff', img_d)
img_u = np.zeros((h, w, 1), np.int8)  ## 검은색 이미지 생성
flag = 0

# img_u의 한 점 (xpu, ypu) normalize
for ypu in range(h): 
    for xpu in range(w):
        ynu = (ypu - cy)/fy
        xnu = (xpu - cx) / fx - (skew_c * ynu)
        ru = xnu**2 + ynu**2
        xnd = (1 + k1 * ru + k2 * ru**2 + k3 * ru**3) * xnu + 2 * p1 * xnu * ynu + p2 * (ru + 2 * xnu**2)
        ynd = (1 + k1 * ru + k2 * ru**2 + k3 * ru**3) * ynu + p1 * (ru + 2 * ynu**2) + 2 * p2 * xnu * ynu
        xpd = int(fx * (xnd + skew_c * ynd) + cx) 
        ypd = int(fy * ynd + cy)
        img_u[ypu][xpu] = img_d[ypd][xpd]
        cv2.imshow('black', img_u)
        key = cv2.waitKey(25)
        if key == 27:  ## ESC 누르면 종료
            flag = 1
            break
    if flag == 1:
        break

print(img_u)
cv2.imshow('dd', img_u)
cv2.waitKey(0)

만약 전체 사진(잘린 바깥 영역을 포함한)을 보고 싶으시다면, 왜곡된 이미지의 (0, 0), (w, 0), (0, h)에 대응하는 xpu, ypu값을 구한 후 이미지 크기를 w', h'으로 for문을 돌리시면 됩니다.

그런데 이렇게 하기 위해서는 xpd, ypd를 구하는 식에서 xpu, ypu구하는 식으로 거꾸로 올라가는 식을 작성해야 하기 때문에 이 부분은 다음 글에서 다시 해보도록 하겠습니다.

'OpenCV > OpenCV' 카테고리의 다른 글

[OpenCV]차선 인식  (2) 2020.05.26
[OpenCV]Camera Calibration(카메라 왜곡 펴기)  (0) 2020.05.03