새소식

Perception

2장의 이미지로 3차원 복원하기 with Python Open3D

  • -

이전 포스팅 (https://zzziito.tistory.com/43) 에서도 언급했듯이, Stereo Matching 을 이용하면 이론상으로는 두 장의 이미지를 통해 3차원을 복원할 수 있습니다. 

 

Python, OpenCV, Open3D 를 이용하여 2장의 이미지로 3차원을 복원하는 예제를 통해 과정을 더 자세히 살펴보도록 하겠습니다. 

(전체 코드는 글 하단에 있습니다.)

 

과정

 

1. Undistortion

2. Rectification

3. Disparity Map 얻기

4. Disparity map 을 Depth map 으로 바꾸기

5. Depth Map 을 이용한 Point Cloud reconstruction

 

예제 사진

Undistortion

카메라 왜곡을 먼저 펴주기 위해 Undistortion 을 수행합니다. 

# Undistortion
imgL_undist = cv2.undistort(imgL, K, dist_coef, None, K)
imgR_undist = cv2.undistort(imgR, K, dist_coef, None, K)

# 검은 부분을 줄이기 위해 ROI 대로 이미지를 자르기
x, y, w, h = roi
imgL_undist = imgL_undist[y:y+h, x:x+w]
imgR_undist = imgR_undist[y:y+h, x:x+w]

 

Rectification

# Rectification
R1, R2, P1, P2, Q, _, _ = cv2.stereoRectify(K, dist_coef, K, dist_coef, imgL_undist.shape[::-1], R, T)
mapLx, mapLy = cv2.initUndistortRectifyMap(K, dist_coef, R1, P1, imgL_undist.shape[::-1], cv2.CV_32FC1)
mapRx, mapRy = cv2.initUndistortRectifyMap(K, dist_coef, R2, P2, imgR_undist.shape[::-1], cv2.CV_32FC1)

imgL_rect = cv2.remap(imgL_undist, mapLx, mapLy, cv2.INTER_LINEAR)
imgR_rect = cv2.remap(imgR_undist, mapRx, mapRy, cv2.INTER_LINEAR)

Rectification 결과

 

 

Disparity Map 얻기

Stereo Matching 을 하기 위해서는 다양한 메소드들이 있습니다. 

  • StereoBM_create()
    • 전통적인 블록 기반 매칭 알고리즘으로, 각 픽셀을 중심으로 지정된 윈도우 내에서 다른 이미지의 대응되는 윈도우와의 차이를 계산하고, 가장 유사한 차이를 갖는 대응점을 찾아 disparity 맵을 계산합니다.
    • 계산 속도가 빠르고, 작은 이미지에서 잘 동작하지만 큰 이미지에서는 성능이 떨어질 수 있습니다.
  • StereoSGBM_create()
    • semi - global matching (SGM)
    • 블록 기반 매칭 알고리즘의 단점을 보완하여, 좀 더 일관된 disparity map 을 계산할 수 있습니다.

StereoBM_create() 와 StereoSGCM_create() 의 결과를 비교해보면 다음과 같습니다. 

좌측 : StereoBM_create() 의 결과물 / 우측 : StereoSGBM_create() 의 결과물

StereoSGBM_create() 가 더 정확도 높은 결과를 생성했기 때문에 이 함수를 사용하여 Disparity Map 을 만들었습니다.

 

좌측 : 예시 1의 Disparity map / 우측 : 예시 2의 Disparity map

이후 disparity map 을 통해서 depth map 을 생성했습니다.

depth = np.zeros_like(disparity).astype(np.float32)
depth[disparity > 0] = K[0, 0] * T[0, 0] / disparity[disparity > 0]

좌측 : 예시 1의 Depth map / 우측 : 예시 2의 Depth map

 

Depth Map 을 이용한 Point Cloud reconstruction

 

다음 식을 이용해서 각 점들의 3D 좌표를 구합니다. 

for y in range(h):
    for x in range(w):
        z = depth[y, x]
        if z > min_z and z < max_z:
            x_ = (x - center_x) * z / focal_length
            y_ = (y - center_y) * z / focal_length
            xyz[y, x,:] = (int(x_), int(y_), int(z))
            # Get RGB color
            rgb[y, x] = imgL_ori_rgb[y, x]

 

이 과정을 통해 point cloud 를 획득할 수 있습니다. 

 

+ Point cloud processing : 이후 속도 증가 및 PC 노이즈 제거를 위해 다음과 같은 후처리 과정을 진행했습니다. 

 

# Voxel Downsampling
voxel_size = 0.05
pcd_down = pcd.voxel_down_sample(voxel_size=voxel_size)

# Statistical Outlier Removal
nb_neighbors = 20
std_ratio = 2.0
pcd_filtered, _ = pcd_down.remove_statistical_outlier(nb_neighbors=nb_neighbors, std_ratio=std_ratio)

*사진을 이용해서 3차원을 복원할 때 주의할 점 : 

1. 사진을 잘 찍어야 합니다. 

이 사진처럼 뒷배경이 균일한 상황에서는 Disparity Map 이 잘 생성되지 않기 때문에 매칭이 잘 될 수 있도록 사진을 잘 찍어야 합니다. 

 

2. OpenCV 는 컬러값을 BGR 을, Open3D 는 RGB 를 사용하기 때문에, 컬러값을 가져올 때 주의해야 합니다. 

 

전체 코드

stereo_matching_final.ipynb
6.22MB
left_right_image.zip
0.48MB

 


위 내용은 경희대학교 소프트웨어융합학과 황효석 교수님의 2023년 <3D데이터처리> 숙제 제출 내용입니다. 

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.