Skip to content

Commit 8a27590

Browse files
mhlotfacebook-github-bot
authored andcommitted
Submeshing TexturesUV
Summary: Implement `submeshes` for TexturesUV. Fix what Meshes.submeshes passes to the texture's submeshes function to make this possible. Reviewed By: bottler Differential Revision: D52192060 fbshipit-source-id: 526734962e3376aaf75654200164cdcebfff6997
1 parent 06cdc31 commit 8a27590

File tree

3 files changed

+99
-5
lines changed

3 files changed

+99
-5
lines changed

pytorch3d/renderer/mesh/textures.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,60 @@ def check_shapes(
13321332
self.verts_uvs_padded().shape[0] == batch_size
13331333
)
13341334

1335+
def submeshes(
1336+
self,
1337+
vertex_ids_list: List[List[torch.LongTensor]],
1338+
faces_ids_list: List[List[torch.LongTensor]],
1339+
) -> "TexturesUV":
1340+
"""
1341+
Extract a sub-texture for use in a submesh.
1342+
1343+
If the meshes batch corresponding to this TexturesUV contains
1344+
`n = len(faces_ids_list)` meshes, then self.faces_uvs_padded()
1345+
will be of length n. After submeshing, we obtain a batch of
1346+
`k = sum(len(f) for f in faces_ids_list` submeshes (see Meshes.submeshes). This
1347+
function creates a corresponding TexturesUV object with `faces_uvs_padded`
1348+
of length `k`.
1349+
1350+
Args:
1351+
vertex_ids_list: Not used when submeshing TexturesUV.
1352+
1353+
face_ids_list: A list of length equal to self.faces_uvs_padded. Each
1354+
element is a LongTensor listing the face ids that the submesh keeps in
1355+
each respective mesh.
1356+
1357+
1358+
Returns:
1359+
A "TexturesUV in which faces_uvs_padded, verts_uvs_padded, and maps_padded
1360+
have length sum(len(faces) for faces in faces_ids_list)
1361+
"""
1362+
1363+
if len(faces_ids_list) != len(self.faces_uvs_padded()):
1364+
raise IndexError(
1365+
"faces_uvs_padded must be of " "the same length as face_ids_list."
1366+
)
1367+
1368+
sub_faces_uvs, sub_verts_uvs, sub_maps = [], [], []
1369+
for faces_ids, faces_uvs, verts_uvs, map_ in zip(
1370+
faces_ids_list,
1371+
self.faces_uvs_padded(),
1372+
self.verts_uvs_padded(),
1373+
self.maps_padded(),
1374+
):
1375+
for faces_ids_submesh in faces_ids:
1376+
sub_faces_uvs.append(faces_uvs[faces_ids_submesh])
1377+
sub_verts_uvs.append(verts_uvs)
1378+
sub_maps.append(map_)
1379+
1380+
return self.__class__(
1381+
sub_maps,
1382+
sub_faces_uvs,
1383+
sub_verts_uvs,
1384+
self.padding_mode,
1385+
self.align_corners,
1386+
self.sampling_mode,
1387+
)
1388+
13351389

13361390
class TexturesVertex(TexturesBase):
13371391
def __init__(

pytorch3d/structures/meshes.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1576,7 +1576,7 @@ def submeshes(
15761576
Returns:
15771577
Meshes object of length `sum(len(ids) for ids in face_indices)`.
15781578
1579-
Submeshing only works with no textures or with the TexturesVertex texture.
1579+
Submeshing only works with no textures, TexturesVertex, or TexturesUV.
15801580
15811581
Example 1:
15821582
@@ -1616,16 +1616,13 @@ def submeshes(
16161616
sub_verts = []
16171617
sub_verts_ids = []
16181618
sub_faces = []
1619-
sub_face_ids = []
16201619

16211620
for face_ids_per_mesh, faces, verts in zip(
16221621
face_indices, self.faces_list(), self.verts_list()
16231622
):
16241623
sub_verts_ids.append([])
1625-
sub_face_ids.append([])
16261624
for submesh_face_ids in face_ids_per_mesh:
16271625
faces_to_keep = faces[submesh_face_ids]
1628-
sub_face_ids[-1].append(faces_to_keep)
16291626

16301627
# Say we are keeping two faces from a mesh with six vertices:
16311628
# faces_to_keep = [[0, 6, 4],
@@ -1652,7 +1649,7 @@ def submeshes(
16521649
verts=sub_verts,
16531650
faces=sub_faces,
16541651
textures=(
1655-
self.textures.submeshes(sub_verts_ids, sub_face_ids)
1652+
self.textures.submeshes(sub_verts_ids, face_indices)
16561653
if self.textures
16571654
else None
16581655
),

tests/test_texturing.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,49 @@ def test_sample_textures_error(self):
10021002
with self.assertRaisesRegex(ValueError, "do not match the dimensions"):
10031003
meshes.sample_textures(None)
10041004

1005+
def test_submeshes(self):
1006+
N = 2
1007+
faces_uvs_list = [
1008+
torch.LongTensor([[0, 1, 2], [3, 5, 4], [7, 6, 8]]),
1009+
torch.LongTensor([[0, 1, 2], [3, 4, 5]]),
1010+
]
1011+
verts_uvs_list = [
1012+
torch.arange(18, dtype=torch.float32).reshape(9, 2),
1013+
torch.ones(6, 2),
1014+
]
1015+
tex = TexturesUV(
1016+
maps=torch.rand((N, 16, 16, 3)),
1017+
faces_uvs=faces_uvs_list,
1018+
verts_uvs=verts_uvs_list,
1019+
)
1020+
1021+
sub_faces = [
1022+
[torch.tensor([0, 1]), torch.tensor([1, 2])],
1023+
[],
1024+
]
1025+
1026+
mesh = Meshes(
1027+
verts=[torch.rand(9, 3), torch.rand(6, 3)],
1028+
faces=faces_uvs_list,
1029+
textures=tex,
1030+
)
1031+
subtex = mesh.submeshes(sub_faces).textures
1032+
subtex_faces = subtex.faces_uvs_padded()
1033+
self.assertEqual(len(subtex_faces), 2)
1034+
self.assertClose(
1035+
subtex_faces[0],
1036+
torch.tensor([[0, 1, 2], [3, 5, 4]]),
1037+
)
1038+
self.assertClose(
1039+
subtex.verts_uvs_list()[0][subtex.faces_uvs_list()[0].flatten()]
1040+
.flatten()
1041+
.msort(),
1042+
torch.arange(12, dtype=torch.float32),
1043+
)
1044+
self.assertClose(
1045+
subtex.maps_padded(), tex.maps_padded()[:1].expand(2, -1, -1, -1)
1046+
)
1047+
10051048

10061049
class TestRectanglePacking(TestCaseMixin, unittest.TestCase):
10071050
def setUp(self) -> None:

0 commit comments

Comments
 (0)