Example #1
0
 def forward(self, x: torch.Tensor) -> Tuple[  # type: ignore
         List, List, List]:
     bs, ch, h, w = x.size()
     pixel_distance = 1.0
     cur_sigma = 0.5
     if self.double_image:
         x = F.interpolate(x,
                           scale_factor=2.0,
                           mode='bilinear',
                           align_corners=False)
         pixel_distance = 0.5
         cur_sigma *= 2.0
     if self.init_sigma > cur_sigma:
         sigma = math.sqrt(self.init_sigma**2 - cur_sigma**2)
         cur_sigma = self.init_sigma
         ksize = self.get_kernel_size(sigma)
         cur_level = gaussian_blur2d(x, (ksize, ksize), (sigma, sigma))
     else:
         cur_level = x
     sigmas = [
         cur_sigma * torch.ones(bs, self.n_levels).to(x.device).to(x.dtype)
     ]
     pixel_dists = [
         pixel_distance *
         torch.ones(bs, self.n_levels).to(x.device).to(x.dtype)
     ]
     pyr = [[cur_level.unsqueeze(1)]]
     oct_idx = 0
     while True:
         cur_level = pyr[-1][0].squeeze(1)
         for level_idx in range(1, self.n_levels):
             sigma = cur_sigma * math.sqrt(self.sigma_step**2 - 1.0)
             cur_level = gaussian_blur2d(cur_level, (ksize, ksize),
                                         (sigma, sigma))
             cur_sigma *= self.sigma_step
             pyr[-1].append(cur_level.unsqueeze(1))
             sigmas[-1][:, level_idx] = cur_sigma
             pixel_dists[-1][:, level_idx] = pixel_distance
         nextOctaveFirstLevel = F.interpolate(pyr[-1][-1].squeeze(1),
                                              scale_factor=0.5,
                                              mode='bilinear',
                                              align_corners=False)
         pixel_distance *= 2.0
         cur_sigma = self.init_sigma
         if (min(nextOctaveFirstLevel.size(2), nextOctaveFirstLevel.size(3))
                 <= self.min_size):
             break
         pyr.append([nextOctaveFirstLevel.unsqueeze(1)])
         sigmas.append(cur_sigma *
                       torch.ones(bs, self.n_levels).to(x.device))
         pixel_dists.append(pixel_distance *
                            torch.ones(bs, self.n_levels).to(x.device))
         oct_idx += 1
     for i in range(len(pyr)):
         pyr[i] = torch.cat(pyr[i], dim=1)  # type: ignore
     return pyr, sigmas, pixel_dists
Example #2
0
def unsharp_mask(input: torch.Tensor,
                 kernel_size: Tuple[int, int],
                 sigma: Tuple[float, float],
                 border_type: str = 'reflect') -> torch.Tensor:
    r"""Creates an operator that blurs a tensor using the existing Gaussian filter available with the Kornia library.

    Arguments:
        input (torch.Tensor): the input tensor with shape :math:`(B,C,H,W)`.
        kernel_size (Tuple[int, int]): the size of the kernel.
        sigma (Tuple[float, float]): the standard deviation of the kernel.
        border_type (str): the padding mode to be applied before convolving.
          The expected modes are: ``'constant'``, ``'reflect'``,
          ``'replicate'`` or ``'circular'``. Default: ``'reflect'``.

    Returns:
        torch.Tensor: the blurred tensor with shape :math:`(B,C,H,W)`.

    Examples:
        >>> input = torch.rand(2, 4, 5, 5)
        >>> output = unsharp_mask(input, (3, 3), (1.5, 1.5))
        >>> output.shape
        torch.Size([2, 4, 5, 5])
    """
    data_blur: torch.Tensor = gaussian_blur2d(input, kernel_size, sigma)
    data_sharpened: torch.Tensor = input + (input - data_blur)
    return data_sharpened
Example #3
0
def unsharp_mask(input: torch.Tensor,
                 kernel_size: Tuple[int, int],
                 sigma: Tuple[float, float],
                 border_type: str = 'reflect') -> torch.Tensor:
    r"""Create an operator that sharpens a tensor by applying operation out = 2 * image - gaussian_blur2d(image).

    .. image:: _static/img/unsharp_mask.png

    Args:
        input: the input tensor with shape :math:`(B,C,H,W)`.
        kernel_size: the size of the kernel.
        sigma: the standard deviation of the kernel.
        border_type: the padding mode to be applied before convolving.
          The expected modes are: ``'constant'``, ``'reflect'``,
          ``'replicate'`` or ``'circular'``.

    Returns:
        the blurred tensor with shape :math:`(B,C,H,W)`.

    Examples:
        >>> input = torch.rand(2, 4, 5, 5)
        >>> output = unsharp_mask(input, (3, 3), (1.5, 1.5))
        >>> output.shape
        torch.Size([2, 4, 5, 5])
    """
    data_blur: torch.Tensor = gaussian_blur2d(input, kernel_size, sigma,
                                              border_type)
    data_sharpened: torch.Tensor = input + (input - data_blur)
    return data_sharpened
Example #4
0
 def apply_transform(self,
                     input: Tensor,
                     params: Dict[str, Tensor],
                     transform: Optional[Tensor] = None) -> Tensor:
     return gaussian_blur2d(input, self.flags["kernel_size"],
                            self.flags["sigma"],
                            self.flags["border_type"].name.lower())
Example #5
0
 def __call__(self, image):
     random_kernel = random.randrange(25, 35, 2)
     random_kernel = (random_kernel, random_kernel)
     random_sigma = random.uniform(5, 10)
     random_sigma = (random_sigma, random_sigma)
     image = torch.unsqueeze(kornia.image_to_tensor(image).float(), dim=0)
     image = gaussian_blur2d(image, random_kernel, random_sigma)
     return kornia.tensor_to_image(image)
Example #6
0
 def forward(self, x):
     gau = []
     y = x
     # gaussian
     for i in range(self.levels):
         gau.append(y)
         y = gaussian_blur2d(y, kernel_size=(3, 3), sigma=(2, 2))
         y = self.pool(y)
     return gau
Example #7
0
def defocus_blur(inp, disk_radius, alias_blur):
    kernel_size = (3, 3) if disk_radius <= 8 else (5, 5)
    mesh_range = ch.arange(-max(8, disk_radius), max(8, disk_radius) + 1)
    X, Y = ch.meshgrid(mesh_range, mesh_range)

    aliased_disk = ((X.pow(2) + Y.pow(2)) <= disk_radius**2).float()
    aliased_disk /= aliased_disk.sum()
    kernel = filters.gaussian_blur2d(aliased_disk[None, None, ...],
                                     kernel_size, (alias_blur, alias_blur))[0]
    return ch.clamp(filters.filter2D(inp, kernel), 0, 1)
Example #8
0
    def forward(self,
                x: torch.Tensor) -> Tuple[List, List, List]:  # type: ignore
        bs, ch, h, w = x.size()
        cur_level, cur_sigma, pixel_distance = self.get_first_level(x)

        sigmas = [
            cur_sigma * torch.ones(bs, self.n_levels + self.extra_levels).to(
                x.device).to(x.dtype)
        ]
        pixel_dists = [
            pixel_distance * torch.ones(
                bs, self.n_levels + self.extra_levels).to(x.device).to(x.dtype)
        ]
        pyr = [[cur_level]]
        oct_idx = 0
        while True:
            cur_level = pyr[-1][0]
            for level_idx in range(1, self.n_levels + self.extra_levels):
                sigma = cur_sigma * math.sqrt(self.sigma_step**2 - 1.0)
                ksize = self.get_kernel_size(sigma)

                # Hack, because PyTorch does not allow to pad more than original size.
                # But for the huge sigmas, one needs huge kernel and padding...

                ksize = min(ksize, min(cur_level.size(2), cur_level.size(3)))
                if ksize % 2 == 0:
                    ksize += 1

                cur_level = gaussian_blur2d(cur_level, (ksize, ksize),
                                            (sigma, sigma))
                cur_sigma *= self.sigma_step
                pyr[-1].append(cur_level)
                sigmas[-1][:, level_idx] = cur_sigma
                pixel_dists[-1][:, level_idx] = pixel_distance
            _pyr = pyr[-1][-self.extra_levels]
            nextOctaveFirstLevel = F.interpolate(
                _pyr,
                size=(_pyr.size(-2) // 2, _pyr.size(-1) // 2),
                mode='nearest')  # Nearest matches OpenCV SIFT
            pixel_distance *= 2.0
            cur_sigma = self.init_sigma
            if min(nextOctaveFirstLevel.size(2),
                   nextOctaveFirstLevel.size(3)) <= self.min_size:
                break
            pyr.append([nextOctaveFirstLevel])
            sigmas.append(
                cur_sigma *
                torch.ones(bs, self.n_levels + self.extra_levels).to(x.device))
            pixel_dists.append(
                pixel_distance *
                torch.ones(bs, self.n_levels + self.extra_levels).to(x.device))
            oct_idx += 1
        for i in range(len(pyr)):
            pyr[i] = torch.stack(pyr[i], dim=2)  # type: ignore
        return pyr, sigmas, pixel_dists
Example #9
0
def get_normal_anlges(image, eps=EPS):
    """Calculate the normal direction of edges.

    Ref: https://github.com/nv-tlabs/STEAL/blob/master/utils/edges_nms.m
    """
    first_grads = spatial_gradient(gaussian_blur2d(image, (5, 5), (2, 2)))
    second_grad_x = spatial_gradient(first_grads[:, :, 0, :, :].squeeze_(2))
    second_grad_y = spatial_gradient(first_grads[:, :, 1, :, :].squeeze_(2))

    grad_xx = second_grad_x[:, :, 0, :, :].squeeze_()
    grad_xy = second_grad_y[:, :, 0, :, :].squeeze_()
    grad_yy = second_grad_y[:, :, 1, :, :].squeeze_()
    angle = torch.atan(grad_yy * torch.sign(-(grad_xy + eps)) /
                       (grad_xx + eps))
    return angle
Example #10
0
 def get_first_level(self, input):
     pixel_distance = 1.0
     cur_sigma = 0.5
     # Same as in OpenCV up to interpolation difference
     if self.double_image:
         x = F.interpolate(input, scale_factor=2.0, mode='bilinear', align_corners=False)
         pixel_distance = 0.5
         cur_sigma *= 2.0
     else:
         x = input
     if self.init_sigma > cur_sigma:
         sigma = max(math.sqrt(self.init_sigma**2 - cur_sigma**2), 0.01)
         ksize = self.get_kernel_size(sigma)
         cur_level = gaussian_blur2d(x, (ksize, ksize), (sigma, sigma))
         cur_sigma = self.init_sigma
     else:
         cur_level = x
     return cur_level, cur_sigma, pixel_distance
Example #11
0
def gaussian_blur(inp, blur_std):
    kernel_size = math.ceil((2 * blur_std) * 2)
    if kernel_size % 2 == 0: kernel_size += 1
    return filters.gaussian_blur2d(inp, (kernel_size, kernel_size),
                                   (blur_std, blur_std))
Example #12
0
def harris_response(
    input: torch.Tensor,
    k: Union[torch.Tensor, float] = 0.04,
    grads_mode: str = 'sobel',
    sigmas: Optional[torch.Tensor] = None,
) -> torch.Tensor:
    r"""Computes the Harris cornerness function.

    Function does not do any normalization or nms. The response map is computed according the following formulation:

    .. math::
        R = max(0, det(M) - k \cdot trace(M)^2)

    where:

    .. math::
        M = \sum_{(x,y) \in W}
        \begin{bmatrix}
            I^{2}_x & I_x I_y \\
            I_x I_y & I^{2}_y \\
        \end{bmatrix}

    and :math:`k` is an empirically determined constant
    :math:`k ∈ [ 0.04 , 0.06 ]`

    Args:
        input: input image with shape :math:`(B, C, H, W)`.
        k: the Harris detector free parameter.
        grads_mode: can be ``'sobel'`` for standalone use or ``'diff'`` for use on Gaussian pyramid.
        sigmas: coefficients to be multiplied by multichannel response. Should be shape of :math:`(B)`
          It is necessary for performing non-maxima-suppression across different scale pyramid levels.
          See `vlfeat <https://github.com/vlfeat/vlfeat/blob/master/vl/covdet.c#L874>`_.

    Return:
        the response map per channel with shape :math:`(B, C, H, W)`.

    Example:
        >>> input = torch.tensor([[[
        ...    [0., 0., 0., 0., 0., 0., 0.],
        ...    [0., 1., 1., 1., 1., 1., 0.],
        ...    [0., 1., 1., 1., 1., 1., 0.],
        ...    [0., 1., 1., 1., 1., 1., 0.],
        ...    [0., 1., 1., 1., 1., 1., 0.],
        ...    [0., 1., 1., 1., 1., 1., 0.],
        ...    [0., 0., 0., 0., 0., 0., 0.],
        ... ]]])  # 1x1x7x7
        >>> # compute the response map
        harris_response(input, 0.04)
        tensor([[[[0.0012, 0.0039, 0.0020, 0.0000, 0.0020, 0.0039, 0.0012],
                  [0.0039, 0.0065, 0.0040, 0.0000, 0.0040, 0.0065, 0.0039],
                  [0.0020, 0.0040, 0.0029, 0.0000, 0.0029, 0.0040, 0.0020],
                  [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
                  [0.0020, 0.0040, 0.0029, 0.0000, 0.0029, 0.0040, 0.0020],
                  [0.0039, 0.0065, 0.0040, 0.0000, 0.0040, 0.0065, 0.0039],
                  [0.0012, 0.0039, 0.0020, 0.0000, 0.0020, 0.0039, 0.0012]]]])
    """
    # TODO: Recompute doctest
    if not isinstance(input, torch.Tensor):
        raise TypeError("Input type is not a torch.Tensor. Got {}".format(
            type(input)))

    if not len(input.shape) == 4:
        raise ValueError(
            "Invalid input shape, we expect BxCxHxW. Got: {}".format(
                input.shape))

    if sigmas is not None:
        if not isinstance(sigmas, torch.Tensor):
            raise TypeError("sigmas type is not a torch.Tensor. Got {}".format(
                type(sigmas)))
        if (not len(sigmas.shape) == 1) or (sigmas.size(0) != input.size(0)):
            raise ValueError(
                "Invalid sigmas shape, we expect B == input.size(0). Got: {}".
                format(sigmas.shape))

    gradients: torch.Tensor = spatial_gradient(input, grads_mode)
    dx: torch.Tensor = gradients[:, :, 0]
    dy: torch.Tensor = gradients[:, :, 1]

    # compute the structure tensor M elements

    dx2: torch.Tensor = gaussian_blur2d(dx**2, (7, 7), (1.0, 1.0))
    dy2: torch.Tensor = gaussian_blur2d(dy**2, (7, 7), (1.0, 1.0))
    dxy: torch.Tensor = gaussian_blur2d(dx * dy, (7, 7), (1.0, 1.0))

    det_m: torch.Tensor = dx2 * dy2 - dxy * dxy
    trace_m: torch.Tensor = dx2 + dy2

    # compute the response map
    scores: torch.Tensor = det_m - k * (trace_m**2)

    if sigmas is not None:
        scores = scores * sigmas.pow(4).view(-1, 1, 1, 1)

    return scores
Example #13
0
def gftt_response(input: torch.Tensor,
                  grads_mode: str = 'sobel',
                  sigmas: Optional[torch.Tensor] = None) -> torch.Tensor:
    r"""Computes the Shi-Tomasi cornerness function.

    Function does not do any normalization or nms. The response map is computed according the following formulation:

    .. math::
        R = min(eig(M))

    where:

    .. math::
        M = \sum_{(x,y) \in W}
        \begin{bmatrix}
            I^{2}_x & I_x I_y \\
            I_x I_y & I^{2}_y \\
        \end{bmatrix}

    Args:
        input: input image with shape :math:`(B, C, H, W)`.
        grads_mode: can be ``'sobel'`` for standalone use or ``'diff'`` for use on Gaussian pyramid.
        sigmas: coefficients to be multiplied by multichannel response. Should be shape of :math:`(B)`
          It is necessary for performing non-maxima-suppression across different scale pyramid levels.
          See `vlfeat <https://github.com/vlfeat/vlfeat/blob/master/vl/covdet.c#L874>`_.

    Return:
        the response map per channel with shape :math:`(B, C, H, W)`.

    Example:
        >>> input = torch.tensor([[[
        ...    [0., 0., 0., 0., 0., 0., 0.],
        ...    [0., 1., 1., 1., 1., 1., 0.],
        ...    [0., 1., 1., 1., 1., 1., 0.],
        ...    [0., 1., 1., 1., 1., 1., 0.],
        ...    [0., 1., 1., 1., 1., 1., 0.],
        ...    [0., 1., 1., 1., 1., 1., 0.],
        ...    [0., 0., 0., 0., 0., 0., 0.],
        ... ]]])  # 1x1x7x7
        >>> # compute the response map
        gftt_response(input)
        tensor([[[[0.0155, 0.0334, 0.0194, 0.0000, 0.0194, 0.0334, 0.0155],
                  [0.0334, 0.0575, 0.0339, 0.0000, 0.0339, 0.0575, 0.0334],
                  [0.0194, 0.0339, 0.0497, 0.0000, 0.0497, 0.0339, 0.0194],
                  [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
                  [0.0194, 0.0339, 0.0497, 0.0000, 0.0497, 0.0339, 0.0194],
                  [0.0334, 0.0575, 0.0339, 0.0000, 0.0339, 0.0575, 0.0334],
                  [0.0155, 0.0334, 0.0194, 0.0000, 0.0194, 0.0334, 0.0155]]]])
    """
    # TODO: Recompute doctest
    if not isinstance(input, torch.Tensor):
        raise TypeError("Input type is not a torch.Tensor. Got {}".format(
            type(input)))

    if not len(input.shape) == 4:
        raise ValueError(
            "Invalid input shape, we expect BxCxHxW. Got: {}".format(
                input.shape))

    gradients: torch.Tensor = spatial_gradient(input, grads_mode)
    dx: torch.Tensor = gradients[:, :, 0]
    dy: torch.Tensor = gradients[:, :, 1]

    dx2: torch.Tensor = gaussian_blur2d(dx**2, (7, 7), (1.0, 1.0))
    dy2: torch.Tensor = gaussian_blur2d(dy**2, (7, 7), (1.0, 1.0))
    dxy: torch.Tensor = gaussian_blur2d(dx * dy, (7, 7), (1.0, 1.0))

    det_m: torch.Tensor = dx2 * dy2 - dxy * dxy
    trace_m: torch.Tensor = dx2 + dy2

    e1: torch.Tensor = 0.5 * (trace_m + torch.sqrt(
        (trace_m**2 - 4 * det_m).abs()))
    e2: torch.Tensor = 0.5 * (trace_m - torch.sqrt(
        (trace_m**2 - 4 * det_m).abs()))

    scores: torch.Tensor = torch.min(e1, e2)

    if sigmas is not None:
        scores = scores * sigmas.pow(4).view(-1, 1, 1, 1)

    return scores
Example #14
0
 def forward(self, img):
     if self.p > random():
         sigma = (self.max_sigma - self.min_sigma) * random() + self.min_sigma
         return F.gaussian_blur2d(img, kernel_size=self.kernel_size, sigma=(sigma, sigma))
     else:
         return img
Example #15
0
 def g(x):
     return gaussian_blur2d(x, (3, 3), (1., 1.))
Example #16
0
def canny(
    input: torch.Tensor,
    low_threshold: float = 0.1,
    high_threshold: float = 0.2,
    kernel_size: Tuple[int, int] = (5, 5),
    sigma: Tuple[float, float] = (1, 1),
    hysteresis: bool = True,
    eps: float = 1e-6,
) -> Tuple[torch.Tensor, torch.Tensor]:
    r"""Finds edges of the input image and filters them using the Canny algorithm.

    Args:
        input (torch.Tensor): input image tensor with shape :math:`(B,C,H,W)`.
        low_threshold (float): lower threshold for the hysteresis procedure. Default: 0.1.
        high_threshold (float): upper threshold for the hysteresis procedure. Default: 0.1.
        kernel_size (Tuple[int, int]): the size of the kernel for the gaussian blur.
        sigma (Tuple[float, float]): the standard deviation of the kernel for the gaussian blur.
        hysteresis (bool): if True, applies the hysteresis edge tracking.
            Otherwise, the edges are divided between weak (0.5) and strong (1) edges.
        eps (float): regularization number to avoid NaN during backprop. Default: 1e-6.

    Returns:
        Tuple[torch.Tensor, torch.Tensor]:
        - the canny edge magnitudes map, shape of :math:`(B,1,H,W)`.
        - the canny edge detection filtered by thresholds and hysteresis, shape of :math:`(B,1,H,W)`.

    Example:
        >>> input = torch.rand(5, 3, 4, 4)
        >>> magnitude, edges = canny(input)  # 5x3x4x4
        >>> magnitude.shape
        torch.Size([5, 1, 4, 4])
        >>> edges.shape
        torch.Size([5, 1, 4, 4])
    """
    if not isinstance(input, torch.Tensor):
        raise TypeError("Input type is not a torch.Tensor. Got {}".format(type(input)))

    if not len(input.shape) == 4:
        raise ValueError("Invalid input shape, we expect BxCxHxW. Got: {}".format(input.shape))

    if low_threshold > high_threshold:
        raise ValueError(
            "Invalid input thresholds. low_threshold should be smaller than the high_threshold. Got: {}>{}".format(
                low_threshold, high_threshold
            )
        )

    if low_threshold < 0 and low_threshold > 1:
        raise ValueError(
            "Invalid input threshold. low_threshold should be in range (0,1). Got: {}".format(low_threshold)
        )

    if high_threshold < 0 and high_threshold > 1:
        raise ValueError(
            "Invalid input threshold. high_threshold should be in range (0,1). Got: {}".format(high_threshold)
        )

    device: torch.device = input.device
    dtype: torch.dtype = input.dtype

    # To Grayscale
    if input.shape[1] == 3:
        input = rgb_to_grayscale(input)

    # Gaussian filter
    blurred: torch.Tensor = gaussian_blur2d(input, kernel_size, sigma)

    # Compute the gradients
    gradients: torch.Tensor = spatial_gradient(blurred, normalized=False)

    # Unpack the edges
    gx: torch.Tensor = gradients[:, :, 0]
    gy: torch.Tensor = gradients[:, :, 1]

    # Compute gradient magnitude and angle
    magnitude: torch.Tensor = torch.sqrt(gx * gx + gy * gy + eps)
    angle: torch.Tensor = torch.atan2(gy, gx)

    # Radians to Degrees
    angle = rad2deg(angle)

    # Round angle to the nearest 45 degree
    angle = torch.round(angle / 45) * 45

    # Non-maximal suppression
    nms_kernels: torch.Tensor = get_canny_nms_kernel(device, dtype)
    nms_magnitude: torch.Tensor = F.conv2d(magnitude, nms_kernels, padding=nms_kernels.shape[-1] // 2)

    # Get the indices for both directions
    positive_idx: torch.Tensor = (angle / 45) % 8
    positive_idx = positive_idx.long()

    negative_idx: torch.Tensor = ((angle / 45) + 4) % 8
    negative_idx = negative_idx.long()

    # Apply the non-maximum suppresion to the different directions
    channel_select_filtered_positive: torch.Tensor = torch.gather(nms_magnitude, 1, positive_idx)
    channel_select_filtered_negative: torch.Tensor = torch.gather(nms_magnitude, 1, negative_idx)

    channel_select_filtered: torch.Tensor = torch.stack(
        [channel_select_filtered_positive, channel_select_filtered_negative], 1
    )

    is_max: torch.Tensor = channel_select_filtered.min(dim=1)[0] > 0.0

    magnitude = magnitude * is_max

    # Threshold
    edges: torch.Tensor = F.threshold(magnitude, low_threshold, 0.0)

    low: torch.Tensor = magnitude > low_threshold
    high: torch.Tensor = magnitude > high_threshold

    edges = low * 0.5 + high * 0.5
    edges = edges.to(dtype)

    # Hysteresis
    if hysteresis:
        edges_old: torch.Tensor = -torch.ones(edges.shape, device=edges.device, dtype=dtype)
        hysteresis_kernels: torch.Tensor = get_hysteresis_kernel(device, dtype)

        while ((edges_old - edges).abs() != 0).any():
            weak: torch.Tensor = (edges == 0.5).float()
            strong: torch.Tensor = (edges == 1).float()

            hysteresis_magnitude: torch.Tensor = F.conv2d(
                edges, hysteresis_kernels, padding=hysteresis_kernels.shape[-1] // 2
            )
            hysteresis_magnitude = (hysteresis_magnitude == 1).any(1, keepdim=True).to(dtype)
            hysteresis_magnitude = hysteresis_magnitude * weak + strong

            edges_old = edges.clone()
            edges = hysteresis_magnitude + (hysteresis_magnitude == 0) * weak * 0.5

        edges = hysteresis_magnitude

    return magnitude, edges
Example #17
0
 def g(x):
     return gaussian_blur2d(x, (7, 7), (1., 1.))
Example #18
0
def resize(
    input: torch.Tensor,
    size: Union[int, Tuple[int, int]],
    interpolation: str = 'bilinear',
    align_corners: Optional[bool] = None,
    side: str = "short",
    antialias: bool = False,
) -> torch.Tensor:
    r"""Resize the input torch.Tensor to the given size.

    .. image:: _static/img/resize.png

    Args:
        tensor: The image tensor to be skewed with shape of :math:`(..., H, W)`.
            `...` means there can be any number of dimensions.
        size: Desired output size. If size is a sequence like (h, w),
            output size will be matched to this. If size is an int, smaller edge of the image will
            be matched to this number. i.e, if height > width, then image will be rescaled
            to (size * height / width, size)
        interpolation:  algorithm used for upsampling: ``'nearest'`` | ``'linear'`` | ``'bilinear'`` |
            'bicubic' | 'trilinear' | 'area'.
        align_corners: interpolation flag.
        side: Corresponding side if ``size`` is an integer. Can be one of ``'short'``, ``'long'``, ``'vert'``,
            or ``'horz'``.
        antialias: if True, then image will be filtered with Gaussian before downscaling.
            No effect for upscaling.

    Returns:
        The resized tensor with the shape as the specified size.

    Example:
        >>> img = torch.rand(1, 3, 4, 4)
        >>> out = resize(img, (6, 8))
        >>> print(out.shape)
        torch.Size([1, 3, 6, 8])
    """
    if not isinstance(input, torch.Tensor):
        raise TypeError(
            f"Input tensor type is not a torch.Tensor. Got {type(input)}")

    if len(input.shape) < 2:
        raise ValueError(
            f'Input tensor must have at least two dimensions. Got {len(input.shape)}'
        )

    input_size = h, w = input.shape[-2:]
    if isinstance(size, int):
        aspect_ratio = w / h
        size = _side_to_image_size(size, aspect_ratio, side)

    if size == input_size:
        return input

    factors = (h / size[0], w / size[1])

    # We do bluring only for downscaling
    antialias = antialias and (max(factors) > 1)

    if antialias:
        # First, we have to determine sigma
        # Taken from skimage: https://github.com/scikit-image/scikit-image/blob/v0.19.2/skimage/transform/_warps.py#L171
        sigmas = (max((factors[0] - 1.0) / 2.0,
                      0.001), max((factors[1] - 1.0) / 2.0, 0.001))

        # Now kernel size. Good results are for 3 sigma, but that is kind of slow. Pillow uses 1 sigma
        # https://github.com/python-pillow/Pillow/blob/master/src/libImaging/Resample.c#L206
        # But they do it in the 2 passes, which gives better results. Let's try 2 sigmas for now
        ks = int(max(2.0 * 2 * sigmas[0], 3)), int(max(2.0 * 2 * sigmas[1], 3))

        # Make sure it is odd
        if (ks[0] % 2) == 0:
            ks = ks[0] + 1, ks[1]

        if (ks[1] % 2) == 0:
            ks = ks[0], ks[1] + 1

        input = gaussian_blur2d(input, ks, sigmas)

    output = torch.nn.functional.interpolate(input,
                                             size=size,
                                             mode=interpolation,
                                             align_corners=align_corners)
    return output