          blur_map = cv2.Laplacian(image, cv2.CV_32F)
          score = np.var(blur_map) / np.sqrt(image.shape[0] * image.shape[1])
          return score
  
      @classmethod
      def estimate_blur_fft(cls, image, metadata=None):
          """ Estimate the amount of blur a fft filtered image has.
  
          Parameters
          ----------
          image: :class:`numpy.ndarray`
              Use Fourier Transform to analyze the frequency characteristics of the masked
 
</s> remove         Parameters
        ----------
        image: :class:`numpy.ndarray`
            Use Fourier Transform to analyze the frequency characteristics of the masked
            face using 2D Discrete Fourier Transform (DFT) filter to find the frequency domain.
            A mean value is assigned to the magnitude spectrum and returns a blur score.
            Adapted from https://www.pyimagesearch.com/2020/06/15/
            opencv-fast-fourier-transform-fft-for-blur-detection-in-images-and-video-streams/
        metadata: dict, optional
            The metadata for the face image or ``None`` if no metadata is available. If metadata is
            provided the face will be masked by the "components" mask prior to calculating blur.
            Default:``None``
 </s> add     def _output_groups(self) -> None:
        """ Move the files to folders. </s> remove         if metadata is not None:
            alignments = metadata["alignments"]
            det_face = DetectedFace()
            det_face.from_png_meta(alignments)
            aln_face = AlignedFace(np.array(alignments["landmarks_xy"], dtype="float32"),
                                   image=image,
                                   centering="legacy",
                                   size=256,
                                   is_aligned=True)
            mask = det_face.mask["components"]
            mask.set_sub_crop(aln_face.pose.offset[mask.stored_centering],
                              aln_face.pose.offset["legacy"],
                              centering="legacy")
            mask = cv2.resize(mask.mask, (256, 256), interpolation=cv2.INTER_CUBIC)[..., None]
            image = np.minimum(aln_face.face, mask)
        if image.ndim == 3:
            image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        blur_map = cv2.Laplacian(image, cv2.CV_32F)
        score = np.var(blur_map) / np.sqrt(image.shape[0] * image.shape[1])
        return score
 </s> add         try:
            if self._args.keep_original:
                copyfile(source, destination)
            else:
                os.rename(source, destination)
        except FileNotFoundError as err:
            logger.error("Failed to sort '%s' to '%s'. Original error: %s",
                         source, destination, str(err)) </s> remove     @classmethod
    def estimate_blur(cls, image, metadata=None):
        """ Estimate the amount of blur an image has with the variance of the Laplacian.
        Normalize by pixel number to offset the effect of image size on pixel gradients & variance.
 </s> add     def _sort_file(self, source: str, destination: str) -> None:
        """ Copy or move a file based on whether 'keep original' has been selected and log changes
        if required. </s> remove         image: :class:`numpy.ndarray`
            The face image to calculate blur for
        metadata: dict, optional
            The metadata for the face image or ``None`` if no metadata is available. If metadata is
            provided the face will be masked by the "components" mask prior to calculating blur.
            Default:``None``

        Returns
        -------
        float
            The estimated blur score for the face
 </s> add         source: str
            The full path to the source file that is being sorted
        destination: str
            The full path to where the source file should be moved/renamed </s> remove     @staticmethod
    def find_images(input_dir):
        """ Return list of images at specified location """
        result = []
        extensions = [".jpg", ".png", ".jpeg"]
        for root, _, files in os.walk(input_dir):
            for file in files:
                if os.path.splitext(file)[1].lower() in extensions:
                    result.append(os.path.join(root, file))
            break
        return result
 </s> add         logger.info("Done.")
        if self._args.log_changes:
            self._changes[source] = destination
          blur_map = cv2.Laplacian(image, cv2.CV_32F)
          score = np.var(blur_map) / np.sqrt(image.shape[0] * image.shape[1])
          return score
  
        if self._args.log_changes:
            self._changes[source] = destination
        if self._args.log_changes:
            self._changes[source] = destination
        if self._args.log_changes:
            self._changes[source] = destination
  
          Parameters
          ----------
          image: :class:`numpy.ndarray`
              Use Fourier Transform to analyze the frequency characteristics of the masked
keep keep keep keep replace replace replace keep keep keep keep keep
 Overhaul sort:
  - Standardize image data reading and writing
  - Optimize loading (just one pass required)
  - Make all sort groups binnable (to greater or lesser results)
  - Add sort by pitch
  - Deprecate multiple options
  - linting, docs + locales 
tools/sort/sort.py