File size: 1,868 Bytes
9e15541
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from pathlib import Path

import cv2
import numpy as np
import torch
import torch.nn.functional as F
from scenedino.common.cameras.fisheye import project_to_image

from scenedino.common.geometry import azimuth_elevation_to_rotation


class FisheyeToPinholeSampler:
    def __init__(self, K_target, image_size_target) -> None:
        self.K_target = K_target
        self.image_size_target = image_size_target
        x = (
            torch.linspace(-1, 1, image_size_target[1])
            .view(1, -1)
            .expand(image_size_target)
        )
        y = (
            torch.linspace(-1, 1, image_size_target[0])
            .view(-1, 1)
            .expand(image_size_target)
        )
        z = torch.ones_like(x)
        xyz = torch.stack((x, y, z), dim=-1).view(-1, 3)
        self.pts = (torch.inverse(torch.tensor(K_target)) @ xyz.T).T

    def sample_pinhole(
        self, img: torch.Tensor, calib, rotational_offset: torch.Tensor
    ) -> torch.Tensor:
        xyz_pinhole = (rotational_offset @ self.pts.T).T

        xy, _ = project_to_image(xyz_pinhole, calib)

        img = img.unsqueeze(0)
        resampled_img = F.grid_sample(
            img, xy.view(1, *self.image_size_target, 2), align_corners=True
        ).squeeze(0)
        return resampled_img


def load_image(path: Path) -> torch.Tensor:
    return torch.from_numpy(
        cv2.cvtColor(
            cv2.imread(str(path)),
            cv2.COLOR_BGR2RGB,
        ).astype(np.float32)
        / 255
    )


def load_fisheye_image_as_pinhole(
    path: Path,
    resampler: FisheyeToPinholeSampler,
    calib,
    azimuth: float,
    elevation: float,
) -> tuple[torch.Tensor, torch.Tensor]:
    img = load_image(path)
    rotational_offset = azimuth_elevation_to_rotation(azimuth, elevation)
    return resampler.sample_pinhole(img, calib, rotational_offset), rotational_offset