| 185 | |
| 186 | |
| 187 | class PanoProcessor: |
| 188 | def __init__( |
| 189 | self, |
| 190 | pano_image_dir: Path, |
| 191 | output_image_dir: Path, |
| 192 | mask_dir: Path, |
| 193 | render_options: PanoRenderOptions, |
| 194 | ): |
| 195 | self.render_options = render_options |
| 196 | self.pano_image_dir = pano_image_dir |
| 197 | self.output_image_dir = output_image_dir |
| 198 | self.mask_dir = mask_dir |
| 199 | |
| 200 | self.cams_from_pano_rotation = get_virtual_rotations( |
| 201 | num_steps_yaw=render_options.num_steps_yaw, |
| 202 | pitches_deg=render_options.pitches_deg, |
| 203 | ) |
| 204 | self.rig_config = create_pano_rig_config(self.cams_from_pano_rotation) |
| 205 | |
| 206 | # We assign each pano pixel to the virtual camera |
| 207 | # with the closest camera center. |
| 208 | self.cam_centers_in_pano = np.einsum( |
| 209 | "nij,i->nj", self.cams_from_pano_rotation, [0, 0, 1] |
| 210 | ) |
| 211 | |
| 212 | self._lock = Lock() |
| 213 | |
| 214 | # These are initialized on the first pano image |
| 215 | # to avoid recomputing the rays for each pano image. |
| 216 | self._camera: pycolmap.Camera | None = None |
| 217 | self._pano_size: tuple[int, int] | None = None |
| 218 | self._rays_in_cam: npt.NDArray[np.floating] | None = None |
| 219 | |
| 220 | def process(self, pano_name: str) -> None: |
| 221 | pano_path = self.pano_image_dir / pano_name |
| 222 | try: |
| 223 | pano_pil_image = PIL.Image.open(pano_path) |
| 224 | except PIL.Image.UnidentifiedImageError: |
| 225 | logging.info(f"Skipping file {pano_path} as it cannot be read.") |
| 226 | return |
| 227 | |
| 228 | pano_exif = pano_pil_image.getexif() |
| 229 | gpsonly_exif = PIL.Image.Exif() |
| 230 | gpsonly_exif[PIL.ExifTags.IFD.GPSInfo] = pano_exif.get_ifd( |
| 231 | PIL.ExifTags.IFD.GPSInfo |
| 232 | ) |
| 233 | |
| 234 | pano_image = np.asarray(pano_pil_image) |
| 235 | pano_height, pano_width, *_ = pano_image.shape |
| 236 | if pano_width != pano_height * 2: |
| 237 | raise ValueError("Only 360° panoramas are supported.") |
| 238 | |
| 239 | with self._lock: |
| 240 | if self._camera is None: # First image, precompute rays once. |
| 241 | self._camera = create_virtual_camera( |
| 242 | pano_width=pano_width, |
| 243 | pano_height=pano_height, |
| 244 | hfov_deg=self.render_options.hfov_deg, |
no outgoing calls
no test coverage detected