자율주행

OpenCV 스테레오 비젼

coding art 2022. 8. 3. 11:42
728x90

참조: Introduction to Epipolar Geometry and Stereo Vision

https://learnopencv.com/introduction-to-epipolar-geometry-and-stereo-vision/

참조: Depth Estimation using Stereo Camera and OpenCV

https://learnopencv.com/depth-perception-using-stereo-camera-python-c/

 

양안으로 물체를 볼 경우 망막에 비춰진 2개의 2차원적 영상을 조합하게 되면 object에 대한 입체적인 거리 판단이 가능하며 항상 입체감을 느낄 수 있게 된다.

이 점이 바로 입체 영화의 원리이며 한편 자율주행 차량에 스테레오 카메라비전을 설치할 경우 자율주행 차량 주변의 객체들과의 거리를 평가할수 있어야 할 것이다.

 

아래 그림에서처럼 2개의 카메라로 이루어진 스테레오 비전 시스템에 있어서 기하학적 특성을 살펴보기로 하자.

3차원 object 상의 한 점 X 를 고려하자. 3차원 object 이기때문에 X 말고도 여러 점들로부터 ray 가 발사되어 결국 한 점 즉 광학 센터(Optical Center)에 모이게 될 것이며 이 점 뒤에 촛점거리 만큼 떨어진 곳에 상이 맺히는 평면 예를 들어 카메라의 CMOS  이미지 센서면 또는 망막이 하나씩 있을 수 있다. 

 

두 개로 구성되는 스테레오 카메라에서는 이미지 센서면이 서로 일정한 각을 형성할 수 있겠지만 실제로는 평행하게끔 설치된다는 점에 유의하자.

 

두 개로 구성되는 스테레오 카메라에서는 이미지 센서면이 서로 일정한 각을 형성할 수 있겠지만 실제로는 평행하게끔 설치된다는 점에 유의하자.

스테레오 카메라의 일반적인 기하학적 배치를 살펴보자.

1. Wikipedia에서 가져온 아래 그림에서 이 두개의 이미지 면들이 서로 평행하지 않다고 가정하자.

2. object 상의 한 점 X 는 광학중심 OL 로 ray 가 발사된다. ray 상의 점 X, X1, X2, X3 는 오른쪽 평면상에서 직선으로 표현된다.

3. 2개의 광학 중심을 연결하면 각 평면들과 교차하는 점들이 나오는데 이를 epipole 또는 epipolar point라고 한다.

 

 

만약 2개의 카메라 면이 평행하다면 epipolar pont들은 무한원에 위치하는 것으로 보기로 하자.

카메라 이미지 면상에서 투영점과 epipolar point 둘을 연결하면 epipolar line 이 얻어진다. 아울러 연두색으로 표현된 즉 OL, OR, X 로 이루어진 평면을 epipolar plane 이라 한다.

아울러 각 카메라면의 광학 중심 두개를 연결하여 base line 이라고 한다.

이 그림에서는 광학센터들이 카메라 이미지 면 뒤에 위치한 것으로 그려졌지만 반대로 3차원 오브젝트들로부터의 선들을 연장하여 카메라 이미지 면들이 광학 센터 뒤에 위치할 수도 있음에 유의하자.

4. 만약 2개의 평면이 평행하다면 epipolar pont 들은 무한원에 위치하는 것으로 보기로 가정하자.

5. 두면상에서의 투영점과 epipolar point 둘을 연결하면 epipolar line이 얻어진다. 연두색으로 표현된 즉 OL, OR, X 로 이루어진 평면을 epipolar plane 이라 한다.

6. 아울러 광학 중심 두개를 연결하고 이 선을 base line 이라고 한다.

 

위 그림에서 광학 센터들은 초점에 해당하는 위치이며 다음 그림에서 초점 거리와 함께 카메라 이미지 면이 어디쯤 위치하는지 나타내었으니 참조하자.

 

아울러 임의로 설정한 두 평면은 비례 계산을 쉽게 이해하기 위해 광학 센터 앞에 위치시켰으나 최종적으로는 광학센터 뒤 초점 거리에 해당하는 위치에 실제 카메라 이미지 평면을 위치시키게 될 것이다.

 

스테레오 형태로 배치된 상황에서 두 평면들을 평행하게 일치시켜 스테레오 비젼을 구성한다.

 

 

참조: Stereo calibration using C++ and OpenCV

https://sourishghosh.com/2016/stereo-calibration-cpp-opencv/

 

Camera Calibration

카메라의 파라메터들을 평가하는 과정을 카메라 교정 작업이라한다.

교정이 된 카메라에서 캡츄어되는 이미지에서는 3차원 셰계 좌표계와 그에 상응하는 2차원 투영좌표 사이의 관계를 정확히 결정 가능한 정보를 이미 가지고 있다.

   1. 내부 파라메터: 렌즈의 초점거리 f, 광학센터 위치, 렌즈면 왜곡 계수

   2. 외부 파라메터: 3차원 세계 좌표계에서의 병진과 회전

 

 

 

아래 그림에서처럼 물체의 상 P 는 스테레오 카메라 각 이미지 평면에 작은 상이 맺혀 있으며 이들은 두 개의 평면 상에 비례적으로 축소되어 성이 표현되어 있다. 하지만 왼쪽 카메라에서처럼 광학 센터에서 초점 거리만큼 떨어진 곳에 실제 카메라 이미지 평면이 위치하게 된다.

 

Geometry of Image Formation( 이미지 형성 기하학)

왼쪽 카메라 이미지 면에서 좌표 x 와 y 는 카메라 렌즈 촛점거리 f 와 좌표계를 사용하여 다음과 같이 표현된다.

공식에서 알아내고 싶은 변수는 피사체와의 거리 Zc 이다.

위 그림을 상면도로 보면 다음과 같이 될 것이며 x 와 y 중 x 하나만 살펴보자.

참조: 스테레오 비전(Stereo Vision)

https://blog.naver.com/bluenara/222740233692

x 축만 살펴보자.

이 두식을 뺄셈하여 Xc 를 소거하여 정리하면 Zc 가 다음과 같이 얻어진다.

Zc는 오브젝트 상의  한 점으로부터 거리이며 b는 베이스라인, f 는 촛점거리, d 는 시격차(disparity)이다.

스테레오 카메라에서 베이스 라인 값과 촛점거리는 일정하게 정해진 상수 값으로 볼 수 있다. 

따라서 시격차 값  d 의 정확도에 따라 거리 또는 심도 Zc 의 정확도가 결정 된다.

만약에 하나의 카메라만 사용해서는 시격차 d 가 정의 되지 않는 점에 유의하자.

일단 시격차 d  값을 좌우 카메라 이미지를 사용하여 결정할 수 있으면 오브젝트와의 거리 Zc 를 구할 수 있게 되는 것이다.

 

따라서 스테레오 카메라 배치에서 얻어지는 한 쌍의 이미지에서 객체의 점들에 대한 disparity 값들이 얻어지면 그 객체의 입체적인 거리 분포를 결정할 수 있게 되는 것이다.

 

 

점 대응( point correspondence)

 

카메라 별로 이미지에서 서로 대응하는 3차원 상의 점 X 를 찾는다.

 

사진은 수작업으로 찾아 본 사례이다. 

 

 

 

 

컴퓨터를 사용하면여 얻어 낸 결과를 참조하고 그 알고리듬을 살펴보자.

 

 

 

 

 

 

 

라이브러리 import 후 좌우 이미지 파일을 읽어 들이자.

numDisparities 는 스테레오 비젼에서 심도(depth) 의 해상도에 해당한다.

blockSize 는 좌우 이미지들에서 비교하기 위한 특정그룹의 픽셀들 수를 의미한다.

 

 

참조: Stereo Vision: Depth Estimation between object and camera

https://medium.com/analytics-vidhya/distance-estimation-cf2f2fd709d8

 

다음의 좌우 이미지 데이터를 사용하여 Python Depth 계산 예제를 실행해 보자. 실행프로그램 Stereo_01.py  와 동일한 폴더내 좌우 이미지 데이터 파일들을 두도록 한다.

tsukuba1.png
0.17MB
tsukuba2.png
0.17MB

 

파라메터 block size = 5 와 25 일 때의 결과를 살펴보자. 밝은 색은 가까운 거리이며 짙은 색일수록 멀다.

 

 

@stereo_01.py

import numpy as np
import cv2
from matplotlib import pyplot as plt

imgL = cv2.imread('tsukuba1.png',0)
imgR = cv2.imread('tsukuba2.png',0)

plt.figure(figsize = (10,5))
plt.subplot(1,2,1)
plt.imshow(imgL,'gray')
plt.axis('off')
plt.subplot(1,2,2)
plt.imshow(imgR,'gray')
plt.axis('off')
plt.show()

def ShowDisparity(bsize=5):
    stereo = cv2.StereoBM_create(numDisparities=32, blockSize=bsize)
    disparity = stereo.compute(imgL,imgR)
    
    min = disparity.min()
    max = disparity.max()
    disparity = np.uint8(255*(disparity - min) / (max - min))
    
    return disparity

result = ShowDisparity(bsize=5)
plt.imshow(result,'gray')
plt.axis('off')
plt.show()

result = ShowDisparity(bsize=25)
plt.imshow(result,'gray')
plt.axis('off')
plt.show()

 

구글 Colab에서 실행하려면 첨부 파일을 사용하도록 하자.

Tsukuba.ipynb
0.26MB