Address black + isort fbsource linter warnings

Summary: Address black + isort fbsource linter warnings from D20558374 (previous diff)

Reviewed By: nikhilaravi

Differential Revision: D20558373

fbshipit-source-id: d3607de4a01fb24c0d5269634563a7914bddf1c8
This commit is contained in:
Patrick Labatut
2020-03-29 14:46:33 -07:00
committed by Facebook GitHub Bot
parent eb512ffde3
commit d57daa6f85
110 changed files with 705 additions and 1850 deletions

View File

@@ -2,8 +2,8 @@
from itertools import product
from fvcore.common.benchmark import benchmark
from fvcore.common.benchmark import benchmark
from test_blending import TestBlending
@@ -18,12 +18,7 @@ def bm_blending() -> None:
for case in test_cases:
n, s, k, d = case
kwargs_list.append(
{
"num_meshes": n,
"image_size": s,
"faces_per_pixel": k,
"device": d,
}
{"num_meshes": n, "image_size": s, "faces_per_pixel": k, "device": d}
)
benchmark(

View File

@@ -3,7 +3,6 @@
import torch
from fvcore.common.benchmark import benchmark
from test_chamfer import TestChamfer
@@ -25,9 +24,4 @@ def bm_chamfer() -> None:
{"batch_size": 1, "P1": 1000, "P2": 3000, "return_normals": False},
{"batch_size": 1, "P1": 1000, "P2": 30000, "return_normals": True},
]
benchmark(
TestChamfer.chamfer_with_init,
"CHAMFER",
kwargs_list,
warmup_iters=1,
)
benchmark(TestChamfer.chamfer_with_init, "CHAMFER", kwargs_list, warmup_iters=1)

View File

@@ -1,7 +1,6 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
from fvcore.common.benchmark import benchmark
from test_cubify import TestCubify
@@ -11,6 +10,4 @@ def bm_cubify() -> None:
{"batch_size": 64, "V": 16},
{"batch_size": 16, "V": 32},
]
benchmark(
TestCubify.cubify_with_init, "CUBIFY", kwargs_list, warmup_iters=1
)
benchmark(TestCubify.cubify_with_init, "CUBIFY", kwargs_list, warmup_iters=1)

View File

@@ -2,9 +2,9 @@
from itertools import product
import torch
from fvcore.common.benchmark import benchmark
from test_face_areas_normals import TestFaceAreasNormals

View File

@@ -2,9 +2,9 @@
from itertools import product
import torch
from fvcore.common.benchmark import benchmark
from test_graph_conv import TestGraphConv

View File

@@ -1,9 +1,9 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
from itertools import product
import torch
from fvcore.common.benchmark import benchmark
from pytorch3d import _C
from pytorch3d.ops.knn import _knn_points_idx_naive
@@ -32,9 +32,7 @@ def benchmark_knn_cuda_versions() -> None:
knn_kwargs.append({"N": N, "D": D, "P": P, "K": K, "v": version})
for N, P, D in product(Ns, Ps, Ds):
nn_kwargs.append({"N": N, "D": D, "P": P})
benchmark(
knn_cuda_with_init, "KNN_CUDA_VERSIONS", knn_kwargs, warmup_iters=1
)
benchmark(knn_cuda_with_init, "KNN_CUDA_VERSIONS", knn_kwargs, warmup_iters=1)
benchmark(nn_cuda_with_init, "NN_CUDA", nn_kwargs, warmup_iters=1)
@@ -50,10 +48,7 @@ def benchmark_knn_cuda_vs_naive() -> None:
if P <= 4096:
naive_kwargs.append({"N": N, "D": D, "P": P, "K": K})
benchmark(
knn_python_cuda_with_init,
"KNN_CUDA_PYTHON",
naive_kwargs,
warmup_iters=1,
knn_python_cuda_with_init, "KNN_CUDA_PYTHON", naive_kwargs, warmup_iters=1
)
benchmark(knn_cuda_with_init, "KNN_CUDA", knn_kwargs, warmup_iters=1)
@@ -68,9 +63,7 @@ def benchmark_knn_cpu() -> None:
knn_kwargs.append({"N": N, "D": D, "P": P, "K": K})
for N, P, D in product(Ns, Ps, Ds):
nn_kwargs.append({"N": N, "D": D, "P": P})
benchmark(
knn_python_cpu_with_init, "KNN_CPU_PYTHON", knn_kwargs, warmup_iters=1
)
benchmark(knn_python_cpu_with_init, "KNN_CPU_PYTHON", knn_kwargs, warmup_iters=1)
benchmark(knn_cpu_with_init, "KNN_CPU_CPP", knn_kwargs, warmup_iters=1)
benchmark(nn_cpu_with_init, "NN_CPU_CPP", nn_kwargs, warmup_iters=1)

View File

@@ -5,6 +5,7 @@ import glob
import importlib
from os.path import basename, dirname, isfile, join, sys
if __name__ == "__main__":
# pyre-ignore[16]
if len(sys.argv) > 1:
@@ -25,7 +26,5 @@ if __name__ == "__main__":
for attr in dir(module):
# Run all the functions with names "bm_*" in the module.
if attr.startswith("bm_"):
print(
"Running benchmarks for " + module_name + "/" + attr + "..."
)
print("Running benchmarks for " + module_name + "/" + attr + "...")
getattr(module, attr)()

View File

@@ -2,8 +2,8 @@
from itertools import product
from fvcore.common.benchmark import benchmark
from fvcore.common.benchmark import benchmark
from test_mesh_edge_loss import TestMeshEdgeLoss
@@ -17,8 +17,5 @@ def bm_mesh_edge_loss() -> None:
n, v, f = case
kwargs_list.append({"num_meshes": n, "max_v": v, "max_f": f})
benchmark(
TestMeshEdgeLoss.mesh_edge_loss,
"MESH_EDGE_LOSS",
kwargs_list,
warmup_iters=1,
TestMeshEdgeLoss.mesh_edge_loss, "MESH_EDGE_LOSS", kwargs_list, warmup_iters=1
)

View File

@@ -1,7 +1,6 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
from fvcore.common.benchmark import benchmark
from test_obj_io import TestMeshObjIO
from test_ply_io import TestMeshPlyIO

View File

@@ -2,9 +2,9 @@
from itertools import product
import torch
from fvcore.common.benchmark import benchmark
from test_mesh_laplacian_smoothing import TestLaplacianSmoothing

View File

@@ -2,9 +2,9 @@
from itertools import product
import torch
from fvcore.common.benchmark import benchmark
from test_mesh_normal_consistency import TestMeshNormalConsistency

View File

@@ -2,9 +2,9 @@
from itertools import product
import torch
from fvcore.common.benchmark import benchmark
from test_meshes import TestMeshes
@@ -20,9 +20,7 @@ def bm_compute_packed_padded_meshes() -> None:
test_cases = product(num_meshes, max_v, max_f, devices)
for case in test_cases:
n, v, f, d = case
kwargs_list.append(
{"num_meshes": n, "max_v": v, "max_f": f, "device": d}
)
kwargs_list.append({"num_meshes": n, "max_v": v, "max_f": f, "device": d})
benchmark(
TestMeshes.compute_packed_with_init,
"COMPUTE_PACKED",

View File

@@ -1,9 +1,9 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
from itertools import product
import torch
from fvcore.common.benchmark import benchmark
from test_nearest_neighbor_points import TestNearestNeighborPoints

View File

@@ -2,9 +2,9 @@
from itertools import product
import torch
from fvcore.common.benchmark import benchmark
from test_packed_to_padded import TestPackedToPadded
@@ -23,13 +23,7 @@ def bm_packed_to_padded() -> None:
for case in test_cases:
n, v, f, d, b = case
kwargs_list.append(
{
"num_meshes": n,
"num_verts": v,
"num_faces": f,
"num_d": d,
"device": b,
}
{"num_meshes": n, "num_verts": v, "num_faces": f, "num_d": d, "device": b}
)
benchmark(
TestPackedToPadded.packed_to_padded_with_init,

View File

@@ -2,8 +2,8 @@
from itertools import product
from fvcore.common.benchmark import benchmark
from fvcore.common.benchmark import benchmark
from test_pointclouds import TestPointclouds

View File

@@ -2,11 +2,12 @@
from itertools import product
import torch
from fvcore.common.benchmark import benchmark
from test_rasterize_meshes import TestRasterizeMeshes
# ico levels:
# 0: (12 verts, 20 faces)
# 1: (42 verts, 80 faces)
@@ -39,12 +40,7 @@ def bm_rasterize_meshes() -> None:
for case in test_cases:
n, ic, im, b = case
kwargs_list.append(
{
"num_meshes": n,
"ico_level": ic,
"image_size": im,
"blur_radius": b,
}
{"num_meshes": n, "ico_level": ic, "image_size": im, "blur_radius": b}
)
benchmark(
TestRasterizeMeshes.rasterize_meshes_cpu_with_init,
@@ -63,9 +59,7 @@ def bm_rasterize_meshes() -> None:
test_cases = product(num_meshes, ico_level, image_size, blur, bin_size)
# only keep cases where bin_size == 0 or image_size / bin_size < 16
test_cases = [
elem
for elem in test_cases
if (elem[-1] == 0 or elem[-3] / elem[-1] < 16)
elem for elem in test_cases if (elem[-1] == 0 or elem[-3] / elem[-1] < 16)
]
for case in test_cases:
n, ic, im, b, bn = case

View File

@@ -3,7 +3,6 @@
import torch
from fvcore.common.benchmark import benchmark
from pytorch3d.renderer.points.rasterize_points import (
rasterize_points,
rasterize_points_python,
@@ -40,9 +39,7 @@ def bm_python_vs_cpu() -> None:
{"N": 1, "P": 32, "img_size": 32, "radius": 0.1, "pts_per_pxl": 3},
{"N": 2, "P": 32, "img_size": 32, "radius": 0.1, "pts_per_pxl": 3},
]
benchmark(
_bm_python_with_init, "RASTERIZE_PYTHON", kwargs_list, warmup_iters=1
)
benchmark(_bm_python_with_init, "RASTERIZE_PYTHON", kwargs_list, warmup_iters=1)
benchmark(_bm_cpu_with_init, "RASTERIZE_CPU", kwargs_list, warmup_iters=1)
kwargs_list = [
{"N": 2, "P": 32, "img_size": 32, "radius": 0.1, "pts_per_pxl": 3},

View File

@@ -2,9 +2,9 @@
from itertools import product
import torch
from fvcore.common.benchmark import benchmark
from test_sample_points_from_meshes import TestSamplePoints

View File

@@ -1,7 +1,6 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
from fvcore.common.benchmark import benchmark
from test_so3 import TestSO3

View File

@@ -2,8 +2,8 @@
from itertools import product
from fvcore.common.benchmark import benchmark
from fvcore.common.benchmark import benchmark
from test_subdivide_meshes import TestSubdivideMeshes

View File

@@ -2,9 +2,9 @@
from itertools import product
import torch
from fvcore.common.benchmark import benchmark
from test_vert_align import TestVertAlign
@@ -25,8 +25,5 @@ def bm_vert_align() -> None:
)
benchmark(
TestVertAlign.vert_align_with_init,
"VERT_ALIGN",
kwargs_list,
warmup_iters=1,
TestVertAlign.vert_align_with_init, "VERT_ALIGN", kwargs_list, warmup_iters=1
)

View File

@@ -1,8 +1,9 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
import numpy as np
import unittest
import numpy as np
import torch
@@ -11,17 +12,13 @@ class TestCaseMixin(unittest.TestCase):
"""
Verify that tensor1 and tensor2 have their data in distinct locations.
"""
self.assertNotEqual(
tensor1.storage().data_ptr(), tensor2.storage().data_ptr()
)
self.assertNotEqual(tensor1.storage().data_ptr(), tensor2.storage().data_ptr())
def assertNotSeparate(self, tensor1, tensor2) -> None:
"""
Verify that tensor1 and tensor2 have their data in the same locations.
"""
self.assertEqual(
tensor1.storage().data_ptr(), tensor2.storage().data_ptr()
)
self.assertEqual(tensor1.storage().data_ptr(), tensor2.storage().data_ptr())
def assertAllSeparate(self, tensor_list) -> None:
"""
@@ -57,7 +54,5 @@ class TestCaseMixin(unittest.TestCase):
input, other, rtol=rtol, atol=atol, equal_nan=equal_nan
)
else:
close = np.allclose(
input, other, rtol=rtol, atol=atol, equal_nan=equal_nan
)
close = np.allclose(input, other, rtol=rtol, atol=atol, equal_nan=equal_nan)
self.assertTrue(close)

View File

@@ -1,9 +1,9 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
import numpy as np
import unittest
import torch
import numpy as np
import torch
from pytorch3d.renderer.blending import (
BlendParams,
hard_rgb_blend,
@@ -43,9 +43,7 @@ def sigmoid_blend_naive_loop(colors, fragments, blend_params):
return pixel_colors
def sigmoid_blend_naive_loop_backward(
grad_images, images, fragments, blend_params
):
def sigmoid_blend_naive_loop_backward(grad_images, images, fragments, blend_params):
pix_to_face = fragments.pix_to_face
dists = fragments.dists
sigma = blend_params.sigma
@@ -135,14 +133,7 @@ class TestBlending(unittest.TestCase):
torch.manual_seed(42)
def _compare_impls(
self,
fn1,
fn2,
args1,
args2,
grad_var1=None,
grad_var2=None,
compare_grads=True,
self, fn1, fn2, args1, args2, grad_var1=None, grad_var2=None, compare_grads=True
):
out1 = fn1(*args1)
@@ -160,9 +151,7 @@ class TestBlending(unittest.TestCase):
(out2 * grad_out).sum().backward()
self.assertTrue(hasattr(grad_var2, "grad"))
self.assertTrue(
torch.allclose(
grad_var1.grad.cpu(), grad_var2.grad.cpu(), atol=2e-5
)
torch.allclose(grad_var1.grad.cpu(), grad_var2.grad.cpu(), atol=2e-5)
)
def test_hard_rgb_blend(self):
@@ -199,9 +188,7 @@ class TestBlending(unittest.TestCase):
# # (-) means inside triangle, (+) means outside triangle.
random_sign_flip = torch.rand((N, S, S, K))
random_sign_flip[random_sign_flip > 0.5] *= -1.0
dists = torch.randn(
size=(N, S, S, K), requires_grad=True, device=device
)
dists = torch.randn(size=(N, S, S, K), requires_grad=True, device=device)
fragments = Fragments(
pix_to_face=pix_to_face,
bary_coords=empty, # dummy
@@ -238,9 +225,7 @@ class TestBlending(unittest.TestCase):
# # (-) means inside triangle, (+) means outside triangle.
random_sign_flip = torch.rand((N, S, S, K))
random_sign_flip[random_sign_flip > 0.5] *= -1.0
dists1 = torch.randn(
size=(N, S, S, K), requires_grad=True, device=device
)
dists1 = torch.randn(size=(N, S, S, K), requires_grad=True, device=device)
dists2 = dists1.detach().clone()
dists2.requires_grad = True
@@ -276,9 +261,7 @@ class TestBlending(unittest.TestCase):
# of the image with surrounding padded values.
N, S, K = 1, 8, 2
device = torch.device("cuda")
pix_to_face = -torch.ones(
(N, S, S, K), dtype=torch.int64, device=device
)
pix_to_face = -torch.ones((N, S, S, K), dtype=torch.int64, device=device)
h = int(S / 2)
pix_to_face_full = torch.randint(
size=(N, h, h, K), low=0, high=100, device=device
@@ -294,9 +277,7 @@ class TestBlending(unittest.TestCase):
# randomly flip the sign of the distance
# (-) means inside triangle, (+) means outside triangle.
dists1 = (
torch.randn(size=(N, S, S, K), device=device) * random_sign_flip
)
dists1 = torch.randn(size=(N, S, S, K), device=device) * random_sign_flip
dists2 = dists1.clone()
zbuf2 = zbuf1.clone()
dists1.requires_grad = True
@@ -353,9 +334,7 @@ class TestBlending(unittest.TestCase):
# # (-) means inside triangle, (+) means outside triangle.
random_sign_flip = torch.rand((N, S, S, K), device=device)
random_sign_flip[random_sign_flip > 0.5] *= -1.0
dists1 = torch.randn(
size=(N, S, S, K), requires_grad=True, device=device
)
dists1 = torch.randn(size=(N, S, S, K), requires_grad=True, device=device)
fragments = Fragments(
pix_to_face=pix_to_face,
bary_coords=empty, # dummy
@@ -398,15 +377,10 @@ class TestBlending(unittest.TestCase):
# # (-) means inside triangle, (+) means outside triangle.
random_sign_flip = torch.rand((N, S, S, K), device=device)
random_sign_flip[random_sign_flip > 0.5] *= -1.0
dists1 = torch.randn(
size=(N, S, S, K), requires_grad=True, device=device
)
dists1 = torch.randn(size=(N, S, S, K), requires_grad=True, device=device)
zbuf = torch.randn(size=(N, S, S, K), requires_grad=True, device=device)
fragments = Fragments(
pix_to_face=pix_to_face,
bary_coords=empty, # dummy
zbuf=zbuf,
dists=dists1,
pix_to_face=pix_to_face, bary_coords=empty, zbuf=zbuf, dists=dists1 # dummy
)
blend_params = BlendParams(sigma=1e-3)

View File

@@ -3,6 +3,7 @@ import unittest
from collections import Counter
from pathlib import Path
# This file groups together tests which look at the code without running it.
@@ -61,6 +62,5 @@ class TestBuild(unittest.TestCase):
if firstline.startswith(("# -*-", "#!")):
firstline = f.readline()
self.assertTrue(
firstline.endswith(expect),
f"{i} missing copyright header.",
firstline.endswith(expect), f"{i} missing copyright header."
)

View File

@@ -26,10 +26,11 @@
# SOFTWARE.
import math
import numpy as np
import unittest
import torch
import numpy as np
import torch
from common_testing import TestCaseMixin
from pytorch3d.renderer.cameras import (
OpenGLOrthographicCameras,
OpenGLPerspectiveCameras,
@@ -43,8 +44,6 @@ from pytorch3d.renderer.cameras import (
from pytorch3d.transforms import Transform3d
from pytorch3d.transforms.so3 import so3_exponential_map
from common_testing import TestCaseMixin
# Naive function adapted from SoftRasterizer for test purposes.
def perspective_project_naive(points, fov=60.0):
@@ -58,9 +57,7 @@ def perspective_project_naive(points, fov=60.0):
coordinate (no z renormalization)
"""
device = points.device
halfFov = torch.tensor(
(fov / 2) / 180 * np.pi, dtype=torch.float32, device=device
)
halfFov = torch.tensor((fov / 2) / 180 * np.pi, dtype=torch.float32, device=device)
scale = torch.tan(halfFov[None])
scale = scale[:, None]
z = points[:, :, 2]
@@ -150,9 +147,9 @@ class TestCameraHelpers(TestCaseMixin, unittest.TestCase):
dist = 2.7
elev = 90.0
azim = 0.0
expected_position = torch.tensor(
[0.0, 2.7, 0.0], dtype=torch.float32
).view(1, 3)
expected_position = torch.tensor([0.0, 2.7, 0.0], dtype=torch.float32).view(
1, 3
)
position = camera_position_from_spherical_angles(dist, elev, azim)
self.assertClose(position, expected_position, atol=2e-7)
@@ -171,9 +168,9 @@ class TestCameraHelpers(TestCaseMixin, unittest.TestCase):
dist = torch.tensor(2.7)
elev = torch.tensor(0.0)
azim = torch.tensor(90.0)
expected_position = torch.tensor(
[2.7, 0.0, 0.0], dtype=torch.float32
).view(1, 3)
expected_position = torch.tensor([2.7, 0.0, 0.0], dtype=torch.float32).view(
1, 3
)
position = camera_position_from_spherical_angles(dist, elev, azim)
self.assertClose(position, expected_position, atol=2e-7)
@@ -181,9 +178,9 @@ class TestCameraHelpers(TestCaseMixin, unittest.TestCase):
dist = 2.7
elev = torch.tensor(0.0)
azim = 90.0
expected_position = torch.tensor(
[2.7, 0.0, 0.0], dtype=torch.float32
).view(1, 3)
expected_position = torch.tensor([2.7, 0.0, 0.0], dtype=torch.float32).view(
1, 3
)
position = camera_position_from_spherical_angles(dist, elev, azim)
self.assertClose(position, expected_position, atol=2e-7)
@@ -228,8 +225,7 @@ class TestCameraHelpers(TestCaseMixin, unittest.TestCase):
elev = torch.tensor([0.0])
azim = torch.tensor([90.0])
expected_position = torch.tensor(
[[2.0, 0.0, 0.0], [3.0, 0.0, 0.0], [5.0, 0.0, 0.0]],
dtype=torch.float32,
[[2.0, 0.0, 0.0], [3.0, 0.0, 0.0], [5.0, 0.0, 0.0]], dtype=torch.float32
)
position = camera_position_from_spherical_angles(dist, elev, azim)
self.assertClose(position, expected_position, atol=3e-7)
@@ -239,8 +235,7 @@ class TestCameraHelpers(TestCaseMixin, unittest.TestCase):
elev = 0.0
azim = torch.tensor(90.0)
expected_position = torch.tensor(
[[2.0, 0.0, 0.0], [3.0, 0.0, 0.0], [5.0, 0.0, 0.0]],
dtype=torch.float32,
[[2.0, 0.0, 0.0], [3.0, 0.0, 0.0], [5.0, 0.0, 0.0]], dtype=torch.float32
)
position = camera_position_from_spherical_angles(dist, elev, azim)
self.assertClose(position, expected_position, atol=3e-7)
@@ -364,9 +359,7 @@ class TestCameraHelpers(TestCaseMixin, unittest.TestCase):
):
cam = cam_type(R=R, T=T)
RT_class = cam.get_world_to_view_transform()
self.assertTrue(
torch.allclose(RT.get_matrix(), RT_class.get_matrix())
)
self.assertTrue(torch.allclose(RT.get_matrix(), RT_class.get_matrix()))
self.assertTrue(isinstance(RT, Transform3d))
@@ -539,9 +532,7 @@ class TestOpenGLOrthographicProjection(TestCaseMixin, unittest.TestCase):
# applying the scale puts the z coordinate at the far clipping plane
# so the z is mapped to 1.0
projected_verts = torch.tensor([2, 1, 1], dtype=torch.float32)
cameras = OpenGLOrthographicCameras(
znear=1.0, zfar=10.0, scale_xyz=scale
)
cameras = OpenGLOrthographicCameras(znear=1.0, zfar=10.0, scale_xyz=scale)
P = cameras.get_projection_transform()
v1 = P.transform_points(vertices)
v2 = orthographic_project_naive(vertices, scale)
@@ -578,9 +569,7 @@ class TestOpenGLOrthographicProjection(TestCaseMixin, unittest.TestCase):
far = torch.tensor([10.0])
near = 1.0
scale = torch.tensor([[1.0, 1.0, 1.0]], requires_grad=True)
cameras = OpenGLOrthographicCameras(
znear=near, zfar=far, scale_xyz=scale
)
cameras = OpenGLOrthographicCameras(znear=near, zfar=far, scale_xyz=scale)
P = cameras.get_projection_transform()
vertices = torch.tensor([1.0, 2.0, 10.0], dtype=torch.float32)
vertices_batch = vertices[None, None, :]
@@ -683,15 +672,11 @@ class TestSfMPerspectiveProjection(TestCaseMixin, unittest.TestCase):
self.assertClose(v3[..., :2], v2[..., :2])
def test_perspective_kwargs(self):
cameras = SfMPerspectiveCameras(
focal_length=5.0, principal_point=((2.5, 2.5),)
)
cameras = SfMPerspectiveCameras(focal_length=5.0, principal_point=((2.5, 2.5),))
P = cameras.get_projection_transform(
focal_length=2.0, principal_point=((2.5, 3.5),)
)
vertices = torch.randn([3, 4, 3], dtype=torch.float32)
v1 = P.transform_points(vertices)
v2 = sfm_perspective_project_naive(
vertices, fx=2.0, fy=2.0, p0x=2.5, p0y=3.5
)
v2 = sfm_perspective_project_naive(vertices, fx=2.0, fy=2.0, p0x=2.5, p0y=3.5)
self.assertClose(v1, v2)

View File

@@ -1,12 +1,11 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
import unittest
import torch
import torch.nn.functional as F
from pytorch3d.loss import chamfer_distance
from common_testing import TestCaseMixin
from pytorch3d.loss import chamfer_distance
class TestChamfer(TestCaseMixin, unittest.TestCase):
@@ -19,14 +18,10 @@ class TestChamfer(TestCaseMixin, unittest.TestCase):
"""
device = torch.device("cuda:0")
p1 = torch.rand((batch_size, P1, 3), dtype=torch.float32, device=device)
p1_normals = torch.rand(
(batch_size, P1, 3), dtype=torch.float32, device=device
)
p1_normals = torch.rand((batch_size, P1, 3), dtype=torch.float32, device=device)
p1_normals = p1_normals / p1_normals.norm(dim=2, p=2, keepdim=True)
p2 = torch.rand((batch_size, P2, 3), dtype=torch.float32, device=device)
p2_normals = torch.rand(
(batch_size, P2, 3), dtype=torch.float32, device=device
)
p2_normals = torch.rand((batch_size, P2, 3), dtype=torch.float32, device=device)
p2_normals = p2_normals / p2_normals.norm(dim=2, p=2, keepdim=True)
weights = torch.rand((batch_size,), dtype=torch.float32, device=device)
@@ -47,9 +42,7 @@ class TestChamfer(TestCaseMixin, unittest.TestCase):
for n in range(N):
for i1 in range(P1):
for i2 in range(P2):
dist[n, i1, i2] = torch.sum(
(p1[n, i1, :] - p2[n, i2, :]) ** 2
)
dist[n, i1, i2] = torch.sum((p1[n, i1, :] - p2[n, i2, :]) ** 2)
loss = [
torch.min(dist, dim=2)[0], # (N, P1)
@@ -146,11 +139,7 @@ class TestChamfer(TestCaseMixin, unittest.TestCase):
# Error when point_reduction = "none" and batch_reduction = "none".
with self.assertRaises(ValueError):
chamfer_distance(
p1,
p2,
weights=weights,
batch_reduction="none",
point_reduction="none",
p1, p2, weights=weights, batch_reduction="none", point_reduction="none"
)
# Error when batch_reduction is not in ["none", "mean", "sum"].
@@ -339,9 +328,7 @@ class TestChamfer(TestCaseMixin, unittest.TestCase):
loss, loss_norm = chamfer_distance(p1, p2, weights=weights)
@staticmethod
def chamfer_with_init(
batch_size: int, P1: int, P2: int, return_normals: bool
):
def chamfer_with_init(batch_size: int, P1: int, P2: int, return_normals: bool):
p1, p2, p1_normals, p2_normals, weights = TestChamfer.init_pointclouds(
batch_size, P1, P2
)

View File

@@ -1,8 +1,8 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
import unittest
import torch
import torch
from pytorch3d.renderer.compositing import (
alpha_composite,
norm_weighted_sum,
@@ -37,9 +37,7 @@ class TestAccumulatePoints(unittest.TestCase):
continue
alpha = alphas[b, k, j, i]
output[b, c, j, i] += (
features[c, n_idx] * alpha * t_alpha
)
output[b, c, j, i] += features[c, n_idx] * alpha * t_alpha
t_alpha = (1 - alpha) * t_alpha
return output
@@ -105,17 +103,13 @@ class TestAccumulatePoints(unittest.TestCase):
continue
alpha = alphas[b, k, j, i]
output[b, c, j, i] += (
features[c, n_idx] * alpha / t_alpha
)
output[b, c, j, i] += features[c, n_idx] * alpha / t_alpha
return output
def test_python(self):
device = torch.device("cpu")
self._simple_alphacomposite(
self.accumulate_alphacomposite_python, device
)
self._simple_alphacomposite(self.accumulate_alphacomposite_python, device)
self._simple_wsum(self.accumulate_weightedsum_python, device)
self._simple_wsumnorm(self.accumulate_weightedsumnorm_python, device)
@@ -138,9 +132,7 @@ class TestAccumulatePoints(unittest.TestCase):
self._python_vs_cpu_vs_cuda(
self.accumulate_weightedsumnorm_python, norm_weighted_sum
)
self._python_vs_cpu_vs_cuda(
self.accumulate_weightedsum_python, weighted_sum
)
self._python_vs_cpu_vs_cuda(self.accumulate_weightedsum_python, weighted_sum)
def _python_vs_cpu_vs_cuda(self, accumulate_func_python, accumulate_func):
torch.manual_seed(231)
@@ -208,15 +200,11 @@ class TestAccumulatePoints(unittest.TestCase):
grads2 = [gradsi.grad.data.clone().cpu() for gradsi in grads2]
for i in range(0, len(grads1)):
self.assertTrue(
torch.allclose(grads1[i].cpu(), grads2[i].cpu(), atol=1e-6)
)
self.assertTrue(torch.allclose(grads1[i].cpu(), grads2[i].cpu(), atol=1e-6))
def _simple_wsum(self, accum_func, device):
# Initialise variables
features = torch.Tensor(
[[0.1, 0.4, 0.6, 0.9], [0.1, 0.4, 0.6, 0.9]]
).to(device)
features = torch.Tensor([[0.1, 0.4, 0.6, 0.9], [0.1, 0.4, 0.6, 0.9]]).to(device)
alphas = torch.Tensor(
[
@@ -285,15 +273,11 @@ class TestAccumulatePoints(unittest.TestCase):
]
).to(device)
self.assertTrue(
torch.allclose(result.cpu(), true_result.cpu(), rtol=1e-3)
)
self.assertTrue(torch.allclose(result.cpu(), true_result.cpu(), rtol=1e-3))
def _simple_wsumnorm(self, accum_func, device):
# Initialise variables
features = torch.Tensor(
[[0.1, 0.4, 0.6, 0.9], [0.1, 0.4, 0.6, 0.9]]
).to(device)
features = torch.Tensor([[0.1, 0.4, 0.6, 0.9], [0.1, 0.4, 0.6, 0.9]]).to(device)
alphas = torch.Tensor(
[
@@ -362,15 +346,11 @@ class TestAccumulatePoints(unittest.TestCase):
]
).to(device)
self.assertTrue(
torch.allclose(result.cpu(), true_result.cpu(), rtol=1e-3)
)
self.assertTrue(torch.allclose(result.cpu(), true_result.cpu(), rtol=1e-3))
def _simple_alphacomposite(self, accum_func, device):
# Initialise variables
features = torch.Tensor(
[[0.1, 0.4, 0.6, 0.9], [0.1, 0.4, 0.6, 0.9]]
).to(device)
features = torch.Tensor([[0.1, 0.4, 0.6, 0.9], [0.1, 0.4, 0.6, 0.9]]).to(device)
alphas = torch.Tensor(
[

View File

@@ -1,8 +1,8 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
import unittest
import torch
import torch
from pytorch3d.ops import cubify
@@ -33,9 +33,7 @@ class TestCubify(unittest.TestCase):
# 1st-check
verts, faces = meshes.get_mesh_verts_faces(0)
self.assertTrue(
torch.allclose(faces.max(), torch.tensor([verts.size(0) - 1]))
)
self.assertTrue(torch.allclose(faces.max(), torch.tensor([verts.size(0) - 1])))
self.assertTrue(
torch.allclose(
verts,
@@ -80,9 +78,7 @@ class TestCubify(unittest.TestCase):
)
# 2nd-check
verts, faces = meshes.get_mesh_verts_faces(1)
self.assertTrue(
torch.allclose(faces.max(), torch.tensor([verts.size(0) - 1]))
)
self.assertTrue(torch.allclose(faces.max(), torch.tensor([verts.size(0) - 1])))
self.assertTrue(
torch.allclose(
verts,
@@ -275,9 +271,7 @@ class TestCubify(unittest.TestCase):
@staticmethod
def cubify_with_init(batch_size: int, V: int):
device = torch.device("cuda:0")
voxels = torch.rand(
(batch_size, V, V, V), dtype=torch.float32, device=device
)
voxels = torch.rand((batch_size, V, V, V), dtype=torch.float32, device=device)
torch.cuda.synchronize()
def convert():

View File

@@ -2,13 +2,12 @@
import unittest
import torch
import torch
from common_testing import TestCaseMixin
from pytorch3d.ops import mesh_face_areas_normals
from pytorch3d.structures.meshes import Meshes
from common_testing import TestCaseMixin
class TestFaceAreasNormals(TestCaseMixin, unittest.TestCase):
def setUp(self) -> None:
@@ -27,10 +26,7 @@ class TestFaceAreasNormals(TestCaseMixin, unittest.TestCase):
faces_list = []
for _ in range(num_meshes):
verts = torch.rand(
(num_verts, 3),
dtype=torch.float32,
device=device,
requires_grad=True,
(num_verts, 3), dtype=torch.float32, device=device, requires_grad=True
)
faces = torch.randint(
num_verts, size=(num_faces, 3), dtype=torch.int64, device=device
@@ -55,9 +51,7 @@ class TestFaceAreasNormals(TestCaseMixin, unittest.TestCase):
v02 = vertices_faces[:, 2] - vertices_faces[:, 0]
normals = torch.cross(v01, v02, dim=1) # (F, 3)
face_areas = normals.norm(dim=-1) / 2
face_normals = torch.nn.functional.normalize(
normals, p=2, dim=1, eps=1e-6
)
face_normals = torch.nn.functional.normalize(normals, p=2, dim=1, eps=1e-6)
return face_areas, face_normals
def _test_face_areas_normals_helper(self, device, dtype=torch.float32):
@@ -76,10 +70,7 @@ class TestFaceAreasNormals(TestCaseMixin, unittest.TestCase):
verts_torch = verts.detach().clone().to(dtype)
verts_torch.requires_grad = True
faces_torch = faces.detach().clone()
(
areas_torch,
normals_torch,
) = TestFaceAreasNormals.face_areas_normals_python(
(areas_torch, normals_torch) = TestFaceAreasNormals.face_areas_normals_python(
verts_torch, faces_torch
)
self.assertClose(areas_torch, areas, atol=1e-7)

View File

@@ -1,20 +1,15 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
import unittest
import torch
import torch.nn as nn
from common_testing import TestCaseMixin
from pytorch3d import _C
from pytorch3d.ops.graph_conv import (
GraphConv,
gather_scatter,
gather_scatter_python,
)
from pytorch3d.ops.graph_conv import GraphConv, gather_scatter, gather_scatter_python
from pytorch3d.structures.meshes import Meshes
from pytorch3d.utils import ico_sphere
from common_testing import TestCaseMixin
class TestGraphConv(TestCaseMixin, unittest.TestCase):
def test_undirected(self):
@@ -89,8 +84,7 @@ class TestGraphConv(TestCaseMixin, unittest.TestCase):
w1 = torch.tensor([[-1, -1, -1]], dtype=dtype)
expected_y = torch.tensor(
[[1 + 2 + 3 - 4 - 5 - 6 - 7 - 8 - 9], [4 + 5 + 6], [7 + 8 + 9]],
dtype=dtype,
[[1 + 2 + 3 - 4 - 5 - 6 - 7 - 8 - 9], [4 + 5 + 6], [7 + 8 + 9]], dtype=dtype
)
conv = GraphConv(3, 1, directed=True).to(dtype)
@@ -126,17 +120,13 @@ class TestGraphConv(TestCaseMixin, unittest.TestCase):
def test_cpu_cuda_tensor_error(self):
device = torch.device("cuda:0")
verts = torch.tensor(
[[1, 2, 3], [4, 5, 6], [7, 8, 9]],
dtype=torch.float32,
device=device,
[[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=torch.float32, device=device
)
edges = torch.tensor([[0, 1], [0, 2]])
conv = GraphConv(3, 1, directed=True).to(torch.float32)
with self.assertRaises(Exception) as err:
conv(verts, edges)
self.assertTrue(
"tensors must be on the same device." in str(err.exception)
)
self.assertTrue("tensors must be on the same device." in str(err.exception))
def test_gather_scatter(self):
"""
@@ -178,12 +168,10 @@ class TestGraphConv(TestCaseMixin, unittest.TestCase):
backend: str = "cuda",
):
device = torch.device("cuda") if backend == "cuda" else "cpu"
verts_list = torch.tensor(
num_verts * [[0.11, 0.22, 0.33]], device=device
).view(-1, 3)
faces_list = torch.tensor(num_faces * [[1, 2, 3]], device=device).view(
verts_list = torch.tensor(num_verts * [[0.11, 0.22, 0.33]], device=device).view(
-1, 3
)
faces_list = torch.tensor(num_faces * [[1, 2, 3]], device=device).view(-1, 3)
meshes = Meshes(num_meshes * [verts_list], num_meshes * [faces_list])
gconv = GraphConv(gconv_dim, gconv_dim, directed=directed)
gconv.to(device)
@@ -191,9 +179,7 @@ class TestGraphConv(TestCaseMixin, unittest.TestCase):
total_verts = meshes.verts_packed().shape[0]
# Features.
x = torch.randn(
total_verts, gconv_dim, device=device, requires_grad=True
)
x = torch.randn(total_verts, gconv_dim, device=device, requires_grad=True)
torch.cuda.synchronize()
def run_graph_conv():

View File

@@ -2,8 +2,8 @@
import unittest
from itertools import product
import torch
import torch
from pytorch3d.ops.knn import _knn_points_idx_naive, knn_points_idx

View File

@@ -1,14 +1,13 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
import numpy as np
import unittest
import torch
import numpy as np
import torch
from common_testing import TestCaseMixin
from pytorch3d.renderer.lighting import DirectionalLights, PointLights
from pytorch3d.transforms import RotateAxisAngle
from common_testing import TestCaseMixin
class TestLights(TestCaseMixin, unittest.TestCase):
def test_init_lights(self):
@@ -56,9 +55,7 @@ class TestLights(TestCaseMixin, unittest.TestCase):
self.assertSeparate(new_prop, prop)
def test_lights_accessor(self):
d_light = DirectionalLights(
ambient_color=((0.0, 0.0, 0.0), (1.0, 1.0, 1.0))
)
d_light = DirectionalLights(ambient_color=((0.0, 0.0, 0.0), (1.0, 1.0, 1.0)))
p_light = PointLights(ambient_color=((0.0, 0.0, 0.0), (1.0, 1.0, 1.0)))
for light in [d_light, p_light]:
# Update element
@@ -96,14 +93,12 @@ class TestLights(TestCaseMixin, unittest.TestCase):
"""
with self.assertRaises(ValueError):
DirectionalLights(
ambient_color=torch.randn(10, 3),
diffuse_color=torch.randn(15, 3),
ambient_color=torch.randn(10, 3), diffuse_color=torch.randn(15, 3)
)
with self.assertRaises(ValueError):
PointLights(
ambient_color=torch.randn(10, 3),
diffuse_color=torch.randn(15, 3),
ambient_color=torch.randn(10, 3), diffuse_color=torch.randn(15, 3)
)
def test_initialize_lights_dimensions_fail(self):
@@ -138,8 +133,7 @@ class TestDiffuseLighting(TestCaseMixin, unittest.TestCase):
normals = torch.tensor([0, 0, 1], dtype=torch.float32)
normals = normals[None, None, :]
expected_output = torch.tensor(
[1 / np.sqrt(2), 1 / np.sqrt(2), 1 / np.sqrt(2)],
dtype=torch.float32,
[1 / np.sqrt(2), 1 / np.sqrt(2), 1 / np.sqrt(2)], dtype=torch.float32
)
expected_output = expected_output.view(1, 1, 3).repeat(3, 1, 1)
light = DirectionalLights(diffuse_color=color, direction=direction)
@@ -169,13 +163,10 @@ class TestDiffuseLighting(TestCaseMixin, unittest.TestCase):
points = torch.tensor([0, 0, 0], dtype=torch.float32)
normals = torch.tensor([0, 0, 1], dtype=torch.float32)
expected_output = torch.tensor(
[1 / np.sqrt(2), 1 / np.sqrt(2), 1 / np.sqrt(2)],
dtype=torch.float32,
[1 / np.sqrt(2), 1 / np.sqrt(2), 1 / np.sqrt(2)], dtype=torch.float32
)
expected_output = expected_output.view(-1, 1, 3)
light = PointLights(
diffuse_color=color[None, :], location=location[None, :]
)
light = PointLights(diffuse_color=color[None, :], location=location[None, :])
output_light = light.diffuse(
points=points[None, None, :], normals=normals[None, None, :]
)
@@ -184,9 +175,7 @@ class TestDiffuseLighting(TestCaseMixin, unittest.TestCase):
# Change light direction to be 90 degrees apart from normal direction.
location = torch.tensor([0, 1, 0], dtype=torch.float32)
expected_output = torch.zeros_like(expected_output)
light = PointLights(
diffuse_color=color[None, :], location=location[None, :]
)
light = PointLights(diffuse_color=color[None, :], location=location[None, :])
output_light = light.diffuse(
points=points[None, None, :], normals=normals[None, None, :]
)
@@ -204,8 +193,7 @@ class TestDiffuseLighting(TestCaseMixin, unittest.TestCase):
)
normals = torch.tensor([0, 0, 1], dtype=torch.float32)
expected_out = torch.tensor(
[1 / np.sqrt(2), 1 / np.sqrt(2), 1 / np.sqrt(2)],
dtype=torch.float32,
[1 / np.sqrt(2), 1 / np.sqrt(2), 1 / np.sqrt(2)], dtype=torch.float32
)
# Reshape
@@ -231,8 +219,7 @@ class TestDiffuseLighting(TestCaseMixin, unittest.TestCase):
)
normals = torch.tensor([0, 0, 1], dtype=torch.float32)
expected_out = torch.tensor(
[1 / np.sqrt(2), 1 / np.sqrt(2), 1 / np.sqrt(2)],
dtype=torch.float32,
[1 / np.sqrt(2), 1 / np.sqrt(2), 1 / np.sqrt(2)], dtype=torch.float32
)
# Reshape
@@ -258,9 +245,7 @@ class TestDiffuseLighting(TestCaseMixin, unittest.TestCase):
device = torch.device("cuda:0")
color = torch.tensor([1, 1, 1], dtype=torch.float32, device=device)
direction = torch.tensor(
[0, 1 / np.sqrt(2), 1 / np.sqrt(2)],
dtype=torch.float32,
device=device,
[0, 1 / np.sqrt(2), 1 / np.sqrt(2)], dtype=torch.float32, device=device
)
normals = torch.tensor([0, 0, 1], dtype=torch.float32, device=device)
normals = normals.view(1, 1, 1, 1, 3).expand(N, H, W, K, -1)
@@ -373,9 +358,7 @@ class TestSpecularLighting(TestCaseMixin, unittest.TestCase):
normals = torch.tensor([0, 1, 0], dtype=torch.float32)
expected_output = torch.tensor([1.0, 0.0, 1.0], dtype=torch.float32)
expected_output = expected_output.view(-1, 1, 3)
lights = PointLights(
specular_color=color[None, :], location=location[None, :]
)
lights = PointLights(specular_color=color[None, :], location=location[None, :])
output_light = lights.specular(
points=points[None, None, :],
normals=normals[None, None, :],
@@ -528,8 +511,7 @@ class TestSpecularLighting(TestCaseMixin, unittest.TestCase):
mesh_to_vert_idx = torch.tensor(mesh_to_vert_idx, dtype=torch.int64)
color = torch.tensor([[1, 1, 1], [1, 0, 1]], dtype=torch.float32)
direction = torch.tensor(
[[-1 / np.sqrt(2), 1 / np.sqrt(2), 0], [-1, 1, 0]],
dtype=torch.float32,
[[-1 / np.sqrt(2), 1 / np.sqrt(2), 0], [-1, 1, 0]], dtype=torch.float32
)
camera_position = torch.tensor(
[

View File

@@ -1,11 +1,10 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
import unittest
import torch
from pytorch3d.renderer.materials import Materials
from common_testing import TestCaseMixin
from pytorch3d.renderer.materials import Materials
class TestMaterials(TestCaseMixin, unittest.TestCase):
@@ -64,8 +63,7 @@ class TestMaterials(TestCaseMixin, unittest.TestCase):
"""
with self.assertRaises(ValueError):
Materials(
ambient_color=torch.randn(10, 3),
diffuse_color=torch.randn(15, 3),
ambient_color=torch.randn(10, 3), diffuse_color=torch.randn(15, 3)
)
def test_initialize_materials_dimensions_fail(self):
@@ -80,16 +78,12 @@ class TestMaterials(TestCaseMixin, unittest.TestCase):
Materials(shininess=torch.randn(10, 2))
def test_initialize_materials_mixed_inputs(self):
mat = Materials(
ambient_color=torch.randn(1, 3), diffuse_color=((1, 1, 1),)
)
mat = Materials(ambient_color=torch.randn(1, 3), diffuse_color=((1, 1, 1),))
self.assertTrue(mat.ambient_color.shape == (1, 3))
self.assertTrue(mat.diffuse_color.shape == (1, 3))
def test_initialize_materials_mixed_inputs_broadcast(self):
mat = Materials(
ambient_color=torch.randn(10, 3), diffuse_color=((1, 1, 1),)
)
mat = Materials(ambient_color=torch.randn(10, 3), diffuse_color=((1, 1, 1),))
self.assertTrue(mat.ambient_color.shape == (10, 3))
self.assertTrue(mat.diffuse_color.shape == (10, 3))
self.assertTrue(mat.specular_color.shape == (10, 3))

View File

@@ -1,12 +1,11 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
import unittest
import torch
import torch
from common_testing import TestCaseMixin
from pytorch3d.loss import mesh_edge_loss
from pytorch3d.structures import Meshes
from common_testing import TestCaseMixin
from test_sample_points_from_meshes import TestSamplePoints
@@ -27,9 +26,7 @@ class TestMeshEdgeLoss(TestCaseMixin, unittest.TestCase):
mesh = Meshes(verts=verts_list, faces=faces_list)
loss = mesh_edge_loss(mesh, target_length=target_length)
self.assertClose(
loss, torch.tensor([0.0], dtype=torch.float32, device=device)
)
self.assertClose(loss, torch.tensor([0.0], dtype=torch.float32, device=device))
self.assertTrue(loss.requires_grad)
@staticmethod
@@ -53,9 +50,7 @@ class TestMeshEdgeLoss(TestCaseMixin, unittest.TestCase):
num_edges = mesh_edges.size(0)
for e in range(num_edges):
v0, v1 = verts_edges[e, 0], verts_edges[e, 1]
predlosses[b] += (
(v0 - v1).norm(dim=0, p=2) - target_length
) ** 2.0
predlosses[b] += ((v0 - v1).norm(dim=0, p=2) - target_length) ** 2.0
if num_edges > 0:
predlosses[b] = predlosses[b] / num_edges
@@ -96,12 +91,8 @@ class TestMeshEdgeLoss(TestCaseMixin, unittest.TestCase):
self.assertClose(loss, predloss)
@staticmethod
def mesh_edge_loss(
num_meshes: int = 10, max_v: int = 100, max_f: int = 300
):
meshes = TestSamplePoints.init_meshes(
num_meshes, max_v, max_f, device="cuda:0"
)
def mesh_edge_loss(num_meshes: int = 10, max_v: int = 100, max_f: int = 300):
meshes = TestSamplePoints.init_meshes(num_meshes, max_v, max_f, device="cuda:0")
torch.cuda.synchronize()
def compute_loss():

View File

@@ -2,8 +2,8 @@
import unittest
import torch
import torch
from pytorch3d.loss.mesh_laplacian_smoothing import mesh_laplacian_smoothing
from pytorch3d.structures.meshes import Meshes
@@ -56,9 +56,7 @@ class TestLaplacianSmoothing(unittest.TestCase):
V = verts_packed.shape[0]
L = torch.zeros((V, V), dtype=torch.float32, device=meshes.device)
inv_areas = torch.zeros(
(V, 1), dtype=torch.float32, device=meshes.device
)
inv_areas = torch.zeros((V, 1), dtype=torch.float32, device=meshes.device)
for f in faces_packed:
v0 = verts_packed[f[0], :]
@@ -69,9 +67,7 @@ class TestLaplacianSmoothing(unittest.TestCase):
C = (v0 - v1).norm()
s = 0.5 * (A + B + C)
face_area = (
(s * (s - A) * (s - B) * (s - C)).clamp_(min=1e-12).sqrt()
)
face_area = (s * (s - A) * (s - B) * (s - C)).clamp_(min=1e-12).sqrt()
inv_areas[f[0]] += face_area
inv_areas[f[1]] += face_area
inv_areas[f[2]] += face_area
@@ -114,16 +110,13 @@ class TestLaplacianSmoothing(unittest.TestCase):
return loss.sum() / len(meshes)
@staticmethod
def init_meshes(
num_meshes: int = 10, num_verts: int = 1000, num_faces: int = 3000
):
def init_meshes(num_meshes: int = 10, num_verts: int = 1000, num_faces: int = 3000):
device = torch.device("cuda:0")
verts_list = []
faces_list = []
for _ in range(num_meshes):
verts = (
torch.rand((num_verts, 3), dtype=torch.float32, device=device)
* 2.0
torch.rand((num_verts, 3), dtype=torch.float32, device=device) * 2.0
- 1.0
) # verts in the space of [-1, 1]
faces = torch.stack(
@@ -148,9 +141,7 @@ class TestLaplacianSmoothing(unittest.TestCase):
# feats in list
out = mesh_laplacian_smoothing(meshes, method="uniform")
naive_out = TestLaplacianSmoothing.laplacian_smoothing_naive_uniform(
meshes
)
naive_out = TestLaplacianSmoothing.laplacian_smoothing_naive_uniform(meshes)
self.assertTrue(torch.allclose(out, naive_out))
@@ -190,9 +181,7 @@ class TestLaplacianSmoothing(unittest.TestCase):
verts_list = []
faces_list = []
for _ in range(num_meshes):
verts = torch.rand(
(num_verts, 3), dtype=torch.float32, device=device
)
verts = torch.rand((num_verts, 3), dtype=torch.float32, device=device)
faces = torch.randint(
num_verts, size=(num_faces, 3), dtype=torch.int64, device=device
)

View File

@@ -2,8 +2,8 @@
import unittest
import torch
import torch
from pytorch3d.loss.mesh_normal_consistency import mesh_normal_consistency
from pytorch3d.structures.meshes import Meshes
from pytorch3d.utils.ico_sphere import ico_sphere
@@ -33,17 +33,14 @@ class TestMeshNormalConsistency(unittest.TestCase):
return faces
@staticmethod
def init_meshes(
num_meshes: int = 10, num_verts: int = 1000, num_faces: int = 3000
):
def init_meshes(num_meshes: int = 10, num_verts: int = 1000, num_faces: int = 3000):
device = torch.device("cuda:0")
valid_faces = TestMeshNormalConsistency.init_faces(num_verts).to(device)
verts_list = []
faces_list = []
for _ in range(num_meshes):
verts = (
torch.rand((num_verts, 3), dtype=torch.float32, device=device)
* 2.0
torch.rand((num_verts, 3), dtype=torch.float32, device=device) * 2.0
- 1.0
) # verts in the space of [-1, 1]
"""
@@ -105,8 +102,7 @@ class TestMeshNormalConsistency(unittest.TestCase):
(
1
- torch.cosine_similarity(
normals[i].view(1, 3),
-normals[j].view(1, 3),
normals[i].view(1, 3), -normals[j].view(1, 3)
)
)
)
@@ -137,9 +133,7 @@ class TestMeshNormalConsistency(unittest.TestCase):
device = torch.device("cuda:0")
# mesh1 shown above
verts1 = torch.rand((4, 3), dtype=torch.float32, device=device)
faces1 = torch.tensor(
[[0, 1, 2], [2, 1, 3]], dtype=torch.int64, device=device
)
faces1 = torch.tensor([[0, 1, 2], [2, 1, 3]], dtype=torch.int64, device=device)
# mesh2 is a cuboid with 8 verts, 12 faces and 18 edges
verts2 = torch.tensor(
@@ -181,9 +175,7 @@ class TestMeshNormalConsistency(unittest.TestCase):
[[0, 1, 2], [2, 1, 3], [2, 1, 4]], dtype=torch.int64, device=device
)
meshes = Meshes(
verts=[verts1, verts2, verts3], faces=[faces1, faces2, faces3]
)
meshes = Meshes(verts=[verts1, verts2, verts3], faces=[faces1, faces2, faces3])
# mesh1: normal consistency computation
n0 = (verts1[1] - verts1[2]).cross(verts1[3] - verts1[2])

View File

@@ -2,8 +2,8 @@
import unittest
import torch
import torch
from pytorch3d.renderer.mesh.utils import _clip_barycentric_coordinates

View File

@@ -1,12 +1,11 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
import numpy as np
import unittest
import numpy as np
import torch
from pytorch3d.structures.meshes import Meshes
from common_testing import TestCaseMixin
from pytorch3d.structures.meshes import Meshes
class TestMeshes(TestCaseMixin, unittest.TestCase):
@@ -54,9 +53,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
# For lists of faces and vertices, we can sample different v/f
# per mesh.
f = torch.randint(max_f, size=(num_meshes,), dtype=torch.int32)
v = torch.randint(
3, high=max_v, size=(num_meshes,), dtype=torch.int32
)
v = torch.randint(3, high=max_v, size=(num_meshes,), dtype=torch.int32)
# Generate the actual vertices and faces.
for i in range(num_meshes):
@@ -90,12 +87,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
device=device,
),
torch.tensor(
[
[0.1, 0.3, 0.3],
[0.6, 0.7, 0.8],
[0.2, 0.3, 0.4],
[0.1, 0.5, 0.3],
],
[[0.1, 0.3, 0.3], [0.6, 0.7, 0.8], [0.2, 0.3, 0.4], [0.1, 0.5, 0.3]],
dtype=torch.float32,
device=device,
),
@@ -113,9 +105,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
]
faces = [
torch.tensor([[0, 1, 2]], dtype=torch.int64, device=device),
torch.tensor(
[[0, 1, 2], [1, 2, 3]], dtype=torch.int64, device=device
),
torch.tensor([[0, 1, 2], [1, 2, 3]], dtype=torch.int64, device=device),
torch.tensor(
[
[1, 2, 0],
@@ -136,12 +126,8 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
mesh = TestMeshes.init_simple_mesh("cuda:0")
# Check that faces/verts per mesh are set in init:
self.assertClose(
mesh._num_faces_per_mesh.cpu(), torch.tensor([1, 2, 7])
)
self.assertClose(
mesh._num_verts_per_mesh.cpu(), torch.tensor([3, 4, 5])
)
self.assertClose(mesh._num_faces_per_mesh.cpu(), torch.tensor([1, 2, 7]))
self.assertClose(mesh._num_verts_per_mesh.cpu(), torch.tensor([3, 4, 5]))
# Check computed tensors
self.assertClose(
@@ -163,8 +149,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
mesh.mesh_to_faces_packed_first_idx().cpu(), torch.tensor([0, 1, 3])
)
self.assertClose(
mesh.num_edges_per_mesh().cpu(),
torch.tensor([3, 5, 10], dtype=torch.int32),
mesh.num_edges_per_mesh().cpu(), torch.tensor([3, 5, 10], dtype=torch.int32)
)
def test_simple_random_meshes(self):
@@ -172,9 +157,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
# Define the test mesh object either as a list or tensor of faces/verts.
for lists_to_tensors in (False, True):
N = 10
mesh = TestMeshes.init_mesh(
N, 100, 300, lists_to_tensors=lists_to_tensors
)
mesh = TestMeshes.init_mesh(N, 100, 300, lists_to_tensors=lists_to_tensors)
verts_list = mesh.verts_list()
faces_list = mesh.faces_list()
@@ -207,12 +190,8 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
for n in range(N):
v = verts_list[n].shape[0]
f = faces_list[n].shape[0]
self.assertClose(
verts_packed[curv : curv + v, :], verts_list[n]
)
self.assertClose(
faces_packed[curf : curf + f, :] - curv, faces_list[n]
)
self.assertClose(verts_packed[curv : curv + v, :], verts_list[n])
self.assertClose(faces_packed[curf : curf + f, :] - curv, faces_list[n])
self.assertTrue(vert_to_mesh[curv : curv + v].eq(n).all())
self.assertTrue(face_to_mesh[curf : curf + f].eq(n).all())
self.assertTrue(mesh_to_vert[n] == curv)
@@ -232,9 +211,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
npedges = np.concatenate((e12, e20, e01), axis=0)
npedges = np.sort(npedges, axis=1)
unique_edges, unique_idx = np.unique(
npedges, return_index=True, axis=0
)
unique_edges, unique_idx = np.unique(npedges, return_index=True, axis=0)
self.assertTrue(np.allclose(edges, unique_edges))
temp = face_to_mesh.cpu().numpy()
temp = np.concatenate((temp, temp, temp), axis=0)
@@ -266,13 +243,9 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
v = torch.randint(
3, high=V, size=(1,), dtype=torch.int32, device=device
)[0]
f = torch.randint(
F, size=(1,), dtype=torch.int32, device=device
)[0]
f = torch.randint(F, size=(1,), dtype=torch.int32, device=device)[0]
verts = torch.rand((v, 3), dtype=torch.float32, device=device)
faces = torch.randint(
v, size=(f, 3), dtype=torch.int64, device=device
)
faces = torch.randint(v, size=(f, 3), dtype=torch.int64, device=device)
else:
verts = torch.tensor([], dtype=torch.float32, device=device)
faces = torch.tensor([], dtype=torch.int64, device=device)
@@ -309,16 +282,12 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
)
for n in range(N):
verts.append(torch.rand((V, 3), dtype=torch.float32, device=device))
this_faces = torch.full(
(F, 3), -1, dtype=torch.int64, device=device
)
this_faces = torch.full((F, 3), -1, dtype=torch.int64, device=device)
if valid[n]:
v = torch.randint(
3, high=V, size=(1,), dtype=torch.int32, device=device
)[0]
f = torch.randint(
F, size=(1,), dtype=torch.int32, device=device
)[0]
f = torch.randint(F, size=(1,), dtype=torch.int32, device=device)[0]
this_faces[:f, :] = torch.randint(
v, size=(f, 3), dtype=torch.int64, device=device
)
@@ -329,9 +298,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
mesh = Meshes(verts=torch.stack(verts), faces=torch.stack(faces))
# Check verts/faces per mesh are set correctly in init.
self.assertListEqual(
mesh._num_faces_per_mesh.tolist(), num_faces.tolist()
)
self.assertListEqual(mesh._num_faces_per_mesh.tolist(), num_faces.tolist())
self.assertListEqual(mesh._num_verts_per_mesh.tolist(), [V] * N)
for n, (vv, ff) in enumerate(zip(mesh.verts_list(), mesh.faces_list())):
@@ -339,12 +306,8 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
self.assertClose(vv, verts[n])
new_faces = [ff.clone() for ff in faces]
v = torch.randint(
3, high=V, size=(1,), dtype=torch.int32, device=device
)[0]
f = torch.randint(F - 10, size=(1,), dtype=torch.int32, device=device)[
0
]
v = torch.randint(3, high=V, size=(1,), dtype=torch.int32, device=device)[0]
f = torch.randint(F - 10, size=(1,), dtype=torch.int32, device=device)[0]
this_faces = torch.full((F, 3), -1, dtype=torch.int64, device=device)
this_faces[10 : f + 10, :] = torch.randint(
v, size=(f, 3), dtype=torch.int64, device=device
@@ -376,9 +339,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
torch.allclose(new_mesh._verts_list[0], mesh._verts_list[0])
)
self.assertFalse(
torch.allclose(
mesh.num_verts_per_mesh(), new_mesh.num_verts_per_mesh()
)
torch.allclose(mesh.num_verts_per_mesh(), new_mesh.num_verts_per_mesh())
)
self.assertSeparate(new_mesh.verts_packed(), mesh.verts_packed())
self.assertSeparate(new_mesh.verts_padded(), mesh.verts_padded())
@@ -438,9 +399,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
mesh._compute_face_areas_normals(refresh=True)
mesh._compute_vertex_normals(refresh=True)
deform = torch.rand(
(all_v, 3), dtype=torch.float32, device=mesh.device
)
deform = torch.rand((all_v, 3), dtype=torch.float32, device=mesh.device)
# new meshes class to hold the deformed mesh
new_mesh_naive = naive_offset_verts(mesh, deform)
@@ -458,9 +417,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
self.assertClose(
new_mesh.verts_list()[i], new_mesh_naive.verts_list()[i]
)
self.assertClose(
mesh.faces_list()[i], new_mesh_naive.faces_list()[i]
)
self.assertClose(mesh.faces_list()[i], new_mesh_naive.faces_list()[i])
self.assertClose(
new_mesh.faces_list()[i], new_mesh_naive.faces_list()[i]
)
@@ -475,21 +432,11 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
)
# check padded & packed
self.assertClose(
new_mesh.faces_padded(), new_mesh_naive.faces_padded()
)
self.assertClose(
new_mesh.verts_padded(), new_mesh_naive.verts_padded()
)
self.assertClose(
new_mesh.faces_packed(), new_mesh_naive.faces_packed()
)
self.assertClose(
new_mesh.verts_packed(), new_mesh_naive.verts_packed()
)
self.assertClose(
new_mesh.edges_packed(), new_mesh_naive.edges_packed()
)
self.assertClose(new_mesh.faces_padded(), new_mesh_naive.faces_padded())
self.assertClose(new_mesh.verts_padded(), new_mesh_naive.verts_padded())
self.assertClose(new_mesh.faces_packed(), new_mesh_naive.faces_packed())
self.assertClose(new_mesh.verts_packed(), new_mesh_naive.verts_packed())
self.assertClose(new_mesh.edges_packed(), new_mesh_naive.edges_packed())
self.assertClose(
new_mesh.verts_packed_to_mesh_idx(),
new_mesh_naive.verts_packed_to_mesh_idx(),
@@ -499,8 +446,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
new_mesh_naive.mesh_to_verts_packed_first_idx(),
)
self.assertClose(
new_mesh.num_verts_per_mesh(),
new_mesh_naive.num_verts_per_mesh(),
new_mesh.num_verts_per_mesh(), new_mesh_naive.num_verts_per_mesh()
)
self.assertClose(
new_mesh.faces_packed_to_mesh_idx(),
@@ -511,8 +457,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
new_mesh_naive.mesh_to_faces_packed_first_idx(),
)
self.assertClose(
new_mesh.num_faces_per_mesh(),
new_mesh_naive.num_faces_per_mesh(),
new_mesh.num_faces_per_mesh(), new_mesh_naive.num_faces_per_mesh()
)
self.assertClose(
new_mesh.edges_packed_to_mesh_idx(),
@@ -527,24 +472,19 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
# check face areas, normals and vertex normals
self.assertClose(
new_mesh.verts_normals_packed(),
new_mesh_naive.verts_normals_packed(),
new_mesh.verts_normals_packed(), new_mesh_naive.verts_normals_packed()
)
self.assertClose(
new_mesh.verts_normals_padded(),
new_mesh_naive.verts_normals_padded(),
new_mesh.verts_normals_padded(), new_mesh_naive.verts_normals_padded()
)
self.assertClose(
new_mesh.faces_normals_packed(),
new_mesh_naive.faces_normals_packed(),
new_mesh.faces_normals_packed(), new_mesh_naive.faces_normals_packed()
)
self.assertClose(
new_mesh.faces_normals_padded(),
new_mesh_naive.faces_normals_padded(),
new_mesh.faces_normals_padded(), new_mesh_naive.faces_normals_padded()
)
self.assertClose(
new_mesh.faces_areas_packed(),
new_mesh_naive.faces_areas_packed(),
new_mesh.faces_areas_packed(), new_mesh_naive.faces_areas_packed()
)
def test_scale_verts(self):
@@ -579,13 +519,11 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
for i in range(N):
if test == "tensor":
self.assertClose(
scales[i] * mesh.verts_list()[i],
new_mesh.verts_list()[i],
scales[i] * mesh.verts_list()[i], new_mesh.verts_list()[i]
)
else:
self.assertClose(
scales * mesh.verts_list()[i],
new_mesh.verts_list()[i],
scales * mesh.verts_list()[i], new_mesh.verts_list()[i]
)
self.assertClose(
new_mesh.verts_list()[i], new_mesh_naive.verts_list()[i]
@@ -607,21 +545,11 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
)
# check padded & packed
self.assertClose(
new_mesh.faces_padded(), new_mesh_naive.faces_padded()
)
self.assertClose(
new_mesh.verts_padded(), new_mesh_naive.verts_padded()
)
self.assertClose(
new_mesh.faces_packed(), new_mesh_naive.faces_packed()
)
self.assertClose(
new_mesh.verts_packed(), new_mesh_naive.verts_packed()
)
self.assertClose(
new_mesh.edges_packed(), new_mesh_naive.edges_packed()
)
self.assertClose(new_mesh.faces_padded(), new_mesh_naive.faces_padded())
self.assertClose(new_mesh.verts_padded(), new_mesh_naive.verts_padded())
self.assertClose(new_mesh.faces_packed(), new_mesh_naive.faces_packed())
self.assertClose(new_mesh.verts_packed(), new_mesh_naive.verts_packed())
self.assertClose(new_mesh.edges_packed(), new_mesh_naive.edges_packed())
self.assertClose(
new_mesh.verts_packed_to_mesh_idx(),
new_mesh_naive.verts_packed_to_mesh_idx(),
@@ -631,8 +559,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
new_mesh_naive.mesh_to_verts_packed_first_idx(),
)
self.assertClose(
new_mesh.num_verts_per_mesh(),
new_mesh_naive.num_verts_per_mesh(),
new_mesh.num_verts_per_mesh(), new_mesh_naive.num_verts_per_mesh()
)
self.assertClose(
new_mesh.faces_packed_to_mesh_idx(),
@@ -643,8 +570,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
new_mesh_naive.mesh_to_faces_packed_first_idx(),
)
self.assertClose(
new_mesh.num_faces_per_mesh(),
new_mesh_naive.num_faces_per_mesh(),
new_mesh.num_faces_per_mesh(), new_mesh_naive.num_faces_per_mesh()
)
self.assertClose(
new_mesh.edges_packed_to_mesh_idx(),
@@ -675,8 +601,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
new_mesh_naive.faces_normals_padded(),
)
self.assertClose(
new_mesh.faces_areas_packed(),
new_mesh_naive.faces_areas_packed(),
new_mesh.faces_areas_packed(), new_mesh_naive.faces_areas_packed()
)
def test_extend_list(self):
@@ -730,10 +655,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
self.assertTrue(len(split_meshes[0]) == 2)
self.assertTrue(
split_meshes[0].verts_list()
== [
mesh.get_mesh_verts_faces(0)[0],
mesh.get_mesh_verts_faces(1)[0],
]
== [mesh.get_mesh_verts_faces(0)[0], mesh.get_mesh_verts_faces(1)[0]]
)
self.assertTrue(len(split_meshes[1]) == 3)
self.assertTrue(
@@ -756,9 +678,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
verts_faces = [(10, 100), (20, 200)]
for (V, F) in verts_faces:
verts = torch.rand((V, 3), dtype=torch.float32, device=device)
faces = torch.randint(
V, size=(F, 3), dtype=torch.int64, device=device
)
faces = torch.randint(V, size=(F, 3), dtype=torch.int64, device=device)
verts_list.append(verts)
faces_list.append(faces)
@@ -782,9 +702,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
faces_list = []
for (V, F) in [(10, 100)]:
verts = torch.rand((V, 3), dtype=torch.float32, device=device)
faces = torch.randint(
V, size=(F, 3), dtype=torch.int64, device=device
)
faces = torch.randint(V, size=(F, 3), dtype=torch.int64, device=device)
verts_list.append(verts)
faces_list.append(faces)
@@ -802,9 +720,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
verts_faces = [(10, 100), (20, 200), (30, 300)]
for (V, F) in verts_faces:
verts = torch.rand((V, 3), dtype=torch.float32, device=device)
faces = torch.randint(
V, size=(F, 3), dtype=torch.int64, device=device
)
faces = torch.randint(V, size=(F, 3), dtype=torch.int64, device=device)
verts_list.append(verts)
faces_list.append(faces)
@@ -814,9 +730,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
verts_padded = mesh.verts_padded()
verts_padded_flat = verts_padded.view(-1, 3)
self.assertClose(
verts_padded_flat[verts_padded_to_packed_idx], verts_packed
)
self.assertClose(verts_padded_flat[verts_padded_to_packed_idx], verts_packed)
idx = verts_padded_to_packed_idx.view(-1, 1).expand(-1, 3)
self.assertClose(verts_padded_flat.gather(0, idx), verts_packed)
@@ -828,9 +742,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
verts_faces = [(10, 100), (20, 200), (30, 300)]
for (V, F) in verts_faces:
verts = torch.rand((V, 3), dtype=torch.float32, device=device)
faces = torch.randint(
V, size=(F, 3), dtype=torch.int64, device=device
)
faces = torch.randint(V, size=(F, 3), dtype=torch.int64, device=device)
verts_list.append(verts)
faces_list.append(faces)
@@ -1006,12 +918,10 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
verts_normals_packed = meshes.verts_normals_packed()
faces_normals_packed = meshes.faces_normals_packed()
self.assertTrue(
list(verts_normals_packed.shape)
== [verts.shape[0] + verts2.shape[0], 3]
list(verts_normals_packed.shape) == [verts.shape[0] + verts2.shape[0], 3]
)
self.assertTrue(
list(faces_normals_packed.shape)
== [faces.shape[0] + faces2.shape[0], 3]
list(faces_normals_packed.shape) == [faces.shape[0] + faces2.shape[0], 3]
)
# Single mesh where two faces share one vertex so the normal is
@@ -1079,17 +989,12 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
# with areas > eps=1e-6
nonzero = face_areas_cpu > 1e-6
self.assertClose(
face_normals_cpu[nonzero],
face_normals_cuda.cpu()[nonzero],
atol=1e-6,
face_normals_cpu[nonzero], face_normals_cuda.cpu()[nonzero], atol=1e-6
)
@staticmethod
def compute_packed_with_init(
num_meshes: int = 10,
max_v: int = 100,
max_f: int = 300,
device: str = "cpu",
num_meshes: int = 10, max_v: int = 100, max_f: int = 300, device: str = "cpu"
):
mesh = TestMeshes.init_mesh(num_meshes, max_v, max_f, device=device)
torch.cuda.synchronize()
@@ -1102,10 +1007,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase):
@staticmethod
def compute_padded_with_init(
num_meshes: int = 10,
max_v: int = 100,
max_f: int = 300,
device: str = "cpu",
num_meshes: int = 10, max_v: int = 100, max_f: int = 300, device: str = "cpu"
):
mesh = TestMeshes.init_mesh(num_meshes, max_v, max_f, device=device)
torch.cuda.synchronize()

View File

@@ -2,8 +2,8 @@
import unittest
from itertools import product
import torch
import torch
from pytorch3d import _C
@@ -33,9 +33,7 @@ class TestNearestNeighborPoints(unittest.TestCase):
# to the cpp or cuda versions of the function
# depending on the input type.
idx1 = _C.nn_points_idx(x, y)
idx2 = TestNearestNeighborPoints.nn_points_idx_naive(
x, y
)
idx2 = TestNearestNeighborPoints.nn_points_idx_naive(x, y)
self.assertTrue(idx1.size(1) == P1)
self.assertTrue(torch.all(idx1 == idx2))

View File

@@ -4,14 +4,13 @@ import os
import unittest
from io import StringIO
from pathlib import Path
import torch
import torch
from common_testing import TestCaseMixin
from pytorch3d.io import load_obj, load_objs_as_meshes, save_obj
from pytorch3d.structures import Meshes, Textures, join_meshes
from pytorch3d.utils import torus
from common_testing import TestCaseMixin
class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
def test_load_obj_simple(self):
@@ -34,12 +33,7 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
tex_maps = aux.texture_images
expected_verts = torch.tensor(
[
[0.1, 0.2, 0.3],
[0.2, 0.3, 0.4],
[0.3, 0.4, 0.5],
[0.4, 0.5, 0.6],
],
[[0.1, 0.2, 0.3], [0.2, 0.3, 0.4], [0.3, 0.4, 0.5], [0.4, 0.5, 0.6]],
dtype=torch.float32,
)
expected_faces = torch.tensor(
@@ -124,12 +118,8 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
[[0.749279, 0.501284], [0.999110, 0.501077], [0.999455, 0.750380]],
dtype=torch.float32,
)
expected_faces_normals_idx = torch.tensor(
[[1, 1, 1]], dtype=torch.int64
)
expected_faces_textures_idx = torch.tensor(
[[0, 0, 1]], dtype=torch.int64
)
expected_faces_normals_idx = torch.tensor([[1, 1, 1]], dtype=torch.int64)
expected_faces_textures_idx = torch.tensor([[0, 0, 1]], dtype=torch.int64)
self.assertTrue(torch.all(verts == expected_verts))
self.assertTrue(torch.all(faces.verts_idx == expected_faces))
@@ -153,23 +143,13 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
]
)
obj_file = StringIO(obj_file)
expected_faces_normals_idx = torch.tensor(
[[0, 0, 1]], dtype=torch.int64
)
expected_faces_normals_idx = torch.tensor([[0, 0, 1]], dtype=torch.int64)
expected_normals = torch.tensor(
[
[0.000000, 0.000000, -1.000000],
[-1.000000, -0.000000, -0.000000],
],
[[0.000000, 0.000000, -1.000000], [-1.000000, -0.000000, -0.000000]],
dtype=torch.float32,
)
expected_verts = torch.tensor(
[
[0.1, 0.2, 0.3],
[0.2, 0.3, 0.4],
[0.3, 0.4, 0.5],
[0.4, 0.5, 0.6],
],
[[0.1, 0.2, 0.3], [0.2, 0.3, 0.4], [0.3, 0.4, 0.5], [0.4, 0.5, 0.6]],
dtype=torch.float32,
)
verts, faces, aux = load_obj(obj_file)
@@ -198,19 +178,12 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
]
)
obj_file = StringIO(obj_file)
expected_faces_textures_idx = torch.tensor(
[[0, 0, 1]], dtype=torch.int64
)
expected_faces_textures_idx = torch.tensor([[0, 0, 1]], dtype=torch.int64)
expected_textures = torch.tensor(
[[0.999110, 0.501077], [0.999455, 0.750380]], dtype=torch.float32
)
expected_verts = torch.tensor(
[
[0.1, 0.2, 0.3],
[0.2, 0.3, 0.4],
[0.3, 0.4, 0.5],
[0.4, 0.5, 0.6],
],
[[0.1, 0.2, 0.3], [0.2, 0.3, 0.4], [0.3, 0.4, 0.5], [0.4, 0.5, 0.6]],
dtype=torch.float32,
)
verts, faces, aux = load_obj(obj_file)
@@ -257,9 +230,7 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
with self.assertRaises(ValueError) as err:
load_obj(obj_file)
self.assertTrue(
"Vertex properties are inconsistent" in str(err.exception)
)
self.assertTrue("Vertex properties are inconsistent" in str(err.exception))
def test_load_obj_error_too_many_vertex_properties(self):
obj_file = "\n".join(["f 2/1/1/3"])
@@ -267,9 +238,7 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
with self.assertRaises(ValueError) as err:
load_obj(obj_file)
self.assertTrue(
"Face vertices can ony have 3 properties" in str(err.exception)
)
self.assertTrue("Face vertices can ony have 3 properties" in str(err.exception))
def test_load_obj_error_invalid_vertex_indices(self):
obj_file = "\n".join(
@@ -320,7 +289,9 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
verts = torch.FloatTensor([[0.1, 0.2, 0.3, 0.4]]) # (V, 4)
faces = torch.LongTensor([[0, 1, 2]])
save_obj(StringIO(), verts, faces)
expected_message = "Argument 'verts' should either be empty or of shape (num_verts, 3)."
expected_message = (
"Argument 'verts' should either be empty or of shape (num_verts, 3)."
)
self.assertTrue(expected_message, error.exception)
# Invalid faces shape
@@ -328,7 +299,9 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
verts = torch.FloatTensor([[0.1, 0.2, 0.3]])
faces = torch.LongTensor([[0, 1, 2, 3]]) # (F, 4)
save_obj(StringIO(), verts, faces)
expected_message = "Argument 'faces' should either be empty or of shape (num_faces, 3)."
expected_message = (
"Argument 'faces' should either be empty or of shape (num_faces, 3)."
)
self.assertTrue(expected_message, error.exception)
def test_save_obj_invalid_indices(self):
@@ -395,12 +368,7 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
def test_save_obj(self):
verts = torch.tensor(
[
[0.01, 0.2, 0.301],
[0.2, 0.03, 0.408],
[0.3, 0.4, 0.05],
[0.6, 0.7, 0.8],
],
[[0.01, 0.2, 0.301], [0.2, 0.03, 0.408], [0.3, 0.4, 0.05], [0.6, 0.7, 0.8]],
dtype=torch.float32,
)
faces = torch.tensor(
@@ -424,9 +392,7 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
self.assertEqual(actual_file, expected_file)
def test_load_mtl(self):
DATA_DIR = (
Path(__file__).resolve().parent.parent / "docs/tutorials/data"
)
DATA_DIR = Path(__file__).resolve().parent.parent / "docs/tutorials/data"
obj_filename = "cow_mesh/cow.obj"
filename = os.path.join(DATA_DIR, obj_filename)
verts, faces, aux = load_obj(filename)
@@ -452,19 +418,13 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
# Check all keys and values in dictionary are the same.
for n1, n2 in zip(materials.keys(), expected_materials.keys()):
self.assertTrue(n1 == n2)
for k1, k2 in zip(
materials[n1].keys(), expected_materials[n2].keys()
):
for k1, k2 in zip(materials[n1].keys(), expected_materials[n2].keys()):
self.assertTrue(
torch.allclose(
materials[n1][k1], expected_materials[n2][k2]
)
torch.allclose(materials[n1][k1], expected_materials[n2][k2])
)
def test_load_mtl_noload(self):
DATA_DIR = (
Path(__file__).resolve().parent.parent / "docs/tutorials/data"
)
DATA_DIR = Path(__file__).resolve().parent.parent / "docs/tutorials/data"
obj_filename = "cow_mesh/cow.obj"
filename = os.path.join(DATA_DIR, obj_filename)
verts, faces, aux = load_obj(filename, load_textures=False)
@@ -490,12 +450,7 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
verts, faces, aux = load_obj(obj_file)
expected_verts = torch.tensor(
[
[0.1, 0.2, 0.3],
[0.2, 0.3, 0.4],
[0.3, 0.4, 0.5],
[0.4, 0.5, 0.6],
],
[[0.1, 0.2, 0.3], [0.2, 0.3, 0.4], [0.3, 0.4, 0.5], [0.4, 0.5, 0.6]],
dtype=torch.float32,
)
expected_faces = torch.tensor([[0, 1, 2], [0, 1, 3]], dtype=torch.int64)
@@ -514,12 +469,7 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
verts, faces, aux = load_obj(filename)
expected_verts = torch.tensor(
[
[0.1, 0.2, 0.3],
[0.2, 0.3, 0.4],
[0.3, 0.4, 0.5],
[0.4, 0.5, 0.6],
],
[[0.1, 0.2, 0.3], [0.2, 0.3, 0.4], [0.3, 0.4, 0.5], [0.4, 0.5, 0.6]],
dtype=torch.float32,
)
expected_faces = torch.tensor([[0, 1, 2], [0, 1, 3]], dtype=torch.int64)
@@ -533,12 +483,7 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
verts, faces, aux = load_obj(filename, load_textures=False)
expected_verts = torch.tensor(
[
[0.1, 0.2, 0.3],
[0.2, 0.3, 0.4],
[0.3, 0.4, 0.5],
[0.4, 0.5, 0.6],
],
[[0.1, 0.2, 0.3], [0.2, 0.3, 0.4], [0.3, 0.4, 0.5], [0.4, 0.5, 0.6]],
dtype=torch.float32,
)
expected_faces = torch.tensor([[0, 1, 2], [0, 1, 3]], dtype=torch.int64)
@@ -555,12 +500,7 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
verts, faces, aux = load_obj(filename)
expected_verts = torch.tensor(
[
[0.1, 0.2, 0.3],
[0.2, 0.3, 0.4],
[0.3, 0.4, 0.5],
[0.4, 0.5, 0.6],
],
[[0.1, 0.2, 0.3], [0.2, 0.3, 0.4], [0.3, 0.4, 0.5], [0.4, 0.5, 0.6]],
dtype=torch.float32,
)
expected_faces = torch.tensor([[0, 1, 2], [0, 1, 3]], dtype=torch.int64)
@@ -574,12 +514,7 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
verts, faces, aux = load_obj(filename, load_textures=False)
expected_verts = torch.tensor(
[
[0.1, 0.2, 0.3],
[0.2, 0.3, 0.4],
[0.3, 0.4, 0.5],
[0.4, 0.5, 0.6],
],
[[0.1, 0.2, 0.3], [0.2, 0.3, 0.4], [0.3, 0.4, 0.5], [0.4, 0.5, 0.6]],
dtype=torch.float32,
)
expected_faces = torch.tensor([[0, 1, 2], [0, 1, 3]], dtype=torch.int64)
@@ -607,33 +542,24 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
check_item(mesh.verts_padded(), mesh3.verts_padded())
check_item(mesh.faces_padded(), mesh3.faces_padded())
if mesh.textures is not None:
check_item(mesh.textures.maps_padded(), mesh3.textures.maps_padded())
check_item(
mesh.textures.maps_padded(), mesh3.textures.maps_padded()
mesh.textures.faces_uvs_padded(), mesh3.textures.faces_uvs_padded()
)
check_item(
mesh.textures.faces_uvs_padded(),
mesh3.textures.faces_uvs_padded(),
mesh.textures.verts_uvs_padded(), mesh3.textures.verts_uvs_padded()
)
check_item(
mesh.textures.verts_uvs_padded(),
mesh3.textures.verts_uvs_padded(),
)
check_item(
mesh.textures.verts_rgb_padded(),
mesh3.textures.verts_rgb_padded(),
mesh.textures.verts_rgb_padded(), mesh3.textures.verts_rgb_padded()
)
DATA_DIR = (
Path(__file__).resolve().parent.parent / "docs/tutorials/data"
)
DATA_DIR = Path(__file__).resolve().parent.parent / "docs/tutorials/data"
obj_filename = DATA_DIR / "cow_mesh/cow.obj"
mesh = load_objs_as_meshes([obj_filename])
mesh3 = load_objs_as_meshes([obj_filename, obj_filename, obj_filename])
check_triple(mesh, mesh3)
self.assertTupleEqual(
mesh.textures.maps_padded().shape, (1, 1024, 1024, 3)
)
self.assertTupleEqual(mesh.textures.maps_padded().shape, (1, 1024, 1024, 3))
mesh_notex = load_objs_as_meshes([obj_filename], load_textures=False)
mesh3_notex = load_objs_as_meshes(
@@ -655,9 +581,7 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
teapot_obj = DATA_DIR / "teapot.obj"
mesh_teapot = load_objs_as_meshes([teapot_obj])
teapot_verts, teapot_faces = mesh_teapot.get_mesh_verts_faces(0)
mix_mesh = load_objs_as_meshes(
[obj_filename, teapot_obj], load_textures=False
)
mix_mesh = load_objs_as_meshes([obj_filename, teapot_obj], load_textures=False)
self.assertEqual(len(mix_mesh), 2)
self.assertClose(mix_mesh.verts_list()[0], mesh.verts_list()[0])
self.assertClose(mix_mesh.faces_list()[0], mesh.faces_list()[0])
@@ -671,15 +595,11 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
self.assertClose(cow3_tea.faces_list()[3], mesh_teapot.faces_list()[0])
@staticmethod
def _bm_save_obj(
verts: torch.Tensor, faces: torch.Tensor, decimal_places: int
):
def _bm_save_obj(verts: torch.Tensor, faces: torch.Tensor, decimal_places: int):
return lambda: save_obj(StringIO(), verts, faces, decimal_places)
@staticmethod
def _bm_load_obj(
verts: torch.Tensor, faces: torch.Tensor, decimal_places: int
):
def _bm_load_obj(verts: torch.Tensor, faces: torch.Tensor, decimal_places: int):
f = StringIO()
save_obj(f, verts, faces, decimal_places)
s = f.getvalue()

View File

@@ -1,13 +1,12 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
import unittest
import torch
import torch
from common_testing import TestCaseMixin
from pytorch3d.ops import packed_to_padded, padded_to_packed
from pytorch3d.structures.meshes import Meshes
from common_testing import TestCaseMixin
class TestPackedToPadded(TestCaseMixin, unittest.TestCase):
def setUp(self) -> None:
@@ -25,9 +24,7 @@ class TestPackedToPadded(TestCaseMixin, unittest.TestCase):
verts_list = []
faces_list = []
for _ in range(num_meshes):
verts = torch.rand(
(num_verts, 3), dtype=torch.float32, device=device
)
verts = torch.rand((num_verts, 3), dtype=torch.float32, device=device)
faces = torch.randint(
num_verts, size=(num_faces, 3), dtype=torch.int64, device=device
)
@@ -47,9 +44,7 @@ class TestPackedToPadded(TestCaseMixin, unittest.TestCase):
if D == 0:
inputs_padded = torch.zeros((num_meshes, max_size), device=device)
else:
inputs_padded = torch.zeros(
(num_meshes, max_size, D), device=device
)
inputs_padded = torch.zeros((num_meshes, max_size, D), device=device)
for m in range(num_meshes):
s = first_idxs[m]
if m == num_meshes - 1:
@@ -92,13 +87,9 @@ class TestPackedToPadded(TestCaseMixin, unittest.TestCase):
max_faces = meshes.num_faces_per_mesh().max().item()
if D == 0:
values = torch.rand(
(faces.shape[0],), device=device, requires_grad=True
)
values = torch.rand((faces.shape[0],), device=device, requires_grad=True)
else:
values = torch.rand(
(faces.shape[0], D), device=device, requires_grad=True
)
values = torch.rand((faces.shape[0], D), device=device, requires_grad=True)
values_torch = values.detach().clone()
values_torch.requires_grad = True
values_padded = packed_to_padded(
@@ -120,10 +111,7 @@ class TestPackedToPadded(TestCaseMixin, unittest.TestCase):
values_padded_torch.backward(grad_inputs)
grad_outputs_torch1 = values_torch.grad
grad_outputs_torch2 = TestPackedToPadded.padded_to_packed_python(
grad_inputs,
mesh_to_faces_packed_first_idx,
values.size(0),
device=device,
grad_inputs, mesh_to_faces_packed_first_idx, values.size(0), device=device
)
self.assertClose(grad_outputs, grad_outputs_torch1)
self.assertClose(grad_outputs, grad_outputs_torch2)
@@ -165,9 +153,7 @@ class TestPackedToPadded(TestCaseMixin, unittest.TestCase):
values_torch = values.detach().clone()
values_torch.requires_grad = True
values_packed = padded_to_packed(
values,
mesh_to_faces_packed_first_idx,
num_faces_per_mesh.sum().item(),
values, mesh_to_faces_packed_first_idx, num_faces_per_mesh.sum().item()
)
values_packed_torch = TestPackedToPadded.padded_to_packed_python(
values_torch,
@@ -180,9 +166,7 @@ class TestPackedToPadded(TestCaseMixin, unittest.TestCase):
# check backward
if D == 0:
grad_inputs = torch.rand(
(num_faces_per_mesh.sum().item()), device=device
)
grad_inputs = torch.rand((num_faces_per_mesh.sum().item()), device=device)
else:
grad_inputs = torch.rand(
(num_faces_per_mesh.sum().item(), D), device=device
@@ -192,10 +176,7 @@ class TestPackedToPadded(TestCaseMixin, unittest.TestCase):
values_packed_torch.backward(grad_inputs)
grad_outputs_torch1 = values_torch.grad
grad_outputs_torch2 = TestPackedToPadded.packed_to_padded_python(
grad_inputs,
mesh_to_faces_packed_first_idx,
values.size(1),
device=device,
grad_inputs, mesh_to_faces_packed_first_idx, values.size(1), device=device
)
self.assertClose(grad_outputs, grad_outputs_torch1)
self.assertClose(grad_outputs, grad_outputs_torch2)
@@ -219,34 +200,24 @@ class TestPackedToPadded(TestCaseMixin, unittest.TestCase):
self._test_padded_to_packed_helper(16, "cuda:0")
def test_invalid_inputs_shapes(self, device="cuda:0"):
with self.assertRaisesRegex(
ValueError, "input can only be 2-dimensional."
):
with self.assertRaisesRegex(ValueError, "input can only be 2-dimensional."):
values = torch.rand((100, 50, 2), device=device)
first_idxs = torch.tensor([0, 80], dtype=torch.int64, device=device)
packed_to_padded(values, first_idxs, 100)
with self.assertRaisesRegex(
ValueError, "input can only be 3-dimensional."
):
with self.assertRaisesRegex(ValueError, "input can only be 3-dimensional."):
values = torch.rand((100,), device=device)
first_idxs = torch.tensor([0, 80], dtype=torch.int64, device=device)
padded_to_packed(values, first_idxs, 20)
with self.assertRaisesRegex(
ValueError, "input can only be 3-dimensional."
):
with self.assertRaisesRegex(ValueError, "input can only be 3-dimensional."):
values = torch.rand((100, 50, 2, 2), device=device)
first_idxs = torch.tensor([0, 80], dtype=torch.int64, device=device)
padded_to_packed(values, first_idxs, 20)
@staticmethod
def packed_to_padded_with_init(
num_meshes: int,
num_verts: int,
num_faces: int,
num_d: int,
device: str = "cpu",
num_meshes: int, num_verts: int, num_faces: int, num_d: int, device: str = "cpu"
):
meshes = TestPackedToPadded.init_meshes(
num_meshes, num_verts, num_faces, device
@@ -268,11 +239,7 @@ class TestPackedToPadded(TestCaseMixin, unittest.TestCase):
@staticmethod
def packed_to_padded_with_init_torch(
num_meshes: int,
num_verts: int,
num_faces: int,
num_d: int,
device: str = "cpu",
num_meshes: int, num_verts: int, num_faces: int, num_d: int, device: str = "cpu"
):
meshes = TestPackedToPadded.init_meshes(
num_meshes, num_verts, num_faces, device

View File

@@ -3,13 +3,12 @@
import struct
import unittest
from io import BytesIO, StringIO
import torch
import torch
from common_testing import TestCaseMixin
from pytorch3d.io.ply_io import _load_ply_raw, load_ply, save_ply
from pytorch3d.utils import torus
from common_testing import TestCaseMixin
class TestMeshPlyIO(TestCaseMixin, unittest.TestCase):
def test_raw_load_simple_ascii(self):
@@ -155,14 +154,7 @@ class TestMeshPlyIO(TestCaseMixin, unittest.TestCase):
def test_load_simple_binary(self):
for big_endian in [True, False]:
verts = (
"0 0 0 "
"0 0 1 "
"0 1 1 "
"0 1 0 "
"1 0 0 "
"1 0 1 "
"1 1 1 "
"1 1 0"
"0 0 0 " "0 0 1 " "0 1 1 " "0 1 0 " "1 0 0 " "1 0 1 " "1 1 1 " "1 1 0"
).split()
faces = (
"4 0 1 2 3 "
@@ -176,9 +168,7 @@ class TestMeshPlyIO(TestCaseMixin, unittest.TestCase):
"3 4 5 1"
).split()
short_one = b"\00\01" if big_endian else b"\01\00"
mixed_data = b"\00\00" b"\03\03" + (
short_one + b"\00\01\01\01" b"\00\02"
)
mixed_data = b"\00\00" b"\03\03" + (short_one + b"\00\01\01\01" b"\00\02")
minus_one_data = b"\xff" * 14
endian_char = ">" if big_endian else "<"
format = (
@@ -306,9 +296,7 @@ class TestMeshPlyIO(TestCaseMixin, unittest.TestCase):
lines2 = lines.copy()
lines2[8] = "1 2"
with self.assertRaisesRegex(
ValueError, "Inconsistent data for vertex."
):
with self.assertRaisesRegex(ValueError, "Inconsistent data for vertex."):
_load_ply_raw(StringIO("\n".join(lines2)))
lines2 = lines[:-1]
@@ -344,9 +332,7 @@ class TestMeshPlyIO(TestCaseMixin, unittest.TestCase):
lines2 = lines.copy()
lines2.insert(4, "element bad 1")
with self.assertRaisesRegex(
ValueError, "Found an element with no properties."
):
with self.assertRaisesRegex(ValueError, "Found an element with no properties."):
_load_ply_raw(StringIO("\n".join(lines2)))
lines2 = lines.copy()
@@ -369,25 +355,19 @@ class TestMeshPlyIO(TestCaseMixin, unittest.TestCase):
lines2 = lines.copy()
lines2.insert(4, "property double y")
with self.assertRaisesRegex(
ValueError, "Too little data for an element."
):
with self.assertRaisesRegex(ValueError, "Too little data for an element."):
_load_ply_raw(StringIO("\n".join(lines2)))
lines2[-2] = "3.3 4.2"
_load_ply_raw(StringIO("\n".join(lines2)))
lines2[-2] = "3.3 4.3 2"
with self.assertRaisesRegex(
ValueError, "Too much data for an element."
):
with self.assertRaisesRegex(ValueError, "Too much data for an element."):
_load_ply_raw(StringIO("\n".join(lines2)))
# Now make the ply file actually be readable as a Mesh
with self.assertRaisesRegex(
ValueError, "The ply file has no face element."
):
with self.assertRaisesRegex(ValueError, "The ply file has no face element."):
load_ply(StringIO("\n".join(lines)))
lines2 = lines.copy()
@@ -398,9 +378,7 @@ class TestMeshPlyIO(TestCaseMixin, unittest.TestCase):
lines2.insert(5, "property float z")
lines2.insert(5, "property float y")
lines2[-2] = "0 0 0"
with self.assertRaisesRegex(
ValueError, "Faces must have at least 3 vertices."
):
with self.assertRaisesRegex(ValueError, "Faces must have at least 3 vertices."):
load_ply(StringIO("\n".join(lines2)))
# Good one
@@ -408,17 +386,11 @@ class TestMeshPlyIO(TestCaseMixin, unittest.TestCase):
load_ply(StringIO("\n".join(lines2)))
@staticmethod
def _bm_save_ply(
verts: torch.Tensor, faces: torch.Tensor, decimal_places: int
):
return lambda: save_ply(
StringIO(), verts, faces, decimal_places=decimal_places
)
def _bm_save_ply(verts: torch.Tensor, faces: torch.Tensor, decimal_places: int):
return lambda: save_ply(StringIO(), verts, faces, decimal_places=decimal_places)
@staticmethod
def _bm_load_ply(
verts: torch.Tensor, faces: torch.Tensor, decimal_places: int
):
def _bm_load_ply(verts: torch.Tensor, faces: torch.Tensor, decimal_places: int):
f = StringIO()
save_ply(f, verts, faces, decimal_places)
s = f.getvalue()

View File

@@ -1,13 +1,12 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
import numpy as np
import unittest
import numpy as np
import torch
from pytorch3d.structures.pointclouds import Pointclouds
from common_testing import TestCaseMixin
from pytorch3d.structures.pointclouds import Pointclouds
class TestPointclouds(TestCaseMixin, unittest.TestCase):
@@ -52,13 +51,11 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
normals_list, features_list = None, None
if with_normals:
normals_list = [
torch.rand((i, 3), device=device, dtype=torch.float32)
for i in p
torch.rand((i, 3), device=device, dtype=torch.float32) for i in p
]
if with_features:
features_list = [
torch.rand((i, channels), device=device, dtype=torch.float32)
for i in p
torch.rand((i, channels), device=device, dtype=torch.float32) for i in p
]
if lists_to_tensors:
@@ -68,9 +65,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
if with_features:
features_list = torch.stack(features_list)
return Pointclouds(
points_list, normals=normals_list, features=features_list
)
return Pointclouds(points_list, normals=normals_list, features=features_list)
def test_simple(self):
device = torch.device("cuda:0")
@@ -81,12 +76,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
device=device,
),
torch.tensor(
[
[0.1, 0.3, 0.3],
[0.6, 0.7, 0.8],
[0.2, 0.3, 0.4],
[0.1, 0.5, 0.3],
],
[[0.1, 0.3, 0.3], [0.6, 0.7, 0.8], [0.2, 0.3, 0.4], [0.1, 0.5, 0.3]],
dtype=torch.float32,
device=device,
),
@@ -111,9 +101,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
self.assertClose(
clouds.cloud_to_packed_first_idx().cpu(), torch.tensor([0, 3, 7])
)
self.assertClose(
clouds.num_points_per_cloud().cpu(), torch.tensor([3, 4, 5])
)
self.assertClose(clouds.num_points_per_cloud().cpu(), torch.tensor([3, 4, 5]))
self.assertClose(
clouds.padded_to_packed_idx().cpu(),
torch.tensor([0, 1, 2, 5, 6, 7, 8, 10, 11, 12, 13, 14]),
@@ -129,11 +117,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
"points_padded",
"padded_to_packed_idx",
]
public_normals_getters = [
"normals_list",
"normals_packed",
"normals_padded",
]
public_normals_getters = ["normals_list", "normals_packed", "normals_padded"]
public_features_getters = [
"features_list",
"features_packed",
@@ -147,17 +131,13 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
points_data = [torch.zeros((max_len, 3)).uniform_() for i in lengths]
normals_data = [torch.zeros((max_len, 3)).uniform_() for i in lengths]
features_data = [torch.zeros((max_len, C)).uniform_() for i in lengths]
for length, p, n, f in zip(
lengths, points_data, normals_data, features_data
):
for length, p, n, f in zip(lengths, points_data, normals_data, features_data):
p[length:] = 0.0
n[length:] = 0.0
f[length:] = 0.0
points_list = [d[:length] for length, d in zip(lengths, points_data)]
normals_list = [d[:length] for length, d in zip(lengths, normals_data)]
features_list = [
d[:length] for length, d in zip(lengths, features_data)
]
features_list = [d[:length] for length, d in zip(lengths, features_data)]
points_packed = torch.cat(points_data)
normals_packed = torch.cat(normals_data)
features_packed = torch.cat(features_data)
@@ -173,13 +153,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
("emptylist_emptylist_emptylist", [], [], []),
]
false_cases_inputs = [
(
"list_packed",
points_list,
normals_packed,
features_packed,
ValueError,
),
("list_packed", points_list, normals_packed, features_packed, ValueError),
("packed_0", points_packed, None, None, ValueError),
]
@@ -230,15 +204,11 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
self.assertIsNone(features_padded)
for n in range(N):
p = points_list[n].shape[0]
self.assertClose(
points_padded[n, :p, :], points_list[n]
)
self.assertClose(points_padded[n, :p, :], points_list[n])
if with_normals:
norms = normals_list[n].shape[0]
self.assertEqual(p, norms)
self.assertClose(
normals_padded[n, :p, :], normals_list[n]
)
self.assertClose(normals_padded[n, :p, :], normals_list[n])
if with_features:
f = features_list[n].shape[0]
self.assertEqual(p, f)
@@ -248,9 +218,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
if points_padded.shape[1] > p:
self.assertTrue(points_padded[n, p:, :].eq(0).all())
if with_features:
self.assertTrue(
features_padded[n, p:, :].eq(0).all()
)
self.assertTrue(features_padded[n, p:, :].eq(0).all())
self.assertEqual(points_per_cloud[n], p)
# Check compute packed.
@@ -272,17 +240,13 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
)
if with_normals:
self.assertClose(
normals_packed[cur : cur + p, :],
normals_list[n],
normals_packed[cur : cur + p, :], normals_list[n]
)
if with_features:
self.assertClose(
features_packed[cur : cur + p, :],
features_list[n],
features_packed[cur : cur + p, :], features_list[n]
)
self.assertTrue(
packed_to_cloud[cur : cur + p].eq(n).all()
)
self.assertTrue(packed_to_cloud[cur : cur + p].eq(n).all())
self.assertTrue(cloud_to_packed[n] == cur)
cur += p
@@ -312,9 +276,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
)[0]
points = torch.rand((p, 3), dtype=torch.float32, device=device)
normals = torch.rand((p, 3), dtype=torch.float32, device=device)
features = torch.rand(
(p, C), dtype=torch.float32, device=device
)
features = torch.rand((p, C), dtype=torch.float32, device=device)
else:
points = torch.tensor([], dtype=torch.float32, device=device)
normals = torch.tensor([], dtype=torch.float32, device=device)
@@ -331,9 +293,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
if with_features:
this_features = features_list
clouds = Pointclouds(
points=points_list,
normals=this_normals,
features=this_features,
points=points_list, normals=this_normals, features=this_features
)
points_padded = clouds.points_padded()
normals_padded = clouds.normals_padded()
@@ -346,13 +306,9 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
for n in range(N):
p = len(points_list[n])
if p > 0:
self.assertClose(
points_padded[n, :p, :], points_list[n]
)
self.assertClose(points_padded[n, :p, :], points_list[n])
if with_normals:
self.assertClose(
normals_padded[n, :p, :], normals_list[n]
)
self.assertClose(normals_padded[n, :p, :], normals_list[n])
if with_features:
self.assertClose(
features_padded[n, :p, :], features_list[n]
@@ -360,13 +316,9 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
if points_padded.shape[1] > p:
self.assertTrue(points_padded[n, p:, :].eq(0).all())
if with_normals:
self.assertTrue(
normals_padded[n, p:, :].eq(0).all()
)
self.assertTrue(normals_padded[n, p:, :].eq(0).all())
if with_features:
self.assertTrue(
features_padded[n, p:, :].eq(0).all()
)
self.assertTrue(features_padded[n, p:, :].eq(0).all())
self.assertTrue(points_per_cloud[n] == p)
def test_clone_list(self):
@@ -379,12 +331,8 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
new_clouds = clouds.clone()
# Check cloned and original objects do not share tensors.
self.assertSeparate(
new_clouds.points_list()[0], clouds.points_list()[0]
)
self.assertSeparate(
new_clouds.normals_list()[0], clouds.normals_list()[0]
)
self.assertSeparate(new_clouds.points_list()[0], clouds.points_list()[0])
self.assertSeparate(new_clouds.normals_list()[0], clouds.normals_list()[0])
self.assertSeparate(
new_clouds.features_list()[0], clouds.features_list()[0]
)
@@ -412,12 +360,8 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
new_clouds = clouds.clone()
# Check cloned and original objects do not share tensors.
self.assertSeparate(
new_clouds.points_list()[0], clouds.points_list()[0]
)
self.assertSeparate(
new_clouds.normals_list()[0], clouds.normals_list()[0]
)
self.assertSeparate(new_clouds.points_list()[0], clouds.points_list()[0])
self.assertSeparate(new_clouds.normals_list()[0], clouds.normals_list()[0])
self.assertSeparate(
new_clouds.features_list()[0], clouds.features_list()[0]
)
@@ -442,9 +386,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
for i in range(N):
self.assertClose(cloud1.points_list()[i], cloud2.points_list()[i])
self.assertClose(cloud1.normals_list()[i], cloud2.normals_list()[i])
self.assertClose(
cloud1.features_list()[i], cloud2.features_list()[i]
)
self.assertClose(cloud1.features_list()[i], cloud2.features_list()[i])
has_normals = cloud1.normals_list() is not None
self.assertTrue(has_normals == (cloud2.normals_list() is not None))
has_features = cloud1.features_list() is not None
@@ -459,22 +401,13 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
if has_features:
self.assertClose(cloud1.features_padded(), cloud2.features_padded())
self.assertClose(cloud1.features_packed(), cloud2.features_packed())
self.assertClose(cloud1.packed_to_cloud_idx(), cloud2.packed_to_cloud_idx())
self.assertClose(
cloud1.packed_to_cloud_idx(), cloud2.packed_to_cloud_idx()
)
self.assertClose(
cloud1.cloud_to_packed_first_idx(),
cloud2.cloud_to_packed_first_idx(),
)
self.assertClose(
cloud1.num_points_per_cloud(), cloud2.num_points_per_cloud()
)
self.assertClose(
cloud1.packed_to_cloud_idx(), cloud2.packed_to_cloud_idx()
)
self.assertClose(
cloud1.padded_to_packed_idx(), cloud2.padded_to_packed_idx()
cloud1.cloud_to_packed_first_idx(), cloud2.cloud_to_packed_first_idx()
)
self.assertClose(cloud1.num_points_per_cloud(), cloud2.num_points_per_cloud())
self.assertClose(cloud1.packed_to_cloud_idx(), cloud2.packed_to_cloud_idx())
self.assertClose(cloud1.padded_to_packed_idx(), cloud2.padded_to_packed_idx())
self.assertTrue(all(cloud1.valid == cloud2.valid))
self.assertTrue(cloud1.equisized == cloud2.equisized)
@@ -482,9 +415,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
def naive_offset(clouds, offsets_packed):
new_points_packed = clouds.points_packed() + offsets_packed
new_points_list = list(
new_points_packed.split(
clouds.num_points_per_cloud().tolist(), 0
)
new_points_packed.split(clouds.num_points_per_cloud().tolist(), 0)
)
return Pointclouds(
points=new_points_list,
@@ -502,9 +433,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
clouds._compute_padded()
clouds.padded_to_packed_idx()
deform = torch.rand(
(all_p, 3), dtype=torch.float32, device=clouds.device
)
deform = torch.rand((all_p, 3), dtype=torch.float32, device=clouds.device)
new_clouds_naive = naive_offset(clouds, deform)
new_clouds = clouds.offset(deform)
@@ -521,8 +450,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
clouds.normals_list()[i], new_clouds_naive.normals_list()[i]
)
self.assertClose(
clouds.features_list()[i],
new_clouds_naive.features_list()[i],
clouds.features_list()[i], new_clouds_naive.features_list()[i]
)
self.assertCloudsEqual(new_clouds, new_clouds_naive)
@@ -550,15 +478,13 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
new_clouds = clouds.scale(scales)
for i in range(N):
self.assertClose(
scales[i] * clouds.points_list()[i],
new_clouds.points_list()[i],
scales[i] * clouds.points_list()[i], new_clouds.points_list()[i]
)
self.assertClose(
clouds.normals_list()[i], new_clouds_naive.normals_list()[i]
)
self.assertClose(
clouds.features_list()[i],
new_clouds_naive.features_list()[i],
clouds.features_list()[i], new_clouds_naive.features_list()[i]
)
self.assertCloudsEqual(new_clouds, new_clouds_naive)
@@ -576,20 +502,15 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
for i in range(len(clouds)):
for n in range(N):
self.assertClose(
clouds.points_list()[i],
new_clouds.points_list()[i * N + n],
clouds.points_list()[i], new_clouds.points_list()[i * N + n]
)
self.assertClose(
clouds.normals_list()[i],
new_clouds.normals_list()[i * N + n],
clouds.normals_list()[i], new_clouds.normals_list()[i * N + n]
)
self.assertClose(
clouds.features_list()[i],
new_clouds.features_list()[i * N + n],
)
self.assertTrue(
clouds.valid[i] == new_clouds.valid[i * N + n]
clouds.features_list()[i], new_clouds.features_list()[i * N + n]
)
self.assertTrue(clouds.valid[i] == new_clouds.valid[i * N + n])
self.assertAllSeparate(
clouds.points_list()
+ new_clouds.points_list()
@@ -627,8 +548,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
"padded_to_packed_idx",
]:
self.assertClose(
getattr(new_cloud, attrib)().cpu(),
getattr(cloud, attrib)().cpu(),
getattr(new_cloud, attrib)().cpu(), getattr(cloud, attrib)().cpu()
)
for i in range(len(cloud)):
self.assertClose(
@@ -638,8 +558,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
cloud.normals_list()[i].cpu(), new_cloud.normals_list()[i].cpu()
)
self.assertClose(
cloud.features_list()[i].cpu(),
new_cloud.features_list()[i].cpu(),
cloud.features_list()[i].cpu(), new_cloud.features_list()[i].cpu()
)
self.assertTrue(all(cloud.valid.cpu() == new_cloud.valid.cpu()))
self.assertTrue(cloud.equisized == new_cloud.equisized)
@@ -666,8 +585,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
"padded_to_packed_idx",
]:
self.assertClose(
getattr(new_cloud, attrib)().cpu(),
getattr(cloud, attrib)().cpu(),
getattr(new_cloud, attrib)().cpu(), getattr(cloud, attrib)().cpu()
)
for i in range(len(cloud)):
self.assertClose(
@@ -677,8 +595,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
cloud.normals_list()[i].cpu(), new_cloud.normals_list()[i].cpu()
)
self.assertClose(
cloud.features_list()[i].cpu(),
new_cloud.features_list()[i].cpu(),
cloud.features_list()[i].cpu(), new_cloud.features_list()[i].cpu()
)
self.assertTrue(all(cloud.valid.cpu() == new_cloud.valid.cpu()))
self.assertTrue(cloud.equisized == new_cloud.equisized)
@@ -698,11 +615,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
self.assertEqual(len(split_clouds[1]), 3)
self.assertTrue(
split_clouds[1].points_list()
== [
clouds.get_cloud(2)[0],
clouds.get_cloud(3)[0],
clouds.get_cloud(4)[0],
]
== [clouds.get_cloud(2)[0], clouds.get_cloud(3)[0], clouds.get_cloud(4)[0]]
)
split_sizes = [2, 0.3]
@@ -751,9 +664,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
points_padded = clouds.points_padded()
points_padded_flat = points_padded.view(-1, 3)
self.assertClose(
points_padded_flat[padded_to_packed_idx], points_packed
)
self.assertClose(points_padded_flat[padded_to_packed_idx], points_packed)
idx = padded_to_packed_idx.view(-1, 1).expand(-1, 3)
self.assertClose(points_padded_flat.gather(0, idx), points_packed)
@@ -765,16 +676,13 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
def check_equal(selected, indices):
for selectedIdx, index in indices:
self.assertClose(
selected.points_list()[selectedIdx],
clouds.points_list()[index],
selected.points_list()[selectedIdx], clouds.points_list()[index]
)
self.assertClose(
selected.normals_list()[selectedIdx],
clouds.normals_list()[index],
selected.normals_list()[selectedIdx], clouds.normals_list()[index]
)
self.assertClose(
selected.features_list()[selectedIdx],
clouds.features_list()[index],
selected.features_list()[selectedIdx], clouds.features_list()[index]
)
# int index
@@ -820,11 +728,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
for with_normfeat in (True, False):
for with_new_normfeat in (True, False):
clouds = self.init_cloud(
N,
P,
C,
with_normals=with_normfeat,
with_features=with_normfeat,
N, P, C, with_normals=with_normfeat, with_features=with_normfeat
)
num_points_per_cloud = clouds.num_points_per_cloud()
@@ -843,8 +747,7 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
clouds.points_padded().shape, device=clouds.device
)
new_normals_list = [
new_normals[i, : num_points_per_cloud[i]]
for i in range(N)
new_normals[i, : num_points_per_cloud[i]] for i in range(N)
]
feat_shape = [
clouds.points_padded().shape[0],
@@ -853,14 +756,11 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
]
new_features = torch.rand(feat_shape, device=clouds.device)
new_features_list = [
new_features[i, : num_points_per_cloud[i]]
for i in range(N)
new_features[i, : num_points_per_cloud[i]] for i in range(N)
]
# update
new_clouds = clouds.update_padded(
new_points, new_normals, new_features
)
new_clouds = clouds.update_padded(new_points, new_normals, new_features)
self.assertIsNone(new_clouds._points_list)
self.assertIsNone(new_clouds._points_packed)
@@ -868,13 +768,9 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
self.assertTrue(all(new_clouds.valid == clouds.valid))
self.assertClose(new_clouds.points_padded(), new_points)
self.assertClose(
new_clouds.points_packed(), torch.cat(new_points_list)
)
self.assertClose(new_clouds.points_packed(), torch.cat(new_points_list))
for i in range(N):
self.assertClose(
new_clouds.points_list()[i], new_points_list[i]
)
self.assertClose(new_clouds.points_list()[i], new_points_list[i])
if with_new_normfeat:
for i in range(N):
@@ -890,27 +786,22 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
)
self.assertClose(new_clouds.features_padded(), new_features)
self.assertClose(
new_clouds.features_packed(),
torch.cat(new_features_list),
new_clouds.features_packed(), torch.cat(new_features_list)
)
else:
if with_normfeat:
for i in range(N):
self.assertClose(
new_clouds.normals_list()[i],
clouds.normals_list()[i],
new_clouds.normals_list()[i], clouds.normals_list()[i]
)
self.assertClose(
new_clouds.features_list()[i],
clouds.features_list()[i],
new_clouds.features_list()[i], clouds.features_list()[i]
)
self.assertNotSeparate(
new_clouds.normals_list()[i],
clouds.normals_list()[i],
new_clouds.normals_list()[i], clouds.normals_list()[i]
)
self.assertNotSeparate(
new_clouds.features_list()[i],
clouds.features_list()[i],
new_clouds.features_list()[i], clouds.features_list()[i]
)
self.assertClose(
@@ -920,19 +811,16 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase):
new_clouds.normals_packed(), clouds.normals_packed()
)
self.assertClose(
new_clouds.features_padded(),
clouds.features_padded(),
new_clouds.features_padded(), clouds.features_padded()
)
self.assertClose(
new_clouds.features_packed(),
clouds.features_packed(),
new_clouds.features_packed(), clouds.features_packed()
)
self.assertNotSeparate(
new_clouds.normals_padded(), clouds.normals_padded()
)
self.assertNotSeparate(
new_clouds.features_padded(),
clouds.features_padded(),
new_clouds.features_padded(), clouds.features_padded()
)
else:
self.assertIsNone(new_clouds.normals_list())

View File

@@ -2,8 +2,9 @@
import functools
import unittest
import torch
import torch
from common_testing import TestCaseMixin
from pytorch3d import _C
from pytorch3d.renderer.mesh.rasterize_meshes import (
rasterize_meshes,
@@ -12,20 +13,14 @@ from pytorch3d.renderer.mesh.rasterize_meshes import (
from pytorch3d.structures import Meshes
from pytorch3d.utils import ico_sphere
from common_testing import TestCaseMixin
class TestRasterizeMeshes(TestCaseMixin, unittest.TestCase):
def test_simple_python(self):
device = torch.device("cpu")
self._simple_triangle_raster(
rasterize_meshes_python, device, bin_size=-1
)
self._simple_triangle_raster(rasterize_meshes_python, device, bin_size=-1)
self._simple_blurry_raster(rasterize_meshes_python, device, bin_size=-1)
self._test_behind_camera(rasterize_meshes_python, device, bin_size=-1)
self._test_perspective_correct(
rasterize_meshes_python, device, bin_size=-1
)
self._test_perspective_correct(rasterize_meshes_python, device, bin_size=-1)
def test_simple_cpu_naive(self):
device = torch.device("cpu")
@@ -350,9 +345,7 @@ class TestRasterizeMeshes(TestCaseMixin, unittest.TestCase):
fn1 = functools.partial(rasterize_meshes, meshes1, **kwargs)
fn2 = functools.partial(rasterize_meshes_python, meshes2, **kwargs)
args = ()
self._compare_impls(
fn1, fn2, args, args, verts1, verts2, compare_grads=True
)
self._compare_impls(fn1, fn2, args, args, verts1, verts2, compare_grads=True)
def test_cpp_vs_cuda_perspective_correct(self):
meshes = ico_sphere(2, device=torch.device("cpu"))
@@ -367,9 +360,7 @@ class TestRasterizeMeshes(TestCaseMixin, unittest.TestCase):
fn1 = functools.partial(rasterize_meshes, meshes1, **kwargs)
fn2 = functools.partial(rasterize_meshes, meshes2, bin_size=0, **kwargs)
args = ()
self._compare_impls(
fn1, fn2, args, args, verts1, verts2, compare_grads=True
)
self._compare_impls(fn1, fn2, args, args, verts1, verts2, compare_grads=True)
def test_cuda_naive_vs_binned_perspective_correct(self):
meshes = ico_sphere(2, device=torch.device("cuda"))
@@ -384,9 +375,7 @@ class TestRasterizeMeshes(TestCaseMixin, unittest.TestCase):
fn1 = functools.partial(rasterize_meshes, meshes1, bin_size=0, **kwargs)
fn2 = functools.partial(rasterize_meshes, meshes2, bin_size=8, **kwargs)
args = ()
self._compare_impls(
fn1, fn2, args, args, verts1, verts2, compare_grads=True
)
self._compare_impls(fn1, fn2, args, args, verts1, verts2, compare_grads=True)
def _compare_impls(
self,
@@ -433,9 +422,7 @@ class TestRasterizeMeshes(TestCaseMixin, unittest.TestCase):
grad_verts2 = grad_var2.grad.data.clone().cpu()
self.assertClose(grad_verts1, grad_verts2, rtol=1e-3)
def _test_perspective_correct(
self, rasterize_meshes_fn, device, bin_size=None
):
def _test_perspective_correct(self, rasterize_meshes_fn, device, bin_size=None):
# fmt: off
verts = torch.tensor([
[-0.4, -0.4, 10], # noqa: E241, E201
@@ -542,12 +529,8 @@ class TestRasterizeMeshes(TestCaseMixin, unittest.TestCase):
zbuf_f_bary = w0_f * z0 + w1_f * z1 + w2_f * z2
zbuf_t_bary = w0_t * z0 + w1_t * z1 + w2_t * z2
mask = idx_expected != -1
zbuf_f_bary_diff = (
(zbuf_f_bary[mask] - zbuf_f_expected[mask]).abs().max()
)
zbuf_t_bary_diff = (
(zbuf_t_bary[mask] - zbuf_t_expected[mask]).abs().max()
)
zbuf_f_bary_diff = (zbuf_f_bary[mask] - zbuf_f_expected[mask]).abs().max()
zbuf_t_bary_diff = (zbuf_t_bary[mask] - zbuf_t_expected[mask]).abs().max()
self.assertLess(zbuf_f_bary_diff, 1e-4)
self.assertLess(zbuf_t_bary_diff, 1e-4)
@@ -719,9 +702,7 @@ class TestRasterizeMeshes(TestCaseMixin, unittest.TestCase):
# k = 1, second closest point.
expected_p2face_k1 = expected_p2face_k0.clone()
expected_p2face_k1[0, :] = (
torch.ones_like(expected_p2face_k1[0, :]) * -1
)
expected_p2face_k1[0, :] = torch.ones_like(expected_p2face_k1[0, :]) * -1
# fmt: off
expected_p2face_k1[1, :] = torch.tensor(
@@ -763,9 +744,7 @@ class TestRasterizeMeshes(TestCaseMixin, unittest.TestCase):
# Coordinate conventions +Y up, +Z in, +X left
if bin_size == -1:
# simple python, no bin_size
p2face, zbuf, bary, pix_dists = raster_fn(
meshes, image_size, 0.0, 2
)
p2face, zbuf, bary, pix_dists = raster_fn(meshes, image_size, 0.0, 2)
else:
p2face, zbuf, bary, pix_dists = raster_fn(
meshes, image_size, 0.0, 2, bin_size
@@ -914,9 +893,7 @@ class TestRasterizeMeshes(TestCaseMixin, unittest.TestCase):
# Expected faces using axes convention +Y down, + X right, + Z in
bin_faces_expected = (
torch.ones(
(1, 2, 2, max_faces_per_bin), dtype=torch.int32, device=device
)
torch.ones((1, 2, 2, max_faces_per_bin), dtype=torch.int32, device=device)
* -1
)
bin_faces_expected[0, 0, 0, 0] = torch.tensor([1])
@@ -979,12 +956,7 @@ class TestRasterizeMeshes(TestCaseMixin, unittest.TestCase):
def rasterize():
rasterize_meshes(
meshes_batch,
image_size,
blur_radius,
8,
bin_size,
max_faces_per_bin,
meshes_batch, image_size, blur_radius, 8, bin_size, max_faces_per_bin
)
torch.cuda.synchronize()

View File

@@ -1,10 +1,11 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
import numpy as np
import unittest
import torch
import numpy as np
import torch
from common_testing import TestCaseMixin
from pytorch3d import _C
from pytorch3d.renderer.points.rasterize_points import (
rasterize_points,
@@ -12,8 +13,6 @@ from pytorch3d.renderer.points.rasterize_points import (
)
from pytorch3d.structures.pointclouds import Pointclouds
from common_testing import TestCaseMixin
class TestRasterizePoints(TestCaseMixin, unittest.TestCase):
def test_python_simple_cpu(self):
@@ -38,9 +37,7 @@ class TestRasterizePoints(TestCaseMixin, unittest.TestCase):
self._test_behind_camera(rasterize_points, torch.device("cpu"))
def test_cuda_behind_camera(self):
self._test_behind_camera(
rasterize_points, torch.device("cuda"), bin_size=0
)
self._test_behind_camera(rasterize_points, torch.device("cuda"), bin_size=0)
def test_cpp_vs_naive_vs_binned(self):
# Make sure that the backward pass runs for all pathways
@@ -167,20 +164,8 @@ class TestRasterizePoints(TestCaseMixin, unittest.TestCase):
points_cuda = points_cpu.cuda().detach().requires_grad_(True)
pointclouds_cpu = Pointclouds(points=points_cpu)
pointclouds_cuda = Pointclouds(points=points_cuda)
args_cpu = (
pointclouds_cpu,
image_size,
radius,
points_per_pixel,
bin_size,
)
args_cuda = (
pointclouds_cuda,
image_size,
radius,
points_per_pixel,
bin_size,
)
args_cpu = (pointclouds_cpu, image_size, radius, points_per_pixel, bin_size)
args_cuda = (pointclouds_cuda, image_size, radius, points_per_pixel, bin_size)
self._compare_impls(
rasterize_points,
rasterize_points,
@@ -332,9 +317,7 @@ class TestRasterizePoints(TestCaseMixin, unittest.TestCase):
], device=device)
# fmt: on
dists1_expected = torch.zeros(
(5, 5, 2), dtype=torch.float32, device=device
)
dists1_expected = torch.zeros((5, 5, 2), dtype=torch.float32, device=device)
# fmt: off
dists1_expected[:, :, 0] = torch.tensor([
[-1.00, -1.00, 0.16, -1.00, -1.00], # noqa: E241

View File

@@ -1,22 +1,17 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
import numpy as np
import unittest
from pathlib import Path
import numpy as np
import torch
from PIL import Image
from pytorch3d.renderer.cameras import (
OpenGLPerspectiveCameras,
look_at_view_transform,
)
from pytorch3d.renderer.mesh.rasterizer import (
MeshRasterizer,
RasterizationSettings,
)
from pytorch3d.renderer.cameras import OpenGLPerspectiveCameras, look_at_view_transform
from pytorch3d.renderer.mesh.rasterizer import MeshRasterizer, RasterizationSettings
from pytorch3d.utils.ico_sphere import ico_sphere
DATA_DIR = Path(__file__).resolve().parent / "data"
DEBUG = False # Set DEBUG to true to save outputs from the tests.
@@ -52,9 +47,7 @@ class TestMeshRasterizer(unittest.TestCase):
)
# Init rasterizer
rasterizer = MeshRasterizer(
cameras=cameras, raster_settings=raster_settings
)
rasterizer = MeshRasterizer(cameras=cameras, raster_settings=raster_settings)
####################################
# 1. Test rasterizing a single mesh

View File

@@ -4,23 +4,17 @@
"""
Sanity checks for output images from the renderer.
"""
import numpy as np
import unittest
from pathlib import Path
import numpy as np
import torch
from PIL import Image
from pytorch3d.io import load_objs_as_meshes
from pytorch3d.renderer.cameras import (
OpenGLPerspectiveCameras,
look_at_view_transform,
)
from pytorch3d.renderer.cameras import OpenGLPerspectiveCameras, look_at_view_transform
from pytorch3d.renderer.lighting import PointLights
from pytorch3d.renderer.materials import Materials
from pytorch3d.renderer.mesh.rasterizer import (
MeshRasterizer,
RasterizationSettings,
)
from pytorch3d.renderer.mesh.rasterizer import MeshRasterizer, RasterizationSettings
from pytorch3d.renderer.mesh.renderer import MeshRenderer
from pytorch3d.renderer.mesh.shader import (
BlendParams,
@@ -34,6 +28,7 @@ from pytorch3d.renderer.mesh.texturing import Textures
from pytorch3d.structures.meshes import Meshes
from pytorch3d.utils.ico_sphere import ico_sphere
# If DEBUG=True, save out images generated in the tests for debugging.
# All saved images have prefix DEBUG_
DEBUG = False
@@ -65,9 +60,7 @@ class TestRenderingMeshes(unittest.TestCase):
verts_padded = sphere_mesh.verts_padded()
faces_padded = sphere_mesh.faces_padded()
textures = Textures(verts_rgb=torch.ones_like(verts_padded))
sphere_mesh = Meshes(
verts=verts_padded, faces=faces_padded, textures=textures
)
sphere_mesh = Meshes(verts=verts_padded, faces=faces_padded, textures=textures)
# Init rasterizer settings
if elevated_camera:
@@ -90,9 +83,7 @@ class TestRenderingMeshes(unittest.TestCase):
raster_settings = RasterizationSettings(
image_size=512, blur_radius=0.0, faces_per_pixel=1, bin_size=0
)
rasterizer = MeshRasterizer(
cameras=cameras, raster_settings=raster_settings
)
rasterizer = MeshRasterizer(cameras=cameras, raster_settings=raster_settings)
# Test several shaders
shaders = {
@@ -101,9 +92,7 @@ class TestRenderingMeshes(unittest.TestCase):
"flat": HardFlatShader,
}
for (name, shader_init) in shaders.items():
shader = shader_init(
lights=lights, cameras=cameras, materials=materials
)
shader = shader_init(lights=lights, cameras=cameras, materials=materials)
renderer = MeshRenderer(rasterizer=rasterizer, shader=shader)
images = renderer(sphere_mesh)
filename = "simple_sphere_light_%s%s.png" % (name, postfix)
@@ -125,9 +114,7 @@ class TestRenderingMeshes(unittest.TestCase):
phong_shader = HardPhongShader(
lights=lights, cameras=cameras, materials=materials
)
phong_renderer = MeshRenderer(
rasterizer=rasterizer, shader=phong_shader
)
phong_renderer = MeshRenderer(rasterizer=rasterizer, shader=phong_shader)
images = phong_renderer(sphere_mesh, lights=lights)
rgb = images[0, ..., :3].squeeze().cpu()
if DEBUG:
@@ -137,9 +124,7 @@ class TestRenderingMeshes(unittest.TestCase):
)
# Load reference image
image_ref_phong_dark = load_rgb_image(
"test_simple_sphere_dark%s.png" % postfix
)
image_ref_phong_dark = load_rgb_image("test_simple_sphere_dark%s.png" % postfix)
self.assertTrue(torch.allclose(rgb, image_ref_phong_dark, atol=0.05))
def test_simple_sphere_elevated_camera(self):
@@ -184,18 +169,14 @@ class TestRenderingMeshes(unittest.TestCase):
lights.location = torch.tensor([0.0, 0.0, +2.0], device=device)[None]
# Init renderer
rasterizer = MeshRasterizer(
cameras=cameras, raster_settings=raster_settings
)
rasterizer = MeshRasterizer(cameras=cameras, raster_settings=raster_settings)
shaders = {
"phong": HardGouraudShader,
"gouraud": HardGouraudShader,
"flat": HardFlatShader,
}
for (name, shader_init) in shaders.items():
shader = shader_init(
lights=lights, cameras=cameras, materials=materials
)
shader = shader_init(lights=lights, cameras=cameras, materials=materials)
renderer = MeshRenderer(rasterizer=rasterizer, shader=shader)
images = renderer(sphere_meshes)
image_ref = load_rgb_image("test_simple_sphere_light_%s.png" % name)
@@ -228,9 +209,7 @@ class TestRenderingMeshes(unittest.TestCase):
# Init renderer
renderer = MeshRenderer(
rasterizer=MeshRasterizer(
cameras=cameras, raster_settings=raster_settings
),
rasterizer=MeshRasterizer(cameras=cameras, raster_settings=raster_settings),
shader=SoftSilhouetteShader(blend_params=blend_params),
)
images = renderer(sphere_mesh)
@@ -258,9 +237,7 @@ class TestRenderingMeshes(unittest.TestCase):
The pupils in the eyes of the cow should always be looking to the left.
"""
device = torch.device("cuda:0")
DATA_DIR = (
Path(__file__).resolve().parent.parent / "docs/tutorials/data"
)
DATA_DIR = Path(__file__).resolve().parent.parent / "docs/tutorials/data"
obj_filename = DATA_DIR / "cow_mesh/cow.obj"
# Load mesh + texture
@@ -283,9 +260,7 @@ class TestRenderingMeshes(unittest.TestCase):
# Init renderer
renderer = MeshRenderer(
rasterizer=MeshRasterizer(
cameras=cameras, raster_settings=raster_settings
),
rasterizer=MeshRasterizer(cameras=cameras, raster_settings=raster_settings),
shader=TexturedSoftPhongShader(
lights=lights, cameras=cameras, materials=materials
),
@@ -306,9 +281,7 @@ class TestRenderingMeshes(unittest.TestCase):
# Check grad exists
[verts] = mesh.verts_list()
verts.requires_grad = True
mesh2 = Meshes(
verts=[verts], faces=mesh.faces_list(), textures=mesh.textures
)
mesh2 = Meshes(verts=[verts], faces=mesh.faces_list(), textures=mesh.textures)
images = renderer(mesh2)
images[0, ...].sum().backward()
self.assertIsNotNone(verts.grad)

View File

@@ -1,13 +1,12 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
import numpy as np
import unittest
import numpy as np
import torch
from pytorch3d.renderer.utils import TensorProperties
from common_testing import TestCaseMixin
from pytorch3d.renderer.utils import TensorProperties
# Example class for testing
@@ -81,9 +80,5 @@ class TestTensorProperties(TestCaseMixin, unittest.TestCase):
if inds.sum() > 0:
# Check the gathered points in the output have the same value from
# the input.
self.assertClose(
test_class_gathered.x[inds].mean(dim=0), x[i, ...]
)
self.assertClose(
test_class_gathered.y[inds].mean(dim=0), y[i, ...]
)
self.assertClose(test_class_gathered.x[inds].mean(dim=0), x[i, ...])
self.assertClose(test_class_gathered.y[inds].mean(dim=0), y[i, ...])

View File

@@ -4,8 +4,8 @@
import itertools
import math
import unittest
import torch
import torch
from pytorch3d.transforms.rotation_conversions import (
euler_angles_to_matrix,
matrix_to_euler_angles,
@@ -45,9 +45,7 @@ class TestRandomRotation(unittest.TestCase):
)
# The 0.1 significance level for chisquare(8-1) is
# scipy.stats.chi2(7).ppf(0.9) == 12.017.
self.assertLess(
chisquare_statistic, 12, (counts, chisquare_statistic, k)
)
self.assertLess(chisquare_statistic, 12, (counts, chisquare_statistic, k))
class TestRotationConversion(unittest.TestCase):

View File

@@ -3,14 +3,13 @@
import unittest
from pathlib import Path
import torch
import torch
from common_testing import TestCaseMixin
from pytorch3d.ops import sample_points_from_meshes
from pytorch3d.structures.meshes import Meshes
from pytorch3d.utils.ico_sphere import ico_sphere
from common_testing import TestCaseMixin
class TestSamplePoints(TestCaseMixin, unittest.TestCase):
def setUp(self) -> None:
@@ -28,9 +27,7 @@ class TestSamplePoints(TestCaseMixin, unittest.TestCase):
verts_list = []
faces_list = []
for _ in range(num_meshes):
verts = torch.rand(
(num_verts, 3), dtype=torch.float32, device=device
)
verts = torch.rand((num_verts, 3), dtype=torch.float32, device=device)
faces = torch.randint(
num_verts, size=(num_faces, 3), dtype=torch.int64, device=device
)
@@ -48,13 +45,9 @@ class TestSamplePoints(TestCaseMixin, unittest.TestCase):
device = torch.device("cuda:0")
verts1 = torch.tensor([], dtype=torch.float32, device=device)
faces1 = torch.tensor([], dtype=torch.int64, device=device)
meshes = Meshes(
verts=[verts1, verts1, verts1], faces=[faces1, faces1, faces1]
)
meshes = Meshes(verts=[verts1, verts1, verts1], faces=[faces1, faces1, faces1])
with self.assertRaises(ValueError) as err:
sample_points_from_meshes(
meshes, num_samples=100, return_normals=True
)
sample_points_from_meshes(meshes, num_samples=100, return_normals=True)
self.assertTrue("Meshes are empty." in str(err.exception))
def test_sampling_output(self):
@@ -67,12 +60,7 @@ class TestSamplePoints(TestCaseMixin, unittest.TestCase):
# Unit simplex.
verts_pyramid = torch.tensor(
[
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
],
[[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
dtype=torch.float32,
device=device,
)
@@ -113,12 +101,8 @@ class TestSamplePoints(TestCaseMixin, unittest.TestCase):
pyramid_verts = samples[2, :]
pyramid_normals = normals[2, :]
self.assertClose(
pyramid_verts.lt(1).float(), torch.ones_like(pyramid_verts)
)
self.assertClose(
(pyramid_verts >= 0).float(), torch.ones_like(pyramid_verts)
)
self.assertClose(pyramid_verts.lt(1).float(), torch.ones_like(pyramid_verts))
self.assertClose((pyramid_verts >= 0).float(), torch.ones_like(pyramid_verts))
# Face 1: z = 0, x + y <= 1, normals = (0, 0, 1).
face_1_idxs = pyramid_verts[:, 2] == 0
@@ -126,14 +110,10 @@ class TestSamplePoints(TestCaseMixin, unittest.TestCase):
pyramid_verts[face_1_idxs, :],
pyramid_normals[face_1_idxs, :],
)
self.assertTrue(
torch.all((face_1_verts[:, 0] + face_1_verts[:, 1]) <= 1)
)
self.assertTrue(torch.all((face_1_verts[:, 0] + face_1_verts[:, 1]) <= 1))
self.assertClose(
face_1_normals,
torch.tensor([0, 0, 1], dtype=torch.float32).expand(
face_1_normals.size()
),
torch.tensor([0, 0, 1], dtype=torch.float32).expand(face_1_normals.size()),
)
# Face 2: x = 0, z + y <= 1, normals = (1, 0, 0).
@@ -142,14 +122,10 @@ class TestSamplePoints(TestCaseMixin, unittest.TestCase):
pyramid_verts[face_2_idxs, :],
pyramid_normals[face_2_idxs, :],
)
self.assertTrue(
torch.all((face_2_verts[:, 1] + face_2_verts[:, 2]) <= 1)
)
self.assertTrue(torch.all((face_2_verts[:, 1] + face_2_verts[:, 2]) <= 1))
self.assertClose(
face_2_normals,
torch.tensor([1, 0, 0], dtype=torch.float32).expand(
face_2_normals.size()
),
torch.tensor([1, 0, 0], dtype=torch.float32).expand(face_2_normals.size()),
)
# Face 3: y = 0, x + z <= 1, normals = (0, -1, 0).
@@ -158,14 +134,10 @@ class TestSamplePoints(TestCaseMixin, unittest.TestCase):
pyramid_verts[face_3_idxs, :],
pyramid_normals[face_3_idxs, :],
)
self.assertTrue(
torch.all((face_3_verts[:, 0] + face_3_verts[:, 2]) <= 1)
)
self.assertTrue(torch.all((face_3_verts[:, 0] + face_3_verts[:, 2]) <= 1))
self.assertClose(
face_3_normals,
torch.tensor([0, -1, 0], dtype=torch.float32).expand(
face_3_normals.size()
),
torch.tensor([0, -1, 0], dtype=torch.float32).expand(face_3_normals.size()),
)
# Face 4: x + y + z = 1, normals = (1, 1, 1)/sqrt(3).
@@ -279,22 +251,15 @@ class TestSamplePoints(TestCaseMixin, unittest.TestCase):
num_faces = 50
for device in ["cpu", "cuda:0"]:
for invalid in ["nan", "inf"]:
verts = torch.rand(
(num_verts, 3), dtype=torch.float32, device=device
)
verts = torch.rand((num_verts, 3), dtype=torch.float32, device=device)
# randomly assign an invalid type
verts[torch.randperm(num_verts)[:10]] = float(invalid)
faces = torch.randint(
num_verts,
size=(num_faces, 3),
dtype=torch.int64,
device=device,
num_verts, size=(num_faces, 3), dtype=torch.int64, device=device
)
meshes = Meshes(verts=[verts], faces=[faces])
with self.assertRaisesRegex(
ValueError, "Meshes contain nan or inf."
):
with self.assertRaisesRegex(ValueError, "Meshes contain nan or inf."):
sample_points_from_meshes(
meshes, num_samples=100, return_normals=True
)
@@ -310,9 +275,7 @@ class TestSamplePoints(TestCaseMixin, unittest.TestCase):
verts_list = []
faces_list = []
for _ in range(num_meshes):
verts = torch.rand(
(num_verts, 3), dtype=torch.float32, device=device
)
verts = torch.rand((num_verts, 3), dtype=torch.float32, device=device)
faces = torch.randint(
num_verts, size=(num_faces, 3), dtype=torch.int64, device=device
)

View File

@@ -1,10 +1,10 @@
# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
import numpy as np
import unittest
import torch
import numpy as np
import torch
from pytorch3d.transforms.so3 import (
hat,
so3_exponential_map,
@@ -26,9 +26,7 @@ class TestSO3(unittest.TestCase):
randomly generated logarithms of rotation matrices.
"""
device = torch.device("cuda:0")
log_rot = torch.randn(
(batch_size, 3), dtype=torch.float32, device=device
)
log_rot = torch.randn((batch_size, 3), dtype=torch.float32, device=device)
return log_rot
@staticmethod
@@ -85,16 +83,12 @@ class TestSO3(unittest.TestCase):
log_rot = torch.randn(size=[5, 4], device=device)
with self.assertRaises(ValueError) as err:
so3_exponential_map(log_rot)
self.assertTrue(
"Input tensor shape has to be Nx3." in str(err.exception)
)
self.assertTrue("Input tensor shape has to be Nx3." in str(err.exception))
rot = torch.randn(size=[5, 3, 5], device=device)
with self.assertRaises(ValueError) as err:
so3_log_map(rot)
self.assertTrue(
"Input has to be a batch of 3x3 Tensors." in str(err.exception)
)
self.assertTrue("Input has to be a batch of 3x3 Tensors." in str(err.exception))
# trace of rot definitely bigger than 3 or smaller than -1
rot = torch.cat(

View File

@@ -2,11 +2,10 @@
import unittest
import torch
from pytorch3d.structures import utils as struct_utils
from common_testing import TestCaseMixin
from pytorch3d.structures import utils as struct_utils
class TestStructUtils(TestCaseMixin, unittest.TestCase):
@@ -27,22 +26,16 @@ class TestStructUtils(TestCaseMixin, unittest.TestCase):
self.assertEqual(x_padded.shape[1], K)
self.assertEqual(x_padded.shape[2], K)
for i in range(N):
self.assertClose(
x_padded[i, : x[i].shape[0], : x[i].shape[1]], x[i]
)
self.assertClose(x_padded[i, : x[i].shape[0], : x[i].shape[1]], x[i])
# check for no pad size (defaults to max dimension)
x_padded = struct_utils.list_to_padded(
x, pad_value=0.0, equisized=False
)
x_padded = struct_utils.list_to_padded(x, pad_value=0.0, equisized=False)
max_size0 = max(y.shape[0] for y in x)
max_size1 = max(y.shape[1] for y in x)
self.assertEqual(x_padded.shape[1], max_size0)
self.assertEqual(x_padded.shape[2], max_size1)
for i in range(N):
self.assertClose(
x_padded[i, : x[i].shape[0], : x[i].shape[1]], x[i]
)
self.assertClose(x_padded[i, : x[i].shape[0], : x[i].shape[1]], x[i])
# check for equisized
x = [torch.rand((K, 10), device=device) for _ in range(N)]
@@ -88,9 +81,7 @@ class TestStructUtils(TestCaseMixin, unittest.TestCase):
split_size = torch.randint(1, K, size=(2 * N,)).view(N, 2).unbind(0)
x_list = struct_utils.padded_to_list(x, split_size)
for i in range(N):
self.assertClose(
x_list[i], x[i, : split_size[i][0], : split_size[i][1]]
)
self.assertClose(x_list[i], x[i, : split_size[i][0], : split_size[i][1]])
with self.assertRaisesRegex(ValueError, "Supports only"):
x = torch.rand((N, K, K, K, K), device=device)
@@ -124,32 +115,24 @@ class TestStructUtils(TestCaseMixin, unittest.TestCase):
# Add some random values in the input which are the same as the pad_value.
# These should not be filtered out.
x_list.append(
torch.randint(
low=pad_value, high=10, size=(dim, K), device=device
)
torch.randint(low=pad_value, high=10, size=(dim, K), device=device)
)
split_size.append(dim)
x_padded = struct_utils.list_to_padded(x_list, pad_value=pad_value)
x_packed = struct_utils.padded_to_packed(x_padded, pad_value=pad_value)
curr = 0
for i in range(N):
self.assertClose(
x_packed[curr : curr + split_size[i], ...], x_list[i]
)
self.assertClose(x_packed[curr : curr + split_size[i], ...], x_list[i])
self.assertClose(torch.cat(x_list), x_packed)
curr += split_size[i]
# Case 3: split_size is provided.
# Check each section of the packed tensor matches the corresponding
# unpadded elements.
x_packed = struct_utils.padded_to_packed(
x_padded, split_size=split_size
)
x_packed = struct_utils.padded_to_packed(x_padded, split_size=split_size)
curr = 0
for i in range(N):
self.assertClose(
x_packed[curr : curr + split_size[i], ...], x_list[i]
)
self.assertClose(x_packed[curr : curr + split_size[i], ...], x_list[i])
self.assertClose(torch.cat(x_list), x_packed)
curr += split_size[i]
@@ -157,17 +140,13 @@ class TestStructUtils(TestCaseMixin, unittest.TestCase):
# Raise an error.
split_size = torch.randint(1, K, size=(2 * N,)).view(N, 2).unbind(0)
with self.assertRaisesRegex(ValueError, "1-dimensional"):
x_packed = struct_utils.padded_to_packed(
x_padded, split_size=split_size
)
x_packed = struct_utils.padded_to_packed(x_padded, split_size=split_size)
split_size = torch.randint(1, K, size=(2 * N,)).view(N * 2).tolist()
with self.assertRaisesRegex(
ValueError, "same length as inputs first dimension"
):
x_packed = struct_utils.padded_to_packed(
x_padded, split_size=split_size
)
x_packed = struct_utils.padded_to_packed(x_padded, split_size=split_size)
# Case 5: both pad_value and split_size are provided.
# Raise an error.
@@ -204,8 +183,6 @@ class TestStructUtils(TestCaseMixin, unittest.TestCase):
for i in range(N):
self.assertTrue(num_items[i] == x_dims[i])
self.assertTrue(item_packed_first_idx[i] == cur)
self.assertTrue(
item_packed_to_list_idx[cur : cur + x_dims[i]].eq(i).all()
)
self.assertTrue(item_packed_to_list_idx[cur : cur + x_dims[i]].eq(i).all())
self.assertClose(x_packed[cur : cur + x_dims[i]], x[i])
cur += x_dims[i]

View File

@@ -2,14 +2,13 @@
import unittest
import torch
import torch
from common_testing import TestCaseMixin
from pytorch3d.ops.subdivide_meshes import SubdivideMeshes
from pytorch3d.structures.meshes import Meshes
from pytorch3d.utils.ico_sphere import ico_sphere
from common_testing import TestCaseMixin
class TestSubdivideMeshes(TestCaseMixin, unittest.TestCase):
def test_simple_subdivide(self):
@@ -72,25 +71,14 @@ class TestSubdivideMeshes(TestCaseMixin, unittest.TestCase):
)
faces1 = torch.tensor([[0, 1, 2]], dtype=torch.int64, device=device)
verts2 = torch.tensor(
[
[0.5, 1.0, 0.0],
[1.0, 0.0, 0.0],
[0.0, 0.0, 0.0],
[1.5, 1.0, 0.0],
],
[[0.5, 1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 0.0], [1.5, 1.0, 0.0]],
dtype=torch.float32,
device=device,
requires_grad=True,
)
faces2 = torch.tensor(
[[0, 1, 2], [0, 3, 1]], dtype=torch.int64, device=device
)
faces3 = torch.tensor(
[[0, 1, 2], [0, 2, 3]], dtype=torch.int64, device=device
)
mesh = Meshes(
verts=[verts1, verts2, verts2], faces=[faces1, faces2, faces3]
)
faces2 = torch.tensor([[0, 1, 2], [0, 3, 1]], dtype=torch.int64, device=device)
faces3 = torch.tensor([[0, 1, 2], [0, 2, 3]], dtype=torch.int64, device=device)
mesh = Meshes(verts=[verts1, verts2, verts2], faces=[faces1, faces2, faces3])
subdivide = SubdivideMeshes()
new_mesh = subdivide(mesh.clone())
@@ -218,9 +206,7 @@ class TestSubdivideMeshes(TestCaseMixin, unittest.TestCase):
self.assertTrue(new_feats.requires_grad == gt_feats.requires_grad)
@staticmethod
def subdivide_meshes_with_init(
num_meshes: int = 10, same_topo: bool = False
):
def subdivide_meshes_with_init(num_meshes: int = 10, same_topo: bool = False):
device = torch.device("cuda:0")
meshes = ico_sphere(0, device=device)
if num_meshes > 1:

View File

@@ -2,9 +2,10 @@
import unittest
import torch
import torch.nn.functional as F
from common_testing import TestCaseMixin
from pytorch3d.renderer.mesh.rasterizer import Fragments
from pytorch3d.renderer.mesh.texturing import (
interpolate_face_attributes,
@@ -13,8 +14,6 @@ from pytorch3d.renderer.mesh.texturing import (
)
from pytorch3d.structures import Meshes, Textures
from pytorch3d.structures.utils import list_to_padded
from common_testing import TestCaseMixin
from test_meshes import TestMeshes
@@ -68,12 +67,7 @@ class TestTexturing(TestCaseMixin, unittest.TestCase):
dists=torch.ones_like(pix_to_face),
)
grad_vert_tex = torch.tensor(
[
[0.3, 0.3, 0.3],
[0.9, 0.9, 0.9],
[0.5, 0.5, 0.5],
[0.3, 0.3, 0.3],
],
[[0.3, 0.3, 0.3], [0.9, 0.9, 0.9], [0.5, 0.5, 0.5], [0.3, 0.3, 0.3]],
dtype=torch.float32,
)
texels = interpolate_vertex_colors(fragments, mesh)
@@ -115,9 +109,7 @@ class TestTexturing(TestCaseMixin, unittest.TestCase):
[[0.5, 0.3, 0.2], [0.3, 0.6, 0.1]], dtype=torch.float32
).view(1, 1, 1, 2, -1)
dummy_verts = torch.zeros(4, 3)
vert_uvs = torch.tensor(
[[1, 0], [0, 1], [1, 1], [0, 0]], dtype=torch.float32
)
vert_uvs = torch.tensor([[1, 0], [0, 1], [1, 1], [0, 0]], dtype=torch.float32)
face_uvs = torch.tensor([[0, 1, 2], [1, 2, 3]], dtype=torch.int64)
interpolated_uvs = torch.tensor(
[[0.5 + 0.2, 0.3 + 0.2], [0.6, 0.3 + 0.6]], dtype=torch.float32
@@ -137,9 +129,7 @@ class TestTexturing(TestCaseMixin, unittest.TestCase):
dists=pix_to_face,
)
tex = Textures(
maps=tex_map,
faces_uvs=face_uvs[None, ...],
verts_uvs=vert_uvs[None, ...],
maps=tex_map, faces_uvs=face_uvs[None, ...], verts_uvs=vert_uvs[None, ...]
)
meshes = Meshes(verts=[dummy_verts], faces=[face_uvs], textures=tex)
texels = interpolate_texture_map(fragments, meshes)
@@ -151,9 +141,7 @@ class TestTexturing(TestCaseMixin, unittest.TestCase):
tex_map = tex_map.permute(0, 3, 1, 2)
tex_map = torch.cat([tex_map, tex_map], dim=0)
expected_out = F.grid_sample(tex_map, pixel_uvs, align_corners=False)
self.assertTrue(
torch.allclose(texels.squeeze(), expected_out.squeeze())
)
self.assertTrue(torch.allclose(texels.squeeze(), expected_out.squeeze()))
def test_init_rgb_uv_fail(self):
V = 20
@@ -183,9 +171,7 @@ class TestTexturing(TestCaseMixin, unittest.TestCase):
Textures(verts_rgb=torch.ones((5, 16, 16, 3)))
# maps provided without verts/faces uvs
with self.assertRaisesRegex(
ValueError, "faces_uvs and verts_uvs are required"
):
with self.assertRaisesRegex(ValueError, "faces_uvs and verts_uvs are required"):
Textures(maps=torch.ones((5, 16, 16, 3)))
def test_padded_to_packed(self):
@@ -209,9 +195,7 @@ class TestTexturing(TestCaseMixin, unittest.TestCase):
# This is set inside Meshes when textures is passed as an input.
# Here we set _num_faces_per_mesh and _num_verts_per_mesh explicity.
tex1 = tex.clone()
tex1._num_faces_per_mesh = (
faces_uvs_padded.gt(-1).all(-1).sum(-1).tolist()
)
tex1._num_faces_per_mesh = faces_uvs_padded.gt(-1).all(-1).sum(-1).tolist()
tex1._num_verts_per_mesh = torch.tensor([5, 4])
faces_packed = tex1.faces_uvs_packed()
verts_packed = tex1.verts_uvs_packed()
@@ -245,16 +229,12 @@ class TestTexturing(TestCaseMixin, unittest.TestCase):
for i in range(N):
self.assertTrue(
(faces_list[i] == faces_uvs_padded[i, ...].squeeze())
.all()
.item()
(faces_list[i] == faces_uvs_padded[i, ...].squeeze()).all().item()
)
for i in range(N):
self.assertTrue(
(verts_list[i] == verts_uvs_padded[i, ...].squeeze())
.all()
.item()
(verts_list[i] == verts_uvs_padded[i, ...].squeeze()).all().item()
)
def test_clone(self):
@@ -344,9 +324,7 @@ class TestTexturing(TestCaseMixin, unittest.TestCase):
verts_uvs=torch.randn((B, V, 2)),
)
tex_mesh = Meshes(
verts=mesh.verts_padded(),
faces=mesh.faces_padded(),
textures=tex_uv,
verts=mesh.verts_padded(), faces=mesh.faces_padded(), textures=tex_uv
)
N = 20
new_mesh = tex_mesh.extend(N)
@@ -359,12 +337,10 @@ class TestTexturing(TestCaseMixin, unittest.TestCase):
for i in range(len(tex_mesh)):
for n in range(N):
self.assertClose(
tex_init.faces_uvs_list()[i],
new_tex.faces_uvs_list()[i * N + n],
tex_init.faces_uvs_list()[i], new_tex.faces_uvs_list()[i * N + n]
)
self.assertClose(
tex_init.verts_uvs_list()[i],
new_tex.verts_uvs_list()[i * N + n],
tex_init.verts_uvs_list()[i], new_tex.verts_uvs_list()[i * N + n]
)
self.assertAllSeparate(
[
@@ -384,9 +360,7 @@ class TestTexturing(TestCaseMixin, unittest.TestCase):
# 2. Texture vertex RGB
tex_rgb = Textures(verts_rgb=torch.randn((B, V, 3)))
tex_mesh_rgb = Meshes(
verts=mesh.verts_padded(),
faces=mesh.faces_padded(),
textures=tex_rgb,
verts=mesh.verts_padded(), faces=mesh.faces_padded(), textures=tex_rgb
)
N = 20
new_mesh_rgb = tex_mesh_rgb.extend(N)
@@ -399,8 +373,7 @@ class TestTexturing(TestCaseMixin, unittest.TestCase):
for i in range(len(tex_mesh_rgb)):
for n in range(N):
self.assertClose(
tex_init.verts_rgb_list()[i],
new_tex.verts_rgb_list()[i * N + n],
tex_init.verts_rgb_list()[i], new_tex.verts_rgb_list()[i * N + n]
)
self.assertAllSeparate(
[tex_init.verts_rgb_padded(), new_tex.verts_rgb_padded()]

View File

@@ -3,8 +3,8 @@
import math
import unittest
import torch
import torch
from pytorch3d.transforms.so3 import so3_exponential_map
from pytorch3d.transforms.transform3d import (
Rotate,
@@ -18,9 +18,7 @@ from pytorch3d.transforms.transform3d import (
class TestTransform(unittest.TestCase):
def test_to(self):
tr = Translate(torch.FloatTensor([[1.0, 2.0, 3.0]]))
R = torch.FloatTensor(
[[0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]]
)
R = torch.FloatTensor([[0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]])
R = Rotate(R)
t = Transform3d().compose(R, tr)
for _ in range(3):
@@ -36,9 +34,7 @@ class TestTransform(unittest.TestCase):
the same as composition of clones of translation and rotation.
"""
tr = Translate(torch.FloatTensor([[1.0, 2.0, 3.0]]))
R = torch.FloatTensor(
[[0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]]
)
R = torch.FloatTensor([[0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]])
R = Rotate(R)
# check that the _matrix property of clones of
@@ -63,9 +59,9 @@ class TestTransform(unittest.TestCase):
def test_translate(self):
t = Transform3d().translate(1, 2, 3)
points = torch.tensor(
[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.5, 0.5, 0.0]]
).view(1, 3, 3)
points = torch.tensor([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.5, 0.5, 0.0]]).view(
1, 3, 3
)
normals = torch.tensor(
[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 1.0, 0.0]]
).view(1, 3, 3)
@@ -82,9 +78,9 @@ class TestTransform(unittest.TestCase):
def test_scale(self):
t = Transform3d().scale(2.0).scale(0.5, 0.25, 1.0)
points = torch.tensor(
[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.5, 0.5, 0.0]]
).view(1, 3, 3)
points = torch.tensor([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.5, 0.5, 0.0]]).view(
1, 3, 3
)
normals = torch.tensor(
[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 1.0, 0.0]]
).view(1, 3, 3)
@@ -101,9 +97,9 @@ class TestTransform(unittest.TestCase):
def test_scale_translate(self):
t = Transform3d().scale(2, 1, 3).translate(1, 2, 3)
points = torch.tensor(
[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.5, 0.5, 0.0]]
).view(1, 3, 3)
points = torch.tensor([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.5, 0.5, 0.0]]).view(
1, 3, 3
)
normals = torch.tensor(
[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 1.0, 0.0]]
).view(1, 3, 3)
@@ -120,9 +116,9 @@ class TestTransform(unittest.TestCase):
def test_rotate_axis_angle(self):
t = Transform3d().rotate_axis_angle(90.0, axis="Z")
points = torch.tensor(
[[0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 1.0, 1.0]]
).view(1, 3, 3)
points = torch.tensor([[0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 1.0, 1.0]]).view(
1, 3, 3
)
normals = torch.tensor(
[[1.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 0.0, 0.0]]
).view(1, 3, 3)
@@ -194,9 +190,7 @@ class TestTransform(unittest.TestCase):
t_ = Rotate(
so3_exponential_map(
torch.randn(
(batch_size, 3),
dtype=torch.float32,
device=device,
(batch_size, 3), dtype=torch.float32, device=device
)
),
device=device,
@@ -717,9 +711,7 @@ class TestRotate(unittest.TestCase):
def test_inverse(self, batch_size=5):
device = torch.device("cuda:0")
log_rot = torch.randn(
(batch_size, 3), dtype=torch.float32, device=device
)
log_rot = torch.randn((batch_size, 3), dtype=torch.float32, device=device)
R = so3_exponential_map(log_rot)
t = Rotate(R)
im = t.inverse()._matrix
@@ -749,9 +741,7 @@ class TestRotateAxisAngle(unittest.TestCase):
transformed_points = t.transform_points(points)
expected_points = torch.tensor([0.0, 0.0, 1.0])
self.assertTrue(
torch.allclose(
transformed_points.squeeze(), expected_points, atol=1e-7
)
torch.allclose(transformed_points.squeeze(), expected_points, atol=1e-7)
)
self.assertTrue(torch.allclose(t._matrix, matrix))
@@ -775,9 +765,7 @@ class TestRotateAxisAngle(unittest.TestCase):
transformed_points = t.transform_points(points)
expected_points = torch.tensor([0.0, 0.0, 1.0])
self.assertTrue(
torch.allclose(
transformed_points.squeeze(), expected_points, atol=1e-7
)
torch.allclose(transformed_points.squeeze(), expected_points, atol=1e-7)
)
self.assertTrue(torch.allclose(t._matrix, matrix, atol=1e-7))
@@ -835,9 +823,7 @@ class TestRotateAxisAngle(unittest.TestCase):
transformed_points = t.transform_points(points)
expected_points = torch.tensor([0.0, 0.0, -1.0])
self.assertTrue(
torch.allclose(
transformed_points.squeeze(), expected_points, atol=1e-7
)
torch.allclose(transformed_points.squeeze(), expected_points, atol=1e-7)
)
self.assertTrue(torch.allclose(t._matrix, matrix, atol=1e-7))
@@ -866,9 +852,7 @@ class TestRotateAxisAngle(unittest.TestCase):
transformed_points = t.transform_points(points)
expected_points = torch.tensor([0.0, 0.0, -1.0])
self.assertTrue(
torch.allclose(
transformed_points.squeeze(), expected_points, atol=1e-7
)
torch.allclose(transformed_points.squeeze(), expected_points, atol=1e-7)
)
self.assertTrue(torch.allclose(t._matrix, matrix, atol=1e-7))
@@ -923,9 +907,7 @@ class TestRotateAxisAngle(unittest.TestCase):
transformed_points = t.transform_points(points)
expected_points = torch.tensor([0.0, 1.0, 0.0])
self.assertTrue(
torch.allclose(
transformed_points.squeeze(), expected_points, atol=1e-7
)
torch.allclose(transformed_points.squeeze(), expected_points, atol=1e-7)
)
self.assertTrue(torch.allclose(t._matrix, matrix, atol=1e-7))
@@ -949,9 +931,7 @@ class TestRotateAxisAngle(unittest.TestCase):
transformed_points = t.transform_points(points)
expected_points = torch.tensor([0.0, 1.0, 0.0])
self.assertTrue(
torch.allclose(
transformed_points.squeeze(), expected_points, atol=1e-7
)
torch.allclose(transformed_points.squeeze(), expected_points, atol=1e-7)
)
self.assertTrue(torch.allclose(t._matrix, matrix, atol=1e-7))

View File

@@ -2,22 +2,18 @@
import unittest
import torch
import torch.nn.functional as F
from common_testing import TestCaseMixin
from pytorch3d.ops.vert_align import vert_align
from pytorch3d.structures.meshes import Meshes
from common_testing import TestCaseMixin
class TestVertAlign(TestCaseMixin, unittest.TestCase):
@staticmethod
def vert_align_naive(
feats,
verts_or_meshes,
return_packed: bool = False,
align_corners: bool = True,
feats, verts_or_meshes, return_packed: bool = False, align_corners: bool = True
):
"""
Naive implementation of vert_align.
@@ -60,16 +56,13 @@ class TestVertAlign(TestCaseMixin, unittest.TestCase):
return out_feats
@staticmethod
def init_meshes(
num_meshes: int = 10, num_verts: int = 1000, num_faces: int = 3000
):
def init_meshes(num_meshes: int = 10, num_verts: int = 1000, num_faces: int = 3000):
device = torch.device("cuda:0")
verts_list = []
faces_list = []
for _ in range(num_meshes):
verts = (
torch.rand((num_verts, 3), dtype=torch.float32, device=device)
* 2.0
torch.rand((num_verts, 3), dtype=torch.float32, device=device) * 2.0
- 1.0
) # verts in the space of [-1, 1]
faces = torch.randint(
@@ -82,15 +75,11 @@ class TestVertAlign(TestCaseMixin, unittest.TestCase):
return meshes
@staticmethod
def init_feats(
batch_size: int = 10, num_channels: int = 256, device: str = "cuda"
):
def init_feats(batch_size: int = 10, num_channels: int = 256, device: str = "cuda"):
H, W = [14, 28], [14, 28]
feats = []
for (h, w) in zip(H, W):
feats.append(
torch.rand((batch_size, num_channels, h, w), device=device)
)
feats.append(torch.rand((batch_size, num_channels, h, w), device=device))
return feats
def test_vert_align_with_meshes(self):
@@ -102,16 +91,12 @@ class TestVertAlign(TestCaseMixin, unittest.TestCase):
# feats in list
out = vert_align(feats, meshes, return_packed=True)
naive_out = TestVertAlign.vert_align_naive(
feats, meshes, return_packed=True
)
naive_out = TestVertAlign.vert_align_naive(feats, meshes, return_packed=True)
self.assertClose(out, naive_out)
# feats as tensor
out = vert_align(feats[0], meshes, return_packed=True)
naive_out = TestVertAlign.vert_align_naive(
feats[0], meshes, return_packed=True
)
naive_out = TestVertAlign.vert_align_naive(feats[0], meshes, return_packed=True)
self.assertClose(out, naive_out)
def test_vert_align_with_verts(self):
@@ -120,30 +105,21 @@ class TestVertAlign(TestCaseMixin, unittest.TestCase):
"""
feats = TestVertAlign.init_feats(10, 256)
verts = (
torch.rand(
(10, 100, 3), dtype=torch.float32, device=feats[0].device
)
* 2.0
torch.rand((10, 100, 3), dtype=torch.float32, device=feats[0].device) * 2.0
- 1.0
)
# feats in list
out = vert_align(feats, verts, return_packed=True)
naive_out = TestVertAlign.vert_align_naive(
feats, verts, return_packed=True
)
naive_out = TestVertAlign.vert_align_naive(feats, verts, return_packed=True)
self.assertClose(out, naive_out)
# feats as tensor
out = vert_align(feats[0], verts, return_packed=True)
naive_out = TestVertAlign.vert_align_naive(
feats[0], verts, return_packed=True
)
naive_out = TestVertAlign.vert_align_naive(feats[0], verts, return_packed=True)
self.assertClose(out, naive_out)
out2 = vert_align(
feats[0], verts, return_packed=True, align_corners=False
)
out2 = vert_align(feats[0], verts, return_packed=True, align_corners=False)
naive_out2 = TestVertAlign.vert_align_naive(
feats[0], verts, return_packed=True, align_corners=False
)
@@ -158,9 +134,7 @@ class TestVertAlign(TestCaseMixin, unittest.TestCase):
verts_list = []
faces_list = []
for _ in range(num_meshes):
verts = torch.rand(
(num_verts, 3), dtype=torch.float32, device=device
)
verts = torch.rand((num_verts, 3), dtype=torch.float32, device=device)
faces = torch.randint(
num_verts, size=(num_faces, 3), dtype=torch.int64, device=device
)