| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- # LICENSE HEADER MANAGED BY add-license-header
- #
- # Copyright 2018 Kornia Team
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- #
- """Module including useful metrics for Structure from Motion."""
- from torch import Tensor
- from kornia.core.check import KORNIA_CHECK_IS_TENSOR
- from kornia.geometry.conversions import convert_points_to_homogeneous
- from kornia.geometry.linalg import point_line_distance
- def sampson_epipolar_distance(
- pts1: Tensor, pts2: Tensor, Fm: Tensor, squared: bool = True, eps: float = 1e-8
- ) -> Tensor:
- """Return Sampson distance for correspondences given the fundamental matrix.
- Args:
- pts1: correspondences from the left images with shape :math:`(*, N, (2|3))`. If they are not homogeneous,
- converted automatically.
- pts2: correspondences from the right images with shape :math:`(*, N, (2|3))`. If they are not homogeneous,
- converted automatically.
- Fm: Fundamental matrices with shape :math:`(*, 3, 3)`. Called Fm to avoid ambiguity with torch.nn.functional.
- squared: if True (default), the squared distance is returned.
- eps: Small constant for safe sqrt.
- Returns:
- the computed Sampson distance with shape :math:`(*, N)`.
- """
- if not isinstance(Fm, Tensor):
- raise TypeError(f"Fm type is not a torch.Tensor. Got {type(Fm)}")
- if (len(Fm.shape) < 3) or not Fm.shape[-2:] == (3, 3):
- raise ValueError(f"Fm must be a (*, 3, 3) tensor. Got {Fm.shape}")
- if pts1.shape[-1] == 2:
- pts1 = convert_points_to_homogeneous(pts1)
- if pts2.shape[-1] == 2:
- pts2 = convert_points_to_homogeneous(pts2)
- # From Hartley and Zisserman, Sampson error (11.9)
- # sam = (x'^T F x) ** 2 / ( (((Fx)_1**2) + (Fx)_2**2)) + (((F^Tx')_1**2) + (F^Tx')_2**2)) )
- # line1_in_2 = (F @ pts1.transpose(dim0=-2, dim1=-1)).transpose(dim0=-2, dim1=-1)
- # line2_in_1 = (F.transpose(dim0=-2, dim1=-1) @ pts2.transpose(dim0=-2, dim1=-1)).transpose(dim0=-2, dim1=-1)
- # Instead we can just transpose F once and switch the order of multiplication
- F_t: Tensor = Fm.transpose(dim0=-2, dim1=-1)
- line1_in_2: Tensor = pts1 @ F_t
- line2_in_1: Tensor = pts2 @ Fm
- # numerator = (x'^T F x) ** 2
- numerator: Tensor = (pts2 * line1_in_2).sum(dim=-1).pow(2)
- # denominator = (((Fx)_1**2) + (Fx)_2**2)) + (((F^Tx')_1**2) + (F^Tx')_2**2))
- denominator: Tensor = line1_in_2[..., :2].norm(2, dim=-1).pow(2) + line2_in_1[..., :2].norm(2, dim=-1).pow(2)
- out: Tensor = numerator / denominator
- if squared:
- return out
- return (out + eps).sqrt()
- def symmetrical_epipolar_distance(
- pts1: Tensor, pts2: Tensor, Fm: Tensor, squared: bool = True, eps: float = 1e-8
- ) -> Tensor:
- """Return symmetrical epipolar distance for correspondences given the fundamental matrix.
- Args:
- pts1: correspondences from the left images with shape :math:`(*, N, (2|3))`. If they are not homogeneous,
- converted automatically.
- pts2: correspondences from the right images with shape :math:`(*, N, (2|3))`. If they are not homogeneous,
- converted automatically.
- Fm: Fundamental matrices with shape :math:`(*, 3, 3)`. Called Fm to avoid ambiguity with torch.nn.functional.
- squared: if True (default), the squared distance is returned.
- eps: Small constant for safe sqrt.
- Returns:
- the computed Symmetrical distance with shape :math:`(*, N)`.
- """
- if not isinstance(Fm, Tensor):
- raise TypeError(f"Fm type is not a torch.Tensor. Got {type(Fm)}")
- if (len(Fm.shape) < 3) or not Fm.shape[-2:] == (3, 3):
- raise ValueError(f"Fm must be a (*, 3, 3) tensor. Got {Fm.shape}")
- if pts1.shape[-1] == 2:
- pts1 = convert_points_to_homogeneous(pts1)
- if pts2.shape[-1] == 2:
- pts2 = convert_points_to_homogeneous(pts2)
- # From Hartley and Zisserman, symmetric epipolar distance (11.10)
- # sed = (x'^T F x) ** 2 / (((Fx)_1**2) + (Fx)_2**2)) + 1/ (((F^Tx')_1**2) + (F^Tx')_2**2))
- # line1_in_2 = (F @ pts1.transpose(dim0=-2, dim1=-1)).transpose(dim0=-2, dim1=-1)
- # line2_in_1 = (F.transpose(dim0=-2, dim1=-1) @ pts2.transpose(dim0=-2, dim1=-1)).transpose(dim0=-2, dim1=-1)
- # Instead we can just transpose F once and switch the order of multiplication
- F_t: Tensor = Fm.transpose(dim0=-2, dim1=-1)
- line1_in_2: Tensor = pts1 @ F_t
- line2_in_1: Tensor = pts2 @ Fm
- # numerator = (x'^T F x) ** 2
- numerator: Tensor = (pts2 * line1_in_2).sum(dim=-1).pow(2)
- # denominator_inv = 1/ (((Fx)_1**2) + (Fx)_2**2)) + 1/ (((F^Tx')_1**2) + (F^Tx')_2**2))
- denominator_inv: Tensor = 1.0 / (line1_in_2[..., :2].norm(2, dim=-1).pow(2)) + 1.0 / (
- line2_in_1[..., :2].norm(2, dim=-1).pow(2)
- )
- out: Tensor = numerator * denominator_inv
- if squared:
- return out
- return (out + eps).sqrt()
- def left_to_right_epipolar_distance(pts1: Tensor, pts2: Tensor, Fm: Tensor) -> Tensor:
- r"""Return one-sided epipolar distance for correspondences given the fundamental matrix.
- This method measures the distance from points in the right images to the epilines
- of the corresponding points in the left images as they reflect in the right images.
- Args:
- pts1: correspondences from the left images with shape
- :math:`(*, N, 2 or 3)`. If they are not homogeneous, converted automatically.
- pts2: correspondences from the right images with shape
- :math:`(*, N, 2 or 3)`. If they are not homogeneous, converted automatically.
- Fm: Fundamental matrices with shape :math:`(*, 3, 3)`. Called Fm to
- avoid ambiguity with torch.nn.functional.
- Returns:
- the computed Symmetrical distance with shape :math:`(*, N)`.
- """
- KORNIA_CHECK_IS_TENSOR(pts1)
- KORNIA_CHECK_IS_TENSOR(pts2)
- KORNIA_CHECK_IS_TENSOR(Fm)
- if (len(Fm.shape) < 3) or not Fm.shape[-2:] == (3, 3):
- raise ValueError(f"Fm must be a (*, 3, 3) tensor. Got {Fm.shape}")
- if pts1.shape[-1] == 2:
- pts1 = convert_points_to_homogeneous(pts1)
- F_t: Tensor = Fm.transpose(dim0=-2, dim1=-1)
- line1_in_2: Tensor = pts1 @ F_t
- return point_line_distance(pts2, line1_in_2)
- def right_to_left_epipolar_distance(pts1: Tensor, pts2: Tensor, Fm: Tensor) -> Tensor:
- r"""Return one-sided epipolar distance for correspondences given the fundamental matrix.
- This method measures the distance from points in the left images to the epilines
- of the corresponding points in the right images as they reflect in the left images.
- Args:
- pts1: correspondences from the left images with shape
- :math:`(*, N, 2 or 3)`. If they are not homogeneous, converted automatically.
- pts2: correspondences from the right images with shape
- :math:`(*, N, 2 or 3)`. If they are not homogeneous, converted automatically.
- Fm: Fundamental matrices with shape :math:`(*, 3, 3)`. Called Fm to
- avoid ambiguity with torch.nn.functional.
- Returns:
- the computed Symmetrical distance with shape :math:`(*, N)`.
- """
- KORNIA_CHECK_IS_TENSOR(pts1)
- KORNIA_CHECK_IS_TENSOR(pts2)
- KORNIA_CHECK_IS_TENSOR(Fm)
- if (len(Fm.shape) < 3) or not Fm.shape[-2:] == (3, 3):
- raise ValueError(f"Fm must be a (*, 3, 3) tensor. Got {Fm.shape}")
- if pts2.shape[-1] == 2:
- pts2 = convert_points_to_homogeneous(pts2)
- line2_in_1: Tensor = pts2 @ Fm
- return point_line_distance(pts1, line2_in_1)
|