  
          self._aligned_faces = self._get_aligned_faces(sizes)
          logger.debug("Initialized %s", self.__class__.__name__)
  
      @property
      def aligned_faces(self):
          """ dict: The "a", "b" keys for each side, containing a sub-dictionary with the
          filename as key and :class:`lib.faces.detected_face.aligned` object as value. """
          return self._aligned_faces
  
      @property
      def versions(self):
          """ dict: The "a", "b" keys for each side, with value being the alignment file version
          that provided the data. This is used to crop the faces correctly based on whether the
          extracted faces are legacy or full-head extracts. """
 
</s> remove     def _get_alignments_paths(self):
        """ Obtain the alignments file paths from the command line arguments passed to the model.
 </s> add     @property
    def aligned_faces(self):
        """ dict: The "a", "b" keys for each side, containing a sub-dictionary with the
        filename as key and :class:`lib.align.AlignedFace` object as value. """
        return self._aligned_faces

    # <<< LOAD DETECTED FACE INFORMATION FROM PNG HEADER >>>
    def _load_detected_faces(self, image_list):
        """ Obtain the metadata from the png training image headers for all images used for
        training. </s> remove         self._load_alignments()
        self._check_all_faces()

        self._aligned_faces = self._get_aligned_faces(sizes)
 </s> add         self._validity_check()
        self._aligned_faces = self._get_aligned_faces() </s> remove             filename as key and :class:`lib.faces.detected_face.AlignedFace` object as value.
 </s> add             filename as key and :class:`lib.align.AlignedFace` object as value. </s> remove             The alignments paths for each of the source and destination faces. Key is the
            side, value is the path to the alignments file
 </s> add             For keys "a" and "b" the values are a ``dict`` with the key being the filename of the
            training image and the value being the :class:`lib.align.DetectedFace` object
        """
        metadata = dict()
        for side, filelist in image_list.items():
            meta_side = dict()
            logger.debug("side: %s, file count: %s", side, len(filelist))
            for filename, meta in tqdm(read_image_meta_batch(filelist),
                                       desc="Reading training images ({})".format(side.upper()),
                                       total=len(filelist),
                                       leave=False):

                self._validate_image_size(side, filename, meta["width"], meta["height"])

                if "itxt" not in meta or "alignments" not in meta["itxt"]:
                    meta_side[filename] = None
                else:
                    alignments_version = meta["itxt"]["source"]["alignments_version"]
                    self._alignments_version.setdefault(side, set()).add(alignments_version)
                    detected_face = DetectedFace()
                    detected_face.from_png_meta(meta["itxt"]["alignments"])
                    meta_side[filename] = detected_face
            metadata[side] = meta_side
        return metadata

    def _validate_image_size(self, side, filename, width, height):
        """ Validate that the images are square and that the sizes for all image in a side are
        the same.

        Parameters
        ----------
        side: ["a" or "b"]
            The training side that is being processed
        filename: str
            The filename of the image that is being validated
        width: int
            The width of the image to be validated
        height: int
            The height of the image to be validated
  
          self._aligned_faces = self._get_aligned_faces(sizes)
          logger.debug("Initialized %s", self.__class__.__name__)
  






      @property
      def versions(self):
          """ dict: The "a", "b" keys for each side, with value being the alignment file version
          that provided the data. This is used to crop the faces correctly based on whether the
          extracted faces are legacy or full-head extracts. """
keep keep keep keep replace replace replace replace replace 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 
plugins/train/trainer/_base.py