Knowhow/Vision

BFM face model 파라미터로 변형하기 (cropped BFM 2009 버전 예시)

침닦는수건 2024. 7. 30. 18:37
반응형

https://faces.dmi.unibas.ch/bfm/index.php?nav=1-1-0&id=details

 

Morphace

This page is part of the old Basel Face Model from 2009, find most recent Basel Face Model here. Basel Face Model - Details Details of the Basel Face Model The geometry of the BFM consists of 53,490 3D vertices connected by 160,470 triangles. Faces of diff

faces.dmi.unibas.ch

 

BFM은 2009년 처음 만들어진 모델이지만 2017년, 2019년 총 2차례에 걸쳐 리뉴얼될 정도로 지금까지 관리 잘되고 실제로 표현력도 훌륭한 face morphable model이다. 

 

2009년은 mat 파일로 2017년/2019년에는 h5 파일로 제공되고 있는데 파일 포맷에 상관없이 공통적으로 다음 값을 갖고 있다.

 

1) meanshape (N, 3) 

2) idBase (N*3, 80)

3) exBase (N*3, 64)

4) texBase (N*3, 80) 

 

PCA 모델이다보니 2)~4)는 각각 사람의 id, expression, texture를 변형시키는 eigenvector를 표현하는 값이고, 여기에 적당한 eigenvalue (i.e. coefficient)들이 곱해질 경우 face deformation (offset)을 얻을 수 있다. 

 

근데 이게 dimension이 저 모양이다 보니 곱하는게 가끔 헷갈리고 대충 구현하면 속도가 느리다. 

 

다음와 같이 einsum을 이용해 구현하는 것이 가장 빠르다. 

 

import os
import cv2
import trimesh
import numpy as np
import open3d as o3d
from scipy.io import loadmat

def random_bfm(bfm_path):
    bfm_meta_data = loadmat(bfm_path)
    vertex = bfm_meta_data['meanshape'].reshape(-1, 3)
    faces = bfm_meta_data['tri'] - 1
    color = bfm_meta_data['meantex'].reshape(-1, 3)/255.0

    id_base = bfm_meta_data["idBase"]
    ex_base = bfm_meta_data["exBase"]
    tex_base = bfm_meta_data["texBase"]

    id_coeff = np.random.randn(80).reshape(1, -1)
    ex_coeff = np.random.randn(64).reshape(1, -1)
    tex_coeff = np.random.randn(80).reshape(1, -1)

    offset_id = np.einsum("ij, aj->ai", id_base, id_coeff)[0].reshape(-1, 3)
    offset_ex = np.einsum("ij, aj->ai", ex_base, ex_coeff)[0].reshape(-1, 3)
    offset_tex = np.einsum("ij, aj->ai", tex_base, tex_coeff)[0].reshape(-1, 3) / 255.0


    v_all = offset_id + offset_ex + vertex
    color_all = color + offset_tex
    color_all = np.clip(color_all, 0, 1)

    mesh_raw = o3d.geometry.TriangleMesh()
    mesh_raw.vertices = o3d.utility.Vector3dVector(vertex)
    mesh_raw.triangles = o3d.utility.Vector3iVector(faces)
    mesh_raw.vertex_colors = o3d.utility.Vector3dVector(color)
    o3d.visualization.draw_geometries([mesh_raw])

    mesh_deformed = o3d.geometry.TriangleMesh()
    mesh_deformed.vertices = o3d.utility.Vector3dVector(v_all)
    mesh_deformed.triangles = o3d.utility.Vector3iVector(faces)
    mesh_deformed.vertex_colors = o3d.utility.Vector3dVector(color_all)
    o3d.visualization.draw_geometries([mesh_deformed])
    return mesh_deformed

if __name__ == "__main__":
    root = "/home/jseob/Desktop/yjs/codes/pytorch-nicp/BFM"
    bfm_path = os.path.join(root, "BFM09_model_info.mat")

    mesh = random_bfm(bfm_path)
    print("")

 

위와 같이 변형된 결과를 가장 빠르게 얻을 수 있다. torch를 쓴다고 해도 torch.einsum으로 구현하는게 가장 빠르다.

반응형