Example #1
0
def laf_to_boundary_points(LAF: torch.Tensor, n_pts: int = 50) -> torch.Tensor:
    """
    Converts LAFs to boundary points of the regions + center.
    Used for local features visualization, see visualize_laf function

    Args:
        LAF: (torch.Tensor).
        n_pts: number of points to output

    Returns:
        pts: (torch.Tensor) tensor of boundary points

    Shape:
        - Input: :math:`(B, N, 2, 3)`
        - Output:  :math:`(B, N, n_pts, 2)`
    """
    raise_error_if_laf_is_not_valid(LAF)
    B, N, _, _ = LAF.size()
    pts = torch.cat([torch.sin(torch.linspace(0, 2 * math.pi, n_pts - 1)).unsqueeze(-1),
                     torch.cos(torch.linspace(0, 2 * math.pi, n_pts - 1)).unsqueeze(-1),
                     torch.ones(n_pts - 1, 1)], dim=1)
    # Add origin to draw also the orientation
    pts = torch.cat([torch.tensor([0., 0., 1.]).view(1, 3), pts], dim=0).unsqueeze(0).expand(B * N, n_pts, 3)
    pts = pts.to(LAF.device).to(LAF.dtype)
    aux = torch.tensor([0., 0., 1.]).view(1, 1, 3).expand(B * N, 1, 3)
    HLAF = torch.cat([LAF.view(-1, 2, 3), aux.to(LAF.device).to(LAF.dtype)], dim=1)
    pts_h = torch.bmm(HLAF, pts.permute(0, 2, 1)).permute(0, 2, 1)
    return kornia.convert_points_from_homogeneous(pts_h.view(B, N, n_pts, 3))
Example #2
0
    def test_points_batch(self, device, dtype):
        # generate input data
        points_h = torch.tensor([[
            [2., 1., 0.],
        ], [
            [0., 1., 2.],
        ], [
            [0., 1., -2.],
        ]],
                                device=device,
                                dtype=dtype)

        expected = torch.tensor([[
            [2., 1.],
        ], [
            [0., 0.5],
        ], [
            [0., -0.5],
        ]],
                                device=device,
                                dtype=dtype)

        # to euclidean
        points = kornia.convert_points_from_homogeneous(points_h)
        assert_allclose(points, expected, atol=1e-4, rtol=1e-4)
Example #3
0
def triangulate_points(P1: torch.Tensor, P2: torch.Tensor,
                       points1: torch.Tensor,
                       points2: torch.Tensor) -> torch.Tensor:
    r"""Reconstructs a bunch of points by triangulation.

    Triangulates the 3d position of 2d correspondences between several images.
    Reference: Internally it uses DLT method from Hartley/Zisserman 12.2 pag.312

    The input points are assumed to be in homogeneous coordinate system and being inliers
    correspondences. The method does not perform any robust estimation.

    Args:
        P1: The projection matrix for the first camera with shape :math:`(*, 3, 4)`.
        P2: The projection matrix for the second camera with shape :math:`(*, 3, 4)`.
        points1: The set of points seen from the first camera frame in the camera plane
          coordinates with shape :math:`(*, N, 2)`.
        points2: The set of points seen from the second camera frame in the camera plane
          coordinates with shape :math:`(*, N, 2)`.

    Returns:
        The reconstructed 3d points in the world frame with shape :math:`(*, N, 3)`.

    """
    if not (len(P1.shape) >= 2 and P1.shape[-2:] == (3, 4)):
        raise AssertionError(P1.shape)
    if not (len(P2.shape) >= 2 and P2.shape[-2:] == (3, 4)):
        raise AssertionError(P2.shape)
    if len(P1.shape[:-2]) != len(P2.shape[:-2]):
        raise AssertionError(P1.shape, P2.shape)
    if not (len(points1.shape) >= 2 and points1.shape[-1] == 2):
        raise AssertionError(points1.shape)
    if not (len(points2.shape) >= 2 and points2.shape[-1] == 2):
        raise AssertionError(points2.shape)
    if len(points1.shape[:-2]) != len(points2.shape[:-2]):
        raise AssertionError(points1.shape, points2.shape)
    if len(P1.shape[:-2]) != len(points1.shape[:-2]):
        raise AssertionError(P1.shape, points1.shape)

    # allocate and construct the equations matrix with shape (*, 4, 4)
    points_shape = max(points1.shape,
                       points2.shape)  # this allows broadcasting
    X = torch.zeros(points_shape[:-1] + (4, 4)).type_as(points1)

    for i in range(4):
        X[..., 0, i] = points1[..., 0] * P1[..., 2:3, i] - P1[..., 0:1, i]
        X[..., 1, i] = points1[..., 1] * P1[..., 2:3, i] - P1[..., 1:2, i]
        X[..., 2, i] = points2[..., 0] * P2[..., 2:3, i] - P2[..., 0:1, i]
        X[..., 3, i] = points2[..., 1] * P2[..., 2:3, i] - P2[..., 1:2, i]

    # 1. Solve the system Ax=0 with smallest eigenvalue
    # 2. Return homogeneous coordinates

    _, _, V = torch.svd(X)

    points3d_h = V[..., -1]
    points3d: torch.Tensor = kornia.convert_points_from_homogeneous(points3d_h)
    return points3d
Example #4
0
    def test_jit(self):
        @torch.jit.script
        def op_script(input):
            return kornia.convert_points_from_homogeneous(input)

        points_h = torch.zeros(1, 2, 3)
        actual = op_script(points_h)
        expected = kornia.convert_points_from_homogeneous(points_h)

        assert_allclose(actual, expected)
Example #5
0
def symmetric_transfer_error(pts1: torch.Tensor,
                             pts2: torch.Tensor,
                             H: torch.Tensor,
                             squared: bool = True,
                             eps: float = 1e-8) -> torch.Tensor:
    r"""Return Symmetric transfer error for correspondences given the homography matrix.

    Args:
        pts1: correspondences from the left images with shape
          (B, N, 2 or 3). If they are homogeneous, converted automatically.
        pts2: correspondences from the right images with shape
          (B, N, 2 or 3). If they are homogeneous, converted automatically.
        H: Homographies with shape :math:`(B, 3, 3)`.
        squared: if True (default), the squared distance is returned.
        eps: Small constant for safe sqrt.

    Returns:
        the computed distance with shape :math:`(B, N)`.

    """
    if not isinstance(H, torch.Tensor):
        raise TypeError(f"H type is not a torch.Tensor. Got {type(H)}")

    if (len(H.shape) != 3) or not H.shape[-2:] == (3, 3):
        raise ValueError(f"H must be a (*, 3, 3) tensor. Got {H.shape}")

    if pts1.size(-1) == 3:
        pts1 = kornia.convert_points_from_homogeneous(pts1)

    if pts2.size(-1) == 3:
        pts2 = kornia.convert_points_from_homogeneous(pts2)

    # From Hartley and Zisserman, Symmetric transfer error (4.7)
    # dist = \sum_{i} (d(x, H^-1 x')**2 + d(x', Hx)**2)
    there: torch.Tensor = oneway_transfer_error(pts1, pts2, H, True, eps)
    back: torch.Tensor = oneway_transfer_error(pts2, pts1, torch.inverse(H),
                                               True, eps)
    out = there + back
    if squared:
        return out
    return (out + eps).sqrt()
Example #6
0
    def forward(self, frame_id, point_id):
        #indexing ## it is the same wiht torch where TODO
        indexKF = torch.where(frame_id == self.idxKF)[1]
        indexMP = torch.where(point_id == self.idxMP)[1]

        # In test set, tKF is the Twc inverse. We may need to store something else in Memory
        points = (self.tKF[indexKF]
                  @ self.tMPhomo[indexMP].unsqueeze(-1)).squeeze(-1)

        Pc = kn.convert_points_from_homogeneous(points)

        return kn.project_points(Pc, self.K)
Example #7
0
def oneway_transfer_error(pts1: torch.Tensor,
                          pts2: torch.Tensor,
                          H: torch.Tensor,
                          squared: bool = True,
                          eps: float = 1e-8) -> torch.Tensor:
    r"""Return transfer error in image 2 for correspondences given the homography matrix.

    Args:
        pts1: correspondences from the left images with shape
          (B, N, 2 or 3). If they are homogeneous, converted automatically.
        pts2: correspondences from the right images with shape
          (B, N, 2 or 3). If they are homogeneous, converted automatically.
        H: Homographies with shape :math:`(B, 3, 3)`.
        squared: if True (default), the squared distance is returned.
        eps: Small constant for safe sqrt.

    Returns:
        the computed distance with shape :math:`(B, N)`.

    """
    if not isinstance(H, torch.Tensor):
        raise TypeError(f"H type is not a torch.Tensor. Got {type(H)}")

    if (len(H.shape) != 3) or not H.shape[-2:] == (3, 3):
        raise ValueError(f"H must be a (*, 3, 3) tensor. Got {H.shape}")

    if pts1.size(-1) == 3:
        pts1 = kornia.convert_points_from_homogeneous(pts1)

    if pts2.size(-1) == 3:
        pts2 = kornia.convert_points_from_homogeneous(pts2)

    # From Hartley and Zisserman, Error in one image (4.6)
    # dist = \sum_{i} ( d(x', Hx)**2)
    pts1_in_2: torch.Tensor = kornia.transform_points(H, pts1)
    error_squared: torch.Tensor = (pts1_in_2 - pts2).pow(2).sum(dim=-1)
    if squared:
        return error_squared
    return (error_squared + eps).sqrt()
Example #8
0
def get_shifts_from_sky_hom(hom, horizon_line):
    hom = torch.from_numpy(hom).float().unsqueeze(0)
    horizon_line = horizon_line * 2 - 1
    points_src = torch.tensor([[
        [-1, -1],  # left top
        [1, -1],  # right top
        [-1, horizon_line],  # left bottom
        [1, horizon_line]
    ]]).float()  # right bottom
    points_src_hom = convert_points_to_homogeneous(points_src)
    points_tgt_hom = (hom @ points_src_hom.transpose(1, 2)).transpose(1, 2)
    points_tgt = convert_points_from_homogeneous(points_tgt_hom)

    shifts_flat = points_tgt - points_src
    shifts = shifts_flat.view(2, 2, 2)

    return shifts.numpy()
Example #9
0
    def test_convert_points_batch(self, device_type):
        # generate input data
        points_h = torch.FloatTensor([[
            [2, 1, 0],
        ], [
            [0, 1, 2],
        ], [
            [0, 1, -2],
        ]]).to(torch.device(device_type))

        expected = torch.FloatTensor([[
            [2, 1],
        ], [
            [0, 0.5],
        ], [
            [0, -0.5],
        ]]).to(torch.device(device_type))

        # to euclidean
        points = kornia.convert_points_from_homogeneous(points_h)
        assert_allclose(points, expected)
Example #10
0
    def test_convert_points(self, device):
        # generate input data
        points_h = torch.tensor([
            [1., 2., 1.],
            [0., 1., 2.],
            [2., 1., 0.],
            [-1., -2., -1.],
            [0., 1., -2.],
        ]).to(device)

        expected = torch.tensor([
            [1., 2.],
            [0., 0.5],
            [2., 1.],
            [1., 2.],
            [0., -0.5],
        ]).to(device)

        # to euclidean
        points = kornia.convert_points_from_homogeneous(points_h)
        assert_allclose(points, expected)
Example #11
0
def project_to_image(project, points):
    """
    Project points to image
    Args:
        project [torch.tensor(..., 3, 4)]: Projection matrix
        points [torch.Tensor(..., 3)]: 3D points
    Returns:
        points_img [torch.Tensor(..., 2)]: Points in image
        points_depth [torch.Tensor(...)]: Depth of each point
    """
    # Reshape tensors to expected shape
    points = kornia.convert_points_to_homogeneous(points)
    points = points.unsqueeze(dim=-1)
    project = project.unsqueeze(dim=1)

    # Transform points to image and get depths
    points_t = project @ points
    points_t = points_t.squeeze(dim=-1)
    points_img = kornia.convert_points_from_homogeneous(points_t)
    points_depth = points_t[..., -1] - project[..., 2, 3]

    return points_img, points_depth
Example #12
0
 def test_cardinality(self, device, dtype, batch_shape):
     points_h = torch.rand(batch_shape, device=device, dtype=dtype)
     points = kornia.convert_points_from_homogeneous(points_h)
     assert points.shape == points.shape[:-1] + (2, )
Example #13
0
 def op_script(input):
     return kornia.convert_points_from_homogeneous(input)