          available_vram = (cast(int, self._vram_stats["vram_free"])
                            - plugins_required) // len(gpu_plugins)
          self._set_plugin_batchsize(gpu_plugins, available_vram)
  
      def set_aligner_normalization_method(self, method: Optional[Literal["none",
                                                                          "clahe",
                                                                          "hist",
                                                                          "mean"]]) -> None:
          """ Change the normalization method for faces fed into the aligner.
  
          Parameters
          ----------
          method: {"none", "clahe", "hist", "mean"}
              The normalization method to apply to faces prior to feeding into the aligner's model
          """
          assert self._align is not None
          logger.debug("Setting to: '%s'", method)
          self._align.set_normalize_method(method)
  
      def _set_plugin_batchsize(self, gpu_plugins: List[str], available_vram: float) -> None:
          """ Set the batch size for the given plugin based on given available vram.
          Do not update plugins which have a vram_per_batch of 0 (CPU plugins) due to
          zero division error.
          """
 
</s> remove             aligner.set_aligner_normalization_method(method)
 </s> add             logger.debug("Setting to: '%s'", method)
            aligner.aligner.set_normalize_method(method) </s> remove     def _check_thread_error(self) -> None:
        """ Check if any errors have occurred in the running threads and their errors """
        for thread in self._threads:
            thread.check_and_raise_error()

 </s> add  </s> remove 

class FaceFilter(PostProcessAction):
    """ Filter in or out faces based on input image(s). Extract or Convert

    Parameters
    -----------
    args: tuple
        Unused
    kwargs: dict
        Keyword arguments for face filter:

        * **detector** (`str`) - The detector to use

        * **aligner** (`str`) - The aligner to use

        * **multiprocess** (`bool`) - Whether to run the extraction pipeline in single process \
        mode or not

        * **ref_threshold** (`float`) - The reference threshold for a positive match

        * **filter_lists** (`dict`) - The filter and nfilter image paths
    """

    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        logger.info("Extracting and aligning face for Face Filter...")
        self._filter = self._load_face_filter(**kwargs)
        logger.debug("Initialized %s", self.__class__.__name__)

    def _load_face_filter(self,
                          filter_lists: Dict[str, str],
                          ref_threshold: float,
                          aligner: str,
                          detector: str,
                          multiprocess: bool) -> Optional[FilterFunc]:
        """ Set up and load the :class:`~lib.face_filter.FaceFilter`.

        Parameters
        ----------
        filter_lists: dict
            The filter and nfilter image paths
        ref_threshold: float
            The reference threshold for a positive match
        aligner: str
            The aligner to use
        detector: str
            The detector to use
        multiprocess: bool
            Whether to run the extraction pipeline in single process mode or not

        Returns
        -------
        :class:`~lib.face_filter.FaceFilter`
            The face filter
        """
        if not any(val for val in filter_lists.values()):
            return None

        facefilter = None
        filter_files = [self._set_face_filter(f_type, filter_lists[f_type])
                        for f_type in get_args(Literal["filter", "nfilter"])]

        if any(filters for filters in filter_files):
            facefilter = FilterFunc(filter_files[0],
                                    filter_files[1],
                                    detector,
                                    aligner,
                                    multiprocess,
                                    ref_threshold)
            logger.debug("Face filter: %s", facefilter)
        else:
            self._valid = False
        return facefilter

    @classmethod
    def _set_face_filter(cls,
                         f_type: Literal["filter", "nfilter"],
                         f_args: Union[str, List[str]]) -> List[str]:
        """ Check filter files exist and add the filter file paths to a list.

        Parameters
        ----------
        f_type: {"filter", "nfilter"}
            The type of filter to create this list for
        f_args: str or list
            The filter image(s) to use

        Returns
        -------
        list
            The confirmed existing paths to filter files to use
        """
        if not f_args:
            return []

        logger.info("%s: %s", f_type.title(), f_args)
        filter_files = f_args if isinstance(f_args, list) else [f_args]
        filter_files = [fpath for fpath in filter_files if os.path.exists(fpath)]
        if not filter_files:
            logger.warning("Face %s files were requested, but no files could be found. This "
                           "filter will not be applied.", f_type)
        logger.debug("Face Filter files: %s", filter_files)
        return filter_files

    def process(self, extract_media: "ExtractMedia") -> None:
        """ Filters in or out any wanted or unwanted faces based on command line arguments.

        Parameters
        ----------
        extract_media: :class:`~plugins.extract.pipeline.ExtractMedia`
            The :class:`~plugins.extract.pipeline.ExtractMedia` object to perform the
            face filtering on.
        """
        if not self._filter:
            return
        ret_faces = []
        for idx, detect_face in enumerate(extract_media.detected_faces):
            check_item = detect_face["face"] if isinstance(detect_face, dict) else detect_face
            if not self._filter.check(extract_media.image, check_item):
                logger.verbose("Skipping not recognized face: (Frame: %s Face %s)",  # type: ignore
                               extract_media.filename, idx)
                continue
            logger.trace("Accepting recognised face. Frame: %s. Face: %s",  # type: ignore
                         extract_media.filename, idx)
            ret_faces.append(detect_face)
        extract_media.add_detected_faces(ret_faces)
 </s> add  </s> remove         Should only be called from  :class:`lib.cli.launcher.ScriptExecutor`
 </s> add         if len(item.detected_faces) > 1 and is_filter:
            logger.warning("%s faces detected for filter in '%s'. These identies will not be used",
                           len(item.detected_faces), os.path.basename(item.filename))
            filelist.pop(idx)
            embeddings.pop(idx)
            return

        if len(item.detected_faces) > 1 and not is_filter:
            logger.warning("%s faces detected for nfilter in '%s'. All of these identies will be "
                           "used", len(item.detected_faces), os.path.basename(item.filename))
            embeddings[idx] = identities
            return

    def _identity_from_extractor(self, file_list: List[str], aligned: List[str]) -> None:
        """ Obtain the identity embeddings from the extraction pipeline

        Parameters
        ----------
        filesile_list: list
            List of full path to images to run through the extraction pipeline
        aligned: list
            List of full path to images that exist in attr:`filelist` that are faceswap aligned
            images
          available_vram = (cast(int, self._vram_stats["vram_free"])
                            - plugins_required) // len(gpu_plugins)
          self._set_plugin_batchsize(gpu_plugins, available_vram)
  















      def _set_plugin_batchsize(self, gpu_plugins: List[str], available_vram: float) -> None:
          """ Set the batch size for the given plugin based on given available vram.
          Do not update plugins which have a vram_per_batch of 0 (CPU plugins) due to
          zero division error.
          """
keep keep keep keep replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace keep keep keep keep keep
 Update Face Filter
  - Remove old face filter
  - plugins.extract.pipeline: Expose plugins directly
  - Change `is_aligned` from plugin level to ExtractMedia level
  - Allow extract pipeline to take faceswap aligned images
  - Add ability for recognition plugins to accept aligned faces as input
  - Add face filter to recognition plugin
  - Move extractor pipeline IO ops to own class 
plugins/extract/pipeline.py