Пример #1
0
    def _update_faces(self, extractor_output):
        """ Update alignments for the mask if the input type is a faces folder

        If an output location has been indicated, then puts the mask preview to the save queue

        Parameters
        ----------
        extractor_output: dict
            The output from the :class:`plugins.extract.pipeline.Extractor` object
        """
        for face in extractor_output.detected_faces:
            frame_name = extractor_output.mask_tool_face_info[
                "source_filename"]
            face_index = extractor_output.mask_tool_face_info["face_index"]
            logger.trace("Saving face: (frame: %s, face index: %s)",
                         frame_name, face_index)

            self._alignments.update_face(frame_name, face_index,
                                         face.to_alignment())
            metadata = dict(alignments=face.to_png_meta(),
                            source=extractor_output.mask_tool_face_info)
            self._faces_saver.save(
                extractor_output.filename,
                encode_image(extractor_output.image, ".png",
                             metadata=metadata))

            if self._saver is not None:
                face.image = extractor_output.image
                self._save(frame_name, face_index, face)
Пример #2
0
    def _output_faces(self, saver, extract_media):
        """ Output faces to save thread
        Set the face filename based on the frame name and put the face to the
        :class:`~lib.image.ImagesSaver` save queue and add the face information to the alignments
        data.
        Parameters
        ----------
        saver: lib.images.ImagesSaver
            The background saver for saving the image
        extract_media: :class:`~plugins.extract.pipeline.ExtractMedia`
            The output from :class:`~plugins.extract.Pipeline.Extractor`
        """
        logger.trace("Outputting faces for %s", extract_media.filename)
        final_faces = list()
        filename = os.path.splitext(os.path.basename(extract_media.filename))[0]
        extension = ".png"

        for idx, face in enumerate(extract_media.detected_faces):
            output_filename = "{}_{}{}".format(filename, str(idx), extension)
            meta = dict(alignments=face.to_png_meta(),
                        source=dict(alignments_version=self._alignments.version,
                                    original_filename=output_filename,
                                    face_index=idx,
                                    source_filename=os.path.basename(extract_media.filename),
                                    source_is_video=self._images.is_video))
            image = encode_image(face.aligned.face, extension, metadata=meta)

            if not self._args.skip_saving_faces:
                saver.save(output_filename, image)
            final_faces.append(face.to_alignment())
        self._alignments.data[os.path.basename(extract_media.filename)] = dict(faces=final_faces)
        del extract_media
Пример #3
0
    def _background_extract(self, output_folder, progress_queue):
        """ Perform the background extraction in a thread so GUI doesn't become unresponsive.

        Parameters
        ----------
        output_folder: str
            The location to save the output faces to
        progress_queue: :class:`queue.Queue`
            The queue to place incremental counts to for updating the GUI's progress bar
        """
        _io = dict(saver=ImagesSaver(str(get_folder(output_folder)), as_bytes=True),
                   loader=ImagesLoader(self._input_location, count=self._alignments.frames_count))

        for frame_idx, (filename, image) in enumerate(_io["loader"].load()):
            logger.trace("Outputting frame: %s: %s", frame_idx, filename)
            src_filename = os.path.basename(filename)
            frame_name = os.path.splitext(src_filename)[0]
            progress_queue.put(1)

            for face_idx, face in enumerate(self._frame_faces[frame_idx]):
                output = "{}_{}{}".format(frame_name, str(face_idx), ".png")
                aligned = AlignedFace(face.landmarks_xy,
                                      image=image,
                                      centering="head",
                                      size=512)  # TODO user selectable size
                meta = dict(alignments=face.to_png_meta(),
                            source=dict(alignments_version=self._alignments.version,
                                        original_filename=output,
                                        face_index=face_idx,
                                        source_filename=src_filename,
                                        source_is_video=self._globals.is_video))

                b_image = encode_image(aligned.face, ".png", metadata=meta)
                _io["saver"].save(output, b_image)
        _io["saver"].close()
Пример #4
0
    def _output_faces(self, filename, image):
        """ For each frame save out the faces

        Parameters
        ----------
        filename: str
            The filename (without the full path) of the current frame
        image: :class:`numpy.ndarray`
            The full frame that faces are to be extracted from

        Returns
        -------
        int
            The total number of faces that have been extracted
        """
        logger.trace("Outputting frame: %s", filename)
        face_count = 0
        frame_name = os.path.splitext(filename)[0]
        faces = self._select_valid_faces(filename, image)
        if not faces:
            return face_count
        if self._is_legacy:
            faces = self._process_legacy(filename, image, faces)

        for idx, face in enumerate(faces):
            output = "{}_{}.png".format(frame_name, str(idx))
            meta = dict(alignments=face.to_png_meta(),
                        source=dict(
                            alignments_version=self._alignments.version,
                            original_filename=output,
                            face_index=idx,
                            source_filename=filename,
                            source_is_video=self._frames.is_video,
                            source_frame_dims=image.shape[:2]))
            self._saver.save(
                output, encode_image(face.aligned.face, ".png", metadata=meta))
            if not self._arguments.large and self._is_legacy:
                face.thumbnail = generate_thumbnail(face.aligned.face,
                                                    size=96,
                                                    quality=60)
                self._alignments.data[filename]["faces"][
                    idx] = face.to_alignment()
            face_count += 1
        self._saver.close()
        return face_count
Пример #5
0
def update_legacy_png_header(filename, alignments):
    """ Update a legacy extracted face from pre v2.1 alignments by placing the alignment data for
    the face in the png exif header for the given filename with the given alignment data.

    If the given file is not a .png then a png is created and the original file is removed

    Parameters
    ----------
    filename: str
        The image file to update
    alignments: :class:`lib.align.alignments.Alignments`
        The alignments data the contains the information to store in the image header. This must be
        a v2.0 or less alignments file as later versions no longer store the face hash (unrequired)

    Returns
    -------
    dict
        The metadata that has been applied to the given image
    """
    if alignments.version > 2.0:
        raise FaceswapError(
            "The faces being passed in do not correspond to the given Alignments "
            "file. Please double check your sources and try again.")
    # Track hashes for multiple files with the same hash. Not the most robust but should be
    # effective enough
    folder = os.path.dirname(filename)
    if folder not in _HASHES_SEEN:
        _HASHES_SEEN[folder] = dict()
    hashes_seen = _HASHES_SEEN[folder]

    in_image = read_image(filename, raise_error=True)
    in_hash = sha1(in_image).hexdigest()
    hashes_seen[in_hash] = hashes_seen.get(in_hash, -1) + 1

    alignment = alignments.hashes_to_alignment.get(in_hash)
    if not alignment:
        logger.debug("Alignments not found for image: '%s'", filename)
        return None

    detected_face = DetectedFace()
    detected_face.from_alignment(alignment)
    # For dupe hash handling, make sure we get a different filename for repeat hashes
    src_fname, face_idx = list(
        alignments.hashes_to_frame[in_hash].items())[hashes_seen[in_hash]]
    orig_filename = "{}_{}.png".format(
        os.path.splitext(src_fname)[0], face_idx)
    meta = dict(alignments=detected_face.to_png_meta(),
                source=dict(alignments_version=alignments.version,
                            original_filename=orig_filename,
                            face_index=face_idx,
                            source_filename=src_fname,
                            source_is_video=False))  # Can't check so set false

    out_filename = f"{os.path.splitext(filename)[0]}.png"  # Make sure saved file is png
    out_image = encode_image(in_image, ".png", metadata=meta)

    with open(out_filename, "wb") as out_file:
        out_file.write(out_image)

    if filename != out_filename:  # Remove the old non-png:
        logger.debug("Removing replaced face with deprecated extension: '%s'",
                     filename)
        os.remove(filename)

    return meta