카테고리 없음

Instar360 ONE X2를 이용한 3D 360 VR 만들기

Wood Pecker 2021. 1. 26. 11:56

360 VR 파노라마 동영상 촬영은 보급형 장비가 많이 나오고 있다. 이런 장비들은 Sphere방식의 Monoscopic 360 Video 방식으로 아래 그림과 같이 구형 텍스처를 만들어 준다.

                                 Monoscopic 360 Video 방식

유니티에서 Inverted Sphere를 만들고 위와 같은 이미지를 텍스처로 Sphere 내부 면에 설정하고 Main Camera의 위치를 구의 원점에 설치하면 360VR 파노라마의 프로그램을 구현할 수 있다. Inverted Sphere를 만드는 방법은 아래의 사이트를 참조한다.

https://answers.unity.com/questions/514637/how-do-i-invert-normals-of-a-sphere.html

Stereoscopic 3D 360 Video

Monoscopic 360 Video는 입체가 아니다. 입체는 2대의 카메라의 시차효과를 활용하여 얻을 수 있다. 그러므로 360 동영상을 찍을 수 있는 장비를 나란히 장착하고 동시에 촬영을 한다. 동기화 찰영을 하면 좋겠지만 전문 장비가 아니라서 그런기능이 없다면 일단 최대한 동시에 버튼을 눌러 촬영하고 편집 단계에서 동기화 작업을 하면 된다.

위와 같이 보급형 360 파노라마 카메라를 Side by Side로 배치하고 동시 촬영을 한다. 그러면 두개의 동영상이 만들어진다. 각각의 동영상을 만들 때 편집프로그램에서 아래 그림과 같이 Lock Direction 옵션을 채크하도록 한다.

Processing for 3D

이제 2개의 동영상을 한개의 동영상 파일로 만들어 준다. 각각의 카메라로 찍은 동영상 파일은 동기화 되어있지 않다. 그래서 어느 지점이 동기화 지점인지를 알아내야한다. 찾는 방법은 특정 지점의 프레임을 기준으로 다른 동영상의 프레임과 동영상의 픽셀 substraction 이미지를 계산하고 픽셀 값의 전체 합이 제일 적은 위치의 프레임을 동기화 지점으로 찾을 수 있다. 아래의 프로그램은 동기되는 프레임의 위치를 알아내는 파이썬 프로그램이다. 비교적 잘 동작한다.

import numpy as np  
import cv2  
import sys  
import time  

def compareVideos(file1,file2,offseet_frame, search_range):  
    frame_count=0  
    frame2_count\=0  
    min_sumval= sys.maxsize  
    at_frame1=0  
    at_frame2=0  
    cap1 = cv2.VideoCapture('CameraA.mp4') # 5760x2880  
    cap2 = cv2.VideoCapture('CameraB.mp4') # 5760x2880  

    print("Processing1...")  
    while(cap1.isOpened() and cap2.isOpened()):  
        ret, frame1 = cap1.read()  
        frame_count = frame_count + 1  
        if frame_count<offseet_frame:  
            #print("skip frame1={:d}".format(frame_count))  
            continue  

        gray1 = cv2.cvtColor(frame1, cv2.COLOR\_BGR2GRAY)  
        frame2_count = 0  
        print("Processing2...")  
        while (cap1.isOpened() and cap2.isOpened()):  
             ret, frame2 = cap2.read()  
             frame2_count = frame2_count +1  
             #print("skip frame2={:d}".format(frame2_count))  
             if frame2_count < frame_count-search_range:  
                continue  

             if frame1 is None:  
                 break  
             if frame2 is None:  
                 break  
             gray2 = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)  
             diff_frame = gray1 - gray2  
             diff_count=frame_count -frame2_count  
             sumval=np.sum(diff\_frame)  
             if(min_sumval>sumval):  
                    min_sumval=sumval  
                    at_frame1 = frame_count  
                     at_frame2 = frame2_count  
             cv2.imshow('frame',diff_frame)  
             if frame2_count > frame_count+search_range:  
                  break  
             if cv2.waitKey(1) & 0xFF == ord('q'):  
                  break  
           break  
    cap1.release()  
    cap2.release()  
    cv2.destroyAllWindows()  
    return at_frame1,at_frame2  

if __name__ == '__main__':  
    file1= "CameraA.mp4"  
    file2= "CameraB.mp4"  
    at_frame1,at_frame2= compareVideos(file1,file2, offseet_frame=2800, search_range=500)  
    print("file1:"+str(at_frame1)+", file2:"+str(at_frame2)+" are believed to be sync points." )  
    pass  

동기되는 영상 위치를 적용하여 아래와 같이 Top-Bottom방식의 배치를 갖는 동영상을 만들어 보자.

instar360 ONE X2로 촬영한 영상은 5760x2880이다 이를 top-down Stereo Mode로 배치하면 5760x5760로 만들 수 있다. 그렇지만 현재의 일반적인 HMD 장비들이 용량상의 문제로 플레이가 안될 수 있다. 사이즈를 어쩔 수 없이 좀 줄여서 만들어 보자. 다음은 심플 파이썬 프로그램이다. (음성부분은 빠져 있다. )

# ffmpeg -i CameraA.mp4 -vcodec libx264 -f mp4 output.mp4  
# pip install opencv-pytyon  
# pip install numpy  
# pip install openh264  
import numpy as np  
import cv2  

def resizeSaveVideo():  
    cap1 = cv2.VideoCapture('CameraA.mp4') # 5760x2880  
    cap2 = cv2.VideoCapture('CameraB.mp4') # 5760x2880  
    fourcc = cv2.VideoWriter_fourcc(*'avc1')  
    out = None  
    frame_count=0  
    offseet_frame=180  
    FirstFlag= True  
    width=0  
    height=0  
    while(cap1.isOpened() and cap2.isOpened()):  
       ret,frame1= cap1.read()  
       frame_count = frame_count +1  
       if frame_count<offseet_frame:  
            print("skip frame={:d}".format(frame_count))  
            continue  
       ret, frame2 = cap2.read()  

       if frame1 is None:  
           break  
       if frame2 is None:  
           break  
       frame_count = frame_count + 1  
       scale_percent = 50  
       # calculate the 50 percent of original dimensions  
       if FirstFlag:  
            width = int(frame1.shape[1] * scale_percent / 100)  
            height = int(frame1.shape[0] * scale_percent / 100)  
            out = cv2.VideoWriter('output.mp4', fourcc, 20.0, (width, width)) # (width, width)  
            FirstFlag= False  

        print("frame =%d width =%d height=%d" %(frame_count, width, height))  

        #  dsize  
        dsize = (width, height)  
        outputFrame1 = cv2.resize(frame1, dsize)  
        outputFrame2 = cv2.resize(frame2, dsize)  
        numpy_vertical_concat = np.concatenate((outputFrame1, outputFrame2), axis=0)  
        out.write(numpy_vertical_concat)  
        #cv2.imshow('frame',numpy_vertical_concat)  

        if cv2.waitKey(1) & 0xFF == ord('q'):  
             break  
    cap1.release()  
    cap2.release()  
    out.release()  
    cv2.destroyAllWindows()  
    pass  

if __name__ == '__main__':  
    resizeSaveVideo()  
    pass  

Spatial Media Metadata Injector

유튜브에서 3D 360 파노라마 영상을 인식하기 위하여 영상 파일에 유튜브 방식의 메타태그를 넣어 주어야 한다.

위 링크에서 프로그램을 다운받아 실행한다.

https://github.com/google/spatial-media/releases/tag/v2.0

https://support.google.com/youtube/answer/6178631?hl=ko

유튜브에 게시하기 전에 컴퓨터에서 동영상 파일이 360도 재생을 지원하는지 확인할 수 있다. 360도 동영상은 왼쪽 상단에 이동 버튼이 표시되며 키보드의 W, A, S, D 키로 회전할 수 있다. 이러한 기능을 활용해 동영상의 360도 회전이 가능한지 확인해 본다. 유튜브에서 게시한 이후에 바로 공개되지 않고 용량에 따라 몇일 소요될 수도 있다.

 

반응형