alexnasa's picture
Upload 66 files
cf92dec verified
import os
import numpy as np
import cv2
# borrowed from https://github.com/YadiraF/PRNet/blob/master/utils/write.py
def write_obj(obj_name,
vertices,
faces,
colors=None,
texture=None,
uvcoords=None,
uvfaces=None,
inverse_face_order=False,
normal_map=None,
):
''' Save 3D face model with texture.
Ref: https://github.com/patrikhuber/eos/blob/bd00155ebae4b1a13b08bf5a991694d682abbada/include/eos/core/Mesh.hpp
Args:
obj_name: str
vertices: shape = (nver, 3)
colors: shape = (nver, 3)
faces: shape = (ntri, 3)
texture: shape = (uv_size, uv_size, 3)
uvcoords: shape = (nver, 2) max value<=1
'''
if os.path.splitext(obj_name)[-1] != '.obj':
obj_name = obj_name + '.obj'
mtl_name = obj_name.replace('.obj', '.mtl')
texture_name = obj_name.replace('.obj', '.png')
material_name = 'FaceTexture'
faces = faces.copy()
# mesh lab start with 1, python/c++ start from 0
faces += 1
if inverse_face_order:
faces = faces[:, [2, 1, 0]]
if uvfaces is not None:
uvfaces = uvfaces[:, [2, 1, 0]]
# write obj
with open(obj_name, 'w') as f:
# first line: write mtlib(material library)
# f.write('# %s\n' % os.path.basename(obj_name))
# f.write('#\n')
# f.write('\n')
if texture is not None:
f.write('mtllib %s\n\n' % os.path.basename(mtl_name))
# write vertices
if colors is None:
for i in range(vertices.shape[0]):
f.write('v {} {} {}\n'.format(vertices[i, 0], vertices[i, 1], vertices[i, 2]))
else:
for i in range(vertices.shape[0]):
f.write('v {} {} {} {} {} {}\n'.format(vertices[i, 0], vertices[i, 1], vertices[i, 2], colors[i, 0], colors[i, 1], colors[i, 2]))
# write uv coords
if texture is None and (uvcoords is None or uvfaces is None):
for i in range(faces.shape[0]):
f.write('f {} {} {}\n'.format(faces[i, 0], faces[i, 1], faces[i, 2]))
else:
for i in range(uvcoords.shape[0]):
f.write('vt {} {}\n'.format(uvcoords[i,0], uvcoords[i,1]))
f.write('usemtl %s\n' % material_name)
# write f: ver ind/ uv ind
uvfaces = uvfaces + 1
for i in range(faces.shape[0]):
f.write('f {}/{} {}/{} {}/{}\n'.format(
# faces[i, 2], uvfaces[i, 2],
# faces[i, 1], uvfaces[i, 1],
# faces[i, 0], uvfaces[i, 0]
faces[i, 0], uvfaces[i, 0],
faces[i, 1], uvfaces[i, 1],
faces[i, 2], uvfaces[i, 2]
)
)
# write mtl
if texture is not None:
with open(mtl_name, 'w') as f:
f.write('newmtl %s\n' % material_name)
s = 'map_Kd {}\n'.format(os.path.basename(texture_name)) # map to image
f.write(s)
if normal_map is not None:
name, _ = os.path.splitext(obj_name)
normal_name = f'{name}_normals.png'
f.write(f'disp {normal_name}')
# out_normal_map = normal_map / (np.linalg.norm(
# normal_map, axis=-1, keepdims=True) + 1e-9)
# out_normal_map = (out_normal_map + 1) * 0.5
cv2.imwrite(
normal_name,
# (out_normal_map * 255).astype(np.uint8)[:, :, ::-1]
normal_map
)
cv2.imwrite(texture_name, texture)
## load obj, similar to load_obj from pytorch3d
def load_obj(obj_filename):
""" Ref: https://github.com/facebookresearch/pytorch3d/blob/25c065e9dafa90163e7cec873dbb324a637c68b7/pytorch3d/io/obj_io.py
Load a mesh from a file-like object.
"""
with open(obj_filename, 'r') as f:
lines = [line.strip() for line in f]
verts, uvcoords = [], []
colors = []
faces, uv_faces = [], []
# startswith expects each line to be a string. If the file is read in as
# bytes then first decode to strings.
if lines and isinstance(lines[0], bytes):
lines = [el.decode("utf-8") for el in lines]
for line in lines:
tokens = line.strip().split()
if line.startswith("v "): # Line is a vertex.
vert = [float(x) for x in tokens[1:4]]
if len(vert) != 3:
msg = "Vertex %s does not have 3 values. Line: %s"
raise ValueError(msg % (str(vert), str(line)))
verts.append(vert)
if len(tokens) > 4:
if '.' in tokens[4]:
color = [int(float(x)*255) for x in tokens[4:7]]
else:
color = [int(x) for x in tokens[4:7]]
if len(color) != 3:
msg = "Color %s does not have 3 values. Line: %s"
raise ValueError(msg % (str(vert), str(line)))
colors.append(color)
elif line.startswith("vt "): # Line is a texture.
tx = [float(x) for x in tokens[1:3]]
if len(tx) != 2:
raise ValueError(
"Texture %s does not have 2 values. Line: %s" % (str(tx), str(line))
)
uvcoords.append(tx)
elif line.startswith("f "): # Line is a face.
# Update face properties info.
face = tokens[1:]
face_list = [f.split("/") for f in face]
for vert_props in face_list:
# Vertex index.
faces.append(int(vert_props[0]))
if len(vert_props) > 1:
if vert_props[1] != "":
# Texture index is present e.g. f 4/1/1.
uv_faces.append(int(vert_props[1]))
verts = np.array(verts).astype(np.float32)
uvcoords = np.array(uvcoords ).astype(np.float32)
colors = np.array(colors).astype(int)
faces = np.array(faces).astype(np.int64); faces = faces.reshape(-1, 3) - 1
uv_faces = np.array(uv_faces).astype(np.int64); uv_faces = uv_faces.reshape(-1, 3) - 1
return verts, uvcoords, colors, faces, uv_faces