Skip to content

Commit 22f8607

Browse files
Krzysztof Chalupkafacebook-github-bot
Krzysztof Chalupka
authored andcommitted
Submesh 4/n: TexturesVertex submeshing
Summary: Add submeshing capability for meshes with TexturesVertex. Reviewed By: bottler Differential Revision: D35448534 fbshipit-source-id: 6d16a31a5bfb24ce122cf3c300a7616bc58353d1
1 parent 050f650 commit 22f8607

File tree

3 files changed

+109
-7
lines changed

3 files changed

+109
-7
lines changed

pytorch3d/renderer/mesh/textures.py

+51
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,16 @@ def sample_textures(self) -> torch.Tensor:
242242
"""
243243
raise NotImplementedError()
244244

245+
def submeshes(
246+
self,
247+
vertex_ids_list: Optional[List[List[torch.LongTensor]]],
248+
faces_ids_list: Optional[List[List[torch.LongTensor]]],
249+
) -> "TexturesBase":
250+
"""
251+
Extract sub-textures used for submeshing.
252+
"""
253+
raise NotImplementedError(f"{self.__class__} does not support submeshes")
254+
245255
def faces_verts_textures_packed(self) -> torch.Tensor:
246256
"""
247257
Returns the texture for each vertex for each face in the mesh.
@@ -1465,6 +1475,47 @@ def sample_textures(self, fragments, faces_packed=None) -> torch.Tensor:
14651475
)
14661476
return texels
14671477

1478+
def submeshes(
1479+
self,
1480+
vertex_ids_list: Optional[List[List[torch.LongTensor]]],
1481+
faces_ids_list: Optional[List[List[torch.LongTensor]]],
1482+
) -> "TexturesVertex":
1483+
"""
1484+
Extract a sub-texture for use in a submesh.
1485+
1486+
If the meshes batch corresponding to this TexturesVertex contains
1487+
`n = len(vertex_ids_list)` meshes, then self.verts_features_list()
1488+
will be of length n. After submeshing, we obtain a batch of
1489+
`k = sum(len(v) for v in vertex_ids_list` submeshes (see Meshes.submeshes). This
1490+
function creates a corresponding TexturesVertex object with `verts_features_list`
1491+
of length `k`.
1492+
1493+
Args:
1494+
vertex_ids_list: A list of length equal to self.verts_features_list. Each
1495+
element is a LongTensor listing the vertices that the submesh keeps in
1496+
each respective mesh.
1497+
1498+
face_ids_list: Not used when submeshing TexturesVertex.
1499+
1500+
Returns:
1501+
A TexturesVertex in which verts_features_list has length
1502+
sum(len(vertices) for vertices in vertex_ids_list). Each element contains
1503+
vertex features corresponding to the subset of vertices in that submesh.
1504+
"""
1505+
if vertex_ids_list is None or len(vertex_ids_list) != len(
1506+
self.verts_features_list()
1507+
):
1508+
raise IndexError(
1509+
"verts_features_list must be of " "the same length as vertex_ids_list."
1510+
)
1511+
1512+
sub_features = []
1513+
for vertex_ids, features in zip(vertex_ids_list, self.verts_features_list()):
1514+
for vertex_ids_submesh in vertex_ids:
1515+
sub_features.append(features[vertex_ids_submesh])
1516+
1517+
return self.__class__(sub_features)
1518+
14681519
def faces_verts_textures_packed(self, faces_packed=None) -> torch.Tensor:
14691520
"""
14701521
Samples texture from each vertex and for each face in the mesh.

pytorch3d/structures/meshes.py

+11-7
Original file line numberDiff line numberDiff line change
@@ -1618,34 +1618,33 @@ def submeshes(
16181618
* There are no submeshes of cubes[1] and cubes[3] in subcubes.
16191619
* subcubes[0] and subcubes[1] are not watertight. subcubes[2] is.
16201620
"""
1621-
if not (
1622-
self.textures is None or type(self.textures).__name__ == "TexturesVertex"
1623-
):
1624-
raise ValueError(
1625-
"Submesh extraction only works with no textures or TexturesVertex."
1626-
)
1627-
16281621
if len(face_indices) != len(self):
16291622
raise ValueError(
16301623
"You must specify exactly one set of submeshes"
16311624
" for each mesh in this Meshes object."
16321625
)
16331626

16341627
sub_verts = []
1628+
sub_verts_ids = []
16351629
sub_faces = []
1630+
sub_face_ids = []
16361631

16371632
for face_ids_per_mesh, faces, verts in zip(
16381633
face_indices, self.faces_list(), self.verts_list()
16391634
):
1635+
sub_verts_ids.append([])
1636+
sub_face_ids.append([])
16401637
for submesh_face_ids in face_ids_per_mesh:
16411638
faces_to_keep = faces[submesh_face_ids]
1639+
sub_face_ids[-1].append(faces_to_keep)
16421640

16431641
# Say we are keeping two faces from a mesh with six vertices:
16441642
# faces_to_keep = [[0, 6, 4],
16451643
# [0, 2, 6]]
16461644
# Then we want verts_to_keep to contain only vertices [0, 2, 4, 6]:
16471645
vertex_ids_to_keep = torch.unique(faces_to_keep, sorted=True)
16481646
sub_verts.append(verts[vertex_ids_to_keep])
1647+
sub_verts_ids[-1].append(vertex_ids_to_keep)
16491648

16501649
# Now, convert faces_to_keep to use the new vertex ids.
16511650
# In our example, instead of
@@ -1663,6 +1662,11 @@ def submeshes(
16631662
return self.__class__(
16641663
verts=sub_verts,
16651664
faces=sub_faces,
1665+
textures=(
1666+
self.textures.submeshes(sub_verts_ids, sub_face_ids)
1667+
if self.textures
1668+
else None
1669+
),
16661670
)
16671671

16681672

tests/test_texturing.py

+47
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,53 @@ def test_faces_verts_textures(self):
145145

146146
self.assertClose(faces_verts_texts_packed, faces_verts_texts)
147147

148+
def test_submeshes(self):
149+
# define TexturesVertex
150+
verts_features = torch.tensor(
151+
[
152+
[1, 0, 0],
153+
[1, 0, 0],
154+
[1, 0, 0],
155+
[1, 0, 0],
156+
[0, 1, 0],
157+
[0, 1, 0],
158+
[0, 1, 0],
159+
[0, 1, 0],
160+
],
161+
dtype=torch.float32,
162+
)
163+
164+
textures = TexturesVertex(
165+
verts_features=[verts_features, verts_features, verts_features]
166+
)
167+
subtextures = textures.submeshes(
168+
[
169+
[
170+
torch.LongTensor([0, 2, 3]),
171+
torch.LongTensor(list(range(8))),
172+
],
173+
[],
174+
[
175+
torch.LongTensor([4]),
176+
],
177+
],
178+
None,
179+
)
180+
181+
subtextures_features = subtextures.verts_features_list()
182+
183+
self.assertEqual(len(subtextures_features), 3)
184+
self.assertTrue(
185+
torch.equal(
186+
subtextures_features[0],
187+
torch.FloatTensor([[1, 0, 0], [1, 0, 0], [1, 0, 0]]),
188+
)
189+
)
190+
self.assertTrue(torch.equal(subtextures_features[1], verts_features))
191+
self.assertTrue(
192+
torch.equal(subtextures_features[2], torch.FloatTensor([[0, 1, 0]]))
193+
)
194+
148195
def test_clone(self):
149196
tex = TexturesVertex(verts_features=torch.rand(size=(10, 100, 128)))
150197
tex.verts_features_list()

0 commit comments

Comments
 (0)