Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
3D Deep Learning with Python

You're reading from   3D Deep Learning with Python Design and develop your computer vision model with 3D data using PyTorch3D and more

Arrow left icon
Product type Paperback
Published in Oct 2022
Publisher Packt
ISBN-13 9781803247823
Length 236 pages
Edition 1st Edition
Languages
Arrow right icon
Authors (4):
Arrow left icon
Xudong Ma Xudong Ma
Author Profile Icon Xudong Ma
Xudong Ma
Vishakh Hegde Vishakh Hegde
Author Profile Icon Vishakh Hegde
Vishakh Hegde
Lilit Yolyan Lilit Yolyan
Author Profile Icon Lilit Yolyan
Lilit Yolyan
David Farrugia David Farrugia
Author Profile Icon David Farrugia
David Farrugia
Arrow right icon
View More author details
Toc

Table of Contents (16) Chapters Close

Preface 1. PART 1: 3D Data Processing Basics
2. Chapter 1: Introducing 3D Data Processing FREE CHAPTER 3. Chapter 2: Introducing 3D Computer Vision and Geometry 4. PART 2: 3D Deep Learning Using PyTorch3D
5. Chapter 3: Fitting Deformable Mesh Models to Raw Point Clouds 6. Chapter 4: Learning Object Pose Detection and Tracking by Differentiable Rendering 7. Chapter 5: Understanding Differentiable Volumetric Rendering 8. Chapter 6: Exploring Neural Radiance Fields (NeRF) 9. PART 3: State-of-the-art 3D Deep Learning Using PyTorch3D
10. Chapter 7: Exploring Controllable Neural Feature Fields 11. Chapter 8: Modeling the Human Body in 3D 12. Chapter 9: Performing End-to-End View Synthesis with SynSin 13. Chapter 10: Mesh R-CNN 14. Index 15. Other Books You May Enjoy

3D data file format – OBJ files

In this section, we are going to discuss another widely used 3D data file format, the OBJ file format. The OBJ file format was first developed by Wavefront Technologies Inc. Like the PLY file format, the OBJ format also has both an ASCII version and a binary version. The binary version is proprietary and undocumented. So, we are going to discuss the ASCII version in this section.

Like the previous section, here we are going to learn the file format by looking at examples. The first example, cube.obj, is shown as follows. As you can guess, the OBJ file defines a mesh of a cube.

The first line, mtlib ./cube.mtl, declares the companion Material Template Library (MTL) file. The MTL file describes surface shading properties, which will be explained in the next code snippet.

For the o cube line, the starting letter, o, indicates that the line defines an object, where the name of the object is cube. Any line starting with # is a comment line – that is, the rest of the line will be ignored by a computer. Each line starts with v, which indicates that each line defines a vertex. For example, v -0.5 -0.5 0.5 defines a vertex with an x coordinate of 0.5, a y coordinate of 0.5, and a z coordination of 0.5. For each line starting with f, f indicates that each line contains a definition for one face. For example, the f 1 2 3 line defines a face, with its three vertices being the vertices with indices 1, 2, and 3.

The usemtl Door line declares that the surfaces declared after this line should be shaded using a material property defined in the MTL file, named Door:

mtllib ./cube.mtl
o cube
# Vertex list
v -0.5 -0.5 0.5
v -0.5 -0.5 -0.5
v -0.5 0.5 -0.5
v -0.5 0.5 0.5
v 0.5 -0.5 0.5
v 0.5 -0.5 -0.5
v 0.5 0.5 -0.5
v 0.5 0.5 0.5
# Point/Line/Face list
usemtl Door
f 1 2 3
f 6 5 8
f 7 3 2
f 4 8 5
f 8 4 3
f 6 2 1
f 1 3 4
f 6 8 7
f 7 2 6
f 4 5 1
f 8 3 7
f 6 1 5

The cube.mtl companion MTL file is shown as follows. The file defines a material property called Door:

newmtl Door
Ka  0.8 0.6 0.4
Kd  0.8 0.6 0.4
Ks  0.9 0.9 0.9
d  1.0
Ns  0.0
illum 2

We will not discuss these material properties in detail except for map_Kd. If you are curious, you can refer to a standard computer graphics textbook such as Computer Graphics: Principles and Practice. We will list some rough descriptions of these properties as follows, just for the sake of completeness:

  • Ka: Specifies an ambient color
  • Kd: Specifies a diffuse color
  • Ks: Specifies a specular color
  • Ns: Defines the focus of specular highlights
  • Ni: Defines the optical density (a.k.a index of refraction)
  • d: Specifies a factor for dissolve
  • illum: Specifies an illumination model
  • map_Kd: Specifies a color texture file to be applied to the diffuse reflectivity of the material

The cube.obj file can be opened by both Open3D and PyTorch3D. The following code snippet, obj_example1.py, can be downloaded from our GitHub repository:

import open3d
from pytorch3d.io import load_obj
mesh_file = "cube.obj"
print('visualizing the mesh using open3D')
mesh = open3d.io.read_triangle_mesh(mesh_file)
open3d.visualization.draw_geometries([mesh],
                 mesh_show_wireframe = True,
                 mesh_show_back_face = True)
print("Loading the same file with PyTorch3D")
vertices, faces, aux = load_obj(mesh_file)
print('Type of vertices = ', type(vertices))
print("Type of faces = ", type(faces))
print("Type of aux = ", type(aux))
print('vertices = ', vertices)
print('faces = ', faces)
print('aux = ', aux)

In the preceding code snippet, the defined mesh of a cube can be interactively visualized by using the Open3D draw_geometries function. The mesh will be shown in a window, and you can rotate, zoom into, and zoom out of the mesh using your mouse. The mesh can also be loaded using the PyTorch3D load_obj function. The load_obj function will return the vertices, faces, and aux variables, either in the format of a PyTorch tensor or tuples of PyTorch tensors.

An example output of the obj_example1.py code snippet is shown as follows:

visualizing the mesh using open3D
Loading the same file with PyTorch3D
Type of vertices =  <class 'torch.Tensor'>
Type of faces =  <class 'pytorch3d.io.obj_io.Faces'>
Type of aux =  <class 'pytorch3d.io.obj_io.Properties'>
vertices =  tensor([[-0.5000, -0.5000,  0.5000],
        [-0.5000, -0.5000, -0.5000],
        [-0.5000,  0.5000, -0.5000],
        [-0.5000,  0.5000,  0.5000],
        [ 0.5000, -0.5000,  0.5000],
        [ 0.5000, -0.5000, -0.5000],
        [ 0.5000,  0.5000, -0.5000],
        [ 0.5000,  0.5000,  0.5000]])
faces =  Faces(verts_idx=tensor([[0, 1, 2],
        [5, 4, 7],
        [6, 2, 1],
        ...
        [3, 4, 0],
        [7, 2, 6],
        [5, 0, 4]]), normals_idx=tensor([[-1, -1, -1],
        [-1, -1, -1],
        [-1, -1, -1],
        [-1, -1, -1],
        ...
        [-1, -1, -1],
        [-1, -1, -1]]), textures_idx=tensor([[-1, -1, -1],
        [-1, -1, -1],
        [-1, -1, -1],
        ...
        [-1, -1, -1],
        [-1, -1, -1]]), materials_idx=tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]))
aux =  Properties(normals=None, verts_uvs=None, material_colors={'Door': {'ambient_color': tensor([0.8000, 0.6000, 0.4000]), 'diffuse_color': tensor([0.8000, 0.6000, 0.4000]), 'specular_color': tensor([0.9000, 0.9000, 0.9000]), 'shininess': tensor([0.])}}, texture_images={}, texture_atlas=None)

From the code snippet output here, we know that the returned vertices variable is a PyTorch tensor with a shape of 8 x 3, where each row is a vertex with the x, y, and z coordinates. The returned variable, faces, is a named tuple of three PyTorch tensors, verts_idx, normals_idx, and textures_idx. In the preceding example, all the normals_idx and textures_idx tensors are invalid because cube.obj does not include definitions for normal and textures. We will see in the next example how normals and textures can be defined in the OBJ file format. verts_idx is the vertex indices for each face. Note that the vertex indices are 0-indexed here in PyTorch3D, where the indices start from 0. However, the vertex indices in OBJ files are 1-indexed, where the indices start from 1. PyTorch3D has already made the conversion between the two ways of vertex indexing for us.

The return variable, aux, contains some extra mesh information. Note that the texture_image field of the aux variable is empty. The texture images are used in MTL files to define colors on vertices and faces. Again, we will show how to use this feature in our next example.

In the second example, we will use an example cube_texture.obj file to highlight more OBJ file features. The file is shown as follows.

The cube_texture.obj file is like the cube.obj file, except for the following differences:

  • There are some additional lines starting with vt. Each such line declares a texture vertex with x and y coordinates. Each texture vertex defines a color. The color is the pixel color at a so-called texture image, where the pixel location is the x coordinate of the texture vertex x width, and the y coordinate of the texture vertex x height. The texture image would be defined in the cube_texture.mtl companion.
  • There are additional lines starting with vn. Each such line declares a normal vector – for example, the vn 0.000000 -1.000000 0.000000 line declares a normal vector pointing to the negative z axis.
  • Each face definition line now contains more information about each vertex. For example, the f 2/1/1 3/2/1 4/3/1 line contains the definitions for the three vertices. The first triple, 2/1/1, defines the first vertex, the second triple, 3/2/1, defines the second vertex, and the third triple, 4/3/1, defines the third vertex. Each such triplet is the vertex index, texture vertex index, and normal vector index. For example, 2/1/1 defines a vertex, where the vertex geometric location is defined in the second line starting with v, the color is defined in the first line starting with vt, and the normal vector is defined in the first line starting with vn:
mtllib cube_texture.mtl
v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -0.999999
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
vt 1.000000 0.333333
vt 1.000000 0.666667
vt 0.666667 0.666667
vt 0.666667 0.333333
vt 0.666667 0.000000
vt 0.000000 0.333333
vt 0.000000 0.000000
vt 0.333333 0.000000
vt 0.333333 1.000000
vt 0.000000 1.000000
vt 0.000000 0.666667
vt 0.333333 0.333333
vt 0.333333 0.666667
vt 1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 1.000000 0.000000 0.000000
vn -0.000000 0.000000 1.000000
vn -1.000000 -0.000000 -0.000000
vn 0.000000 0.000000 -1.000000
g main
usemtl Skin
s 1
f 2/1/1 3/2/1 4/3/1
f 8/1/2 7/4/2 6/5/2
f 5/6/3 6/7/3 2/8/3
f 6/8/4 7/5/4 3/4/4
f 3/9/5 7/10/5 8/11/5
f 1/12/6 4/13/6 8/11/6
f 1/4/1 2/1/1 4/3/1
f 5/14/2 8/1/2 6/5/2
f 1/12/3 5/6/3 2/8/3
f 2/12/4 6/8/4 3/4/4
f 4/13/5 3/9/5 8/11/5
f 5/6/6 1/12/6 8/11/6

The cube_texture.mtl companion is as follows, where the line starting with map_Kd declares the texture image. Here, wal67ar_small.jpg is a 250 x 250 RGB image file in the same folder as the MTL file:

newmtl Skin
Ka 0.200000 0.200000 0.200000
Kd 0.827451 0.792157 0.772549
Ks 0.000000 0.000000 0.000000
Ns 0.000000
map_Kd ./wal67ar_small.jpg

Again, we can use Open3D and PyTorch3D to load the mesh in the cube_texture.obj file – for example, by using the following obj_example2.py file:

import open3d
from pytorch3d.io import load_obj
import torch
mesh_file = "cube_texture.obj"
print('visualizing the mesh using open3D')
mesh = open3d.io.read_triangle_mesh(mesh_file)
open3d.visualization.draw_geometries([mesh],
                  mesh_show_wireframe = True,
                  mesh_show_back_face = True)
print("Loading the same file with PyTorch3D")
vertices, faces, aux = load_obj(mesh_file)
print('Type of vertices = ', type(vertices))
print("Type of faces = ", type(faces))
print("Type of aux = ", type(aux))
print('vertices = ', vertices)
print('faces = ', faces)
print('aux = ', aux)
texture_images = getattr(aux, 'texture_images')
print('texture_images type = ', type(texture_images))
print(texture_images['Skin'].shape)

The output of the obj_example2.py code snippet should be as follows:

visualizing the mesh using open3D
Loading the same file with PyTorch3D
Type of vertices =  <class 'torch.Tensor'>
Type of faces =  <class 'pytorch3d.io.obj_io.Faces'>
Type of aux =  <class 'pytorch3d.io.obj_io.Properties'>
vertices =  tensor([[ 1.0000, -1.0000, -1.0000],
        [ 1.0000, -1.0000,  1.0000],
        [-1.0000, -1.0000,  1.0000],
        [-1.0000, -1.0000, -1.0000],
        [ 1.0000,  1.0000, -1.0000],
        [ 1.0000,  1.0000,  1.0000],
        [-1.0000,  1.0000,  1.0000],
        [-1.0000,  1.0000, -1.0000]])
faces =  Faces(verts_idx=tensor([[1, 2, 3],
        [7, 6, 5],
        [4, 5, 1],
        [5, 6, 2],
        [2, 6, 7],
        [0, 3, 7],
        [0, 1, 3],
        ...
        [3, 3, 3],
        [4, 4, 4],
        [5, 5, 5]]), textures_idx=tensor([[ 0,  1,  2],
        [ 0,  3,  4],
        [ 5,  6,  7],
        [ 7,  4,  3],
        [ 8,  9, 10],
        [11, 12, 10],
        ...
        [12,  8, 10],
        [ 5, 11, 10]]), materials_idx=tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]))
aux =  Properties(normals=tensor([[ 0., -1.,  0.],
        [ 0.,  1.,  0.],
        [ 1.,  0.,  0.],
        [-0.,  0.,  1.],
        [-1., -0., -0.],
        [ 0.,  0., -1.]]), verts_uvs=tensor([[1.0000, 0.3333],
        ...
        [0.3333, 0.6667],
        [1.0000, 0.0000]]), material_colors={'Skin': {'ambient_color': tensor([0.2000, 0.2000, 0.2000]), 'diffuse_color': tensor([0.8275, 0.7922, 0.7725]), 'specular_color': tensor([0., 0., 0.]), 'shininess': tensor([0.])}}, texture_images={'Skin': tensor([[[0.2078, 0.1765, 0.1020],
         [0.2039, 0.1725, 0.0980],
         [0.1961, 0.1647, 0.0902],
         ...,
          [0.2235, 0.1882, 0.1294]]])}, texture_atlas=None)
texture_images type =  <class 'dict'>
Skin
torch.Size([250, 250, 3])

Note

This is not the complete output; please check this while you run the code.

Compared with the output of the obj_example1.py code snippet, the preceding output has the following differences.

  • The normals_idx and textures_idx fields of the faces variable all contain valid indices now instead of taking a -1 value.
  • The normals field of the aux variable is a PyTorch tensor now, instead of being None.
  • The verts_uvs field of the aux variable is a PyTorch tensor now, instead of being None.
  • The texture_images field of the aux variable is not an empty dictionary any longer. The texture_images dictionary contains one entry with a key, Skin, and a PyTorch tensor with a shape of (250, 250, 3). This tensor is exactly the same as the image contained in the wal67ar_small.jpg file, as defined in the mtl_texture.mtl file.

We have learned how to use basic 3D data file formats and PLY and OBJ files. In the next section, we will learn the basic concepts of 3D coordination systems.

You have been reading a chapter from
3D Deep Learning with Python
Published in: Oct 2022
Publisher: Packt
ISBN-13: 9781803247823
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime