File size: 2,700 Bytes
e03a824
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import numpy as np
import torch
from shap_e.models.nn.camera import DifferentiableCameraBatch, DifferentiableProjectiveCamera
from shap_e.util.collections import AttrDict

def create_custom_cameras(size: int, device: torch.device, azimuths=None, elevations=None, fov_degrees=30, distance=3.0) -> DifferentiableCameraBatch:
    """
    Create custom camera angles for rendering.
    
    Args:
        size: The width and height of the rendered image.
        device: The device to put the camera parameters on.
        azimuths: List of azimuth angles in degrees.
        elevations: List of elevation angles in degrees.
        fov_degrees: Field of view in degrees.
        distance: Distance from the origin.
    
    Returns:
        A DifferentiableCameraBatch containing the specified cameras.
    """
    if azimuths is None:
        azimuths = [0]
    if elevations is None:
        elevations = [0]
    
    origins = []
    xs = []
    ys = []
    zs = []
    
    for azimuth, elevation in zip(azimuths, elevations):
        # Convert to radians
        azimuth_rad = np.deg2rad(azimuth)
        elevation_rad = np.deg2rad(elevation)
        
        # Calculate camera position
        x_pos = distance * np.cos(elevation_rad) * np.sin(azimuth_rad)
        y_pos = distance * np.cos(elevation_rad) * np.cos(azimuth_rad)
        z_pos = distance * np.sin(elevation_rad)
        
        # Camera origin (position)
        origin = np.array([x_pos, y_pos, z_pos])
        
        # Camera z-axis (looking at the origin)
        z = -origin / np.linalg.norm(origin)
        
        # Camera x-axis (right)
        x = np.array([np.cos(azimuth_rad + np.pi/2), np.sin(azimuth_rad + np.pi/2), 0.0])
        x = x - np.dot(x, z) * z  # Make orthogonal to z
        x = x / np.linalg.norm(x)  # Normalize
        
        # Camera y-axis (up, computed as z cross x)
        y = np.cross(z, x)
        y = y / np.linalg.norm(y)  # Normalize
        
        origins.append(origin)
        xs.append(x)
        ys.append(y)
        zs.append(z)
    
    # Convert from radians to the appropriate x and y fov
    fov_rad = np.deg2rad(fov_degrees)
    
    return DifferentiableCameraBatch(
        shape=(1, len(origins)),
        flat_camera=DifferentiableProjectiveCamera(
            origin=torch.from_numpy(np.stack(origins, axis=0)).float().to(device),
            x=torch.from_numpy(np.stack(xs, axis=0)).float().to(device),
            y=torch.from_numpy(np.stack(ys, axis=0)).float().to(device),
            z=torch.from_numpy(np.stack(zs, axis=0)).float().to(device),
            width=size,
            height=size,
            x_fov=fov_rad,
            y_fov=fov_rad,
        ),
    )