OpenCV 강좌, 스테레오 비전과 깊이 정보 추출

스테레오 비전은 인간의 눈이 물체의 깊이를 인식하는 방식을 모방한 기술입니다. 이는 두 개의 카메라를 사용하여 3D 환경에서 깊이 정보를 추출하는 과정으로, 로봇 비전, 자율주행차, 3D 재구성 및 증강 현실에 광범위하게 사용됩니다.

1. 스테레오 비전의 기초

스테레오 비전은 두 개의 카메라가 동시에 동일한 장면을 캡처하여 깊이 정보를 생성하는 과정입니다. 각 카메라의 시점 차이 때문에 각 카메라에서 찍힌 동일한 점의 위치가 서로 다르게 보이게 됩니다. 이 두 이미지 간의 차이를 분석하여 각 점의 깊이를 추정할 수 있습니다.

1.1. 삼각측량 (Triangulation)

삼각측량은 두 카메라의 위치와 각 카메라에서 캡처된 점의 위치 정보를 기반으로 깊이를 계산하는 기본적인 방법입니다. 두 카메라의 위치와 내적 관계를 이해하는 것이 중요합니다.

2. OpenCV 설치

OpenCV를 사용하기 위해서는 먼저 OpenCV 라이브러리를 설치해야 합니다. 아래의 명령어를 사용하여 OpenCV를 설치할 수 있습니다:

pip install opencv-python opencv-python-headless

3. 스테레오 비전 시스템 구축

스테레오 비전 시스템을 구축하는 과정은 다음과 같습니다:

  1. 카메라 캘리브레이션 (Camera Calibration)
  2. 스테레오 정합 (Stereo Matching)
  3. 깊이 맵 생성 (Depth Map Generation)

3.1. 카메라 캘리브레이션

카메라의 내부 매개변수와 왜곡 계수를 추정하는 과정이 필수적입니다. 이를 통해 카메라의 왜곡을 보정하고, 정확한 3D 좌표를 얻을 수 있습니다. OpenCV에서는 체스판 패턴을 사용하여 카메라를 캘리브레이션하는 방법을 제공합니다.

코드 예제: 카메라 캘리브레이션


import cv2
import numpy as np
import glob

# 체스판 패턴의 크기와 내부 코너 수
chessboard_size = (7, 6)  # 내부 코너 수
square_size = 1.0  # 체스보드 정사각형의 실제 크기

# 3D 포인트와 2D 포인트를 저장할 리스트
objpoints = []  # 3D 포인트
imgpoints = []  # 2D 포인트

# 3D 포인트 생성
objp = np.zeros((chessboard_size[0] * chessboard_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1, 2) * square_size

# 이미지 파일 경로
images = glob.glob('path_to_your_images/*.jpg')

for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 체스판 찾기
    ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None)

    if ret:
        imgpoints.append(corners)
        objpoints.append(objp)

# 카메라 캘리브레이션
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)

print("Camera Matrix:\n", mtx)
print("Distortion Coefficients:\n", dist)
    

3.2. 스테레오 정합

두 개의 카메라에서 촬영한 이미지 간의 대응 관계를 찾는 과정입니다. OpenCV에서는 `StereoBM` 및 `StereoSGBM` 알고리즘을 사용하여 스테레오 이미지를 처리할 수 있습니다.

코드 예제: 스테레오 정합


# 두 개의 이미지 로드
imgL = cv2.imread('left_image.jpg', cv2.IMREAD_GRAYSCALE)
imgR = cv2.imread('right_image.jpg', cv2.IMREAD_GRAYSCALE)

# StereoSGBM 객체 생성
stereo = cv2.StereoSGBM_create(minDisparity=0,
                                numDisparities=16,
                                blockSize=5,
                                P1=8 * 3 * blockSize**2,
                                P2=32 * 3 * blockSize**2,
                                disp12MaxDiff=1,
                                uniquenessRatio=15,
                                speckleWindowSize=0,
                                speckleRange=2,
                                mode=cv2.STEREO_SGBM_MODE_SGBM)
    
# Disparity 계산
disparity = stereo.compute(imgL, imgR).astype(np.float32) / 16.0

# Disparity 맵을 시각화
plt.imshow(disparity, 'gray')
plt.show()
    

3.3. 깊이 맵 생성

Disparity 맵을 기반으로 깊이 정보를 추출할 수 있습니다. 깊이는 disparity 값과 카메라의 내부 매개변수를 이용하여 계산할 수 있습니다.

코드 예제: 깊이 정보 추출


# 깊이 맵 계산 (단순한 비례 계산)
focal_length = mtx[0, 0]  # 카메라의 초점거리
baseline = 0.54  # 카메라 간 거리 (미터)

# 깊이 맵 계산
depth_map = (focal_length * baseline) / disparity
depth_map[disparity == 0] = 0  # Disparity가 0인 부분은 깊이 정보가 없음

# 깊이 맵 시각화
plt.imshow(depth_map, cmap='plasma')
plt.colorbar()
plt.title('Depth Map')
plt.show()
    

4. 결론

스테레오 비전은 3D 환경에서 깊이 정보를 추출하는 강력한 기술입니다. OpenCV를 사용하면 이 과정을 매우 효율적으로 수행할 수 있으며, 다양한 응용 분야에서 meaningful한 결과를 얻을 수 있습니다. 본 강좌에서는 기본적인 스테레오 비전 시스템 구축 방법과 OpenCV를 이용한 코드 예제를 소개하였습니다.

세부적인 매개변수와 조정을 통해 각 응용 프로그램의 요구에 맞게 성능을 개선할 수 있습니다. 향후 더 발전된 기법인 머신러닝 기반의 스테레오 비전과 결합하여 더욱 정교한 시스템을 만들을 수 있습니다.

참고 자료

이 강좌가 도움이 되었길 바라며, 질문이나 더 알고 싶은 점이 있으면 댓글을 통해 문의해 주세요!