                                 "default": 1,
                                 "help": "Number of GPUs to use for conversion"})
          return argument_list
  
      def process(self):
          # Original & LowMem models go with Adjust or Masked converter
          # Note: GAN prediction outputs a mask + an image, while other predicts only an image
          model_name = self.arguments.trainer
          conv_name = self.arguments.converter
          self.input_aligned_dir = None
  
          model = PluginLoader.get_model(model_name)(get_folder(self.arguments.model_dir), self.arguments.gpus)
          if not model.load(self.arguments.swap_model):
              print('Model Not Found! A valid model must be provided to continue!')
              exit(1)
  
          input_aligned_dir = Path(self.arguments.input_dir)/Path('aligned')
          if self.arguments.input_aligned_dir is not None:
              input_aligned_dir = self.arguments.input_aligned_dir
          try:
 
</s> remove         batch = BackgroundGenerator(self.prepare_images(), 1)
 </s> add     def process(self):
        """ Original & LowMem models go with Adjust or Masked converter
            Note: GAN prediction outputs a mask + an image, while other predicts only an image """
        Utils.set_verbosity(self.args.verbose) </s> remove         input_aligned_dir = Path(self.arguments.input_dir)/Path('aligned')
        if self.arguments.input_aligned_dir is not None:
            input_aligned_dir = self.arguments.input_aligned_dir
        try:
            self.input_aligned_dir = [Path(path) for path in get_image_paths(input_aligned_dir)]
            if len(self.input_aligned_dir) == 0:
                print('Aligned directory is empty, no faces will be converted!')
            elif len(self.input_aligned_dir) <= len(self.input_dir)/3:
                print('Aligned directory contains an amount of images much less than the input, are you sure this is the right directory?')
        except:
            print('Aligned directory not found. All faces listed in the alignments file will be converted.')

        converter = PluginLoader.get_converter(conv_name)(model.converter(False),
            trainer=self.arguments.trainer,
            blur_size=self.arguments.blur_size,
            seamless_clone=self.arguments.seamless_clone,
            sharpen_image=self.arguments.sharpen_image,
            mask_type=self.arguments.mask_type,
            erosion_kernel_size=self.arguments.erosion_kernel_size,
            match_histogram=self.arguments.match_histogram,
            smooth_mask=self.arguments.smooth_mask,
            avg_color_adjust=self.arguments.avg_color_adjust
        )
 </s> add         self.opts = OptionalActions(self.args, self.images.input_images) </s> remove class ConvertImage(DirectoryProcessor):
    filename = ''
    def create_parser(self, subparser, command, description):
        self.optional_arguments = self.get_optional_arguments()
        self.parser = subparser.add_parser(
            command,
            help="Convert a source image to a new one with the face swapped.",
            description=description,
            epilog="Questions and feedback: \
            https://github.com/deepfakes/faceswap-playground"
        )

    @staticmethod
    def get_optional_arguments():
        ''' Put the arguments in a list so that they are accessible from both argparse and gui '''
        argument_list = []
        argument_list.append({ "opts": ('-m', '--model-dir'),
                               "action": FullPaths,
                               "dest": "model_dir",
                               "default": "models",
                               "help": "Model directory. A directory containing the trained model \
                               you wish to process. Defaults to 'models'"})
        argument_list.append({ "opts": ('-a', '--input-aligned-dir'),
                               "action": FullPaths,
                               "dest": "input_aligned_dir",
                               "default": None,
                               "help": "Input \"aligned directory\". A directory that should contain the \
                               aligned faces extracted from the input files. If you delete faces from \
                               this folder, they'll be skipped during conversion. If no aligned dir is \
                               specified, all faces will be converted."})
        argument_list.append({ "opts": ('-t', '--trainer'),
                               "type": str,
                               "choices": PluginLoader.get_available_models(), # case sensitive because this is used to load a plug-in.
                               "default": PluginLoader.get_default_model(),
                               "help": "Select the trainer that was used to create the model."})
        argument_list.append({ "opts": ('-s', '--swap-model'),
                               "action": "store_true",
                               "dest": "swap_model",
                               "default": False,
                               "help": "Swap the model. Instead of A -> B, swap B -> A."})
        argument_list.append({ "opts": ('-c', '--converter'),
                               "type": str,
                               "choices": ("Masked", "Adjust"), # case sensitive because this is used to load a plugin.
                               "default": "Masked",
                               "help": "Converter to use."})
        argument_list.append({ "opts": ('-D', '--detector'),
                               "type": str,
                               "choices": ("hog", "cnn"), # case sensitive because this is used to load a plugin.
                               "default": "hog",
                               "help": "Detector to use. 'cnn' detects much more angles but will be much more resource intensive and may fail on large files."})
        argument_list.append({ "opts": ('-fr', '--frame-ranges'),
                               "nargs": "+",
                               "type": str,
                               "help": "frame ranges to apply transfer to e.g. For frames 10 to 50 and 90 to 100 use --frame-ranges 10-50 90-100. \
                               Files must have the frame-number as the last number in the name!"})
        argument_list.append({ "opts": ('-d', '--discard-frames'),
                               "action": "store_true",
                               "dest": "discard_frames",
                               "default": False,
                               "help": "When used with --frame-ranges discards frames that are not processed instead of writing them out unchanged."})
        argument_list.append({ "opts": ('-l', '--ref_threshold'),
                               "type": float,
                               "dest": "ref_threshold",
                               "default": 0.6,
                               "help": "Threshold for positive face recognition"})
        argument_list.append({ "opts": ('-n', '--nfilter'),
                               "type": str,
                               "dest": "nfilter",
                               "nargs": '+',
                               "default": "nfilter.jpg",
                               "help": "Reference image for the persons you do not want to process. Should be a front portrait"})
        argument_list.append({ "opts": ('-f', '--filter'),
                               "type": str,
                               "dest": "filter",
                               "nargs": "+",
                               "default": "filter.jpg",
                               "help": "Reference images for the person you want to process. Should be a front portrait"})
        argument_list.append({ "opts": ('-b', '--blur-size'),
                               "type": int,
                               "default": 2,
                               "help": "Blur size. (Masked converter only)"})
        argument_list.append({ "opts": ('-S', '--seamless'),
                               "action": "store_true",
                               "dest": "seamless_clone",
                               "default": False,
                               "help": "Use cv2's seamless clone. (Masked converter only)"})
        argument_list.append({ "opts": ('-M', '--mask-type'),
                               "type": str.lower, #lowercase this, because its just a string later on.
                               "dest": "mask_type",
                               "choices": ["rect", "facehull", "facehullandrect"],
                               "default": "facehullandrect",
                               "help": "Mask to use to replace faces. (Masked converter only)"})
        argument_list.append({ "opts": ('-e', '--erosion-kernel-size'),
                               "dest": "erosion_kernel_size",
                               "type": int,
                               "default": None,
                               "help": "Erosion kernel size. (Masked converter only). Positive values apply erosion which reduces the edge of the swapped face. Negative values apply dilation which allows the swapped face to cover more space."})
        argument_list.append({ "opts": ('-mh', '--match-histgoram'),
                               "action": "store_true",
                               "dest": "match_histogram",
                               "default": False,
                               "help": "Use histogram matching. (Masked converter only)"})
        argument_list.append({ "opts": ('-sh', ),
                               "type": str.lower,
                               "dest": "sharpen_image",
                               "choices": ["bsharpen", "gsharpen"],
                               "default": None,
                               "help": "Use Sharpen Image - bsharpen = Box Blur, gsharpen = Gaussian Blur (Masked converter only)"})
        argument_list.append({ "opts": ('-sm', '--smooth-mask'),
                               "action": "store_true",
                               "dest": "smooth_mask",
                               "default": True,
                               "help": "Smooth mask (Adjust converter only)"})
        argument_list.append({ "opts": ('-aca', '--avg-color-adjust'),
                               "action": "store_true",
                               "dest": "avg_color_adjust",
                               "default": True,
                               "help": "Average color adjust. (Adjust converter only)"})
        argument_list.append({ "opts": ('-g', '--gpus'),
                               "type": int,
                               "default": 1,
                               "help": "Number of GPUs to use for conversion"})
        return argument_list
 </s> add class Convert(object):
    """ The convert process. """
    def __init__(self, arguments):
        self.args = arguments
        self.output_dir = get_folder(self.args.output_dir) </s> remove             if not skip: # process frame as normal
                for idx, face in faces:
                    if self.input_aligned_dir is not None and self.check_skipface(filename, idx):
                        print ('face {} for frame {} was deleted, skipping'.format(idx, os.path.basename(filename)))
                        continue
                    # Check for image rotations and rotate before mapping face
                    if face.r != 0:
                        height, width = image.shape[:2]
                        image = rotate_image(image, face.r)
                        image = converter.patch_image(image, face, 64 if "128" not in self.arguments.trainer else 128)
                        # TODO: This switch between 64 and 128 is a hack for now. We should have a separate cli option for size
                        image = rotate_image(image, face.r * -1, rotated_width=width, rotated_height=height)
                    else:
                        image = converter.patch_image(image, face, 64 if "128" not in self.arguments.trainer else 128)
                        # TODO: This switch between 64 and 128 is a hack for now. We should have a separate cli option for size

            output_file = get_folder(self.output_dir) / Path(filename).name
            cv2.imwrite(str(output_file), image)
        except Exception as e:
            print('Failed to convert image: {}. Reason: {}'.format(filename, e))
 </s> add         if not model.load(self.args.swap_model):
            print("Model Not Found! A valid model must be provided to continue!")
            exit(1)

        return model

    def load_converter(self, model):
        """ Load the requested converter for conversion """
        args = self.args
        conv = args.converter

        converter = PluginLoader.get_converter(conv)(model.converter(False),
                                                     trainer=args.trainer,
                                                     blur_size=args.blur_size,
                                                     seamless_clone=args.seamless_clone,
                                                     sharpen_image=args.sharpen_image,
                                                     mask_type=args.mask_type,
                                                     erosion_kernel_size=args.erosion_kernel_size,
                                                     match_histogram=args.match_histogram,
                                                     smooth_mask=args.smooth_mask,
                                                     avg_color_adjust=args.avg_color_adjust)
        return converter </s> remove     def parse_arguments(self, description, subparser, command):
        for option in self.argument_list:
            args = option['opts']
            kwargs = {key: option[key] for key in option.keys() if key != 'opts'}
            self.parser.add_argument(*args, **kwargs)
        
        self.parser = self.add_optional_arguments(self.parser)
        self.parser.set_defaults(func=self.process_arguments)
 </s> add     @staticmethod
    def get_optional_arguments():
        """ Put the arguments in a list so that they are accessible from both argparse and gui """
        argument_list = []
        argument_list.append({"opts": ("-m", "--model-dir"),
                              "action": FullPaths,
                              "dest": "model_dir",
                              "default": "models",
                              "help": "Model directory. A directory containing the trained model "
                                      "you wish to process. Defaults to 'models'"})
        argument_list.append({"opts": ("-a", "--input-aligned-dir"),
                              "action": FullPaths,
                              "dest": "input_aligned_dir",
                              "default": None,
                              "help": "Input \"aligned directory\". A directory that should "
                                      "contain the aligned faces extracted from the input files. "
                                      "If you delete faces from this folder, they'll be skipped "
                                      "during conversion. If no aligned dir is specified, all "
                                      "faces will be converted"})
        argument_list.append({"opts": ("-t", "--trainer"),
                              "type": str,
                              # case sensitive because this is used to load a plug-in.
                              "choices": PluginLoader.get_available_models(),
                              "default": PluginLoader.get_default_model(),
                              "help": "Select the trainer that was used to create the model"})
        argument_list.append({"opts": ("-c", "--converter"),
                              "type": str,
                              # case sensitive because this is used to load a plugin.
                              "choices": ("Masked", "Adjust"),
                              "default": "Masked",
                              "help": "Converter to use"})
        argument_list.append({"opts": ("-b", "--blur-size"),
                              "type": int,
                              "default": 2,
                              "help": "Blur size. (Masked converter only)"})
        argument_list.append({"opts": ("-e", "--erosion-kernel-size"),
                              "dest": "erosion_kernel_size",
                              "type": int,
                              "default": None,
                              "help": "Erosion kernel size. Positive values apply erosion "
                                      "which reduces the edge of the swapped face. Negative "
                                      "values apply dilation which allows the swapped face "
                                      "to cover more space. (Masked converter only)"})
        argument_list.append({"opts": ("-M", "--mask-type"),
                              #lowercase this, because its just a string later on.
                              "type": str.lower,
                              "dest": "mask_type",
                              "choices": ["rect", "facehull", "facehullandrect"],
                              "default": "facehullandrect",
                              "help": "Mask to use to replace faces. (Masked converter only)"})
        argument_list.append({"opts": ("-sh", "--sharpen"),
                              "type": str.lower,
                              "dest": "sharpen_image",
                              "choices": ["bsharpen", "gsharpen"],
                              "default": None,
                              "help": "Use Sharpen Image.bsharpen for Box Blur, gsharpen for "
                                      "Gaussian Blur (Masked converter only)"})
        argument_list.append({"opts": ("-g", "--gpus"),
                              "type": int,
                              "default": 1,
                              "help": "Number of GPUs to use for conversion"})
        argument_list.append({"opts": ("-fr", "--frame-ranges"),
                              "nargs": "+",
                              "type": str,
                              "help": "frame ranges to apply transfer to e.g. For frames 10 to "
                                      "50 and 90 to 100 use --frame-ranges 10-50 90-100. Files "
                                      "must have the frame-number as the last number in the "
                                      "name!"})
        argument_list.append({"opts": ("-d", "--discard-frames"),
                              "action": "store_true",
                              "dest": "discard_frames",
                              "default": False,
                              "help": "When used with --frame-ranges discards frames that are "
                                      "not processed instead of writing them out unchanged"})
        argument_list.append({"opts": ("-s", "--swap-model"),
                              "action": "store_true",
                              "dest": "swap_model",
                              "default": False,
                              "help": "Swap the model. Instead of A -> B, swap B -> A"})
        argument_list.append({"opts": ("-S", "--seamless"),
                              "action": "store_true",
                              "dest": "seamless_clone",
                              "default": False,
                              "help": "Use cv2's seamless clone. (Masked converter only)"})
        argument_list.append({"opts": ("-mh", "--match-histgoram"),
                              "action": "store_true",
                              "dest": "match_histogram",
                              "default": False,
                              "help": "Use histogram matching. (Masked converter only)"})
        argument_list.append({"opts": ("-sm", "--smooth-mask"),
                              "action": "store_true",
                              "dest": "smooth_mask",
                              "default": True,
                              "help": "Smooth mask (Adjust converter only)"})
        argument_list.append({"opts": ("-aca", "--avg-color-adjust"),
                              "action": "store_true",
                              "dest": "avg_color_adjust",
                              "default": True,
                              "help": "Average color adjust. (Adjust converter only)"})
        return argument_list
        self.images = Images(self.args)
        self.faces = Faces(self.args)
        self.alignments = Alignments(self.args)
                                 "default": 1,
                                 "help": "Number of GPUs to use for conversion"})
          return argument_list
  
        self.images = Images(self.args)
        self.faces = Faces(self.args)
        self.alignments = Alignments(self.args)
        self.images = Images(self.args)
        self.faces = Faces(self.args)
        self.alignments = Alignments(self.args)
        self.images = Images(self.args)
        self.faces = Faces(self.args)
        self.alignments = Alignments(self.args)
        self.images = Images(self.args)
        self.faces = Faces(self.args)
        self.alignments = Alignments(self.args)
        self.images = Images(self.args)
        self.faces = Faces(self.args)
        self.alignments = Alignments(self.args)
        self.images = Images(self.args)
        self.faces = Faces(self.args)
        self.alignments = Alignments(self.args)
        self.images = Images(self.args)
        self.faces = Faces(self.args)
        self.alignments = Alignments(self.args)
        self.images = Images(self.args)
        self.faces = Faces(self.args)
        self.alignments = Alignments(self.args)
        self.images = Images(self.args)
        self.faces = Faces(self.args)
        self.alignments = Alignments(self.args)
        self.images = Images(self.args)
        self.faces = Faces(self.args)
        self.alignments = Alignments(self.args)
        self.images = Images(self.args)
        self.faces = Faces(self.args)
        self.alignments = Alignments(self.args)
  
          input_aligned_dir = Path(self.arguments.input_dir)/Path('aligned')
          if self.arguments.input_aligned_dir is not None:
              input_aligned_dir = self.arguments.input_aligned_dir
          try:
keep keep keep keep replace replace replace replace replace replace replace replace replace replace replace keep keep keep keep keep
 cli/scripts Refactor (#367)

* Refactor for PEP 8 and split process function

* Remove backwards compatibility for skip frames

* Split optional functions into own class. Make functions more modular

* Conform scripts folder to PEP 8

* train.py - Fix write image bug. Make more modular

* extract.py - Make more modular, Put optional actions into own class

* cli.py - start PEP 8

* cli,py - Pep 8. Refactor and make modular. Bugfixes

* 1st round refactor. Completely untested and probably broken.

* convert.py: Extract alignments from frames if they don't exist

* BugFix: SkipExisting broken since face name refactor

* Extract.py tested

* Minor formatting

* convert.py + train.py amended not tested

* train.py - Semi-fix for hang on reaching target iteration. Now quits on preview mode
Make tensorflow / system warning less verbose

* 2nd pass refactor. Semi tested

bugfixes

* Remove obsolete code. imread/write to Utils

* rename inout.py to fsmedia.py

* Final bugfixes 
scripts/convert.py