          converter = self.load_converter(model)
  
          batch = BackgroundGenerator(self.prepare_images(), 1)
  
          for item in batch.iterator():
              self.convert(converter, item)
  
          if self.extractor:
              queue_manager.terminate_queues()
  
          Utils.finalize(self.images.images_found,
 
</s> remove         model = self.load_model()
        converter = self.load_converter(model)

        batch = BackgroundGenerator(self.prepare_images(), 1)
 </s> add         self.load_queue = None
        self.save_queue = None
        self.load_thread = None
        self.save_thread = None
        self.init_threads()
        logger.debug("Initialized %s", self.__class__.__name__) </s> remove         if self.extractor:
            queue_manager.terminate_queues()
 </s> add     def get_frame_ranges(self):
        """ split out the frame ranges and parse out 'min' and 'max' values """
        if not self.args.frame_ranges:
            logger.debug("No frame range set")
            return None </s> remove         Utils.finalize(self.images.images_found,
                       self.faces_count,
                       self.verify_output)
 </s> add         minmax = {"min": 0,  # never any frames less than 0
                  "max": float("inf")}
        retval = [tuple(map(lambda q: minmax[q] if q in minmax.keys() else int(q), v.split("-")))
                  for v in self.args.frame_ranges]
        logger.debug("frame ranges: %s", retval)
        return retval </s> remove             Note: GAN prediction outputs a mask + an image, while other
            predicts only an image. """
        Utils.set_verbosity(self.args.loglevel)
 </s> add         Utils.finalize(self.images.images_found,
                       self.predictor.faces_count,
                       self.predictor.verify_output)
        logger.debug("Completed Conversion")

    def convert_images(self):
        """ Convert the images """
        logger.debug("Converting images")
        save_queue = queue_manager.get_queue("save")
        patch_queue = queue_manager.get_queue("patch")
        pool = PoolProcess(self.converter.process, patch_queue, save_queue,
                           processes=self.pool_processes)
        pool.start()
        while True:
            self.check_thread_error()
            if self.disk_io.completion_event.is_set():
                break
            sleep(1)
        pool.join()

        save_queue.put("EOF")
        logger.debug("Converted images")

    def check_thread_error(self):
        """ Check and raise thread errors """
        for thread in (self.predictor.thread, self.disk_io.load_thread, self.disk_io.save_thread):
            thread.check_and_raise_error()

    def patch_iterator(self, processes):
        """ Prepare the images for conversion """
        out_queue = queue_manager.get_queue("out")
        completed = 0

        while True:
            try:
                item = out_queue.get(True, 1)
            except QueueEmpty:
                self.check_thread_error()
                continue
            self.check_thread_error() </s> remove                 cv2.imwrite(filename, image)  # pylint: disable=no-member
        except Exception as err:
            logger.error("Failed to convert image: '%s'. Reason: %s", filename, err)
            raise
 </s> add             if batch:
                detected_batch = [detected_face for item in batch
                                  for detected_face in item["detected_faces"]]
                feed_faces = self.compile_feed_faces(detected_batch)
                predicted = self.predict(feed_faces)

                self.queue_out_frames(batch, predicted)

            faces_seen = 0
            batch = list()
            if item == "EOF":
                logger.debug("Load queue complete")
                break
        self.out_queue.put("EOF")

    def load_aligned(self, item):
        """ Load the feed faces and reference output faces """
        logger.trace("Loading aligned faces: '%s'", item["filename"])
        for detected_face in item["detected_faces"]:
            detected_face.load_feed_face(item["image"],
                                         size=self.input_size,
                                         coverage_ratio=self.coverage_ratio,
                                         dtype="float32")
            if self.input_size == self.output_size:
                detected_face.reference = detected_face.feed
            else:
                detected_face.load_reference_face(item["image"],
                                                  size=self.output_size,
                                                  coverage_ratio=self.coverage_ratio,
                                                  dtype="float32")
        logger.trace("Loaded aligned faces: '%s'", item["filename"])

    @staticmethod
    def compile_feed_faces(detected_faces):
        """ Compile the faces for feeding into the predictor """
        logger.trace("Compiling feed face. Batchsize: %s", len(detected_faces))
        feed_faces = np.stack([detected_face.feed_face for detected_face in detected_faces])
        logger.trace("Compiled Feed faces. Shape: %s", feed_faces.shape)
        return feed_faces

    def predict(self, feed_faces):
        """ Perform inference on the feed """
        logger.trace("Predicting: Batchsize: %s", len(feed_faces))
        feed = [feed_faces]
        if self.has_predicted_mask:
            feed.append(np.repeat(self.input_mask, feed_faces.shape[0], axis=0))
        logger.trace("Input shape(s): %s", [item.shape for item in feed])

        predicted = self.predictor(feed)
        predicted = predicted if isinstance(predicted, list) else [predicted]
        logger.trace("Output shape(s): %s", [predict.shape for predict in predicted])

        # Compile masks into alpha channel or keep raw faces
        predicted = np.concatenate(predicted, axis=-1) if len(predicted) == 2 else predicted[0]
        predicted = predicted.astype("float32")

        logger.trace("Final shape: %s", predicted.shape)
        return predicted

    def queue_out_frames(self, batch, swapped_faces):
        """ Compile the batch back to original frames and put to out_queue """
        logger.trace("Queueing out batch. Batchsize: %s", len(batch))
        pointer = 0
        for item in batch:
            num_faces = len(item["detected_faces"])
            if num_faces == 0:
                item["swapped_faces"] = np.array(list())
            else:
                item["swapped_faces"] = swapped_faces[pointer:pointer + num_faces]

            logger.trace("Putting to queue. ('%s', detected_faces: %s, swapped_faces: %s)",
                         item["filename"], len(item["detected_faces"]),
                         item["swapped_faces"].shape[0])
            self.out_queue.put(item)
            pointer += num_faces
        logger.trace("Queued out batch. Batchsize: %s", len(batch))
    @property
    def draw_transparent(self):
        """ Draw transparent is an image writer only parameter.
            Return the value here for easy access for predictor """
        return self.writer.config.get("draw_transparent", False)

    @property
    def pre_encode(self):
        """ Return the writer's pre-encoder """
        dummy = np.zeros((20, 20, 3)).astype("uint8")
        test = self.writer.pre_encode(dummy)
        retval = None if test is None else self.writer.pre_encode
        logger.debug("Writer pre_encode function: %s", retval)
        return retval

    @property
    def total_count(self):
        """ Return the total number of frames to be converted """
        if self.frame_ranges and not self.args.keep_unchanged:
            retval = sum([fr[1] - fr[0] for fr in self.frame_ranges])
        else:
            retval = self.images.images_found
        logger.debug(retval)
        return retval

    # Initalization
    def get_writer(self):
        """ Return the writer plugin """
        args = [self.args.output_dir]
        if self.args.writer in ("ffmpeg", "gif"):
            args.append(self.total_count)
        if self.args.writer == "ffmpeg":
            if self.images.is_video:
                args.append(self.args.input_dir)
            else:
                args.append(self.args.reference_video)
        logger.debug("Writer args: %s", args)
        return PluginLoader.get_converter("writer", self.args.writer)(*args)
          converter = self.load_converter(model)
  
          batch = BackgroundGenerator(self.prepare_images(), 1)
  
    @property
    def draw_transparent(self):
        """ Draw transparent is an image writer only parameter.
            Return the value here for easy access for predictor """
        return self.writer.config.get("draw_transparent", False)

    @property
    def pre_encode(self):
        """ Return the writer's pre-encoder """
        dummy = np.zeros((20, 20, 3)).astype("uint8")
        test = self.writer.pre_encode(dummy)
        retval = None if test is None else self.writer.pre_encode
        logger.debug("Writer pre_encode function: %s", retval)
        return retval

    @property
    def total_count(self):
        """ Return the total number of frames to be converted """
        if self.frame_ranges and not self.args.keep_unchanged:
            retval = sum([fr[1] - fr[0] for fr in self.frame_ranges])
        else:
            retval = self.images.images_found
        logger.debug(retval)
        return retval

    # Initalization
    def get_writer(self):
        """ Return the writer plugin """
        args = [self.args.output_dir]
        if self.args.writer in ("ffmpeg", "gif"):
            args.append(self.total_count)
        if self.args.writer == "ffmpeg":
            if self.images.is_video:
                args.append(self.args.input_dir)
            else:
                args.append(self.args.reference_video)
        logger.debug("Writer args: %s", args)
        return PluginLoader.get_converter("writer", self.args.writer)(*args)
    @property
    def draw_transparent(self):
        """ Draw transparent is an image writer only parameter.
            Return the value here for easy access for predictor """
        return self.writer.config.get("draw_transparent", False)

    @property
    def pre_encode(self):
        """ Return the writer's pre-encoder """
        dummy = np.zeros((20, 20, 3)).astype("uint8")
        test = self.writer.pre_encode(dummy)
        retval = None if test is None else self.writer.pre_encode
        logger.debug("Writer pre_encode function: %s", retval)
        return retval

    @property
    def total_count(self):
        """ Return the total number of frames to be converted """
        if self.frame_ranges and not self.args.keep_unchanged:
            retval = sum([fr[1] - fr[0] for fr in self.frame_ranges])
        else:
            retval = self.images.images_found
        logger.debug(retval)
        return retval

    # Initalization
    def get_writer(self):
        """ Return the writer plugin """
        args = [self.args.output_dir]
        if self.args.writer in ("ffmpeg", "gif"):
            args.append(self.total_count)
        if self.args.writer == "ffmpeg":
            if self.images.is_video:
                args.append(self.args.input_dir)
            else:
                args.append(self.args.reference_video)
        logger.debug("Writer args: %s", args)
        return PluginLoader.get_converter("writer", self.args.writer)(*args)
  
          if self.extractor:
              queue_manager.terminate_queues()
  
          Utils.finalize(self.images.images_found,
keep keep keep keep replace replace keep keep keep keep keep
 Convert refactor (#703)

* Separate predict and implement pool

* Add check and raise error to multithreading

Box functions to config. Add crop box option.

* All masks to mask module. Refactor convert masks

Predicted mask passed from model. Cli update

* Intesect box with mask and fixes

* Use raw NN output for convert

Use raw mask for face adjustments. Split adjustments to pre and post warp

* Separate out adjustments. Add unmask sharpen

* Set sensible defaults. Pre PR Testing

* Fix queue sizes. Move masked.py to lib

* Fix Skip Frames. Fix GUI Config popup

* Sensible queue limits. Add a less resource intensive single processing mode

* Fix predicted mask. Amend smooth box defaults

* Deterministic ordering for video output

* Video to Video convert implemented

* Fixups

- Remove defaults from folders across all stages
- Move match-hist and aca into color adjustments selectable
- Handle crashes properly for pooled processes
- Fix output directory does not exist error when creating a new output folder
- Force output to frames if input is not a video

* Add Color Transfer adjustment method

Wrap info text in GUI plugin configure popup

* Refactor image adjustments. Easier to create plugins

Start implementing config options for video encoding

* Add video encoding config options

Allow video encoding for frames input (must pass in a reference video)
Move video and image output writers to plugins

* Image writers config options

Move scaling to cli
Move draw_transparent to images config
Add config options for cv2 writer
Add Pillow image writer

* Add gif filetype to Pillow. Fix draw transparent for Pillow

* Add Animated GIF writer

standardize opencv/pillow defaults

* [speedup] Pre-encode supported writers in the convert pool (opencv, pillow)

Move scaling to convert pool
Remove dfaker mask

* Fix default writer

* Bugfixes

* Better custom argparse formatting 
scripts/convert.py