              queue = args[0]
          for filename, image, hsh in tqdm(self._loader.load(), total=self._loader.count):
              if hsh not in self._alignments.hashes_to_frame:
                  self._counts["skip"] += 1
                  logger.warning("Skipping face not in alignments file: '%s'", filename)
                  continue
  
              frames = self._alignments.hashes_to_frame[hsh]
              if len(frames) > 1:
                  # Filter the output by filename in case of multiple frames with the same face
 
</s> remove         for filename, image, hsh in tqdm(self._loader.load(), total=self._loader.count):
            if hsh not in self._alignments.hashes_to_frame:
 </s> add         for filename, image, metadata in tqdm(self._loader.load(), total=self._loader.count):
            if not metadata:  # Legacy faces. Update the headers
                if not log_once:
                    logger.warning("Legacy faces discovered. These faces will be updated")
                    log_once = True
                metadata = update_legacy_png_header(filename, self._alignments)
                if not metadata:
                    # Face not found
                    self._counts["skip"] += 1
                    logger.warning("Legacy face not found in alignments file. This face has not "
                                   "been updated: '%s'", filename)
                    continue

            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: </s> remove             frames = self._alignments.hashes_to_frame[hsh]
            if len(frames) > 1:
                # Filter the output by filename in case of multiple frames with the same face
                logger.debug("Filtering multiple hashes to current filename: (filename: '%s', "
                             "frames: %s", filename, frames)
                lookup = os.path.splitext(os.path.basename(filename))[0]
                frames = {k: v
                          for k, v in frames.items()
                          if lookup.startswith(os.path.splitext(k)[0])}
                logger.debug("Filtered: (filename: '%s', frame: '%s')", filename, frames)

            for frame, idx in frames.items():
                self._counts["face"] += 1
                alignment = self._alignments.get_faces_in_frame(frame)[idx]
                if self._check_for_missing(frame, idx, alignment):
                    continue
                detected_face = self._get_detected_face(alignment)
                if self._update_type == "output":
                    detected_face.image = image
                    self._save(frame, idx, detected_face)
                else:
                    queue.put(ExtractMedia(filename, image, detected_faces=[detected_face]))
                    self._counts["update"] += 1
 </s> add             if self._check_for_missing(frame_name, face_index, alignment):
                continue

            detected_face = self._get_detected_face(alignment)
            if self._update_type == "output":
                detected_face.image = image
                self._save(frame_name, face_index, detected_face)
            else:
                media = ExtractMedia(filename, image, detected_faces=[detected_face])
                setattr(media, "mask_tool_face_info", metadata["source"])  # TODO formalize
                queue.put(media)
                self._counts["update"] += 1 </s> remove         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

 </s> add         for filename, image, metadata in tqdm(self._loader.load(),
                                              desc="Classifying Faces...",
                                              total=self._loader.count,
                                              leave=False):
            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["alignments"]
            face = AlignedFace(np.array(alignments["landmarks_xy"], dtype="float32"),
                               image=image,
                               centering="legacy",
                               size=self._vgg_face.input_size,
                               is_aligned=True).face </s> remove 

class UpdateHashes():
    """ Update hashes in an alignments file """
    def __init__(self, alignments, arguments):
        logger.debug("Initializing %s: (arguments: %s)", self.__class__.__name__, arguments)
        self._alignments = alignments
        self.faces = Faces(arguments.faces_dir).file_list_sorted
        self.face_hashes = dict()
        logger.debug("Initialized %s", self.__class__.__name__)

    def process(self):
        """ Update Face Hashes to the alignments file """
        logger.info("[UPDATE FACE HASHES]")  # Tidy up cli output
        self.get_hashes()
        updated = self.update_hashes()
        if updated == 0:
            logger.info("No hashes were updated. Exiting")
            return
        self._alignments.save()
        logger.info("%s frame(s) had their face hashes updated.", updated)

    def get_hashes(self):
        """ Read the face hashes from the faces """
        logger.info("Getting original filenames, indexes and hashes...")
        for face in self.faces:
            filename = face["face_name"]
            extension = face["face_extension"]
            if "_" not in face["face_name"]:
                logger.warning("Unable to determine index of file. Skipping: '%s'", filename)
                continue
            index = filename[filename.rfind("_") + 1:]
            if not index.isdigit():
                logger.warning("Unable to determine index of file. Skipping: '%s'", filename)
                continue
            orig_frame = filename[:filename.rfind("_")] + extension
            self.face_hashes.setdefault(orig_frame, dict())[int(index)] = face["face_hash"]

    def update_hashes(self):
        """ Update hashes to alignments """
        logger.info("Updating hashes to alignments...")
        updated = 0
        for frame, hashes in self.face_hashes.items():
            if not self._alignments.frame_exists(frame):
                logger.warning("Frame not found in alignments file. Skipping: '%s'", frame)
                continue
            if not self._alignments.frame_has_faces(frame):
                logger.warning("Frame does not have faces. Skipping: '%s'", frame)
                continue
            existing = [face.get("hash", None)
                        for face in self._alignments.get_faces_in_frame(frame)]
            if any(hsh not in existing for hsh in list(hashes.values())):
                self._alignments.add_face_hashes(frame, hashes)
                updated += 1
        return updated
 </s> add 
                logger.warning("Skipping Face not found in alignments file. skipping: '%s'",
                               filename)
              queue = args[0]
          for filename, image, hsh in tqdm(self._loader.load(), total=self._loader.count):
              if hsh not in self._alignments.hashes_to_frame:
                  self._counts["skip"] += 1
                logger.warning("Skipping Face not found in alignments file. skipping: '%s'",
                               filename)
                  continue
  
              frames = self._alignments.hashes_to_frame[hsh]
              if len(frames) > 1:
                  # Filter the output by filename in case of multiple frames with the same face
keep keep keep keep replace keep keep keep keep keep
 Update extracted faces to use PNG EXIF data (#1123)

Documentation
  - Update Usage.md, align.rst and image.rst
lib.image.py
  - read_image - Remove hash return, add metadata return
  - Remove read_image_hash functions
  - Add read_image_meta functios
  - Replace encode_image_with_hash with encode_image (to store metadata)
  - Add png meta reading and writing functions
  - Update Image Loaders/Savers to handle metadata rather than hashes
lib.training_data
  - Naming updates to remove references to hashes
lib.align.Alignments
  - Add versioning notes
  - Increment alignments version to 2.1
  - Deprecate hashing lookup functions
  - Replace filter_hashes with filter_faces
lib.align.detected_face
  - DetectedFace
    - Remove hash property
    - Add png header data serializing/deserializing functions
  - Mask
    - Add png header data serializing/deserializing functions
  - add update_legacy_png_header function to update png meta data
lib.cli.args - Deprecate alignments files for training
- plugins.train.trainer
  - Update alignments/mask code to read png header data
- scripts.convert
  - Aligned images folder - read data from png headers
- scripts.extract
  - Write png header information and no longer store hash of face
- tools.alignments
  - remove leftover-faces, merge and update-hashes jobs
  - Update jobs to use png meta data rather than hashes
- tools.manual
  - Update extract code to output png meta data and don't store hashes
  - Perform check on launch that tool is not pointing at a faces folder
tools.mask
  - Update to use png meta data
tools.sort
  - Update to use png meta data 
tools/mask/mask.py