              if item == "EOF":
                  logger.trace("EOF received")  # type:ignore
                  exhausted = True
                  break
              # Put frames with no faces into the out queue to keep TQDM consistent
              if not item.detected_faces:
                  self._queues["out"].put(item)
                  continue
  
              converted_image = item.get_image_copy(self.color_format)
              for f_idx, face in enumerate(item.detected_faces):
 
</s> remove             if not item.detected_faces:
 </s> add             if not item.is_aligned and not item.detected_faces: </s> remove                 if not self._image_is_aligned:
 </s> add                 if not item.is_aligned: </s> remove         if self._skip_num == 1 and not self._alignments.data:
            logger.debug("No frames to be skipped")
 </s> add         error = False
        retval: List[List[str]] = []
        for files in (filter_files, nfilter_files):
            filt_files = [files] if isinstance(files, str) else files
            filt_files = [] if filt_files is None else filt_files
            for file in filt_files:
                if (not os.path.isfile(file) or
                        os.path.splitext(file)[-1].lower() not in _image_extensions):
                    logger.warning("Filter file '%s' does not exist or is not an image file", file)
                    error = True
            retval.append(filt_files)

        filters = retval[0]
        nfilters = retval[1]
        f_fnames = set(os.path.basename(fname) for fname in filters)
        n_fnames = set(os.path.basename(fname) for fname in nfilters)
        if f_fnames.intersection(n_fnames):
            error = True
            logger.warning("filter and nfilter filenames should be unique. The following "
                           "filenames exist in both folders: %s", f_fnames.intersection(n_fnames))

        if error:
            logger.error("There was a problem processing filter files. See the above warnings for "
                         "details")
            sys.exit(1)
        logger.debug("filter_files: %s, nfilter_files: %s", retval[0], retval[1])

        return filters, nfilters

    @classmethod
    def _identity_from_extracted(cls, filename) -> Tuple[np.ndarray, bool]:
        """ Test whether the given image is a faceswap extracted face and contains identity
        information. If so, return the identity embedding

        Parameters
        ----------
        filename: str
            Full path to the image file to load

        Returns
        -------
        :class:`numpy.ndarray`
            The identity embeddings, if they can be obtained from the image header, otherwise an
            empty array
        bool
            ``True`` if the image is a faceswap extracted image otherwise ``False``
        """
        if os.path.splitext(filename)[-1].lower() != ".png":
            logger.info("'%s' not a png. Returning empty array", filename)
            return np.array([]), False

        meta = read_image_meta(filename)
        if "itxt" not in meta or "alignments" not in meta["itxt"]:
            logger.debug("'%s' does not contain faceswap data. Returning empty array", filename)
            return np.array([]), False

        align: "PNGHeaderAlignmentsDict" = meta["itxt"]["alignments"]
        if "identity" not in align or "vggface2" not in align["identity"]:
            logger.debug("'%s' does not contain identity data. Returning empty array", filename)
            return np.array([]), True

        retval = np.array(align["identity"]["vggface2"])
        logger.debug("Obtained identity for '%s'. Shape: %s", filename, retval.shape)

        return retval, True

    def _process_extracted(self, item: ExtractMedia) -> None:
        """ Process the output from the extraction pipeline.

        If no face has been detected, or multiple faces are detected for the inclusive filter,
        embeddings and filenames are removed from the filter.

        if a single face is detected or multiple faces are detected for the exclusive filter,
        embeddings are added to the relevent filter list

        Parameters
        ----------
        item: :class:`plugins.extract.Pipeline.ExtracMedia`
            The output from the extraction pipeline containing the identity encodings
        """
        is_filter = item.filename in self._filter_files
        lbl = "filter" if is_filter else "nfilter"
        filelist = self._filter_files if is_filter else self._nfilter_files
        embeddings = self._embeddings if is_filter else self._nembeddings
        identities = np.array([face.identity["vggface2"] for face in item.detected_faces])
        idx = filelist.index(item.filename)

        if len(item.detected_faces) == 0:
            logger.warning("No faces detected for %s in file '%s'. Image will not be used",
                           lbl, os.path.basename(item.filename))
            filelist.pop(idx)
            embeddings.pop(idx)
            # Put frames with no faces or are already aligned into the out queue
            if not item.detected_faces or item.is_aligned:
              if item == "EOF":
                  logger.trace("EOF received")  # type:ignore
                  exhausted = True
                  break
            # Put frames with no faces or are already aligned into the out queue
            if not item.detected_faces or item.is_aligned:
            # Put frames with no faces or are already aligned into the out queue
            if not item.detected_faces or item.is_aligned:
                  self._queues["out"].put(item)
                  continue
  
              converted_image = item.get_image_copy(self.color_format)
              for f_idx, face in enumerate(item.detected_faces):
keep keep keep keep 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/align/_base.py