A Camera implementation for a standard pinhole camera. The camera rays shoot away from the origin in the z direction, with the x and y directions corresponding to the positive horizontal and vertical axes in image space.
| 66 | |
| 67 | @dataclass |
| 68 | class ProjectiveCamera(Camera): |
| 69 | """ |
| 70 | A Camera implementation for a standard pinhole camera. |
| 71 | |
| 72 | The camera rays shoot away from the origin in the z direction, with the x |
| 73 | and y directions corresponding to the positive horizontal and vertical axes |
| 74 | in image space. |
| 75 | """ |
| 76 | |
| 77 | origin: np.ndarray |
| 78 | x: np.ndarray |
| 79 | y: np.ndarray |
| 80 | z: np.ndarray |
| 81 | width: int |
| 82 | height: int |
| 83 | x_fov: float |
| 84 | y_fov: float |
| 85 | |
| 86 | def image_coords(self) -> np.ndarray: |
| 87 | ind = np.arange(self.width * self.height) |
| 88 | coords = np.stack([ind % self.width, ind // self.width], axis=1).astype(np.float32) |
| 89 | return coords |
| 90 | |
| 91 | def camera_rays(self, coords: np.ndarray) -> np.ndarray: |
| 92 | fracs = (coords / (np.array([self.width, self.height], dtype=np.float32) - 1)) * 2 - 1 |
| 93 | fracs = fracs * np.tan(np.array([self.x_fov, self.y_fov]) / 2) |
| 94 | directions = self.z + self.x * fracs[:, :1] + self.y * fracs[:, 1:] |
| 95 | directions = directions / np.linalg.norm(directions, axis=-1, keepdims=True) |
| 96 | return np.stack([np.broadcast_to(self.origin, directions.shape), directions], axis=1) |
| 97 | |
| 98 | def depth_directions(self, coords: np.ndarray) -> np.ndarray: |
| 99 | return np.tile((self.z / np.linalg.norm(self.z))[None], [len(coords), 1]) |
| 100 | |
| 101 | def resize_image(self, width: int, height: int) -> "ProjectiveCamera": |
| 102 | """ |
| 103 | Creates a new camera for the resized view assuming the aspect ratio does not change. |
| 104 | """ |
| 105 | assert width * self.height == height * self.width, "The aspect ratio should not change." |
| 106 | return ProjectiveCamera( |
| 107 | origin=self.origin, |
| 108 | x=self.x, |
| 109 | y=self.y, |
| 110 | z=self.z, |
| 111 | width=width, |
| 112 | height=height, |
| 113 | x_fov=self.x_fov, |
| 114 | y_fov=self.y_fov, |
| 115 | ) |
| 116 | |
| 117 | def center_crop(self) -> "ProjectiveCamera": |
| 118 | """ |
| 119 | Creates a new camera for the center-cropped view |
| 120 | """ |
| 121 | size = min(self.width, self.height) |
| 122 | fov = min(self.x_fov, self.y_fov) |
| 123 | return ProjectiveCamera( |
| 124 | origin=self.origin, |
| 125 | x=self.x, |
no outgoing calls
no test coverage detected