mirror of
https://github.com/facebookresearch/pytorch3d.git
synced 2026-10-31 02:19:59 +08:00
Compare commits
4 Commits
978cd99221
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d224e6652 | ||
|
|
204fe5831b | ||
|
|
8b3ba6ede4 | ||
|
|
88e182f989 |
@@ -104,7 +104,6 @@ class _SymEig3x3(nn.Module):
|
|||||||
)
|
)
|
||||||
|
|
||||||
p = torch.sqrt(p2 / 6.0)
|
p = torch.sqrt(p2 / 6.0)
|
||||||
# pyrefly: ignore [unsupported-operation]
|
|
||||||
B = (inputs - q[..., None, None] * self._identity) / p[..., None, None]
|
B = (inputs - q[..., None, None] * self._identity) / p[..., None, None]
|
||||||
|
|
||||||
r = torch.det(B) / 2.0
|
r = torch.det(B) / 2.0
|
||||||
@@ -189,10 +188,8 @@ class _SymEig3x3(nn.Module):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Find the eigenvector corresponding to alpha0, its eigenvalue is distinct
|
# Find the eigenvector corresponding to alpha0, its eigenvalue is distinct
|
||||||
# pyrefly: ignore [unsupported-operation]
|
|
||||||
ev0 = self._get_ev0(inputs - alpha0[..., None, None] * self._identity)
|
ev0 = self._get_ev0(inputs - alpha0[..., None, None] * self._identity)
|
||||||
u, v = self._get_uv(ev0)
|
u, v = self._get_uv(ev0)
|
||||||
# pyrefly: ignore [unsupported-operation]
|
|
||||||
ev1 = self._get_ev1(inputs - alpha1[..., None, None] * self._identity, u, v)
|
ev1 = self._get_ev1(inputs - alpha1[..., None, None] * self._identity, u, v)
|
||||||
# Third eigenvector is computed as the cross-product of the other two
|
# Third eigenvector is computed as the cross-product of the other two
|
||||||
ev2 = torch.cross(ev0, ev1, dim=-1)
|
ev2 = torch.cross(ev0, ev1, dim=-1)
|
||||||
@@ -267,7 +264,6 @@ class _SymEig3x3(nn.Module):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
min_idx = w.abs().argmin(dim=-1)
|
min_idx = w.abs().argmin(dim=-1)
|
||||||
# pyrefly: ignore [bad-index]
|
|
||||||
rotation_2d = self._rotations_3d[min_idx].to(w)
|
rotation_2d = self._rotations_3d[min_idx].to(w)
|
||||||
|
|
||||||
u = F.normalize((rotation_2d @ w[..., None])[..., 0], dim=-1)
|
u = F.normalize((rotation_2d @ w[..., None])[..., 0], dim=-1)
|
||||||
|
|||||||
@@ -182,10 +182,12 @@ def generate_eval_video_cameras(
|
|||||||
)
|
)
|
||||||
|
|
||||||
test_cameras = PerspectiveCameras(
|
test_cameras = PerspectiveCameras(
|
||||||
|
# pyrefly: ignore [bad-argument-type]
|
||||||
focal_length=focal_length,
|
focal_length=focal_length,
|
||||||
principal_point=principal_point,
|
principal_point=principal_point,
|
||||||
R=R,
|
R=R,
|
||||||
T=T,
|
T=T,
|
||||||
|
# pyrefly: ignore [missing-attribute]
|
||||||
device=focal_length.device,
|
device=focal_length.device,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -472,26 +472,26 @@ def _parse_obj(f, data_dir: str):
|
|||||||
materials_idx = len(material_names) - 1
|
materials_idx = len(material_names) - 1
|
||||||
else:
|
else:
|
||||||
materials_idx = material_names.index(material_name)
|
materials_idx = material_names.index(material_name)
|
||||||
elif line.startswith("v "): # Line is a vertex.
|
elif tokens[:1] == ["v"]: # Line is a vertex.
|
||||||
vert = [float(x) for x in tokens[1:4]]
|
vert = [float(x) for x in tokens[1:4]]
|
||||||
if len(vert) != 3:
|
if len(vert) != 3:
|
||||||
msg = "Vertex %s does not have 3 values. Line: %s"
|
msg = "Vertex %s does not have 3 values. Line: %s"
|
||||||
raise ValueError(msg % (str(vert), str(line)))
|
raise ValueError(msg % (str(vert), str(line)))
|
||||||
verts.append(vert)
|
verts.append(vert)
|
||||||
elif line.startswith("vt "): # Line is a texture.
|
elif tokens[:1] == ["vt"]: # Line is a texture.
|
||||||
tx = [float(x) for x in tokens[1:3]]
|
tx = [float(x) for x in tokens[1:3]]
|
||||||
if len(tx) != 2:
|
if len(tx) != 2:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Texture %s does not have 2 values. Line: %s" % (str(tx), str(line))
|
"Texture %s does not have 2 values. Line: %s" % (str(tx), str(line))
|
||||||
)
|
)
|
||||||
verts_uvs.append(tx)
|
verts_uvs.append(tx)
|
||||||
elif line.startswith("vn "): # Line is a normal.
|
elif tokens[:1] == ["vn"]: # Line is a normal.
|
||||||
norm = [float(x) for x in tokens[1:4]]
|
norm = [float(x) for x in tokens[1:4]]
|
||||||
if len(norm) != 3:
|
if len(norm) != 3:
|
||||||
msg = "Normal %s does not have 3 values. Line: %s"
|
msg = "Normal %s does not have 3 values. Line: %s"
|
||||||
raise ValueError(msg % (str(norm), str(line)))
|
raise ValueError(msg % (str(norm), str(line)))
|
||||||
normals.append(norm)
|
normals.append(norm)
|
||||||
elif line.startswith("f "): # Line is a face.
|
elif tokens[:1] == ["f"]: # Line is a face.
|
||||||
# Update face properties info.
|
# Update face properties info.
|
||||||
_parse_face(
|
_parse_face(
|
||||||
line,
|
line,
|
||||||
@@ -794,7 +794,7 @@ def save_obj(
|
|||||||
mtl_path = str(mtl_path)
|
mtl_path = str(mtl_path)
|
||||||
|
|
||||||
# Save texture map to output folder
|
# Save texture map to output folder
|
||||||
# pyre-fixme[16] # undefined attribute cpu
|
# undefined attribute cpu
|
||||||
texture_map = texture_map.detach().cpu() * 255.0
|
texture_map = texture_map.detach().cpu() * 255.0
|
||||||
image = Image.fromarray(texture_map.numpy().astype(np.uint8))
|
image = Image.fromarray(texture_map.numpy().astype(np.uint8))
|
||||||
with _open_file(image_path, path_manager, "wb") as im_f:
|
with _open_file(image_path, path_manager, "wb") as im_f:
|
||||||
|
|||||||
@@ -340,8 +340,11 @@ def corresponding_points_alignment(
|
|||||||
total_weight = torch.clamp(num_points, 1)
|
total_weight = torch.clamp(num_points, 1)
|
||||||
# special handling for heterogeneous point clouds and/or input weights
|
# special handling for heterogeneous point clouds and/or input weights
|
||||||
if weights is not None:
|
if weights is not None:
|
||||||
|
# pyrefly: ignore [bad-index]
|
||||||
Xc *= weights[:, :, None]
|
Xc *= weights[:, :, None]
|
||||||
|
# pyrefly: ignore [bad-index]
|
||||||
Yc *= weights[:, :, None]
|
Yc *= weights[:, :, None]
|
||||||
|
# pyrefly: ignore [missing-attribute]
|
||||||
total_weight = torch.clamp(weights.sum(1), eps)
|
total_weight = torch.clamp(weights.sum(1), eps)
|
||||||
|
|
||||||
if (num_points < (dim + 1)).any():
|
if (num_points < (dim + 1)).any():
|
||||||
|
|||||||
@@ -427,9 +427,11 @@ class CamerasBase(TensorProperties):
|
|||||||
if isinstance(index, tensor_types["bool"]):
|
if isinstance(index, tensor_types["bool"]):
|
||||||
# LongTensor]` has no attribute `ndim`.
|
# LongTensor]` has no attribute `ndim`.
|
||||||
# LongTensor]` has no attribute `shape`.
|
# LongTensor]` has no attribute `shape`.
|
||||||
|
# pyrefly: ignore [missing-attribute]
|
||||||
if index.ndim != 1 or index.shape[0] != len(self):
|
if index.ndim != 1 or index.shape[0] != len(self):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
# LongTensor]` has no attribute `shape`.
|
# LongTensor]` has no attribute `shape`.
|
||||||
|
# pyrefly: ignore [missing-attribute]
|
||||||
f"Boolean index of shape {index.shape} does not match cameras"
|
f"Boolean index of shape {index.shape} does not match cameras"
|
||||||
)
|
)
|
||||||
elif max(index) >= len(self):
|
elif max(index) >= len(self):
|
||||||
@@ -1790,11 +1792,13 @@ def get_ndc_to_screen_transform(
|
|||||||
image_size = torch.tensor(image_size, device=cameras.device)
|
image_size = torch.tensor(image_size, device=cameras.device)
|
||||||
# pyrefly: ignore [missing-attribute]
|
# pyrefly: ignore [missing-attribute]
|
||||||
image_size = image_size.view(-1, 2) # of shape (1 or B)x2
|
image_size = image_size.view(-1, 2) # of shape (1 or B)x2
|
||||||
|
# pyrefly: ignore [missing-attribute]
|
||||||
height, width = image_size.unbind(1)
|
height, width = image_size.unbind(1)
|
||||||
|
|
||||||
# For non square images, we scale the points such that smallest side
|
# For non square images, we scale the points such that smallest side
|
||||||
# has range [-1, 1] and the largest side has range [-u, u], with u > 1.
|
# has range [-1, 1] and the largest side has range [-u, u], with u > 1.
|
||||||
# This convention is consistent with the PyTorch3D renderer
|
# This convention is consistent with the PyTorch3D renderer
|
||||||
|
# pyrefly: ignore [missing-attribute]
|
||||||
scale = (image_size.min(dim=1).values - 0.0) / 2.0
|
scale = (image_size.min(dim=1).values - 0.0) / 2.0
|
||||||
|
|
||||||
K[:, 0, 0] = scale
|
K[:, 0, 0] = scale
|
||||||
|
|||||||
@@ -136,16 +136,13 @@ class HarmonicEmbedding(torch.nn.Module):
|
|||||||
[..., (n_harmonic_functions * 2 + int(append_input)) * num_points_per_ray]
|
[..., (n_harmonic_functions * 2 + int(append_input)) * num_points_per_ray]
|
||||||
"""
|
"""
|
||||||
# [..., dim, n_harmonic_functions]
|
# [..., dim, n_harmonic_functions]
|
||||||
# pyrefly: ignore [unsupported-operation]
|
|
||||||
embed = x[..., None] * self._frequencies
|
embed = x[..., None] * self._frequencies
|
||||||
# [..., 1, dim, n_harmonic_functions] + [2, 1, 1] => [..., 2, dim, n_harmonic_functions]
|
# [..., 1, dim, n_harmonic_functions] + [2, 1, 1] => [..., 2, dim, n_harmonic_functions]
|
||||||
# pyrefly: ignore [bad-index]
|
|
||||||
embed = embed[..., None, :, :] + self._zero_half_pi[..., None, None]
|
embed = embed[..., None, :, :] + self._zero_half_pi[..., None, None]
|
||||||
# Use the trig identity cos(x) = sin(x + pi/2)
|
# Use the trig identity cos(x) = sin(x + pi/2)
|
||||||
# and do one vectorized call to sin([x, x+pi/2]) instead of (sin(x), cos(x)).
|
# and do one vectorized call to sin([x, x+pi/2]) instead of (sin(x), cos(x)).
|
||||||
embed = embed.sin()
|
embed = embed.sin()
|
||||||
if diag_cov is not None:
|
if diag_cov is not None:
|
||||||
# pyrefly: ignore [no-matching-overload]
|
|
||||||
x_var = diag_cov[..., None] * torch.pow(self._frequencies, 2)
|
x_var = diag_cov[..., None] * torch.pow(self._frequencies, 2)
|
||||||
exp_var = torch.exp(-0.5 * x_var)
|
exp_var = torch.exp(-0.5 * x_var)
|
||||||
# [..., 2, dim, n_harmonic_functions]
|
# [..., 2, dim, n_harmonic_functions]
|
||||||
@@ -184,7 +181,6 @@ class HarmonicEmbedding(torch.nn.Module):
|
|||||||
"""
|
"""
|
||||||
return self.get_output_dim_static(
|
return self.get_output_dim_static(
|
||||||
input_dims,
|
input_dims,
|
||||||
# pyrefly: ignore [bad-argument-type]
|
|
||||||
len(self._frequencies),
|
len(self._frequencies),
|
||||||
self.append_input,
|
self.append_input,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -239,12 +239,10 @@ class MeshRasterizer(nn.Module):
|
|||||||
if raster_settings.perspective_correct is not None:
|
if raster_settings.perspective_correct is not None:
|
||||||
perspective_correct = raster_settings.perspective_correct
|
perspective_correct = raster_settings.perspective_correct
|
||||||
else:
|
else:
|
||||||
# pyrefly: ignore [missing-attribute]
|
|
||||||
perspective_correct = cameras.is_perspective()
|
perspective_correct = cameras.is_perspective()
|
||||||
if raster_settings.z_clip_value is not None:
|
if raster_settings.z_clip_value is not None:
|
||||||
z_clip = raster_settings.z_clip_value
|
z_clip = raster_settings.z_clip_value
|
||||||
else:
|
else:
|
||||||
# pyrefly: ignore [missing-attribute]
|
|
||||||
znear = cameras.get_znear()
|
znear = cameras.get_znear()
|
||||||
if isinstance(znear, torch.Tensor):
|
if isinstance(znear, torch.Tensor):
|
||||||
znear = znear.min().item()
|
znear = znear.min().item()
|
||||||
|
|||||||
@@ -482,6 +482,7 @@ class TexturesAtlas(TexturesBase):
|
|||||||
else self._num_faces_per_mesh
|
else self._num_faces_per_mesh
|
||||||
)
|
)
|
||||||
tex.valid = self.valid.clone()
|
tex.valid = self.valid.clone()
|
||||||
|
# pyrefly: ignore[bad-assignment]
|
||||||
tex._num_faces_per_mesh = num_faces
|
tex._num_faces_per_mesh = num_faces
|
||||||
return tex
|
return tex
|
||||||
|
|
||||||
@@ -496,6 +497,7 @@ class TexturesAtlas(TexturesBase):
|
|||||||
else self._num_faces_per_mesh
|
else self._num_faces_per_mesh
|
||||||
)
|
)
|
||||||
tex.valid = self.valid.detach()
|
tex.valid = self.valid.detach()
|
||||||
|
# pyrefly: ignore[bad-assignment]
|
||||||
tex._num_faces_per_mesh = num_faces
|
tex._num_faces_per_mesh = num_faces
|
||||||
return tex
|
return tex
|
||||||
|
|
||||||
@@ -982,6 +984,7 @@ class TexturesUV(TexturesBase):
|
|||||||
if torch.is_tensor(self._num_faces_per_mesh)
|
if torch.is_tensor(self._num_faces_per_mesh)
|
||||||
else self._num_faces_per_mesh
|
else self._num_faces_per_mesh
|
||||||
)
|
)
|
||||||
|
# pyrefly: ignore[bad-assignment]
|
||||||
tex._num_faces_per_mesh = num_faces
|
tex._num_faces_per_mesh = num_faces
|
||||||
tex.valid = self.valid.clone()
|
tex.valid = self.valid.clone()
|
||||||
return tex
|
return tex
|
||||||
@@ -1014,6 +1017,7 @@ class TexturesUV(TexturesBase):
|
|||||||
if torch.is_tensor(self._num_faces_per_mesh)
|
if torch.is_tensor(self._num_faces_per_mesh)
|
||||||
else self._num_faces_per_mesh
|
else self._num_faces_per_mesh
|
||||||
)
|
)
|
||||||
|
# pyrefly: ignore[bad-assignment]
|
||||||
tex._num_faces_per_mesh = num_faces
|
tex._num_faces_per_mesh = num_faces
|
||||||
tex.valid = self.valid.detach()
|
tex.valid = self.valid.detach()
|
||||||
return tex
|
return tex
|
||||||
|
|||||||
@@ -510,7 +510,9 @@ class Meshes:
|
|||||||
index = index.squeeze(1) if index.numel() > 0 else index
|
index = index.squeeze(1) if index.numel() > 0 else index
|
||||||
# pyrefly: ignore [missing-attribute]
|
# pyrefly: ignore [missing-attribute]
|
||||||
index = index.tolist()
|
index = index.tolist()
|
||||||
|
# pyrefly: ignore [not-iterable]
|
||||||
verts = [self.verts_list()[i] for i in index]
|
verts = [self.verts_list()[i] for i in index]
|
||||||
|
# pyrefly: ignore [not-iterable]
|
||||||
faces = [self.faces_list()[i] for i in index]
|
faces = [self.faces_list()[i] for i in index]
|
||||||
else:
|
else:
|
||||||
raise IndexError(index)
|
raise IndexError(index)
|
||||||
|
|||||||
@@ -411,10 +411,13 @@ class Pointclouds:
|
|||||||
index = index.squeeze(1) if index.numel() > 0 else index
|
index = index.squeeze(1) if index.numel() > 0 else index
|
||||||
# pyrefly: ignore [missing-attribute]
|
# pyrefly: ignore [missing-attribute]
|
||||||
index = index.tolist()
|
index = index.tolist()
|
||||||
|
# pyrefly: ignore [not-iterable]
|
||||||
points = [self.points_list()[i] for i in index]
|
points = [self.points_list()[i] for i in index]
|
||||||
if normals_list is not None:
|
if normals_list is not None:
|
||||||
|
# pyrefly: ignore [not-iterable]
|
||||||
normals = [normals_list[i] for i in index]
|
normals = [normals_list[i] for i in index]
|
||||||
if features_list is not None:
|
if features_list is not None:
|
||||||
|
# pyrefly: ignore [not-iterable]
|
||||||
features = [features_list[i] for i in index]
|
features = [features_list[i] for i in index]
|
||||||
else:
|
else:
|
||||||
raise IndexError(index)
|
raise IndexError(index)
|
||||||
|
|||||||
@@ -319,6 +319,29 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
|
|||||||
self.assertTrue(materials is None)
|
self.assertTrue(materials is None)
|
||||||
self.assertTrue(tex_maps is None)
|
self.assertTrue(tex_maps is None)
|
||||||
|
|
||||||
|
def test_load_obj_tab_separated(self):
|
||||||
|
# Tabs are whitespace too, including between the keyword and the values.
|
||||||
|
obj_file = "\n".join(
|
||||||
|
[
|
||||||
|
"v\t0.1\t0.2\t0.3",
|
||||||
|
"v 0.2 0.3 0.4",
|
||||||
|
"v\t0.3 0.4 0.5",
|
||||||
|
"vt\t0.5\t0.5",
|
||||||
|
"vn\t0.0\t0.0\t1.0",
|
||||||
|
"f\t1/1/1\t2/1/1\t3/1/1",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
verts, faces, aux = load_obj(StringIO(obj_file))
|
||||||
|
expected_verts = torch.tensor(
|
||||||
|
[[0.1, 0.2, 0.3], [0.2, 0.3, 0.4], [0.3, 0.4, 0.5]], dtype=torch.float32
|
||||||
|
)
|
||||||
|
self.assertClose(verts, expected_verts)
|
||||||
|
self.assertClose(faces.verts_idx, torch.tensor([[0, 1, 2]]))
|
||||||
|
self.assertClose(faces.textures_idx, torch.tensor([[0, 0, 0]]))
|
||||||
|
self.assertClose(faces.normals_idx, torch.tensor([[0, 0, 0]]))
|
||||||
|
self.assertClose(aux.verts_uvs, torch.tensor([[0.5, 0.5]]))
|
||||||
|
self.assertClose(aux.normals, torch.tensor([[0.0, 0.0, 1.0]]))
|
||||||
|
|
||||||
def test_load_obj_error_textures(self):
|
def test_load_obj_error_textures(self):
|
||||||
obj_file = "\n".join(["vt 0.1"])
|
obj_file = "\n".join(["vt 0.1"])
|
||||||
with NamedTemporaryFile(mode="w", suffix=".obj") as f:
|
with NamedTemporaryFile(mode="w", suffix=".obj") as f:
|
||||||
|
|||||||
Reference in New Issue
Block a user