          logger.trace("Saving face: '%s'", filename)
          with open(filename, "wb") as out_file:
              out_file.write(img)
          return f_hash
  
      @staticmethod
      def align_eyes(face, image):
          """ Re-extract a face with the pupils forced to be absolutely horizontally aligned """
          umeyama_landmarks = face.aligned_landmarks
          left_eye_center = umeyama_landmarks[42:48].mean(axis=0)
          right_eye_center = umeyama_landmarks[36:42].mean(axis=0)
          d_y = right_eye_center[1] - left_eye_center[1]
          d_x = right_eye_center[0] - left_eye_center[0]
          theta = np.pi - np.arctan2(d_y, d_x)
          rot_cos = np.cos(theta)
          rot_sin = np.sin(theta)
          rotation_matrix = np.array([[rot_cos, -rot_sin, 0.],
                                      [rot_sin, rot_cos, 0.],
                                      [0., 0., 1.]])
  
          mat_umeyama = np.concatenate((face.aligned["matrix"], np.array([[0., 0., 1.]])), axis=0)
          corrected_mat = np.dot(rotation_matrix, mat_umeyama)
          face.aligned["matrix"] = corrected_mat[:2]
          face.aligned["face"] = AlignerExtract().transform(image,
                                                            face.aligned["matrix"],
                                                            face.aligned["size"],
                                                            int(face.aligned["size"] * 0.375) // 2)
          logger.trace("Adjusted matrix: %s", face.aligned["matrix"])
          return face
 
</s> remove         logger.debug("full_size: %s, target_size: %s, color: %s",
                     images.shape[1], target_size, color)
        new_images = list()
        full_size = images.shape[1]
        padding = (full_size - target_size) // 2
        length = target_size // 4
        t_l, b_r = (padding, full_size - padding)
 </s> add         logger.debug("full_size: %s, prediction_size: %s, color: %s",
                     images.shape[1], prediction_size, color)

        display_size = int(prediction_size * (1 + (1 - self._coverage_ratio)))
        images = self._resize_sample(side, images, display_size)  # Resize targets to display size
        padding = (display_size - prediction_size) // 2
        if padding == 0:
            logger.debug("Resized background. Shape: %s", images.shape)
            return images

        length = display_size // 4
        t_l, b_r = (padding, display_size - padding) </s> remove     @staticmethod
    def get_dfl_alignment(filename):
        """ Process the alignment of one face """
        ext = os.path.splitext(filename)[1]

        if ext.lower() in (".jpg", ".jpeg"):
            img = Image.open(filename)
            try:
                dfl_alignments = pickle.loads(img.app["APP15"])
                dfl_alignments["source_rect"] = [n.item()  # comes as non-JSONable np.int32
                                                 for n in dfl_alignments["source_rect"]]
                return dfl_alignments
            except pickle.UnpicklingError:
                return None

        with open(filename, "rb") as dfl:
            header = dfl.read(8)
            if header != b"\x89PNG\r\n\x1a\n":
                logger.error("No Valid PNG header: %s", filename)
                return None
            while True:
                chunk_start = dfl.tell()
                chunk_hdr = dfl.read(8)
                if not chunk_hdr:
                    break
                chunk_length, chunk_name = struct.unpack("!I4s", chunk_hdr)
                dfl.seek(chunk_start, os.SEEK_SET)
                if chunk_name == b"fcWp":
                    chunk = dfl.read(chunk_length + 12)
                    retval = pickle.loads(chunk[8:-4])
                    logger.trace("Loaded DFL Alignment: (filename: '%s', alignment: %s",
                                 filename, retval)
                    return retval
                dfl.seek(chunk_length+12, os.SEEK_CUR)
            logger.error("Couldn't find DFL alignments: %s", filename)
 </s> add         If annotating a folder of frames, output will be placed in a sub folder within the frames
        folder. If annotating a video, output will be a folder next to the original video. </s> remove             cv2.rectangle(img, (t_l, t_l), (t_l + length, t_l + length), color, 3)
            cv2.rectangle(img, (b_r, t_l), (b_r - length, t_l + length), color, 3)
            cv2.rectangle(img, (b_r, b_r), (b_r - length, b_r - length), color, 3)
            cv2.rectangle(img, (t_l, b_r), (t_l + length, b_r - length), color, 3)
            new_images.append(img)
        retval = np.array(new_images)
        logger.debug("Overlayed background. Shape: %s", retval.shape)
        return retval
 </s> add             cv2.rectangle(img, (t_l, t_l), (t_l + length, t_l + length), color, 2)
            cv2.rectangle(img, (b_r, t_l), (b_r - length, t_l + length), color, 2)
            cv2.rectangle(img, (b_r, b_r), (b_r - length, b_r - length), color, 2)
            cv2.rectangle(img, (t_l, b_r), (t_l + length, b_r - length), color, 2)
        logger.debug("Overlayed background. Shape: %s", images.shape)
        return images </s> remove     @staticmethod
    def convert_dfl_alignment(dfl_alignments, f_hash, alignments):
        """ Add Deep Face Lab Alignments to alignments in Faceswap format """
        sourcefile = dfl_alignments["source_filename"]
        left, top, right, bottom = dfl_alignments["source_rect"]
        alignment = {"x": left,
                     "w": right - left,
                     "y": top,
                     "h": bottom - top,
                     "hash": f_hash,
                     "landmarks_xy": np.array(dfl_alignments["source_landmarks"], dtype="float32")}
        logger.trace("Adding alignment: (frame: '%s', alignment: %s", sourcefile, alignment)
        alignments.setdefault(sourcefile, dict()).setdefault("faces", []).append(alignment)


class Draw():
    """ Draw Alignments on passed in images """
    def __init__(self, alignments, arguments):
        logger.debug("Initializing %s: (arguments: %s)", self.__class__.__name__, arguments)
        self.arguments = arguments
        self.alignments = alignments
        self.frames = Frames(arguments.frames_dir)
        self.output_folder = self.set_output()
        self.extracted_faces = None
        logger.debug("Initialized %s", self.__class__.__name__)
 </s> add         Returns
        -------
        str
            Full path to the output folder </s> remove         preds = np.empty((self._loader.count, 512), dtype="float32")
        for idx, (filename, image) in enumerate(tqdm(self._loader.load(),
                                                     desc="Classifying Faces...",
                                                     total=self._loader.count)):
 </s> add         preds = []
        no_hash = 0
        for filename, image, hsh in tqdm(self._loader.load(),
                                         desc="Classifying Faces...",
                                         total=self._loader.count):
            if self._alignments is not None and self._alignments.version != 1.0:
                face = self._alignments.hashes_to_alignment.get(hsh)
                if face:
                    image = AlignedFace(face["landmarks_xy"],
                                        image=image,
                                        centering="legacy",
                                        size=self._vgg_face.input_size,
                                        is_aligned=True).face
                elif image.shape[0] != image.shape[1]:
                    logger.warning("Skipping image '%s' as it is not square (probably not a "
                                   "face)", filename)
                    continue
                else:  # Center crop the image and add count to warning count
                    center = image.shape[0] // 2
                    crop = slice(center - int(center * ratio), center + int(center * ratio))
                    image = image[crop, crop, :]
                    no_hash += 1

          logger.trace("Saving face: '%s'", filename)
          with open(filename, "wb") as out_file:
              out_file.write(img)
          return f_hash

























keep keep keep keep replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace
 Extract - Increase area and move centering (#1095)

* Extract
  - Implement aligner re-feeding
  - Add extract type to pipeline.ExtractMedia
  - Add pose annotation to debug
* Convert
  - implement centering
  - remove usage of feed and reference face properties
  - Remove distributed option from convert
  - Force update of alignments file on legacy receive
* Train
  - Resize preview image to model output size
  - Force legacy centering if centering does not exist in model's state file
  - Enable training on legacy face sets

* Alignments Tool
  - Update draw to include head/pose
  - Remove DFL drop + linting
  - Remove remove-frames job
  - remove align-eyes option
  - Update legacy masks to new extract type
  - Exit if attempting to merge version 1.0 alignments files with version 2.0 alignments files
  - Re-generate thumbnails on legacy upgrade
* Mask Tool
  - Update for new extract + bugfix full frame
* Manual Tool
  - Update to new extraction method
   - Disable legacy alignments,
   - extract box bugfix
   - extract faces - size to 512 and center on head
* Preview Tool
  - Display based on model centering
* Sort Tool
  - Use alignments for sort by face

* lib.aligner
  - Add Pose Class
  - Add AlignedFace Class
  - center _MEAN_FACE on x
  - Add meta information with versioning to alignments file
  - lib.aligner.get_align_matrix to use landmarks not face
  - Refactor aligned faces in lib.faces_detect
* lib.logger
  - larger file log padding
* lib.config
  - Fix global changeable_items
* lib.face_filter
  - Use new extracted face images
* lib.image
  - bump thumbnail default size to 96px 
tools/alignments/media.py