class_docstr: Implement the local spatial smoothing defence approach in PyTorch.
  
  | Paper link: https://arxiv.org/abs/1704.01155
  
  | Please keep in mind the limitations of defences. For more information on the limitations of this defence,
      see https://arxiv.org/abs/1803.09868 . For details on how to evaluate classifier security in general, see
      https://arxiv.org/abs/1902.06705
docstr: Create an instance of local spatial smoothing.
  
  :param window_size: Size of spatial smoothing window.
  :param channels_first: Set channels first or last.
  :param clip_values: Tuple of the form `(min, max)` representing the minimum and maximum values allowed
         for features.
  :param apply_fit: True if applied during fitting/training.
  :param apply_predict: True if applied during predicting.
  :param device_type: Type of device on which the classifier is run, either `gpu` or `cpu`.
code: def __init__(
      self,
      window_size: int = 3,
      channels_first: bool = False,
      clip_values: Optional["CLIP_VALUES_TYPE"] = None,
      apply_fit: bool = False,
      apply_predict: bool = True,
      device_type: str = "gpu",
  ) -> None:
      """
      Create an instance of local spatial smoothing.
  
      :param window_size: Size of spatial smoothing window.
      :param channels_first: Set channels first or last.
      :param clip_values: Tuple of the form `(min, max)` representing the minimum and maximum values allowed
             for features.
      :param apply_fit: True if applied during fitting/training.
      :param apply_predict: True if applied during predicting.
      :param device_type: Type of device on which the classifier is run, either `gpu` or `cpu`.
      """
  
      super().__init__(
          device_type=device_type,
          apply_fit=apply_fit,
          apply_predict=apply_predict,
      )
  
      self.channels_first = channels_first
      self.window_size = window_size
      self.clip_values = clip_values
      self._check_params()
  
      from kornia.filters import MedianBlur
  
      class MedianBlurCustom(MedianBlur):
          """
          An ongoing effort to reproduce the median blur function in SciPy.
          """
  
          def __init__(self, kernel_size: Tuple[int, int]) -> None:
              super().__init__(kernel_size)
  
              # Half-pad the input so that the output keeps the same shape.
              # * center pixel located lower right
              half_pad = [int(k % 2 == 0) for k in kernel_size]
              if hasattr(self, "padding"):
                  # kornia < 0.5.0
                  padding = self.padding
              else:
                  # kornia >= 0.5.0
                  from kornia.filters.median import (
                      _compute_zero_padding,
                  )
  
                  padding = _compute_zero_padding(kernel_size)  # type: ignore
              self.p2d = [
                  int(padding[-1]) + half_pad[-1],  # type: ignore
                  int(padding[-1]),  # type: ignore
                  int(padding[-2]) + half_pad[-2],  # type: ignore
                  int(padding[-2]),  # type: ignore
              ]
              # PyTorch requires padding size should be less than the corresponding input dimension.
  
              if not hasattr(self, "kernel"):
                  # kornia >= 0.5.0
                  from kornia.filters.kernels import get_binary_kernel2d
  
                  self.kernel = get_binary_kernel2d(kernel_size)
  
          # pylint: disable=W0622
          def forward(self, input: "torch.Tensor"):  # type: ignore
              import torch
              import torch.nn.functional as F
  
              if not torch.is_tensor(input):
                  raise TypeError(
                      f"Input type is not a torch.Tensor. Got {type(input)}"
                  )
              if not len(input.shape) == 4:
                  raise ValueError(
                      f"Invalid input shape, we expect BxCxHxW. Got: {input.shape}"
                  )
              # prepare kernel
              batch_size, channels, height, width = input.shape
              kernel: torch.Tensor = self.kernel.to(input.device).to(
                  input.dtype
              )
              # map the local window to single vector
  
              _input = input.reshape(
                  batch_size * channels, 1, height, width
              )
              if input.dtype == torch.int64:
                  # "reflection_pad2d" not implemented for 'Long'
                  # "reflect" in scipy.ndimage.median_filter has no equivalence in F.pad.
                  # "reflect" in PyTorch maps to "mirror" in scipy.ndimage.median_filter.
                  _input = _input.to(torch.float32)
                  _input = F.pad(_input, self.p2d, "reflect")
                  _input = _input.to(torch.int64)
              else:
                  _input = F.pad(_input, self.p2d, "reflect")
  
              features: torch.Tensor = F.conv2d(
                  _input, kernel, stride=1
              )
              features = features.view(
                  batch_size, channels, -1, height, width
              )  # BxCx(K_h * K_w)xHxW
  
              # compute the median along the feature axis
              # * torch.median(), if window size even, use smaller value (e.g. median(4,5)=4)
              median: torch.Tensor = torch.median(features, dim=2)[0]
              return median
  
      self.median_blur = MedianBlurCustom(
          kernel_size=(self.window_size, self.window_size)
      )
  
file_path: adversarial-robustness-toolbox/art/defences/preprocessor/spatial_smoothing_pytorch.py
imports: from __future__ import absolute_import, division, print_function, unicode_literals
  import logging
  from typing import Optional, Tuple, TYPE_CHECKING
  import numpy
  from art.defences.preprocessor.preprocessor import PreprocessorPyTorch
label_desc: Use image node_id 1 to create a new SpatialSmoothingPyTorch object from inherited base classes: PreprocessorPyTorch with example: obj = SpatialSmoothingPyTorch(window_size, channels_first, clip_values, apply_fit, apply_predict, device_type)