              new_images.append(cv2.resize(new_img, dsize=sizes, interpolation=interp))
          logger.trace("Cropped images")
          return new_images
  
      @staticmethod
      def transform(points, center_scales, resolutions):
          """ Transform Image """
          logger.debug("Transforming Points")
          num_images, num_landmarks = points.shape[:2]
          transform_matrix = np.eye(3, dtype='float32')
          transform_matrix = np.repeat(transform_matrix[None, :], num_landmarks, axis=0)
          transform_matrix = np.repeat(transform_matrix[None, :, :], num_images, axis=0)
 
</s> remove         new_images = []
        for image, top_left, bottom_right in zip(batch["image"], upper_left, bot_right):
            height, width = image.shape[:2]
            channels = 3 if image.ndim > 2 else 1
            bottom_right_width, bottom_right_height = bottom_right[0].astype('int32')
            top_left_width, top_left_height = top_left[0].astype('int32')
            new_dim = (bottom_right_height - top_left_height,
                       bottom_right_width - top_left_width,
                       channels)
            new_img = np.empty(new_dim, dtype=np.uint8)

            new_x = slice(max(0, -top_left_width), min(bottom_right_width, width) - top_left_width)
            new_y = slice(max(0, -top_left_height),
                          min(bottom_right_height, height) - top_left_height)
            old_x = slice(max(0, top_left_width), min(bottom_right_width, width))
            old_y = slice(max(0, top_left_height), min(bottom_right_height, height))
            new_img[new_y, new_x] = image[old_y, old_x]

            interp = cv2.INTER_CUBIC if new_dim[0] < self.input_size else cv2.INTER_AREA
            new_images.append(cv2.resize(new_img, dsize=sizes, interpolation=interp))
        logger.trace("Cropped images")
 </s> add         new_images = [self._crop_image(image, top_left, bottom_right)
                      for image, top_left, bottom_right in zip(batch.image, upper_left, bot_right)]
        logger.trace("Cropped images")  # type:ignore </s> remove     def get_pts_from_predict(self, batch):
        """ Get points from predictor """
 </s> add     def get_pts_from_predict(self, batch: AlignerBatch):
        """ Get points from predictor and populate the :attr:`landmarks` property of the
        :class:`AlignerBatch`

        Parameters
        ----------
        batch: :class:`AlignerBatch`
            The current batch from the model with :attr:`predictions` populated
        """ </s> remove         logger.trace("Calculated center and scale: %s", center_scale)
 </s> add         logger.trace("Calculated center and scale: %s", center_scale)  # type:ignore </s> remove             landmarks.append(subbatch["landmarks"])
        batch["landmarks"] = np.average(landmarks, axis=0)
 </s> add             landmarks.append(subbatch.landmarks)
        batch.landmarks = np.average(landmarks, axis=0) </s> remove         sizes = (self.input_size, self.input_size)
        batch_shape = batch["center_scale"].shape[:2]
 </s> add         batch_shape = center_scale.shape[:2]
    @classmethod
    def transform(cls,
                  points: np.ndarray,
                  center_scales: np.ndarray,
                  resolutions: np.ndarray) -> np.ndarray:
        """ Transform Image

        Parameters
        ----------
        points: :class:`numpy.ndarray`
            The points to transform
        center_scales: :class:`numpy.ndarray`
            The calculated centers and scales for the batch
        resolutions: :class:`numpy.ndarray`
            The resolutions
        """
              new_images.append(cv2.resize(new_img, dsize=sizes, interpolation=interp))
          logger.trace("Cropped images")
          return new_images
  
    @classmethod
    def transform(cls,
                  points: np.ndarray,
                  center_scales: np.ndarray,
                  resolutions: np.ndarray) -> np.ndarray:
        """ Transform Image

        Parameters
        ----------
        points: :class:`numpy.ndarray`
            The points to transform
        center_scales: :class:`numpy.ndarray`
            The calculated centers and scales for the batch
        resolutions: :class:`numpy.ndarray`
            The resolutions
        """
    @classmethod
    def transform(cls,
                  points: np.ndarray,
                  center_scales: np.ndarray,
                  resolutions: np.ndarray) -> np.ndarray:
        """ Transform Image

        Parameters
        ----------
        points: :class:`numpy.ndarray`
            The points to transform
        center_scales: :class:`numpy.ndarray`
            The calculated centers and scales for the batch
        resolutions: :class:`numpy.ndarray`
            The resolutions
        """
    @classmethod
    def transform(cls,
                  points: np.ndarray,
                  center_scales: np.ndarray,
                  resolutions: np.ndarray) -> np.ndarray:
        """ Transform Image

        Parameters
        ----------
        points: :class:`numpy.ndarray`
            The points to transform
        center_scales: :class:`numpy.ndarray`
            The calculated centers and scales for the batch
        resolutions: :class:`numpy.ndarray`
            The resolutions
        """
          logger.debug("Transforming Points")
          num_images, num_landmarks = points.shape[:2]
          transform_matrix = np.eye(3, dtype='float32')
          transform_matrix = np.repeat(transform_matrix[None, :], num_landmarks, axis=0)
          transform_matrix = np.repeat(transform_matrix[None, :, :], num_images, axis=0)
keep keep keep keep replace replace replace keep keep keep keep keep
 Extract updates:
  - Default CPU detector to MTCNN
  - add basic Aligner false positive filters
  - Typing: align + plugins
  - Use specific AlignerBatch class for alignment
  - 
plugins/extract/align/fan.py