          self.last_move = (pt_x, pt_y)
  
      def update_landmarks(self):
          """ Update the landmarks """
          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]))
          frame = self.media["frame_id"]
  
          if self.interface.get_selected_face_id() is None:
              idx = self.alignments.add_face(frame, alignment)
              self.interface.set_state_value("edit", "selected", idx)
 
</s> remove     def detected_faces(self):
        """ Detect faces from in an image """
        logger.debug("Running Detection. Phase: '%s'", self.phase)
        # If not multiprocessing, intercept the align in queue for
        # detection phase
        out_queue = self.output_queue
        while True:
            try:
                faces = out_queue.get(True, 1)
                if faces == "EOF":
                    break
                if isinstance(faces, dict) and faces.get("exception"):
                    pid = faces["exception"][0]
                    t_back = faces["exception"][1].getvalue()
                    err = "Error in child process {}. {}".format(pid, t_back)
                    raise Exception(err)
            except QueueEmpty:
                continue

            yield faces
        for process in self.processes:
            logger.trace("Joining process: %s", process)
            process.join()
            del process
        if self.final_pass:
            # Cleanup queues
            for q_name in self.queues.keys():
                queue_manager.del_queue(q_name)
            logger.debug("Detection Complete")
        else:
            logger.debug("Switching to align phase")
            self.phase = "align"
 </s> add     def _set_extractor_batchsize(self, vram_required, vram_free):
        """ Sets the batchsize of the used plugins based on their vram and
            vram_per_batch_requirements """
        batch_required = ((self._aligner.vram_per_batch * self._aligner.batchsize) +
                          (self._detector.vram_per_batch * self._detector.batchsize))
        plugin_required = vram_required + batch_required
        if plugin_required <= vram_free:
            logger.verbose("Plugin requirements within threshold: (plugin_required: %sMB, "
                           "vram_free: %sMB)", plugin_required, vram_free)
            return
        # Hacky split across 2 plugins
        available_for_batching = (vram_free - vram_required) // 2
        self._aligner.batchsize = max(1, available_for_batching // self._aligner.vram_per_batch)
        self._detector.batchsize = max(1, available_for_batching // self._detector.vram_per_batch)
        logger.verbose("Reset batchsizes: (aligner: %s, detector: %s)",
                       self._aligner.batchsize, self._detector.batchsize)

    def _join_threads(self):
        """ Join threads for current pass """
        for plugin in self._active_plugins:
            plugin.join()

    def _check_and_raise_error(self):
        """ Check all threads for errors and raise if one occurs """
        for plugin in self._active_plugins:
            if plugin.check_and_raise_error():
                return True
        return False </s> remove         queue_manager.get_queue("in").put(Sort.alignment_dict(image))
 </s> add         feed = Sort.alignment_dict(image)
        feed["filename"] = filename
        queue_manager.get_queue("in").put(feed) </s> remove         landmarks = face["landmarks"][0]
 </s> add         landmarks = face["detected_faces"][0].landmarks_xy </s> remove         out_queue = queue_manager.get_queue("out")
        kwargs = {"in_queue": queue_manager.get_queue("in"),
                  "out_queue": out_queue}

        for plugin in ("fan", "cv2_dnn"):
            aligner = PluginLoader.get_aligner(plugin)(loglevel=self.args.loglevel)
            process = SpawnProcess(aligner.run, **kwargs)
            event = process.event
            process.start()
            # Wait for Aligner to take init
            # The first ever load of the model for FAN has reportedly taken
            # up to 3-4 minutes, hence high timeout.
            event.wait(300)

            if not event.is_set():
                if plugin == "fan":
                    process.join()
                    logger.error("Error initializing FAN. Trying CV2-DNN")
                    continue
                else:
                    raise ValueError("Error inititalizing Aligner")
            if plugin == "cv2_dnn":
                return

            try:
                err = None
                err = out_queue.get(True, 1)
            except QueueEmpty:
                pass
            if not err:
                break
            process.join()
            logger.error("Error initializing FAN. Trying CV2-DNN")
 </s> add         kwargs = dict(in_queue=queue_manager.get_queue("in"),
                      out_queue=queue_manager.get_queue("out"),
                      queue_size=8)
        aligner = PluginLoader.get_aligner("fan")(normalize_method="hist")
        aligner.batchsize = 1
        aligner.initialize(**kwargs)
        aligner.start() </s> remove             landmarks = alignment["landmarksXY"]
 </s> add             landmarks = alignment["landmarks_xy"]
        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()

          self.last_move = (pt_x, pt_y)
  
      def update_landmarks(self):
          """ Update the landmarks """
        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()

        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()

        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()

        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()

        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()

        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()

        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()

        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()

        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()

        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()

        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()

        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()

        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()

        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()

        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()

        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()

          frame = self.media["frame_id"]
  
          if self.interface.get_selected_face_id() is None:
              idx = self.alignments.add_face(frame, alignment)
              self.interface.set_state_value("edit", "selected", idx)
keep keep keep keep replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace 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/jobs_manual.py