본문 바로가기
Opencv 공부

realsense camera calibration 캘리브레이션

by 바위폭주 2023. 3. 28.
728x90
반응형

캘리브레이션이란 우리가 실제 눈으로 보는 세상은 3차원이다. 이것을 카메라로 찍으면 2차원의 이미지로 변하게 된다. 이때, 3차원의 점들이 이미지 상에서 어디에 맺히는지는 카메라의 위치 및 방향에 의해 결정된다. 하지만 실제 이미지는 카메라 내부의 기구적인 부분에 의해서 크게 영향을 받는다. 3차원 점들이 영상에 투영된 위치를 구하거나 역으로 영상좌표로부터 3차원 공간좌표를 복원할 때에는 이러한 내부 요인을 제거해야만 정확하게 계산이 가능하다. 즉 이러한 내부 요인의 파라미터 값을 구하는 과정을 캘리브레이션이라 한다.

이번 글에서는 realsense d435, d435i, l515의 내부파라미터를 구하는 과정을 기술하였다.

import cv2
import pyrealsense2 as rs
import numpy as np

def main():
    # pipeline_1 = rs.pipeline()
    # config_1 = rs.config()
    # config_1.enable_device('831612073906')
    # config_1.enable_stream(rs.stream.color, 1280, 720, rs.format.bgr8, 30)
    # pipeline_1.start(config_1)

    # pipeline_2 = rs.pipeline()
    # config_2 = rs.config()
    # config_2.enable_device('f1270272')
    # config_2.enable_stream(rs.stream.color, 1280, 720, rs.format.bgr8, 30)
    # pipeline_2.start(config_2)
    pipeline_3 = rs.pipeline()
    config_3 = rs.config()
    config_3.enable_device('233722071891')
    config_3.enable_stream(rs.stream.color, 1280, 720, rs.format.bgr8, 30)
    pipeline_3.start(config_3)

    i=0
    while True:
            # frames_2 = pipeline_2.wait_for_frames()
            # color_frame_2 = frames_2.get_color_frame()
            # color_image_2 = np.asanyarray(color_frame_2.get_data())
            # image_l515 = color_image_2

            frames_3 = pipeline_3.wait_for_frames()
            color_frame_3 = frames_3.get_color_frame()
            color_image_3 = np.asanyarray(color_frame_3.get_data())
            image_d435i = color_image_3

            # frames_1 = pipeline_1.wait_for_frames()
            # color_frame_1 = frames_1.get_color_frame()
            # color_image_1 = np.asanyarray(color_frame_1.get_data())
            # image_d435 = color_image_1
            cv2.imshow('l515', image_d435i)
            k = cv2.waitKey(1)
            if ((k%256) == 32):
                # cv2.cvtColor()
                image_name = "/home/jaewoong/opencv_calibration/d435i_images(1280)/image_d435i_" + "{}.jpg".format(i)
                cv2.imwrite(image_name, image_d435i)
                print("save the image name{}".format(image_name))
                i = i + 1
            
    cv2.destroyAllWindows()

 
if __name__ == "__main__":
    main()

3개의 카메라를 한개씩 start하여 스페이스바를 누를 때마다 사진이 저장되는 코드이다.

위에서는 해상도가 1280, 720이지만 depth가 지원하는 해상도가 아니라서 실제로는 640, 480으로 진행하였다.

import cv2
import numpy as np
import os
import glob
# 체커보드의 차원 정의
CHECKERBOARD = (6,8) # 체커보드 행과 열당 내부 코너 수
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 각 체커보드 이미지에 대한 3D 점 벡터를 저장할 벡터 생성
objpoints = []
# 각 체커보드 이미지에 대한 2D 점 벡터를 저장할 벡터 생성
imgpoints = [] 
# 3D 점의 세계 좌표 정의
objp = np.zeros((1, CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32)
objp[0,:,:2] = np.mgrid[0:CHECKERBOARD[0], 0:CHECKERBOARD[1]].T.reshape(-1, 2)
prev_img_shape = None
# 주어진 디렉터리에 저장된 개별 이미지의 경로 추출 
images = glob.glob('/home/jaewoong/opencv_calibration/d435i_images(1280)/*.jpg')
for fname in images: 
    img = cv2.imread(fname)
    # 그레이 스케일로 변환
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  
    # 체커보드 코너 찾기 
    # 이미지에서 원하는 개수의 코너가 발견되면 ret = true
    ret, corners = cv2.findChessboardCorners(gray,
                                             CHECKERBOARD,
                                             cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FAST_CHECK + cv2.CALIB_CB_NORMALIZE_IMAGE)
    # 원하는 개수의 코너가 감지되면,
    # 픽셀 좌표 미세조정 -> 체커보드 이미지 표시
    if ret == True:
        objpoints.append(objp)
        # 주어진 2D 점에 대한 픽셀 좌표 미세조정
        corners2 = cv2.cornerSubPix(gray, corners, (11,11),(-1,-1), criteria)
        imgpoints.append(corners2)
        # 코너 그리기 및 표시
        img = cv2.drawChessboardCorners(img, CHECKERBOARD, corners2, ret)
    cv2.imshow('img',img)
    cv2.waitKey(0)
cv2.destroyAllWindows()
# h,w = img.shape[:2] # 480, 640
print(img.shape)
# 알려진 3D 점(objpoints) 값과 감지된 코너의 해당 픽셀 좌표(imgpoints) 전달, 카메라 캘리브레이션 수행
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
print("Camera matrix : \n") # 내부 카메라 행렬
print(mtx)
print("dist : \n") # 렌즈 왜곡 계수(Lens distortion coefficients)
print(dist)

위에서 만든 체커보드를 찍은 이미지를 통해 내부파라미터를 얻는 코드이다.

D435 new instrinsic_parameter (640, 480)

Camera matrix : 

[[608.26617185   0.         328.29683812]
 [  0.         609.86764559 238.40459802]
 [  0.           0.           1.        ]]
dist : 

[[ 2.02438415e-02  6.13472913e-01 -1.20950082e-03 -1.98880472e-04
  -2.38263200e+00]]



D435i new instrinsic_parameter (640, 480)

Camera matrix : 

[[602.06413899   0.         320.62826269]
 [  0.         603.92424819 246.18136618]
 [  0.           0.           1.        ]]
dist : 

[[ 0.0210422   0.41082636 -0.00245961 -0.0013475  -1.22994319]]



L515 new instrinsic_parameter (640, 480)

Camera matrix : 

[[601.40087321,   0.0,         320.81082249]
 [  0.0,         602.85146252, 245.02322009]
 [  0.0,           0.0,           1.        ]]
dist : 

[[ 0.02756855,  0.45774401, -0.0025181,  -0.00488094, -1.69020468]]

이런식으로 내부파라미터가 나온 것을 확인할 수 있다.

728x90
반응형