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

3D 재구성을 위한 스테레오 비전

by j2b2 2023. 9. 13.

https://medium.com/analytics-vidhya/depth-sensing-and-3d-reconstruction-512ed121aa60

 

Depth Sensing and 3D Reconstruction

Earlier this year, Tesla shared an impressive video showing off their 3D Sensing technology. Their system takes in camera images and…

medium.com

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


올해 초 Tesla는 3D 감지 기술을 보여주는 인상적인 비디오를 공유했습니다. 그들의 시스템은 카메라 이미지를 가져와 주변의 포인트 클라우드를 출력하는데, 이는 LiDAR 포인트 클라우드와 거의 경쟁적입니다.

 

 

https://www.instagram.com/p/BwnWLgohmUE/?utm_source=ig_web_copy_link

 

 

이것의 장점은 무엇입니까? 2D로 사진을 캡처하면 원근 투영이라는 프로세스로 인해 모든 깊이 정보가 손실됩니다. 3D 포인트 클라우드를 얻는 것은 도로 위의 다양한 물체를 정확하게 측정할 수 있으므로 자율 주행 기술에 매우 중요합니다. 깊이 감지는 Computer Vision에서 널리 사용되는 작업이며, 이 문제를 해결하기 위한 다양한 접근 방식이 존재합니다. 주석이 달린 깊이 맵을 신경망의 입력으로 가져와 모든 입력 이미지에 대한 깊이 맵을 생성하도록 훈련할 수 있는 엔드 투 엔드 학습 방법이 인기를 얻고 있습니다. 그러나 첫 번째 원칙을 기반으로 하는 전통적인 접근 방식도 있습니다. 그래서 바로 딥러닝으로 넘어가기보다는 이 접근 방식이 어떻게 작동하는지 살펴보고 싶었습니다. 이미지에서 깊이 정보를 얻을 수 있고 포인트 클라우드도 얻을 수 있지만 하나의 이미지만으로는 이를 수행할 수 없습니다. 이 방법을 스테레오 비전이라고 하며 작동 방식은 인간의 눈과 매우 유사합니다. 왼쪽 눈과 오른쪽 눈이 보는 것을 비교함으로써 우리는 환경의 깊이를 이해할 수 있습니다. 이 기술을 더 잘 이해할 수 있는 리소스가 많이 있으므로 계속해서 진행해 보겠습니다.

 

연습을 통한 학습이 매우 효과적이라는 것을 알았으므로 도로/차선 감지를 위한 KITTI 벤치마크 데이터를 사용하여 이 방법을 빠르게 살펴보겠습니다.

 

이 스테레오 이미지 세트에서

 

3D 포인트 클라우드를 얻습니다.

 

스테레오 비전을 사용한 3D 포인트 클라우드 재구성

첫 번째 단계는 왼쪽 및 오른쪽 이미지를 로드하고 스테레오 이미지에서 시차 맵을 획득하는 것입니다. 스테레오 이미지 세트에 대한 시차 이미지는 각 픽셀이 이미지 1의 픽셀과 이미지 2의 일치하는 픽셀 사이의 거리를 나타내는 이미지로 정의됩니다. 이를 수행하는 방법에는 여러 가지가 있으며, 우리는 opencv StereoBM에서 제공하는 블록 매칭 접근 방식을 사용할 것입니다. 좋은 시차 맵을 얻기 위해 블록 일치 알고리즘에 대해 numDisparities, blockSize를 설정하여 개체를 초기화합니다. Compute()에 대한 입력은 회색조의 왼쪽 및 오른쪽 이미지입니다. 시차 계산을 위한 더 나은 알고리즘이 있지만 이 데모에서는 그것만으로도 충분합니다. OpenCV는 시도해 볼 가치가 있는 다른 일치 알고리즘을 제공하며, 이 외에도 많은 문헌이 있습니다. 이미 수정된 이미지가 있으므로 시차 맵 생성으로 바로 이동할 수 있지만 다른 경우에는 먼저 이미지의 왜곡을 제거하는 것이 중요합니다.

 

stereo = cv2.StereoBM_create(numDisparities=96, blockSize=11)
disparity = stereo.compute(img_left_gray,img_right_gray)

격차 지도

위의 이미지에서 볼 수 있듯이 매칭이 항상 완벽하지는 않으며, 약간의 노이즈와 시차에 구멍이 있습니다. 또한 왼쪽 이미지의 프레임에 차이가 있고 오른쪽 이미지의 흰색 패치에 해당하는 일치 항목이 없기 때문에 왼쪽 열은 비어 있습니다. 따라서 재구성할 때 포인트 클라우드에서 이 부분이 누락되고 대칭적으로 오른쪽 이미지의 오른쪽 열도 누락됩니다.

 

이제 시차 맵이 있으므로 이를 사용하여 XYZRGB 정보가 포함된 3D 포인트 클라우드를 얻을 수 있습니다. 이를 위해 깊이 정보를 얻을 수 있도록 시차 맵을 변환해야 합니다. 이를 위해서는 시차 대 깊이 행렬이 필요합니다. 이를 stereoRectify()로 계산하겠습니다. 두 이미지에 대한 교정 정보를 입력으로 제공할 수 있으며 이는 깊이 대 깊이 투영 행렬을 출력합니다. 두 이미지는 이미 왜곡되지 않고 수정된 좌표계에 있으므로 두 개의 카메라 행렬과 그 사이의 변환 T만 필요합니다. 이 행렬 T는 KITTI 센서 설정에서 계산됩니다.

 

cam1 = calib_matrix_P2[:,:3] # left color image
cam2 = calib_matrix_P3[:,:3] # right color image

rev_proj_matrix = np.zeros((4,4)) # to store the output

cv2.stereoRectify(cameraMatrix1 = cam1,cameraMatrix2 = cam2,
                  distCoeffs1 = 0, distCoeffs2 = 0,
                  imageSize = img_left_color.shape[:2],
                  R = np.identity(3), T = np.array([0.54, 0., 0.]),
                  R1 = None, R2 = None,
                  P1 =  None, P2 =  None, 
                  Q = rev_proj_matrix)

 

이제 시차 맵을 3D 포인트 클라우드에 투영하기 위해 reprojectImageTo3D()를 사용할 수 있습니다.

 

points = cv2.reprojectImageTo3D(img, rev_proj_matrix)

 

포인트 클라우드가 미러링된 것을 볼 수 있습니다. 이미지의 X축이 미러링되기 때문에 이는 예상된 현상입니다. X축을 따라 간단한 2D 반사를 사용하여 이 문제를 해결할 수 있습니다.

 

획득한 포인트를 색상 정보와 함께 저장해 보겠습니다. opencv 3D 재구성 예제의 일부 코드를 다시 사용하겠습니다. 이미지에서 색상을 추출하여 포인트 클라우드에 추가하려면 이미지에서 색상을 가져와 포인트 클라우드 모양과 일치하도록 모양을 변경하기만 하면 됩니다.

 

# opencv loads the image in BGR, so lets make it RGB
colors = cv2.cvtColor(img_left_color, cv2.COLOR_BGR2RGB)
# resize to match point cloud shape
colors = colors.resize(-1, 3)

 

이제 XYZRGB 포인트 클라우드를 플라이 파일에 작성할 수 있습니다. 이는 일반적으로 사용되는 포인트 클라우드 형식이며 Meshlab으로 시각화할 수 있습니다.

 

write_ply('out.ply', out_points, out_colors)

 

포인트 클라우드는 깊이 정보를 매우 훌륭하게 포착하고 한 쌍의 2D 이미지에서 얻은 것이라고 믿기 어렵기 때문에 포인트 클라우드를 보는 것은 매우 흥미롭습니다. 메쉬랩의 모든 각도에서 포인트 클라우드를 자유롭게 감상해 보세요. 아래 이미지에서도 일부 결함이 분명하지만 대부분의 경우 지면에 더 가까운 표면이 꽤 좋아 보입니다. 더 나은 시차 맵을 사용하면 포인트 클라우드 품질을 개선하는 것이 더 쉽습니다.

 

도트 데코레이터 셰이딩을 사용하여 meshlab에서 시각화됨


 

이미지로 투사

이제 원래의 (일종의) 포인트 클라우드를 얻기 위해 뒤로 갔으므로 처음 시작한 이미지로 다시 앞으로 이동할 수 있습니다. 3D 포인트 클라우드를 이미지 프레임에 투영하는 데는 다양한 응용 프로그램이 있습니다. 게다가 이는 포인트 클라우드의 품질을 확인하고 우리가 따른 프로세스가 완전하고 무손실인지 확인하는 좋은 방법입니다.

 

일반적으로 이 단계에는 포인트 클라우드와 이미지 좌표계 간의 외부 교정이 필요합니다. 포인트 클라우드는 이미지 프레임을 기반으로 생성되므로 둘 사이의 회전 및 이동이 없으며 이미지가 이미 왜곡되지 않았으므로 왜곡 계수도 없습니다. X축을 뒤집었으므로 동일한 반사를 적용하여 다시 뒤집을 것임을 기억하세요. 그리고 opencv projectPoints()를 사용하여 점을 투영하기 위해 카메라 매트릭스를 사용합니다. 이 함수는 각 3D 포인트에 대해 2D 투영 포인트를 반환합니다.

 

cv2.projectPoints(reflected_points, np.identity(3), 
                  np.array([0., 0., 0.]),
                  cam1_matrix, np.array([0., 0., 0., 0.]))

 

마지막으로 각 픽셀을 원()으로 플롯하여 이미지에 점을 그릴 수 있습니다. 이 시점에서는 인덱스가 2D 투영 점과 정렬된 이전의 색상 배열을 사용합니다.

 

for i, pt in enumerate(projected_points):
    pt_x = int(pt[0])
    pt_y = int(pt[1])
    # use the BGR format to match the original image type
    col = (colors[i, 2], colors[i, 1], colors[i, 0])
    cv2.circle(blank_img, (pt_x, pt_y), 1, col)

 

 


내 github에 Kitti 데이터 세트를 자동으로 처리하는 Python 노트북이 있습니다. 자유롭게 살펴보고 직접 사용해 보세요. 위 주제에 대한 자세한 설명을 보려면 기사 전체의 링크를 따라가면 됩니다. 읽어 주셔서 감사합니다.