Example #1
0
    def pre_fill(self, filenames, side):
        """ When warp to landmarks is enabled, the cache must be pre-filled, as each side needs
        access to the other side's alignments.

        Parameters
        ----------
        filenames: list
            The list of full paths to the images to load the metadata from
        side: str
            `"a"` or `"b"`. The side of the model being cached. Used for info output
        """
        with self._lock:
            for filename, meta in tqdm(
                    read_image_meta_batch(filenames),
                    desc="WTL: Caching Landmarks ({})".format(side.upper()),
                    total=len(filenames),
                    leave=False):
                if "itxt" not in meta or "alignments" not in meta["itxt"]:
                    raise FaceswapError(
                        f"Invalid face image found. Aborting: '{filename}'")

                size = meta["width"]
                meta = meta["itxt"]
                # Version Check
                self._validate_version(meta, filename)
                detected_face = self._add_aligned_face(filename,
                                                       meta["alignments"],
                                                       size)
                self._cache[os.path.basename(
                    filename)]["detected_face"] = detected_face
            self._partial_load = True
Example #2
0
    def sort_distance(self):
        """ Sort by comparison of face landmark points to mean face by average distance of core
        landmarks. """
        logger.info("Sorting by average distance of landmarks...")
        filenames = []
        distances = []
        filelist = [os.path.join(self._loader.location, fname)
                    for fname in os.listdir(self._loader.location)
                    if os.path.splitext(fname)[-1] == ".png"]
        for filename, metadata in tqdm(read_image_meta_batch(filelist),
                                       total=len(filelist),
                                       desc="Calculating Distances"):
            if not metadata:
                msg = ("The images to be sorted do not contain alignment data. Images must have "
                       "been generated by Faceswap's Extract process.\nIf you are sorting an "
                       "older faceset, then you should re-extract the faces from your source "
                       "alignments file to generate this data.")
                raise FaceswapError(msg)
            alignments = metadata["itxt"]["alignments"]
            aligned_face = AlignedFace(np.array(alignments["landmarks_xy"], dtype="float32"))
            filenames.append(filename)
            distances.append(aligned_face.average_distance)

        logger.info("Sorting...")
        matched_list = list(zip(filenames, distances))
        img_list = sorted(matched_list, key=operator.itemgetter(1))
        return img_list
Example #3
0
    def _get_face_metadata(self):
        """ Check for the existence of an aligned directory for identifying which faces in the
        target frames should be swapped. If it exists, scan the folder for face's metadata

        Returns
        -------
        dict
            Dictionary of source frame names with a list of associated face indices to be skipped
        """
        retval = dict()
        input_aligned_dir = self._args.input_aligned_dir

        if input_aligned_dir is None:
            logger.verbose(
                "Aligned directory not specified. All faces listed in the "
                "alignments file will be converted")
            return retval
        if not os.path.isdir(input_aligned_dir):
            logger.warning(
                "Aligned directory not found. All faces listed in the "
                "alignments file will be converted")
            return retval

        log_once = False
        filelist = get_image_paths(input_aligned_dir)
        for fullpath, metadata in tqdm(read_image_meta_batch(filelist),
                                       total=len(filelist),
                                       desc="Reading Face Data",
                                       leave=False):
            if "itxt" not in metadata or "source" not in metadata["itxt"]:
                # UPDATE LEGACY FACES FROM ALIGNMENTS FILE
                if not log_once:
                    logger.warning(
                        "Legacy faces discovered in '%s'. These faces will be updated",
                        input_aligned_dir)
                    log_once = True
                data = update_legacy_png_header(fullpath, self._alignments)
                if not data:
                    raise FaceswapError(
                        "Some of the faces being passed in from '{}' could not be matched to the "
                        "alignments file '{}'\nPlease double check your sources and try "
                        "again.".format(input_aligned_dir,
                                        self._alignments.file))
                meta = data["source"]
            else:
                meta = metadata["itxt"]["source"]
            retval.setdefault(meta["source_filename"],
                              list()).append(meta["face_index"])

        if not retval:
            raise FaceswapError(
                "Aligned directory is empty, no faces will be converted!")
        if len(retval) <= len(self._input_images) / 3:
            logger.warning(
                "Aligned directory contains far fewer images than the input "
                "directory, are you sure this is the right folder?")
        return retval
Example #4
0
    def process_folder(self):
        """ Iterate through the faces folder pulling out various information for each face.

        Yields
        ------
        dict
            A dictionary for each face found containing the keys returned from
            :class:`lib.image.read_image_meta_batch`
        """
        logger.info("Loading file list from %s", self.folder)

        if self._alignments is not None:  # Legacy updating
            filelist = [
                os.path.join(self.folder, face)
                for face in os.listdir(self.folder)
                if self.valid_extension(face)
            ]
        else:
            filelist = [
                os.path.join(self.folder, face)
                for face in os.listdir(self.folder)
                if os.path.splitext(face)[-1] == ".png"
            ]

        log_once = False
        for fullpath, metadata in tqdm(read_image_meta_batch(filelist),
                                       total=len(filelist),
                                       desc="Reading Face Data"):

            if "itxt" not in metadata or "source" not in metadata["itxt"]:
                if self._alignments is None:  # Can't update legacy
                    raise FaceswapError(
                        f"The folder '{self.folder}' contains images that do not include Faceswap "
                        "metadata.\nAll images in the provided folder should contain faces "
                        "generated from Faceswap's extraction process.\nPlease double check the "
                        "source and try again.")

                if not log_once:
                    logger.warning(
                        "Legacy faces discovered. These faces will be updated")
                    log_once = True
                data = update_legacy_png_header(fullpath, self._alignments)
                if not data:
                    raise FaceswapError(
                        "Some of the faces being passed in from '{}' could not be matched to the "
                        "alignments file '{}'\nPlease double check your sources and try "
                        "again.".format(self.folder, self._alignments.file))
                retval = data["source"]
            else:
                retval = metadata["itxt"]["source"]

            retval["current_filename"] = os.path.basename(fullpath)
            yield retval