      def draw_landmarks(self, color_id=3, radius=1):
          """ Draw the facial landmarks """
          color = self.colors[color_id]
          for alignment in self.alignments:
              landmarks = alignment["landmarksXY"]
              logger.trace("Drawing Landmarks: (landmarks: %s, color: %s, radius: %s)",
                           landmarks, color, radius)
              for (pos_x, pos_y) in landmarks:
                  cv2.circle(self.image,  # pylint: disable=no-member
                             (pos_x, pos_y),
 
</s> remove             landmarks = alignment["landmarksXY"]
 </s> add             landmarks = alignment["landmarks_xy"] </s> remove         landmarks = [{"landmarksXY": face.aligned_landmarks}
 </s> add         landmarks = [{"landmarks_xy": face.aligned_landmarks} </s> remove         queue_manager.get_queue("in").put({"image": self.media["image"],
                                           "filename": self.media["frame_id"],
                                           "face": self.media["bounding_box"]})
        landmarks = queue_manager.get_queue("out").get()

        if isinstance(landmarks, dict) and landmarks.get("exception"):
            cv2.destroyAllWindows()  # pylint: disable=no-member
            pid = landmarks["exception"][0]
            t_back = landmarks["exception"][1].getvalue()
            err = "Error in child process {}. {}".format(pid, t_back)
            raise Exception(err)
        if landmarks == "EOF":
            exit(0)

        alignment = self.extracted_to_alignment((landmarks["detected_faces"][0],
                                                 landmarks["landmarks"][0]))
 </s> add         self.queues["in"].put({"image": self.media["image"],
                               "filename": self.media["frame_id"],
                               "manual_face": self.media["bounding_box"]})
        detected_face = next(self.extractor.detected_faces())["detected_faces"][0]
        alignment = detected_face.to_alignment()
 </s> remove             cvt_image = image.copy()
        return cvt_image

    def process_landmarks(self, image, detected_faces):
        """ Align image and process landmarks """
        logger.trace("Processing landmarks")
        retval = list()
        for detected_face in detected_faces:
            feed_dict = self.align_image(detected_face, image)
            self.normalize_face(feed_dict)
            landmarks = self.predict_landmarks(feed_dict)
            retval.append(landmarks)
        logger.trace("Processed landmarks: %s", retval)
        return retval
 </s> add             logger.trace(item)
        return exhausted, batch

    def _collect_item(self, queue):
        """ Collect the item from the _rollover dict or from the queue
            Add face count per frame to self._faces_per_filename for joining
            batches back up in finalize """
        if self._rollover:
            logger.trace("Getting from _rollover: (filename: `%s`, faces: %s)",
                         self._rollover["filename"], len(self._rollover["detected_faces"]))
            item = self._rollover
            self._rollover = dict()
        else:
            item = self._get_item(queue)
            if item != "EOF":
                logger.trace("Getting from queue: (filename: %s, faces: %s)",
                             item["filename"], len(item["detected_faces"]))
                self._faces_per_filename[item["filename"]] = len(item["detected_faces"])
        return item

    # <<< FINALIZE METHODS >>> #
    def finalize(self, batch):
        """ Finalize the output from Aligner

        This should be called as the final task of each `plugin`.

        It strips unneeded items from the :attr:`batch` ``dict`` and pairs the detected faces back
        up with their original frame before yielding each frame.

        Outputs items in the format:

        >>> {'image': [<original frame>],
        >>>  'filename': [<frame filename>),
        >>>  'detected_faces': [<lib.faces_detect.DetectedFace objects>]}

        Parameters
        ----------
        batch : dict
            The final ``dict`` from the `plugin` process. It must contain the `keys`:
            ``detected_faces``, ``landmarks``, ``filename``, ``image``

        Yields
        ------
        dict
            A ``dict`` for each frame containing the ``image``, ``filename`` and list of
            :class:`lib.faces_detect.DetectedFace` objects.

        """

        for face, landmarks in zip(batch["detected_faces"], batch["landmarks"]):
            face.landmarks_xy = [(int(round(pt[0])), int(round(pt[1]))) for pt in landmarks]

        self._remove_invalid_keys(batch, ("detected_faces", "filename", "image"))
        logger.trace("Item out: %s", {key: val
                                      for key, val in batch.items()
                                      if key != "image"})
        for filename, image, face in zip(batch["filename"],
                                         batch["image"],
                                         batch["detected_faces"]):
            self._output_faces.append(face)
            if len(self._output_faces) != self._faces_per_filename[filename]:
                continue
            retval = dict(filename=filename, image=image, detected_faces=self._output_faces)

            self._output_faces = []
            logger.trace("Yielding: (filename: '%s', image: %s, detected_faces: %s)",
                         retval["filename"], retval["image"].shape, len(retval["detected_faces"]))
            yield retval

    # <<< PROTECTED METHODS >>> #
    # <<< PREDICT WRAPPER >>> #
    def _predict(self, batch):
        """ Just return the aligner's predict function """
        return self.predict(batch) </s> remove 
    @staticmethod
    def extracted_to_alignment(extract_data):
        """ Convert Extracted Tuple to Alignments data """
        alignment = dict()
        bbox, landmarks = extract_data
        alignment["x"] = bbox["left"]
        alignment["w"] = bbox["right"] - bbox["left"]
        alignment["y"] = bbox["top"]
        alignment["h"] = bbox["bottom"] - bbox["top"]
        alignment["landmarksXY"] = landmarks
        return alignment
 </s> add 
            landmarks = alignment["landmarks_xy"]
      def draw_landmarks(self, color_id=3, radius=1):
          """ Draw the facial landmarks """
          color = self.colors[color_id]
          for alignment in self.alignments:
            landmarks = alignment["landmarks_xy"]
              logger.trace("Drawing Landmarks: (landmarks: %s, color: %s, radius: %s)",
                           landmarks, color, radius)
              for (pos_x, pos_y) in landmarks:
                  cv2.circle(self.image,  # pylint: disable=no-member
                             (pos_x, pos_y),
keep keep keep keep replace keep keep keep keep keep
 De-Multiprocess Extract (#871)

* requirements.txt: - Pin opencv to 4.1.1 (fixes cv2-dnn error)

* lib.face_detect.DetectedFace: change LandmarksXY to landmarks_xy. Add left, right, top, bottom attributes

* lib.model.session: Session manager for loading models into different graphs (for Nvidia + CPU)

* plugins.extract._base: New parent class for all extract plugins

* plugins.extract.pipeline. Remove MultiProcessing. Dynamically limit batchsize for Nvidia cards. Remove loglevel input

* S3FD + FAN plugins. Standardise to Keras version for all backends

* Standardize all extract plugins to new threaded codebase

* Documentation. Start implementing Numpy style docstrings for Sphinx Documentation

* Remove s3fd_amd. Change convert OTF to expect DetectedFace object

* faces_detect - clean up and documentation

* Remove PoolProcess

* Migrate manual tool to new extract workflow

* Remove AMD specific extractor code from cli and plugins

* Sort tool to new extract workflow

* Remove multiprocessing from project

* Remove multiprocessing queues from QueueManager

* Remove multiprocessing support from logger

* Move face_filter to new extraction pipeline

* Alignments landmarksXY > landmarks_xy and legacy handling

* Intercept get_backend for sphinx doc build

# Add Sphinx documentation 
tools/lib_alignments/annotate.py