Beispiel #1
0
def find_homography_dlt(
    points1: torch.Tensor, points2: torch.Tensor, weights: Optional[torch.Tensor] = None
) -> torch.Tensor:
    r"""Computes the homography matrix using the DLT formulation.

    The linear system is solved by using the Weighted Least Squares Solution for the 4 Points algorithm.

    Args:
        points1 (torch.Tensor): A set of points in the first image with a tensor shape :math:`(B, N, 2)`.
        points2 (torch.Tensor): A set of points in the second image with a tensor shape :math:`(B, N, 2)`.
        weights (torch.Tensor, optional): Tensor containing the weights per point correspondence with a shape of
                :math:`(B, N)`. Defaults to all ones.

    Returns:
        torch.Tensor: the computed homography matrix with shape :math:`(B, 3, 3)`.
    """
    assert points1.shape == points2.shape, points1.shape
    assert len(points1.shape) >= 1 and points1.shape[-1] == 2, points1.shape
    assert points1.shape[1] >= 4, points1.shape

    device, dtype = _extract_device_dtype([points1, points2])

    eps: float = 1e-8
    points1_norm, transform1 = normalize_points(points1)
    points2_norm, transform2 = normalize_points(points2)

    x1, y1 = torch.chunk(points1_norm, dim=-1, chunks=2)  # BxNx1
    x2, y2 = torch.chunk(points2_norm, dim=-1, chunks=2)  # BxNx1
    ones, zeros = torch.ones_like(x1), torch.zeros_like(x1)

    # DIAPO 11: https://www.uio.no/studier/emner/matnat/its/nedlagte-emner/UNIK4690/v16/forelesninger/lecture_4_3-estimating-homographies-from-feature-correspondences.pdf  # noqa: E501
    ax = torch.cat([zeros, zeros, zeros, -x1, -y1, -ones, y2 * x1, y2 * y1, y2], dim=-1)
    ay = torch.cat([x1, y1, ones, zeros, zeros, zeros, -x2 * x1, -x2 * y1, -x2], dim=-1)
    A = torch.cat((ax, ay), dim=-1).reshape(ax.shape[0], -1, ax.shape[-1])

    if weights is None:
        # All points are equally important
        A = A.transpose(-2, -1) @ A
    else:
        # We should use provided weights
        assert len(weights.shape) == 2 and weights.shape == points1.shape[:2], weights.shape
        w_diag = torch.diag_embed(weights.unsqueeze(dim=-1).repeat(1, 1, 2).reshape(weights.shape[0], -1))
        A = A.transpose(-2, -1) @ w_diag @ A

    try:
        U, S, V = torch.svd(A)
    except:
        warnings.warn('SVD did not converge', RuntimeWarning)
        return torch.empty((points1_norm.size(0), 3, 3), device=device, dtype=dtype)

    H = V[..., -1].view(-1, 3, 3)
    H = transform2.inverse() @ (H @ transform1)
    H_norm = H / (H[..., -1:, -1:] + eps)
    return H_norm
Beispiel #2
0
def find_homography_dlt(points1: torch.Tensor, points2: torch.Tensor,
                        weights: torch.Tensor) -> torch.Tensor:
    r"""Computes the homography matrix using the DLT formulation.

    The linear system is solved by using the Weighted Least Squares Solution for the 4 Points algorithm.

    Args:
        points1 (torch.Tensor): A set of points in the first image with a tensor shape :math:`(B, N, 2)`.
        points2 (torch.Tensor): A set of points in the second image with a tensor shape :math:`(B, N, 2)`.
        weights (torch.Tensor): Tensor containing the weights per point correspondence with a shape of :math:`(B, N)`.

    Returns:
        torch.Tensor: the computed homography matrix with shape :math:`(B, 3, 3)`.
    """
    assert points1.shape == points2.shape, points1.shape
    assert len(points1.shape) >= 1 and points1.shape[-1] == 2, points1.shape
    assert points1.shape[1] >= 4, points1.shape

    eps: float = 1e-8
    points1_norm, transform1 = normalize_points(points1)
    points2_norm, transform2 = normalize_points(points2)

    x1, y1 = torch.chunk(points1_norm, dim=-1, chunks=2)  # BxNx1
    x2, y2 = torch.chunk(points2_norm, dim=-1, chunks=2)  # BxNx1
    ones, zeros = torch.ones_like(x1), torch.zeros_like(x1)

    # DIAPO 11: https://www.uio.no/studier/emner/matnat/its/nedlagte-emner/UNIK4690/v16/forelesninger/lecture_4_3-estimating-homographies-from-feature-correspondences.pdf  # noqa: E501
    ax = torch.cat(
        [zeros, zeros, zeros, -x1, -y1, -ones, y2 * x1, y2 * y1, y2], dim=-1)
    ay = torch.cat(
        [x1, y1, ones, zeros, zeros, zeros, -x2 * x1, -x2 * y1, -x2], dim=-1)

    w_list = []
    axy_list = []
    for i in range(points1.shape[1]):
        axy_list.append(ax[:, i])
        axy_list.append(ay[:, i])
        w_list.append(weights[:, i])
        w_list.append(weights[:, i])
    A = torch.stack(axy_list, dim=1)
    w = torch.stack(w_list, dim=1)

    # apply weights
    w_diag = torch.diag_embed(w)
    A = A.transpose(-2, -1) @ w_diag @ A
    try:
        U, S, V = torch.svd(A)
    except:
        return torch.empty(points1_norm.size(0), 3, 3)
    H = V[..., -1].view(-1, 3, 3)
    H = transform2.inverse() @ (H @ transform1)
    H_norm = H / (H[..., -1:, -1:] + eps)
    return H_norm
Beispiel #3
0
    def test_mean_std(self, device, dtype):
        points = torch.tensor([[[0.0, 0.0], [0.0, 2.0], [1.0, 1.0], [1.0, 3.0]]], device=device, dtype=dtype)

        points_norm, _ = epi.normalize_points(points)
        points_std, points_mean = torch.std_mean(points_norm, dim=1)

        assert_close(points_mean, torch.zeros_like(points_mean))
        assert (points_std < 2.0).all()
Beispiel #4
0
    def test_mean_std(self, device, dtype):
        points = torch.tensor([[[0., 0.], [0., 2.], [1., 1.], [1., 3.]]],
                              device=device,
                              dtype=dtype)

        points_norm, trans = epi.normalize_points(points)
        points_std, points_mean = torch.std_mean(points_norm, dim=1)

        assert_allclose(points_mean, torch.zeros_like(points_mean))
        assert (points_std < 2.).all()
Beispiel #5
0
 def test_shape(self, batch_size, num_points, device, dtype):
     B, N = batch_size, num_points
     points = torch.rand(B, N, 2, device=device, dtype=dtype)
     output = epi.normalize_points(points)
     assert output[0].shape == (B, N, 2)
     assert output[1].shape == (B, 3, 3)
Beispiel #6
0
 def test_smoke(self, device, dtype):
     points = torch.rand(1, 1, 2, device=device, dtype=dtype)
     output = epi.normalize_points(points)
     assert len(output) == 2
     assert output[0].shape == (1, 1, 2)
     assert output[1].shape == (1, 3, 3)