          input_thread.start()
          logger.debug(input_thread)
          return input_thread
  
      def _input_faces(self, *args):
          """ Input pre-aligned faces to the Extractor plugin inside a thread
  
          Parameters
          ----------
          args: tuple
 
</s> remove     def _input_frames(self, *args):
 </s> add     def _input_frames(self, *args: Union[tuple, Tuple["EventQueue"]]) -> None: </s> remove                 self._counts["update"] += 1
 </s> add  </s> remove         args = tuple() if self._update_type == "output" else (self._extractor.input_queue, )
 </s> add         if self._update_type == "output":
            args: tuple = tuple()
        else:
            assert self._extractor is not None
            args = (self._extractor.input_queue, ) </s> remove     def _get_extractor(self, exclude_gpus):
 </s> add     def _get_extractor(self, exclude_gpus: List[int]) -> Optional[Extractor]: </s> remove         masker_input = getattr(self,
                               "_input_{}".format("faces" if self._input_is_faces else "frames"))
 </s> add         masker_input = getattr(self, f"_input_{'faces' if self._input_is_faces else 'frames'}")
    def _process_face(self,
                      filename: str,
                      image: np.ndarray,
                      metadata: Dict[str, Any]) -> Optional["ExtractMedia"]:
        """ Process a single face when masking from face images

        filename: str
            the filename currently being processed
        image: :class:`numpy.ndarray`
            The current face being processed
        metadata: dict
            The source frame metadata from the PNG header

        Returns
        -------
        :class:`plugins.pipeline.ExtractMedia` or ``None``
            If the update type is 'output' then nothing is returned otherwise the extract media for
            the face is returned
        """
        frame_name = metadata["source"]["source_filename"]
        face_index = metadata["source"]["face_index"]
        alignment = self._alignments.get_faces_in_frame(frame_name)
        if not alignment or face_index > len(alignment) - 1:
            self._counts["skip"] += 1
            logger.warning("Skipping Face not found in alignments file: '%s'", filename)
            return None
        alignment = alignment[face_index]
        self._counts["face"] += 1

        if self._check_for_missing(frame_name, face_index, alignment):
            return None

        detected_face = self._get_detected_face(alignment)
        if self._update_type == "output":
            detected_face.image = image
            self._save(frame_name, face_index, detected_face)
            return None

        media = ExtractMedia(filename, image, detected_faces=[detected_face])
        media.add_frame_metadata(metadata["source"])
        self._counts["update"] += 1
        return media

    def _input_faces(self, *args: Union[tuple, Tuple["EventQueue"]]) -> None:
          input_thread.start()
          logger.debug(input_thread)
          return input_thread
  
    def _process_face(self,
                      filename: str,
                      image: np.ndarray,
                      metadata: Dict[str, Any]) -> Optional["ExtractMedia"]:
        """ Process a single face when masking from face images

        filename: str
            the filename currently being processed
        image: :class:`numpy.ndarray`
            The current face being processed
        metadata: dict
            The source frame metadata from the PNG header

        Returns
        -------
        :class:`plugins.pipeline.ExtractMedia` or ``None``
            If the update type is 'output' then nothing is returned otherwise the extract media for
            the face is returned
        """
        frame_name = metadata["source"]["source_filename"]
        face_index = metadata["source"]["face_index"]
        alignment = self._alignments.get_faces_in_frame(frame_name)
        if not alignment or face_index > len(alignment) - 1:
            self._counts["skip"] += 1
            logger.warning("Skipping Face not found in alignments file: '%s'", filename)
            return None
        alignment = alignment[face_index]
        self._counts["face"] += 1

        if self._check_for_missing(frame_name, face_index, alignment):
            return None

        detected_face = self._get_detected_face(alignment)
        if self._update_type == "output":
            detected_face.image = image
            self._save(frame_name, face_index, detected_face)
            return None

        media = ExtractMedia(filename, image, detected_faces=[detected_face])
        media.add_frame_metadata(metadata["source"])
        self._counts["update"] += 1
        return media

    def _input_faces(self, *args: Union[tuple, Tuple["EventQueue"]]) -> None:
          """ Input pre-aligned faces to the Extractor plugin inside a thread
  
          Parameters
          ----------
          args: tuple
keep keep keep keep replace keep keep keep keep keep
 Minor updates and fixups
  - Mask Tool - Typing + BiSeNet mask update fix
  - Alignments Tool - Auto search for alignments file 
tools/mask/mask.py