Knowhow/Vision

3D mesh voxelization

침닦는수건 2022. 6. 17. 17:21
반응형

3D mesh가 차지하고 있는 voxel 공간, 즉, occupancy volume을 구하기 위해 mesh를 voxel화 하는 작업이 필요할 때가 있다.

 

vertex와 face로 구성된 mesh로부터 voxel을 얻어내는 것이 간단해보이지만 막상 구현해보면 꽤나 복잡하고 동작 시간도 오래 걸린다.

 

이 작업을 아주 간단하게, 그리고 아주 빠르진 않지만 직접 구현하는 것보단 빠르게 해결할 수 있는 방법이 있다.

 

binvox라는 프로그램을 사용하는 것인데, 아래 링크에 접속해서 executable 파일을 OS 맞게 받은 후 실행시키면 된다.

 

https://www.patrickmin.com/binvox/ 

 

한 개인이 박사 과정 중에 구현해서 공유한 것인데, 유용하게 사용할 수준이다. 단, 모든 axis dimension이 같은 큐브 형태로만 voxel화 된다. (보통 그렇게 하니 문제될 건 없다.)

 

사용 커맨드는 다음과 같다. 결과는 동일 경로에 확장자만 .binvox로 표시되어 저장된다.

 

./binvox -d VOXEL_DIM -pb TARGET_OBJ_PATH # Linux
binvox.exe -d VOXEL_DIM TARGET_OBJ_PATH # Window

## example

./binvox -d 128 -pb ./data/mesh.obj # Linux
binvox.exe -d 128 ./data/mesh.obj # Window

 

그리고 binvox 파일을 사용하기 쉽게 numpy array로 변환하기 위해 작성한 코드는 다음과 같다. 

def read_header(fvox):
    line = fvox.readline().strip()
    if not line.startswith(b'#binvox'):
        raise IOError('Not a binvox file')
    dims = list(map(int, fvox.readline().strip().split(b' ')[1:]))
    translate = list(map(float, fvox.readline().strip().split(b' ')[1:]))
    scale = list(map(float, fvox.readline().strip().split(b' ')[1:]))[0]
    line = fvox.readline()

    return dims, translate, scale

def binvox2numpy(file_path):
    if not os.path.exists(file_path):
        raise Exception(F"{} does not exist.")

    fvox = open(file_path, 'rb')
    dims, translate, scale = read_header(fvox)
    raw_data = np.frombuffer(fvox.read(), dtype=np.uint8)
    values, counts = raw_data[::2], raw_data[1::2]

    values = values.astype(bool)

    data = np.repeat(values, counts).astype(bool)
    data = data.reshape(dims)
    data = np.transpose(data, (0, 2, 1)) # order to xyz

    xyz = np.argwhere(data==True) / dims[0]
    xyz = xyz*scale + translate

    return data, xyz

data는 voxel 공간을 true, false로 표현한 3차원 numpy array이고, xyz는 각 occupied voxel의 중심점을 실제 scale로 만들었을 때 좌표이다. 

 

예시 결과로 만들어진 xyz, 즉 occupied voxel을 point cloud로 만들어 시각화 하면 다음과 같다. 

 

 

반응형