mirror of
https://github.com/facebookresearch/pytorch3d.git
synced 2025-12-19 22:00:35 +08:00
Fix batching bug from TexturesUV packed ambiguity, other textures tidyup
Summary: faces_uvs_packed and verts_uvs_packed were only used in one place and the definition of the former was ambiguous. This meant that the wrong coordinates could be used for meshes other than the first in the batch. I have therefore removed both functions and build their common result inline. Added a test that a simple batch of two meshes is rendered consistently with the rendering of each alone. This test would have failed before. I hope this fixes https://github.com/facebookresearch/pytorch3d/issues/283. Some other small improvements to the textures code. Reviewed By: nikhilaravi Differential Revision: D23161936 fbshipit-source-id: f99b560a46f6b30262e07028b049812bc04350a7
This commit is contained in:
committed by
Facebook GitHub Bot
parent
9aaba0483c
commit
9a50cf800e
@@ -33,8 +33,9 @@ from pytorch3d.renderer.mesh.shader import (
|
||||
SoftSilhouetteShader,
|
||||
TexturedSoftPhongShader,
|
||||
)
|
||||
from pytorch3d.structures.meshes import Meshes, join_mesh
|
||||
from pytorch3d.structures.meshes import Meshes, join_mesh, join_meshes_as_batch
|
||||
from pytorch3d.utils.ico_sphere import ico_sphere
|
||||
from pytorch3d.utils.torus import torus
|
||||
|
||||
|
||||
# If DEBUG=True, save out images generated in the tests for debugging.
|
||||
@@ -490,6 +491,86 @@ class TestRenderMeshes(TestCaseMixin, unittest.TestCase):
|
||||
|
||||
self.assertClose(rgb, image_ref, atol=0.05)
|
||||
|
||||
def test_batch_uvs(self):
|
||||
"""Test that two random tori with TexturesUV render the same as each individually."""
|
||||
torch.manual_seed(1)
|
||||
device = torch.device("cuda:0")
|
||||
plain_torus = torus(r=1, R=4, sides=10, rings=10, device=device)
|
||||
[verts] = plain_torus.verts_list()
|
||||
[faces] = plain_torus.faces_list()
|
||||
nocolor = torch.zeros((100, 100), device=device)
|
||||
color_gradient = torch.linspace(0, 1, steps=100, device=device)
|
||||
color_gradient1 = color_gradient[None].expand_as(nocolor)
|
||||
color_gradient2 = color_gradient[:, None].expand_as(nocolor)
|
||||
colors1 = torch.stack([nocolor, color_gradient1, color_gradient2], dim=2)
|
||||
colors2 = torch.stack([color_gradient1, color_gradient2, nocolor], dim=2)
|
||||
verts_uvs1 = torch.rand(size=(verts.shape[0], 2), device=device)
|
||||
verts_uvs2 = torch.rand(size=(verts.shape[0], 2), device=device)
|
||||
|
||||
textures1 = TexturesUV(
|
||||
maps=[colors1], faces_uvs=[faces], verts_uvs=[verts_uvs1]
|
||||
)
|
||||
textures2 = TexturesUV(
|
||||
maps=[colors2], faces_uvs=[faces], verts_uvs=[verts_uvs2]
|
||||
)
|
||||
mesh1 = Meshes(verts=[verts], faces=[faces], textures=textures1)
|
||||
mesh2 = Meshes(verts=[verts], faces=[faces], textures=textures2)
|
||||
mesh_both = join_meshes_as_batch([mesh1, mesh2])
|
||||
|
||||
R, T = look_at_view_transform(10, 10, 0)
|
||||
cameras = FoVPerspectiveCameras(device=device, R=R, T=T)
|
||||
|
||||
raster_settings = RasterizationSettings(
|
||||
image_size=128, blur_radius=0.0, faces_per_pixel=1
|
||||
)
|
||||
|
||||
# Init shader settings
|
||||
lights = PointLights(device=device)
|
||||
lights.location = torch.tensor([0.0, 0.0, 2.0], device=device)[None]
|
||||
|
||||
blend_params = BlendParams(
|
||||
sigma=1e-1,
|
||||
gamma=1e-4,
|
||||
background_color=torch.tensor([1.0, 1.0, 1.0], device=device),
|
||||
)
|
||||
# Init renderer
|
||||
renderer = MeshRenderer(
|
||||
rasterizer=MeshRasterizer(cameras=cameras, raster_settings=raster_settings),
|
||||
shader=HardPhongShader(
|
||||
device=device, lights=lights, cameras=cameras, blend_params=blend_params
|
||||
),
|
||||
)
|
||||
|
||||
outputs = []
|
||||
for meshes in [mesh_both, mesh1, mesh2]:
|
||||
outputs.append(renderer(meshes))
|
||||
|
||||
if DEBUG:
|
||||
Image.fromarray(
|
||||
(outputs[0][0, ..., :3].cpu().numpy() * 255).astype(np.uint8)
|
||||
).save(DATA_DIR / "test_batch_uvs0.png")
|
||||
Image.fromarray(
|
||||
(outputs[1][0, ..., :3].cpu().numpy() * 255).astype(np.uint8)
|
||||
).save(DATA_DIR / "test_batch_uvs1.png")
|
||||
Image.fromarray(
|
||||
(outputs[0][1, ..., :3].cpu().numpy() * 255).astype(np.uint8)
|
||||
).save(DATA_DIR / "test_batch_uvs2.png")
|
||||
Image.fromarray(
|
||||
(outputs[2][0, ..., :3].cpu().numpy() * 255).astype(np.uint8)
|
||||
).save(DATA_DIR / "test_batch_uvs3.png")
|
||||
|
||||
diff = torch.abs(outputs[0][0, ..., :3] - outputs[1][0, ..., :3])
|
||||
Image.fromarray(((diff > 1e-5).cpu().numpy().astype(np.uint8) * 255)).save(
|
||||
DATA_DIR / "test_batch_uvs01.png"
|
||||
)
|
||||
diff = torch.abs(outputs[0][1, ..., :3] - outputs[2][0, ..., :3])
|
||||
Image.fromarray(((diff > 1e-5).cpu().numpy().astype(np.uint8) * 255)).save(
|
||||
DATA_DIR / "test_batch_uvs23.png"
|
||||
)
|
||||
|
||||
self.assertClose(outputs[0][0, ..., :3], outputs[1][0, ..., :3], atol=1e-5)
|
||||
self.assertClose(outputs[0][1, ..., :3], outputs[2][0, ..., :3], atol=1e-5)
|
||||
|
||||
def test_joined_spheres(self):
|
||||
"""
|
||||
Test a list of Meshes can be joined as a single mesh and
|
||||
|
||||
@@ -29,8 +29,8 @@ def tryindex(self, index, tex, meshes, source):
|
||||
basic = basic[None]
|
||||
|
||||
if len(basic) == 0:
|
||||
self.assertEquals(len(from_texture), 0)
|
||||
self.assertEquals(len(from_meshes), 0)
|
||||
self.assertEqual(len(from_texture), 0)
|
||||
self.assertEqual(len(from_meshes), 0)
|
||||
else:
|
||||
self.assertClose(basic, from_texture)
|
||||
self.assertClose(basic, from_meshes)
|
||||
@@ -608,12 +608,8 @@ class TestTexturesUV(TestCaseMixin, unittest.TestCase):
|
||||
[
|
||||
tex_init.faces_uvs_padded(),
|
||||
new_tex.faces_uvs_padded(),
|
||||
tex_init.faces_uvs_packed(),
|
||||
new_tex.faces_uvs_packed(),
|
||||
tex_init.verts_uvs_padded(),
|
||||
new_tex.verts_uvs_padded(),
|
||||
tex_init.verts_uvs_packed(),
|
||||
new_tex.verts_uvs_packed(),
|
||||
tex_init.maps_padded(),
|
||||
new_tex.maps_padded(),
|
||||
]
|
||||
@@ -646,11 +642,9 @@ class TestTexturesUV(TestCaseMixin, unittest.TestCase):
|
||||
tex1 = tex.clone()
|
||||
tex1._num_faces_per_mesh = num_faces_per_mesh
|
||||
tex1._num_verts_per_mesh = num_verts_per_mesh
|
||||
verts_packed = tex1.verts_uvs_packed()
|
||||
verts_list = tex1.verts_uvs_list()
|
||||
verts_padded = tex1.verts_uvs_padded()
|
||||
|
||||
faces_packed = tex1.faces_uvs_packed()
|
||||
faces_list = tex1.faces_uvs_list()
|
||||
faces_padded = tex1.faces_uvs_padded()
|
||||
|
||||
@@ -660,9 +654,7 @@ class TestTexturesUV(TestCaseMixin, unittest.TestCase):
|
||||
for f1, f2 in zip(verts_list, verts_uvs_list):
|
||||
self.assertTrue((f1 == f2).all().item())
|
||||
|
||||
self.assertTrue(faces_packed.shape == (3 + 2, 3))
|
||||
self.assertTrue(faces_padded.shape == (2, 3, 3))
|
||||
self.assertTrue(verts_packed.shape == (9 + 6, 2))
|
||||
self.assertTrue(verts_padded.shape == (2, 9, 2))
|
||||
|
||||
# Case where num_faces_per_mesh is not set and faces_verts_uvs
|
||||
@@ -672,16 +664,9 @@ class TestTexturesUV(TestCaseMixin, unittest.TestCase):
|
||||
verts_uvs=verts_padded,
|
||||
faces_uvs=faces_padded,
|
||||
)
|
||||
faces_packed = tex2.faces_uvs_packed()
|
||||
faces_list = tex2.faces_uvs_list()
|
||||
verts_packed = tex2.verts_uvs_packed()
|
||||
verts_list = tex2.verts_uvs_list()
|
||||
|
||||
# Packed is just flattened padded as num_faces_per_mesh
|
||||
# has not been provided.
|
||||
self.assertTrue(faces_packed.shape == (3 * 2, 3))
|
||||
self.assertTrue(verts_packed.shape == (9 * 2, 2))
|
||||
|
||||
for i, (f1, f2) in enumerate(zip(faces_list, faces_uvs_list)):
|
||||
n = num_faces_per_mesh[i]
|
||||
self.assertTrue((f1[:n] == f2).all().item())
|
||||
|
||||
Reference in New Issue
Block a user