          logger.error(msg)
          if raise_error:
              raise Exception(msg)
      logger.trace("Loaded image: '%s'. Success: %s", filename, success)
      retval = (image, sha1(image).hexdigest()) if with_hash else image
      return retval
  
  
  def read_image_batch(filenames):
      """ Load a batch of images from the given file locations.
 
</s> remove         image = cv2.imread(filename)
        if image is None:
            raise ValueError("Image is None")
 </s> add         if not with_metadata:
            retval = cv2.imread(filename)
            if retval is None:
                raise ValueError("Image is None")
        else:
            with open(filename, "rb") as infile:
                raw_file = infile.read()
                metadata = png_read_meta(raw_file)
            image = cv2.imdecode(np.frombuffer(raw_file, dtype="uint8"), cv2.IMREAD_UNCHANGED)
            retval = (image, metadata) </s> remove     img = read_image(filename, raise_error=True)
    retval = sha1(img).hexdigest()
    if output_shape:
        retval = (retval, img.shape)
    logger.trace("filename: '%s', retval: %s", filename, retval)
 </s> add     retval = dict()
    if os.path.splitext(filename)[-1] != ".png":
        raise ValueError(f"Only png files are supported for reading exif data. ({filename})")
    with open(filename, "rb") as infile:
        chunk = infile.read(8)
        if chunk != b"\x89PNG\r\n\x1a\n":
            raise ValueError(f"Invalid header found in png: {filename}")
        while True:
            chunk = infile.read(8)
            length, field = struct.unpack(">I4s", chunk)
            logger.trace("Read chunk: (chunk: %s, length: %s, field: %s", chunk, length, field)
            if not chunk or field == b"IDAT":
                break
            if field == b"IHDR":
                # Get dimensions
                chunk = infile.read(8)
                retval["width"], retval["height"] = struct.unpack(">II", chunk)
                length -= 8
            elif field == b"iTXt":
                retval["itxt"] = eval(infile.read(length).split(b"\0\0\0\0\0", 1)[-1])
                break
            infile.seek(length + 4, 1)
    logger.trace("filename: %s, metadata: %s", filename, retval) </s> remove def read_image_hash_batch(filenames, output_shape=False):
    """ Return the `sha` hash of a batch of images
 </s> add def read_image_meta_batch(filenames):
    """ Read the Faceswap metadata stored in a batch extracted faces' exif headers. </s> remove         filelist = [os.path.join(self.folder, face)
                    for face in os.listdir(self.folder)
                    if self.valid_extension(face)]
        for fullpath, face_hash in tqdm(read_image_hash_batch(filelist),
                                        total=len(filelist),
                                        desc="Reading Face Hashes"):
            filename = os.path.basename(fullpath)
            face_name, extension = os.path.splitext(filename)
            retval = {"face_fullname": filename,
                      "face_name": face_name,
                      "face_extension": extension,
                      "face_hash": face_hash}
            logger.trace(retval)
 </s> add         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) </s> remove         retval = dict()
        for side in ("a", "b"):
            alignments_path = getattr(self._args, "alignments_path_{}".format(side))
            if not alignments_path:
                image_path = getattr(self._args, "input_{}".format(side))
                alignments_path = os.path.join(image_path, "alignments.fsa")
            if not os.path.exists(alignments_path):
                raise FaceswapError("Alignments file does not exist: `{}`".format(alignments_path))
            retval[side] = alignments_path
        logger.debug("Alignments paths: %s", retval)
        return retval
 </s> add         # Add the image size to the sizes dictionary if this is the first image
        if not self._image_sizes[side]:
            self._image_sizes[side] = width

        # Validate image is square
        if width != height:
            msg = ("Training images must be created by the extraction process and must be "
                   "square.\nThe image '{}' has dimensions {}x{} so the process cannot "
                   "continue.\nThere may be more images with these issues. Please double "
                   "check your dataset".format(filename, width, height))
            raise FaceswapError(msg)
          logger.error(msg)
          if raise_error:
              raise Exception(msg)
      logger.trace("Loaded image: '%s'. Success: %s", filename, success)

      return retval
  
  
  def read_image_batch(filenames):
      """ Load a batch of images from the given file locations.
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 
lib/image.py