          extractor.set_batchsize("detector", 1)
          extractor.set_batchsize("aligner", 1)
          extractor.launch()
          logger.debug("Initialized Extractor")
  
      def on_event(self, event, x, y, flags, param):  # pylint: disable=unused-argument,invalid-name
          """ Handle the mouse events """
          # pylint: disable=no-member
          if self.interface.get_edit_mode() != "Edit":
 
</s> remove         out_queue = queue_manager.get_queue("out")

        d_kwargs = {"in_queue": queue_manager.get_queue("in"),
                    "out_queue": queue_manager.get_queue("align")}
        a_kwargs = {"in_queue": queue_manager.get_queue("align"),
                    "out_queue": out_queue}

        detector = PluginLoader.get_detector("manual")(loglevel=loglevel)
        detect_process = SpawnProcess(detector.run, **d_kwargs)
        d_event = detect_process.event
        detect_process.start()

        plugins = ["fan_amd"] if get_backend() == "amd" else ["fan"]
        plugins.append("cv2_dnn")
        for plugin in plugins:
            aligner = PluginLoader.get_aligner(plugin)(loglevel=loglevel,
                                                       normalize_method="hist")
            align_process = SpawnProcess(aligner.run, **a_kwargs)
            a_event = align_process.event
            align_process.start()

            # Wait for Aligner to initialize
            # The first ever load of the model for FAN has reportedly taken
            # up to 3-4 minutes, hence high timeout.
            a_event.wait(300)
            if not a_event.is_set():
                if plugin.startswith("fan"):
                    align_process.join()
                    logger.error("Error initializing FAN. Trying CV2-DNN")
                    continue
                else:
                    raise ValueError("Error inititalizing Aligner")
            if plugin == "cv2_dnn":
                break

            try:
                err = None
                err = out_queue.get(True, 1)
            except QueueEmpty:
                pass
            if not err:
                break
            align_process.join()
            logger.error("Error initializing FAN. Trying CV2-DNN")

        d_event.wait(10)
        if not d_event.is_set():
            raise ValueError("Error inititalizing Detector")

        self.extractor["detect"] = detector
        self.extractor["align"] = aligner
 </s> add         extractor = Extractor("manual", "fan", multiprocess=True, normalize_method="hist")
        self.queues["in"] = extractor.input_queue
        # Set the batchsizes to 1
        extractor.set_batchsize("detector", 1)
        extractor.set_batchsize("aligner", 1)
        extractor.launch() </s> remove     def scale_image(image, scale, pad_to=None):
 </s> add     def _scale_image(image, image_size, scale): </s> remove         self.model = None

    def initialize(self, *args, **kwargs):
        """ Initialization tasks to run prior to alignments """
        try:
            super().initialize(*args, **kwargs)
            logger.info("Initializing cv2 DNN Aligner...")
            logger.debug("cv2 DNN initialize: (args: %s kwargs: %s)", args, kwargs)
            logger.verbose("Using CPU for alignment")

            self.model = cv2.dnn.readNetFromTensorflow(  # pylint: disable=no-member
                self.model_path)
            self.model.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)  # pylint: disable=no-member
            self.init.set()
            logger.info("Initialized cv2 DNN Aligner.")
        except Exception as err:
            self.error.set()
            raise err

    def align_image(self, detected_face, image):
 </s> add         self.batchsize = 1

    def init_model(self):
        """ Initialize CV2 DNN Detector Model"""
        self.model = cv2.dnn.readNetFromTensorflow(self.model_path)  # pylint: disable=no-member
        self.model.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)  # pylint: disable=no-member

    def process_input(self, batch):
        """ Compile the detected faces for prediction """
        faces, batch["roi"] = self.align_image(batch["detected_faces"])
        faces = self._normalize_faces(faces)
        batch["feed"] = np.array(faces, dtype="float32").transpose((0, 3, 1, 2))
        return batch

    def align_image(self, detected_faces): </s> remove     def load_graph(self):
        """ Load the tensorflow Model and weights """
        # pylint: disable=not-context-manager
        logger.verbose("Initializing S3FD Network model...")
        with self.tf.gfile.GFile(self.model_path, "rb") as gfile:
            graph_def = self.tf.GraphDef()
            graph_def.ParseFromString(gfile.read())
        fa_graph = self.tf.Graph()
        with fa_graph.as_default():
            self.tf.import_graph_def(graph_def, name="s3fd")
        return fa_graph

    def get_outputs(self):
        """ Return the output tensors """
        tensor_names = ["concat_31", "transpose_72", "transpose_75", "transpose_78",
                        "transpose_81", "transpose_84", "transpose_87", "transpose_90",
                        "transpose_93", "transpose_96", "transpose_99", "transpose_102"]
        logger.debug("tensor_names: %s", tensor_names)
        tensors = [self.graph.get_tensor_by_name("s3fd/{}:0".format(t_name))
                   for t_name in tensor_names]
        logger.debug("tensors: %s", tensors)
        return tensors

    def set_session(self, target_size, vram_ratio, card_id):
        """ Set the TF Session and initialize """
        # pylint: disable=not-context-manager, no-member
        placeholder = np.zeros((1, 3, target_size[0], target_size[1]))
        config = self.tf.ConfigProto()
        if card_id != -1:
            config.gpu_options.visible_device_list = str(card_id)
        if vram_ratio != 1.0:
            config.gpu_options.per_process_gpu_memory_fraction = vram_ratio

        with self.graph.as_default():
            session = self.tf.Session(config=config)
            self.is_gpu = any("gpu" in str(device).lower() for device in session.list_devices())
            session.run(self.output, feed_dict={self.input: placeholder})
        return session

    def detect_face(self, feed_item):
        """ Detect faces """
        feed_item = feed_item - np.array([104.0, 117.0, 123.0])
        feed_item = feed_item.transpose(2, 0, 1)
        feed_item = feed_item.reshape((1,) + feed_item.shape).astype('float32')
        bboxlist = self.session.run(self.output, feed_dict={self.input: feed_item})
        bboxlist = self.post_process(bboxlist)

        keep = self.nms(bboxlist, 0.3)
        bboxlist = bboxlist[keep, :]
        bboxlist = [x for x in bboxlist if x[-1] >= self.confidence]
 </s> add     @staticmethod
    def prepare_batch(batch):
        """ Prepare a batch for prediction """
        batch = batch - np.array([104.0, 117.0, 123.0])
        batch = batch.transpose(0, 3, 1, 2)
        return batch </s> remove     def init_extractor(self, loglevel):
 </s> add     def init_extractor(self):
        return extractor
          extractor.set_batchsize("detector", 1)
          extractor.set_batchsize("aligner", 1)
          extractor.launch()
          logger.debug("Initialized Extractor")
        return extractor
  
      def on_event(self, event, x, y, flags, param):  # pylint: disable=unused-argument,invalid-name
          """ Handle the mouse events """
          # pylint: disable=no-member
          if self.interface.get_edit_mode() != "Edit":
keep keep keep add 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