본문 바로가기
영상처리/스테레오 비전

OpenCV를 사용하여 저렴한 스테레오 카메라 만들기

by j2b2 2023. 4. 10.

https://learnopencv.com/making-a-low-cost-stereo-camera-using-opencv/

 

Making A Low-Cost Stereo Camera Using OpenCV | LearnOpenCV #

In this post, we will learn how to create a custom low-cost stereo camera (using a pair of webcams ) and capture 3D videos with it using OpenCV. We provide code in Python and C++.

learnopencv.com

위의 글을 구글 번역기로 돌린 결과물


 

OpenCV를 사용하여 저렴한 스테레오 카메라 만들기

이 게시물에서는 맞춤형 저가 스테레오 카메라(한 쌍의 웹캠 사용)를 만들고 OpenCV를 사용하여 3D 비디오를 캡처하는 방법을 배웁니다. Python 및 C++로 코드를 제공합니다.

 

https://www.youtube.com/watch?v=THD-3SwpFkM 

3D 비디오의 예.

 

우리 모두는 위에 표시된 것과 같은 3D 영화와 비디오를 즐겼습니다. 3D 효과를 경험하려면 그림 1과 같은 Red-Cyan 컬러 3D 안경이 필요합니다. 어떻게 작동합니까? 화면이 평면일 때 어떻게 3D 효과를 경험할 수 있습니까? 이들은 스테레오 카메라 설정을 사용하여 캡처됩니다.

그림 1 - 적청색 3D 안경


이전 게시물에서 우리는 스테레오 카메라에 대해 배웠고 컴퓨터가 깊이를 인식하는 데 어떻게 사용되는지 배웠습니다. 이 게시물에서 우리는 우리 자신의 스테레오 카메라를 만드는 방법을 배우고 3D 비디오를 만드는 데 사용할 수 있는 방법을 이해합니다. 구체적으로 다음을 배우게 됩니다.

목차
1. 스테레오 카메라 설정을 만드는 단계
2. 스테레오 교정 및 수정의 중요성
3. 스테레오 보정 및 수정 단계
    3.1. 1단계: 스테레오 설정의 오른쪽 및 왼쪽 카메라 개별 보정
    3.2. 2단계: 고정된 고유 매개변수로 스테레오 보정 수행
    3.3. 3단계: 스테레오 수정
    3.4. 4단계: 왜곡되지 않은 수정된 스테레오 이미지 쌍을 얻는 데 필요한 매핑 계산
4. 3D 안경은 어떻게 작동합니까?
5. 맞춤형 3D 비디오 만들기

 

 

 

스테레오 카메라 설정을 만드는 단계

스테레오 카메라 설정에는 일반적으로 고정된 거리에 배치된 두 개의 동일한 카메라가 포함됩니다. 산업 등급 표준 스테레오 카메라 설정은 동일한 카메라 쌍을 사용합니다.

집에서 만들려면 다음이 필요합니다.

  1. USB 웹캠 2개(동일 모델 권장).
  2. 카메라 고정을 위한 견고한 베이스(목재, 판지, PVC 폼 보드).
  3. 클램프 또는 덕트 테이프.


다른 구성 요소를 사용하여 스테레오 카메라를 만드는 데 창의적일 수 있지만 필수 요구 사항은 카메라를 단단히 고정되고 평행하게 유지하는 것입니다.

그림 2 - 내 DIY 스테레오 카메라(왼쪽) 및 깊이가 있는 OpenCV AI 키트(OAK-D)(오른쪽)의 이미지. OAKD의 측면에 있는 두 대의 카메라는 스테레오 카메라 설정을 구성합니다. OAK-D 이미지 출처.


많은 사람들이 그림 2의 왼쪽 이미지나 이 게시물에서 공유한 것과 같은 DIY 스테레오 카메라 설정을 만들고 공유했습니다.

카메라를 수정하고 올바르게 정렬되었는지 확인하면 작업이 완료된 것입니까? 디스패리티 맵과 3D 비디오를 생성할 준비가 되었습니까?

 

 

 

스테레오 교정 및 수정(Stereo Calibration and Rectification)의 중요성

스테레오 보정(stereo calibration) 및 스테레오 정류(stereo rectification)의 중요성을 이해하기 위해 보정(calibration)이나 스테레오 수정(stereo rectification) 없이 스테레오 설정에서 캡처한 이미지를 사용하여 디스패리티 맵을 생성하려고 합니다.

그림 3 - 스테레오 카메라 설정에서 캡처한 왼쪽 및 오른쪽 이미지

 

그림 4 - 스테레오 보정(stereo calibration) 없이 오른쪽 및 왼쪽 이미지를 사용하여 생성된 디스패리티 맵

 

보정되지 않은 스테레오 카메라 설정으로 생성된 디스패리티 맵은 매우 시끄럽고 부정확(very noisy and inaccurate)합니다. 왜 이런 일이 발생합니까?

이전 게시물을 기반으로 해당 키 포인트(corresponding key points)는 포인트 대응 검색을 단순화하기 위해 동일한 Y 좌표를 가져야 합니다. 그림 5에서 몇 개의 대응하는 점 사이의 일치하는 선을 그릴 때 선이 완전히 수평이 아님을 알 수 있습니다.

그림 5 - 해당 지점 간의 일치를 플로팅합니다.


또한 해당 점의 Y 좌표가 동일하지 않은 것으로 관찰됩니다. 그림 6은 포인트 대응이 있는 한 쌍의 스테레오 이미지와 이러한 이미지를 사용하여 생성된 디스패리티 맵을 보여줍니다. 이제 디스패리티 맵이 이전 맵에 비해 덜 시끄럽다는 것을 관찰했습니다. 이 경우 해당 키 포인트는 동일한 Y 좌표를 갖습니다. 이러한 경우는 카메라가 병렬인 경우에만 가능합니다. 이것은 이미지가 평행하고 수평 변환만으로 관련되는 2-뷰 기하학의 특별한 경우입니다. 이것은 디스패리티 맵을 생성하는 데 사용되는 방법이 포인트 대응을 수평으로만 검색하기 때문에 필수적입니다.

그림 6 - 스테레오 이미지 쌍 및 생성된 디스패리티 맵에 대한 기능 일치.


대박! 카메라를 정렬하고 완전히 평행하게 만들기만 하면 됩니다. 그렇다면 시행 착오를 통해 수동으로 카메라를 조정합니까? 글쎄, 재미있는 활동으로 시도해 볼 수 있습니다! 스포일러 경고!! 명확한 디스패리티 맵을 얻기 위해 수동으로 카메라를 조정하는 데 오랜 시간이 걸립니다. 게다가 설정이 방해받고 카메라가 옮겨질 때마다 이 과정을 반복해야 한다. 이것은 시간이 많이 소요되며 이상적인 솔루션이 아닙니다.

카메라를 물리적으로 조정하는 대신 소프트웨어 측에서 이 작업을 수행합니다. 스테레오 이미지 수정(Stereo Image Rectification)이라는 방법을 사용합니다.[1] 그림 7은 스테레오 정류(stereo rectification) 과정을 설명합니다. 아이디어는 광학 중심을 통과하는 선과 평행한 공통 평면에 두 개의 이미지를 다시 투영하는 것입니다. 이렇게 하면 해당 포인트가 동일한 Y 좌표를 가지며 단순히 수평 평행 이동으로 연결됩니다.

Figure 7 – Process of stereo rectification.

 

 

 

스테레오 보정 및 수정(Stereo Calibration and Rectification) 단계

렌즈 왜곡에 대한 이전 게시물을 기반으로 우리는 카메라로 캡처한 이미지가 렌즈 왜곡을 겪는다는 것을 알고 있습니다. 따라서 스테레오 수정(stereo rectification) 외에도 이미지 왜곡을 제거하는 것도 필수적입니다. 따라서 전체 프로세스는 다음과 같습니다.

  1. 카메라 보정 포스트에 설명된 표준 OpenCV 보정 방법을 사용하여 개별 카메라를 보정합니다.
  2. 스테레오 카메라 설정에 사용된 두 카메라 간의 변환을 결정합니다.
  3. 이전 단계에서 얻은 매개변수와 stereoCalibrate 방법을 사용하여 스테레오 수정을 위해 두 이미지에 적용되는 변환을 결정합니다.
  4. 마지막으로, 왜곡되지 않고 수정된 스테레오 이미지 쌍을 찾는 데 필요한 매핑은 initUndistortRectifyMap 메서드를 사용하여 얻습니다.
  5. 이 매핑은 왜곡되지 않은 수정된 스테레오 이미지 쌍을 얻기 위해 원본 이미지에 적용됩니다.

이러한 단계를 수행하기 위해 보정 패턴의 이미지를 캡처합니다. 다음 비디오는 DIY 스테레오 카메라를 보정하기 위해 캡처한 다양한 이미지를 보여줍니다.


교정 및 수정을 위한 코드를 이해합시다.

 

 

1단계: 스테레오 설정의 오른쪽 및 왼쪽 카메라 개별 보정


https://www.youtube.com/watch?v=z9g0CsEjGA8

스테레오 보정을 위해 이미지를 캡처하는 과정을 보여주는 비디오


스테레오 보정을 수행하기 전에 두 카메라의 개별 보정을 수행합니다. 그러나 stereoCalibrate() 메서드가 두 카메라 각각의 보정도 수행할 수 있는 경우에도 카메라를 개별적으로 보정해야 하는 이유는 무엇입니까?

계산할 매개변수가 많고(큰 매개변수 공간) 모서리 감지 및 정수에 대한 점 근사와 같은 단계에서 오류가 누적됩니다. 이렇게 하면 반복 방법이 올바른 솔루션에서 벗어날 가능성이 높아집니다. 따라서 카메라 매개변수를 개별적으로 계산하고 스테레오 카메라 쌍, 필수 행렬(Essential matrix) 및 기본 행렬(Fundamental matrix) 간의 변환을 찾기 위해서만 stereoCalibrate() 메서드를 사용합니다.

그러나 알고리즘은 개별 카메라의 보정을 건너뛰어야 한다는 것을 어떻게 알 수 있습니까? 이를 위해 CALIB_FIX_INTRINSIC 플래그를 설정하고 메서드에 전달합니다.

C++

// Defining the dimensions of checkerboard
int CHECKERBOARD[2]{6,9}; 

// Creating vector to store vectors of 3D points for each checkerboard image
std::vector<std::vector<cv::Point3f> > objpoints;

// Creating vector to store vectors of 2D points for each checkerboard image
std::vector<std::vector<cv::Point2f> > imgpointsL, imgpointsR;

// Defining the world coordinates for 3D points
std::vector<cv::Point3f> objp;
for(int i{0}; i<CHECKERBOARD[1]; i++)
{
  for(int j{0}; j<CHECKERBOARD[0]; j++)
    objp.push_back(cv::Point3f(j,i,0));
}

// Extracting path of individual image stored in a given directory
std::vector<cv::String> imagesL, imagesR;
// Path of the folder containing checkerboard images
std::string pathL = "./data/stereoL/*.png";
std::string pathR = "./data/stereoR/*.png";

cv::glob(pathL, imagesL);
cv::glob(pathR, imagesR);

cv::Mat frameL, frameR, grayL, grayR;
// vector to store the pixel coordinates of detected checker board corners 
std::vector<cv::Point2f> corner_ptsL, corner_ptsR;
bool successL, successR;

// Looping over all the images in the directory
for(int i{0}; i<imagesL.size(); i++)
{
  frameL = cv::imread(imagesL[i]);
  cv::cvtColor(frameL,grayL,cv::COLOR_BGR2GRAY);

  frameR = cv::imread(imagesR[i]);
  cv::cvtColor(frameR,grayR,cv::COLOR_BGR2GRAY);

  // Finding checker board corners
  // If desired number of corners are found in the image then success = true  
  successL = cv::findChessboardCorners(
    grayL,
    cv::Size(CHECKERBOARD[0],CHECKERBOARD[1]),
    corner_ptsL);
    // cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_FAST_CHECK | cv::CALIB_CB_NORMALIZE_IMAGE);

  successR = cv::findChessboardCorners(
    grayR,
    cv::Size(CHECKERBOARD[0],CHECKERBOARD[1]),
    corner_ptsR);
    // cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_FAST_CHECK | cv::CALIB_CB_NORMALIZE_IMAGE);
  /*
    * If desired number of corner are detected,
    * we refine the pixel coordinates and display 
    * them on the images of checker board
  */
  if((successL) && (successR))
  {
    cv::TermCriteria criteria(cv::TermCriteria::EPS | cv::TermCriteria::MAX_ITER, 30, 0.001);

    // refining pixel coordinates for given 2d points.
    cv::cornerSubPix(grayL,corner_ptsL,cv::Size(11,11), cv::Size(-1,-1),criteria);
    cv::cornerSubPix(grayR,corner_ptsR,cv::Size(11,11), cv::Size(-1,-1),criteria);

    // Displaying the detected corner points on the checker board
    cv::drawChessboardCorners(frameL, cv::Size(CHECKERBOARD[0],CHECKERBOARD[1]), corner_ptsL,successL);
    cv::drawChessboardCorners(frameR, cv::Size(CHECKERBOARD[0],CHECKERBOARD[1]), corner_ptsR,successR);

    objpoints.push_back(objp);
    imgpointsL.push_back(corner_ptsL);
    imgpointsR.push_back(corner_ptsR);
  }

  cv::imshow("ImageL",frameL);
  cv::imshow("ImageR",frameR);
  cv::waitKey(0);
}

cv::destroyAllWindows();

cv::Mat mtxL,distL,R_L,T_L;
cv::Mat mtxR,distR,R_R,T_R;
cv::Mat Rot, Trns, Emat, Fmat;
cv::Mat new_mtxL, new_mtxR;

// Calibrating left camera
cv::calibrateCamera(objpoints,
                    imgpointsL,
                    grayL.size(),
                    mtxL,
                    distL,
                    R_L,
                    T_L);

new_mtxL = cv::getOptimalNewCameraMatrix(mtxL,
                              distL,
                              grayL.size(),
                              1,
                              grayL.size(),
                              0);

// Calibrating right camera
cv::calibrateCamera(objpoints,
                    imgpointsR,
                    grayR.size(),
                    mtxR,
                    distR,
                    R_R,
                    T_R);

new_mtxR = cv::getOptimalNewCameraMatrix(mtxR,
                              distR,
                              grayR.size(),
                              1,
                              grayR.size(),
                              0);


Python

# Set the path to the images captured by the left and right cameras
pathL = "./data/stereoL/"
pathR = "./data/stereoR/"

# Termination criteria for refining the detected corners
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)


objp = np.zeros((9*6,3), np.float32)
objp[:,:2] = np.mgrid[0:9,0:6].T.reshape(-1,2)

img_ptsL = []
img_ptsR = []
obj_pts = []

for i in tqdm(range(1,12)):
imgL = cv2.imread(pathL+"img%d.png"%i)
imgR = cv2.imread(pathR+"img%d.png"%i)
imgL_gray = cv2.imread(pathL+"img%d.png"%i,0)
imgR_gray = cv2.imread(pathR+"img%d.png"%i,0)

outputL = imgL.copy()
outputR = imgR.copy()

retR, cornersR =  cv2.findChessboardCorners(outputR,(9,6),None)
retL, cornersL = cv2.findChessboardCorners(outputL,(9,6),None)

if retR and retL:
obj_pts.append(objp)
cv2.cornerSubPix(imgR_gray,cornersR,(11,11),(-1,-1),criteria)
cv2.cornerSubPix(imgL_gray,cornersL,(11,11),(-1,-1),criteria)
cv2.drawChessboardCorners(outputR,(9,6),cornersR,retR)
cv2.drawChessboardCorners(outputL,(9,6),cornersL,retL)
cv2.imshow('cornersR',outputR)
cv2.imshow('cornersL',outputL)
cv2.waitKey(0)

img_ptsL.append(cornersL)
img_ptsR.append(cornersR)


# Calibrating left camera
retL, mtxL, distL, rvecsL, tvecsL = cv2.calibrateCamera(obj_pts,img_ptsL,imgL_gray.shape[::-1],None,None)
hL,wL= imgL_gray.shape[:2]
new_mtxL, roiL= cv2.getOptimalNewCameraMatrix(mtxL,distL,(wL,hL),1,(wL,hL))

# Calibrating right camera
retR, mtxR, distR, rvecsR, tvecsR = cv2.calibrateCamera(obj_pts,img_ptsR,imgR_gray.shape[::-1],None,None)
hR,wR= imgR_gray.shape[:2]
new_mtxR, roiR= cv2.getOptimalNewCameraMatrix(mtxR,distR,(wR,hR),1,(wR,hR))

 

 


2단계: 고정된 고유 매개변수로 스테레오 보정 수행

카메라가 보정되면 이를 stereoCalibrate() 메서드에 전달하고 CALIB_FIX_INTRINSIC 플래그를 설정합니다. 또한 두 이미지에서 캡처된 3D 포인트와 해당 2D 픽셀 좌표도 전달합니다.

이 방법은 두 카메라와 Essential 및 Fundamental 행렬 간의 회전 및 변환을 계산합니다.


C++

cv::Mat Rot, Trns, Emat, Fmat;
int flag = 0;
flag |= cv::CALIB_FIX_INTRINSIC;

// This step is performed to transformation between the two cameras and calculate Essential and 
// Fundamenatl matrix
cv::stereoCalibrate(objpoints,
                    imgpointsL,
                    imgpointsR,
                    new_mtxL,
                    distL,
                    new_mtxR,
                    distR,
                    grayR.size(),
                    Rot,
                    Trns,
                    Emat,
                    Fmat,
                    flag,
                    cv::TermCriteria(cv::TermCriteria::MAX_ITER + cv::TermCriteria::EPS, 30, 1e-6));


Python

flags = 0
flags |= cv2.CALIB_FIX_INTRINSIC
# Here we fix the intrinsic camara matrixes so that only Rot, Trns, Emat and Fmat are calculated.
# Hence intrinsic parameters are the same 

criteria_stereo= (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)


# This step is performed to transformation between the two cameras and calculate Essential and Fundamenatl matrix
retS, new_mtxL, distL, new_mtxR, distR, Rot, Trns, Emat, Fmat = cv2.stereoCalibrate(obj_pts, img_ptsL, img_ptsR, new_mtxL, distL, new_mtxR, distR, imgL_gray.shape[::-1], criteria_stereo, flags)

 

 

 

3단계: 스테레오 수정

카메라 내장 기능과 카메라 간의 회전 및 변환을 사용하여 이제 스테레오 정류를 적용할 수 있습니다. 스테레오 정류는 두 카메라 이미지 평면이 동일한 평면에 있도록 회전을 적용합니다. 회전 행렬과 함께 stereoRectify 메서드는 새 좌표 공간의 투영 행렬도 반환합니다.

C++

cv::Mat rect_l, rect_r, proj_mat_l, proj_mat_r, Q;

// Once we know the transformation between the two cameras we can perform 
// stereo rectification
cv::stereoRectify(new_mtxL,
                  distL,
                  new_mtxR,
                  distR,
                  grayR.size(),
                  Rot,
                  Trns,
                  rect_l,
                  rect_r,
                  proj_mat_l,
                  proj_mat_r,
                  Q,
                  1);


Python

rectify_scale= 1
rect_l, rect_r, proj_mat_l, proj_mat_r, Q, roiL, roiR= cv2.stereoRectify(new_mtxL, distL, new_mtxR, distR, imgL_gray.shape[::-1], Rot, Trns, rectify_scale,(0,0))

 

 

 

4단계: 왜곡되지 않은 수정된 스테레오 이미지 쌍을 얻는 데 필요한 매핑 계산

카메라가 단단히 고정되어 있다고 가정하므로 변환을 다시 계산할 필요가 없습니다. 따라서 스테레오 이미지 쌍을 왜곡되지 않은 수정된 스테레오 이미지 쌍으로 변환하는 매핑을 계산하고 나중에 사용할 수 있도록 저장합니다.


C++

cv::Mat Left_Stereo_Map1, Left_Stereo_Map2;
cv::Mat Right_Stereo_Map1, Right_Stereo_Map2;

cv::initUndistortRectifyMap(new_mtxL,
                            distL,
                            rect_l,
                            proj_mat_l,
                            grayR.size(),
                            CV_16SC2,
                            Left_Stereo_Map1,
                            Left_Stereo_Map2);

cv::initUndistortRectifyMap(new_mtxR,
                            distR,
                            rect_r,
                            proj_mat_r,
                            grayR.size(),
                            CV_16SC2,
                            Right_Stereo_Map1,
                            Right_Stereo_Map2);

cv::FileStorage cv_file = cv::FileStorage("improved_params2_cpp.xml", cv::FileStorage::WRITE);
cv_file.write("Left_Stereo_Map_x",Left_Stereo_Map1);
cv_file.write("Left_Stereo_Map_y",Left_Stereo_Map2);
cv_file.write("Right_Stereo_Map_x",Right_Stereo_Map1);
cv_file.write("Right_Stereo_Map_y",Right_Stereo_Map2);


Python

Left_Stereo_Map= cv2.initUndistortRectifyMap(new_mtxL, distL, rect_l, proj_mat_l,
                                             imgL_gray.shape[::-1], cv2.CV_16SC2)
Right_Stereo_Map= cv2.initUndistortRectifyMap(new_mtxR, distR, rect_r, proj_mat_r,
                                              imgR_gray.shape[::-1], cv2.CV_16SC2)

print("Saving paraeters ......")
cv_file = cv2.FileStorage("improved_params2.xml", cv2.FILE_STORAGE_WRITE)
cv_file.write("Left_Stereo_Map_x",Left_Stereo_Map[0])
cv_file.write("Left_Stereo_Map_y",Left_Stereo_Map[1])
cv_file.write("Right_Stereo_Map_x",Right_Stereo_Map[0])
cv_file.write("Right_Stereo_Map_y",Right_Stereo_Map[1])
cv_file.release()

 

 

 

3D 안경은 어떻게 작동합니까?

DIY 스테레오 카메라가 보정되면 3D 비디오를 만들 준비가 된 것입니다. 그러나 3D 비디오를 만드는 방법을 이해하기 전에 3D 안경이 어떻게 작동하는지 이해하는 것이 중요합니다.

우리는 양안 비전 시스템을 사용하여 세상을 인식합니다. 우리의 눈은 옆으로 변하는 위치에 있습니다. 따라서 그들은 다소 다른 정보를 캡처합니다.

우리의 왼쪽 눈과 오른쪽 눈이 포착하는 정보의 차이점은 무엇입니까?

간단한 실험을 해보자! 팔을 앞으로 뻗고 손에 물건을 잡습니다. 이제 한쪽 눈을 감고 물체를 보십시오. 잠시 후 다른 쪽 눈으로 이것을 반복하고 교대로 계속하십시오. 두 눈으로 볼 수 있는 것의 차이가 보이시나요?

차이점은 주로 물체의 상대적 수평 위치에 있습니다. 이러한 위치 차이를 수평 불일치라고 합니다. 이전 게시물에서 우리는 한 쌍의 스테레오 이미지를 사용하여 디스패리티 맵을 계산했습니다.

이제 물체를 가까이 가져오고 동일한 실험을 반복하십시오. 지금 어떤 변화를 관찰했습니까? 물체에 해당하는 수평 편차가 증가합니다. 따라서 물체에 대한 시차가 높을수록 더 가깝습니다. 이것이 입체시(stereopsis)를 사용하여 양안 비전 시스템을 사용하여 깊이를 인식하는 방법입니다.

스테레오스코피(stereoscopy)라고 하는 방법을 사용하여 각 눈에 두 개의 서로 다른 이미지를 인위적으로 표시하여 이러한 불일치를 시뮬레이션할 수 있습니다. 처음에는 3D 영화의 경우 빨간색과 청록색 필터를 사용하여 각 눈의 이미지를 인코딩하여 달성했습니다. 그들은 두 이미지 각각이 의도한 눈에 도달하도록 하기 위해 적청 3D 안경을 사용했습니다. 이것은 깊이의 환상(illusion of depth)을 만들었습니다. 이 방법으로 생성된 입체 효과를 애너글리프 3D(anaglyph 3D)라고 합니다. 따라서 이미지를 애너글리프 이미지라고 하고 안경을 애너글리프 3D 안경이라고 합니다.

 


맞춤형 3D 비디오 만들기

입체 이미지 쌍이 입체 이미지로 변환되어 입체 안경을 사용하여 볼 때 깊이의 환상을 만드는 방법을 이해했습니다. 그러나 이러한 스테레오 이미지를 캡처하려면 어떻게 해야 합니까? 예! 이것은 우리가 DIY 스테레오 카메라를 사용하는 곳입니다. DIY 스테레오 카메라 설정을 사용하여 스테레오 이미지를 캡처하고 각 스테레오 이미지 쌍에 대한 애너글리프 이미지를 만듭니다. 그런 다음 모든 연속 애너글리프 이미지를 비디오로 저장합니다. 이것이 우리가 3D 비디오를 만드는 방법입니다!

코드를 자세히 살펴보고 3D 비디오를 만들어 보겠습니다.


C++

cv::imshow("Left image before rectification",frameL);
cv::imshow("Right image before rectification",frameR);

cv::Mat Left_nice, Right_nice;

// Apply the calculated maps for rectification and undistortion 
cv::remap(frameL,
          Left_nice,
          Left_Stereo_Map1,
          Left_Stereo_Map2,
          cv::INTER_LANCZOS4,
          cv::BORDER_CONSTANT,
          0);

cv::remap(frameR,
          Right_nice,
          Right_Stereo_Map1,
          Right_Stereo_Map2,
          cv::INTER_LANCZOS4,
          cv::BORDER_CONSTANT,
          0);


cv::imshow("Left image after rectification",Left_nice);
cv::imshow("Right image after rectification",Right_nice);

cv::waitKey(0);

cv::Mat Left_nice_split[3], Right_nice_split[3];

std::vector<cv::Mat> Anaglyph_channels;

cv::split(Left_nice, Left_nice_split);
cv::split(Right_nice, Right_nice_split);

Anaglyph_channels.push_back(Right_nice_split[0]);
Anaglyph_channels.push_back(Right_nice_split[1]);
Anaglyph_channels.push_back(Left_nice_split[2]);

cv::Mat Anaglyph_img;

cv::merge(Anaglyph_channels, Anaglyph_img);

cv::imshow("Anaglyph image", Anaglyph_img);
cv::waitKey(0);


Python

cv2.imshow("Left image before rectification", imgL)
cv2.imshow("Right image before rectification", imgR)

Left_nice= cv2.remap(imgL,Left_Stereo_Map[0],Left_Stereo_Map[1], cv2.INTER_LANCZOS4, cv2.BORDER_CONSTANT, 0)
Right_nice= cv2.remap(imgR,Right_Stereo_Map[0],Right_Stereo_Map[1], cv2.INTER_LANCZOS4, cv2.BORDER_CONSTANT, 0)

cv2.imshow("Left image after rectification", Left_nice)
cv2.imshow("Right image after rectification", Right_nice)
cv2.waitKey(0)

out = Right_nice.copy()
out[:,:,0] = Right_nice[:,:,0]
out[:,:,1] = Right_nice[:,:,1]
out[:,:,2] = Left_nice[:,:,2]

cv2.imshow("Output image", out)
cv2.waitKey(0)

 

 

https://www.youtube.com/watch?v=btksDYpsWig 

맞춤형 스테레오 카메라 설정과 위에서 설명한 코드를 사용하여 만든 3D 비디오

 

공간 AI 소개(Introduction to Spatial AI)의 첫 번째 게시물에서는 2-뷰 기하학 및 스테레오 비전과 관련된 모든 기본 개념에 대해 논의했습니다. 이 게시물은 저렴한 스테레오 카메라 설정을 만들고 보정하고 사용자 지정 3D 영화를 만드는 데 사용하는 방법에 관한 것입니다.

이 시리즈의 다음 게시물에서는 또 다른 흥미로운 응용 프로그램에 대해 논의하고 스테레오 비전에 대한 몇 가지 기본적인 개념을 설명합니다.

 



참고문헌

[1] C. 루프 및 Z. Zhang. 스테레오 비전에 대한 정류 호모그래피 계산. IEEE Conf. 컴퓨터 비전 및 패턴 인식, 1999.