1
0
Fork 0
mirror of https://github.com/deepfakes/faceswap synced 2025-06-07 10:43:27 -04:00
faceswap/tools/cli.py
torzdf cd00859c40
model_refactor (#571) (#572)
* model_refactor (#571)

* original model to new structure

* IAE model to new structure

* OriginalHiRes to new structure

* Fix trainer for different resolutions

* Initial config implementation

* Configparse library added

* improved training data loader

* dfaker model working

* Add logging to training functions

* Non blocking input for cli training

* Add error handling to threads. Add non-mp queues to queue_handler

* Improved Model Building and NNMeta

* refactor lib/models

* training refactor. DFL H128 model Implementation

* Dfaker - use hashes

* Move timelapse. Remove perceptual loss arg

* Update INSTALL.md. Add logger formatting. Update Dfaker training

* DFL h128 partially ported

* Add mask to dfaker (#573)

* Remove old models. Add mask to dfaker

* dfl mask. Make masks selectable in config (#575)

* DFL H128 Mask. Mask type selectable in config.

* remove gan_v2_2

* Creating Input Size config for models

Creating Input Size config for models

Will be used downstream in converters.

Also name change of image_shape to input_shape to clarify ( for future models with potentially different output_shapes)

* Add mask loss options to config

* MTCNN options to config.ini. Remove GAN config. Update USAGE.md

* Add sliders for numerical values in GUI

* Add config plugins menu to gui. Validate config

* Only backup model if loss has dropped. Get training working again

* bugfixes

* Standardise loss printing

* GUI idle cpu fixes. Graph loss fix.

* mutli-gpu logging bugfix

* Merge branch 'staging' into train_refactor

* backup state file

* Crash protection: Only backup if both total losses have dropped

* Port OriginalHiRes_RC4 to train_refactor (OriginalHiRes)

* Load and save model structure with weights

* Slight code update

* Improve config loader. Add subpixel opt to all models. Config to state

* Show samples... wrong input

* Remove AE topology. Add input/output shapes to State

* Port original_villain (birb/VillainGuy) model to faceswap

* Add plugin info to GUI config pages

* Load input shape from state. IAE Config options.

* Fix transform_kwargs.
Coverage to ratio.
Bugfix mask detection

* Suppress keras userwarnings.
Automate zoom.
Coverage_ratio to model def.

* Consolidation of converters & refactor (#574)

* Consolidation of converters & refactor

Initial Upload of alpha

Items
- consolidate convert_mased & convert_adjust into one converter
-add average color adjust to convert_masked
-allow mask transition blur size to be a fixed integer of pixels and a fraction of the facial mask size
-allow erosion/dilation size to be a fixed integer of pixels and a fraction of the facial mask size
-eliminate redundant type conversions to avoid multiple round-off errors
-refactor loops for vectorization/speed
-reorganize for clarity & style changes

TODO
- bug/issues with warping the new face onto a transparent old image...use a cleanup mask for now
- issues with mask border giving black ring at zero erosion .. investigate
- remove GAN ??
- test enlargment factors of umeyama standard face .. match to coverage factor
- make enlargment factor a model parameter
- remove convert_adjusted and referencing code when finished

* Update Convert_Masked.py

default blur size of 2 to match original...
description of enlargement tests
breakout matrxi scaling into def

* Enlargment scale as a cli parameter

* Update cli.py

* dynamic interpolation algorithm

Compute x & y scale factors from the affine matrix on the fly by QR decomp.
Choose interpolation alogrithm for the affine warp based on an upsample or downsample for each image

* input size
input size from config

* fix issues with <1.0 erosion

* Update convert.py

* Update Convert_Adjust.py

more work on the way to merginf

* Clean up help note on sharpen

* cleanup seamless

* Delete Convert_Adjust.py

* Update umeyama.py

* Update training_data.py

* swapping

* segmentation stub

* changes to convert.str

* Update masked.py

* Backwards compatibility fix for models
Get converter running

* Convert:
Move masks to class.
bugfix blur_size
some linting

* mask fix

* convert fixes

- missing facehull_rect re-added
- coverage to %
- corrected coverage logic
- cleanup of gui option ordering

* Update cli.py

* default for blur

* Update masked.py

* added preliminary low_mem version of OriginalHighRes model plugin

* Code cleanup, minor fixes

* Update masked.py

* Update masked.py

* Add dfl mask to convert

* histogram fix & seamless location

* update

* revert

* bugfix: Load actual configuration in gui

* Standardize nn_blocks

* Update cli.py

* Minor code amends

* Fix Original HiRes model

* Add masks to preview output for mask trainers
refactor trainer.__base.py

* Masked trainers converter support

* convert bugfix

* Bugfix: Converter for masked (dfl/dfaker) trainers

* Additional Losses (#592)

* initial upload

* Delete blur.py

* default initializer = He instead of Glorot (#588)

* Allow kernel_initializer to be overridable

* Add ICNR Initializer option for upscale on all models.

* Hopefully fixes RSoDs with original-highres model plugin

* remove debug line

* Original-HighRes model plugin Red Screen of Death fix, take #2

* Move global options to _base. Rename Villain model

* clipnorm and res block biases

* scale the end of res block

* res block

* dfaker pre-activation res

* OHRES pre-activation

* villain pre-activation

* tabs/space in nn_blocks

* fix for histogram with mask all set to zero

* fix to prevent two networks with same name

* GUI: Wider tooltips. Improve TQDM capture

* Fix regex bug

* Convert padding=48 to ratio of image size

* Add size option to alignments tool extract

* Pass through training image size to convert from model

* Convert: Pull training coverage from model

* convert: coverage, blur and erode to percent

* simplify matrix scaling

* ordering of sliders in train

* Add matrix scaling to utils. Use interpolation in lib.aligner transform

* masked.py Import get_matrix_scaling from utils

* fix circular import

* Update masked.py

* quick fix for matrix scaling

* testing thus for now

* tqdm regex capture bugfix

* Minor ammends

* blur size cleanup

* Remove coverage option from convert (Now cascades from model)

* Implement convert for all model types

* Add mask option and coverage option to all existing models

* bugfix for model loading on convert

* debug print removal

* Bugfix for masks in dfl_h128 and iae

* Update preview display. Add preview scaling to cli

* mask notes

* Delete training_data_v2.py

errant file

* training data variables

* Fix timelapse function

* Add new config items to state file for legacy purposes

* Slight GUI tweak

* Raise exception if problem with loaded model

* Add Tensorboard support (Logs stored in model directory)

* ICNR fix

* loss bugfix

* convert bugfix

* Move ini files to config folder. Make TensorBoard optional

* Fix training data for unbalanced inputs/outputs

* Fix config "none" test

* Keep helptext in .ini files when saving config from GUI

* Remove frame_dims from alignments

* Add no-flip and warp-to-landmarks cli options

* Revert OHR to RC4_fix version

* Fix lowmem mode on OHR model

* padding to variable

* Save models in parallel threads

* Speed-up of res_block stability

* Automated Reflection Padding

* Reflect Padding as a training option

Includes auto-calculation of proper padding shapes, input_shapes, output_shapes

Flag included in config now

* rest of reflect padding

* Move TB logging to cli. Session info to state file

* Add session iterations to state file

* Add recent files to menu. GUI code tidy up

* [GUI] Fix recent file list update issue

* Add correct loss names to TensorBoard logs

* Update live graph to use TensorBoard and remove animation

* Fix analysis tab. GUI optimizations

* Analysis Graph popup to Tensorboard Logs

* [GUI] Bug fix for graphing for models with hypens in name

* [GUI] Correctly split loss to tabs during training

* [GUI] Add loss type selection to analysis graph

* Fix store command name in recent files. Switch to correct tab on open

* [GUI] Disable training graph when 'no-logs' is selected

* Fix graphing race condition

* rename original_hires model to unbalanced
2019-02-09 18:35:12 +00:00

497 lines
28 KiB
Python

#!/usr/bin/env python3
""" Command Line Arguments for tools """
from lib.cli import FaceSwapArgs
from lib.cli import (ContextFullPaths, DirFullPaths,
FileFullPaths, SaveFileFullPaths, Slider)
from lib.utils import _image_extensions
class AlignmentsArgs(FaceSwapArgs):
""" Class to parse the command line arguments for Aligments tool """
def get_argument_list(self):
frames_dir = "\n\tMust Pass in a frames folder/source video file (-fr)."
faces_dir = "\n\tMust Pass in a faces folder (-fc)."
frames_or_faces_dir = ("\n\tMust Pass in either a frames folder/source video file OR a"
"\n\tfaces folder (-fr or -fc).")
frames_and_faces_dir = ("\n\tMust Pass in a frames folder/source video file AND a faces "
"\n\tfolder (-fr and -fc).")
output_opts = "\n\tUse the output option (-o) to process results."
align_eyes = "\n\tCan optionally use the align-eyes switch (-ae)."
argument_list = list()
argument_list.append({
"opts": ("-j", "--job"),
"type": str,
"choices": ("draw", "extract", "extract-large", "manual", "merge",
"missing-alignments", "missing-frames", "legacy", "leftover-faces",
"multi-faces", "no-faces", "reformat", "remove-faces", "remove-frames",
"rename", "sort-x", "sort-y", "spatial", "update-hashes"),
"required": True,
"help": "R|Choose which action you want to perform.\n"
"NB: All actions require an alignments file (-a) to be passed in."
"\n'draw': Draw landmarks on frames in the selected folder/video. A subfolder"
"\n\twill be created within the frames folder to hold the output." +
frames_dir + align_eyes +
"\n'extract': Re-extract faces from the source frames/video based on "
"\n\talignment data. This is a lot quicker than re-detecting faces." +
frames_and_faces_dir + align_eyes +
"\n'extract-large' - Extract all faces that have not been upscaled. Useful"
"\n\tfor excluding low-res images from a training set." +
frames_and_faces_dir + align_eyes +
"\n'manual': Manually view and edit landmarks." + frames_dir + align_eyes +
"\n'merge': Merge multiple alignment files into one. Specify the main"
"\n\talignments file with the -a flag and the file to be merged with the"
"\n\t-a2 flag."
"\n'missing-alignments': Identify frames that do not exist in the alignments"
"\n\tfile." + output_opts + frames_dir +
"\n'missing-frames': Identify frames in the alignments file that do no "
"\n\tappear within the frames folder/video." + output_opts + frames_dir +
"\n'legacy': This updates legacy alignments to the latest format by rotating"
"\n\tthe landmarks and bounding boxes and adding face_hashes." +
frames_and_faces_dir +
"\n'leftover-faces': Identify faces in the faces folder that do not exist in"
"\n\tthe alignments file." + output_opts + faces_dir +
"\n'multi-faces': Identify where multiple faces exist within the alignments"
"\n\tfile." + output_opts + frames_or_faces_dir +
"\n'no-faces': Identify frames that exist within the alignment file but no"
"\n\tfaces were detected." + output_opts + frames_dir +
"\n'reformat': Save a copy of alignments file in a different format. Specify"
"\n\ta format with the -fmt option."
"\n\tAlignments can be converted from DeepFaceLab by specifing:"
"\n\t -a dfl"
"\n\t -fc <source faces folder>"
"\n'remove-faces': Remove deleted faces from an alignments file. The original"
"\n\talignments file will be backed up. A different file format for the"
"\n\talignments file can optionally be specified (-fmt)." + faces_dir +
"\n'remove-frames': Remove deleted frames from an alignments file. The"
"\n\toriginal alignments file will be backed up. A different file format for"
"\n\tthe alignments file can optionally be specified (-fmt)." + frames_dir +
"\n'rename' - Rename faces to correspond with their parent frame and position"
"\n\tindex in the alignments file (i.e. how they are named after running"
"\n\textract)." + faces_dir +
"\n'sort-x': Re-index the alignments from left to right. For alignments with"
"\n\tmultiple faces this will ensure that the left-most face is at index 0"
"\n\tOptionally pass in a faces folder (-fc) to also rename extracted faces."
"\n'sort-y': Re-index the alignments from top to bottom. For alignments with"
"\n\tmultiple faces this will ensure that the top-most face is at index 0"
"\n\tOptionally pass in a faces folder (-fc) to also rename extracted faces."
"\n'spatial': Perform spatial and temporal filtering to smooth alignments"
"\n\t(EXPERIMENTAL!)"
"\n'update-hashes': Recalculate the face hashes. Only use this if you have "
"\n\taltered the extracted faces (e.g. colour adjust). The files MUST be "
"\n\tnamed '<frame_name>_face index' (i.e. how they are named after running"
"\n\textract)." + faces_dir})
argument_list.append({"opts": ("-a", "--alignments_file"),
"action": FileFullPaths,
"dest": "alignments_file",
"required": True,
"filetypes": "alignments",
"help": "Full path to the alignments "
"file to be processed."})
argument_list.append({"opts": ("-a2", "--alignments_file2"),
"action": FileFullPaths,
"dest": "alignments_file2",
"required": False,
"filetypes": "alignments",
"help": "Full path to the alignments file to "
"be merged into the main alignments "
"file (merge only)"})
argument_list.append({"opts": ("-fc", "-faces_folder"),
"action": DirFullPaths,
"dest": "faces_dir",
"help": "Directory containing extracted faces."})
argument_list.append({"opts": ("-fr", "-frames_folder"),
"action": DirFullPaths,
"dest": "frames_dir",
"help": "Directory containing source frames "
"that faces were extracted from."})
argument_list.append({"opts": ("-fmt", "--alignment_format"),
"type": str,
"choices": ("json", "pickle", "yaml"),
"help": "The file format to save the alignment "
"data in. Defaults to same as source."})
argument_list.append({
"opts": ("-o", "--output"),
"type": str,
"choices": ("console", "file", "move"),
"default": "console",
"help": "R|How to output discovered items ('faces' and"
"\n'frames' only):"
"\n'console': Print the list of frames to the screen. (DEFAULT)"
"\n'file': Output the list of frames to a text file (stored within the source"
"\n\tdirectory)."
"\n'move': Move the discovered items to a sub-folder within the source"
"\n\tdirectory."})
argument_list.append({"opts": ("-sz", "--size"),
"type": int,
"action": Slider,
"min_max": (128, 512),
"default": 256,
"rounding": 64,
"help": "The output size of extracted faces. (extract only)"})
argument_list.append({"opts": ("-ae", "--align-eyes"),
"action": "store_true",
"dest": "align_eyes",
"default": False,
"help": "Perform extra alignment to ensure "
"left/right eyes are at the same "
"height. (Draw, Extract and manual "
"only)"})
argument_list.append({"opts": ("-dm", "--disable-monitor"),
"action": "store_true",
"dest": "disable_monitor",
"default": False,
"help": "Enable this option if manual "
"alignments window is closing "
"instantly. (Manual only)"})
return argument_list
class EffmpegArgs(FaceSwapArgs):
""" Class to parse the command line arguments for EFFMPEG tool """
@staticmethod
def __parse_transpose(value):
index = 0
opts = ["(0, 90CounterClockwise&VerticalFlip)",
"(1, 90Clockwise)",
"(2, 90CounterClockwise)",
"(3, 90Clockwise&VerticalFlip)"]
if len(value) == 1:
index = int(value)
else:
for i in range(5):
if value in opts[i]:
index = i
break
return opts[index]
def get_argument_list(self):
argument_list = list()
argument_list.append({"opts": ('-a', '--action'),
"dest": "action",
"choices": ("extract", "gen-vid", "get-fps",
"get-info", "mux-audio", "rescale",
"rotate", "slice"),
"default": "extract",
"help": "Choose which action you want ffmpeg "
"ffmpeg to do.\n"
"'slice' cuts a portion of the video "
"into a separate video file.\n"
"'get-fps' returns the chosen video's "
"fps."})
argument_list.append({"opts": ('-i', '--input'),
"action": ContextFullPaths,
"dest": "input",
"default": "input",
"help": "Input file.",
"required": True,
"action_option": "-a",
"filetypes": "video"})
argument_list.append({"opts": ('-o', '--output'),
"action": ContextFullPaths,
"dest": "output",
"default": "",
"help": "Output file. If no output is "
"specified then: if the output is "
"meant to be a video then a video "
"called 'out.mkv' will be created in "
"the input directory; if the output is "
"meant to be a directory then a "
"directory called 'out' will be "
"created inside the input "
"directory.\n"
"Note: the chosen output file "
"extension will determine the file "
"encoding.",
"action_option": "-a",
"filetypes": "video"})
argument_list.append({"opts": ('-r', '--reference-video'),
"action": FileFullPaths,
"dest": "ref_vid",
"default": None,
"help": "Path to reference video if 'input' "
"was not a video.",
"filetypes": "video"})
argument_list.append({"opts": ('-fps', '--fps'),
"type": str,
"dest": "fps",
"default": "-1.0",
"help": "Provide video fps. Can be an integer, "
"float or fraction. Negative values "
"will make the program try to get the "
"fps from the input or reference "
"videos."})
argument_list.append({"opts": ("-ef", "--extract-filetype"),
"choices": _image_extensions,
"dest": "extract_ext",
"default": ".png",
"help": "Image format that extracted images "
"should be saved as. '.bmp' will offer "
"the fastest extraction speed, but "
"will take the most storage space. "
"'.png' will be slower but will take "
"less storage."})
argument_list.append({"opts": ('-s', '--start'),
"type": str,
"dest": "start",
"default": "00:00:00",
"help": "Enter the start time from which an "
"action is to be applied.\n"
"Default: 00:00:00, in HH:MM:SS "
"format. You can also enter the time "
"with or without the colons, e.g. "
"00:0000 or 026010."})
argument_list.append({"opts": ('-e', '--end'),
"type": str,
"dest": "end",
"default": "00:00:00",
"help": "Enter the end time to which an action "
"is to be applied. If both an end time "
"and duration are set, then the end "
"time will be used and the duration "
"will be ignored.\n"
"Default: 00:00:00, in HH:MM:SS."})
argument_list.append({"opts": ('-d', '--duration'),
"type": str,
"dest": "duration",
"default": "00:00:00",
"help": "Enter the duration of the chosen "
"action, for example if you enter "
"00:00:10 for slice, then the first 10 "
"seconds after and including the start "
"time will be cut out into a new "
"video.\n"
"Default: 00:00:00, in HH:MM:SS "
"format. You can also enter the time "
"with or without the colons, e.g. "
"00:0000 or 026010."})
argument_list.append({"opts": ('-m', '--mux-audio'),
"action": "store_true",
"dest": "mux_audio",
"default": False,
"help": "Mux the audio from the reference "
"video into the input video. This "
"option is only used for the 'gen-vid' "
"action. 'mux-audio' action has this "
"turned on implicitly."})
argument_list.append(
{"opts": ('-tr', '--transpose'),
"choices": ("(0, 90CounterClockwise&VerticalFlip)",
"(1, 90Clockwise)",
"(2, 90CounterClockwise)",
"(3, 90Clockwise&VerticalFlip)"),
"type": lambda v: self.__parse_transpose(v),
"dest": "transpose",
"default": None,
"help": "Transpose the video. If transpose is "
"set, then degrees will be ignored. For "
"cli you can enter either the number "
"or the long command name, "
"e.g. to use (1, 90Clockwise) "
"-tr 1 or -tr 90Clockwise"})
argument_list.append({"opts": ('-de', '--degrees'),
"type": str,
"dest": "degrees",
"default": None,
"help": "Rotate the video clockwise by the "
"given number of degrees."})
argument_list.append({"opts": ('-sc', '--scale'),
"type": str,
"dest": "scale",
"default": "1920x1080",
"help": "Set the new resolution scale if the "
"chosen action is 'rescale'."})
argument_list.append({"opts": ('-pr', '--preview'),
"action": "store_true",
"dest": "preview",
"default": False,
"help": "Uses ffplay to preview the effects of "
"actions that have a video output. "
"Currently preview does not work when "
"muxing audio."})
argument_list.append({"opts": ('-q', '--quiet'),
"action": "store_true",
"dest": "quiet",
"default": False,
"help": "Reduces output verbosity so that only "
"serious errors are printed. If both "
"quiet and verbose are set, verbose "
"will override quiet."})
argument_list.append({"opts": ('-v', '--verbose'),
"action": "store_true",
"dest": "verbose",
"default": False,
"help": "Increases output verbosity. If both "
"quiet and verbose are set, verbose "
"will override quiet."})
return argument_list
class SortArgs(FaceSwapArgs):
""" Class to parse the command line arguments for sort tool """
@staticmethod
def get_argument_list():
""" Put the arguments in a list so that they are accessible from both
argparse and gui """
argument_list = list()
argument_list.append({"opts": ('-i', '--input'),
"action": DirFullPaths,
"dest": "input_dir",
"default": "input_dir",
"help": "Input directory of aligned faces.",
"required": True})
argument_list.append({"opts": ('-o', '--output'),
"action": DirFullPaths,
"dest": "output_dir",
"default": "_output_dir",
"help": "Output directory for sorted aligned "
"faces."})
argument_list.append({"opts": ('-fp', '--final-process'),
"type": str,
"choices": ("folders", "rename"),
"dest": 'final_process',
"default": "rename",
"help": "R|\n'folders': files are sorted using "
"the -s/--sort-by\n\tmethod, then they "
"are organized into\n\tfolders using "
"the -g/--group-by grouping\n\tmethod."
"\n'rename': files are sorted using "
"the -s/--sort-by\n\tthen they are "
"renamed.\nDefault: rename"})
argument_list.append({"opts": ('-k', '--keep'),
"action": 'store_true',
"dest": 'keep_original',
"default": False,
"help": "Keeps the original files in the input "
"directory. Be careful when using this "
"with rename grouping and no specified "
"output directory as this would keep "
"the original and renamed files in the "
"same directory."})
argument_list.append({"opts": ('-s', '--sort-by'),
"type": str,
"choices": ("blur", "face", "face-cnn",
"face-cnn-dissim", "face-dissim",
"face-yaw", "hist",
"hist-dissim"),
"dest": 'sort_method',
"default": "hist",
"help": "Sort by method. "
"Choose how images are sorted. "
"Default: hist"})
argument_list.append({"opts": ('-g', '--group-by'),
"type": str,
"choices": ("blur", "face", "face-cnn",
"face-yaw", "hist"),
"dest": 'group_method',
"default": "hist",
"help": "Group by method. "
"When -fp/--final-processing by "
"folders choose the how the images are "
"grouped after sorting. "
"Default: hist"})
argument_list.append({"opts": ('-t', '--ref_threshold'),
"action": Slider,
"min_max": (-1.0, 10.0),
"rounding": 2,
"type": float,
"dest": 'min_threshold',
"default": -1.0,
"help": "Float value. "
"Minimum threshold to use for grouping "
"comparison with 'face' and 'hist' "
"methods. The lower the value the more "
"discriminating the grouping is. "
"Leaving -1.0 will make the program "
"set the default value automatically. "
"For face 0.6 should be enough, with "
"0.5 being very discriminating. "
"For face-cnn 7.2 should be enough, "
"with 4 being very discriminating. "
"For hist 0.3 should be enough, with "
"0.2 being very discriminating. "
"Be careful setting a value that's too "
"low in a directory with many images, "
"as this could result in a lot of "
"directories being created. "
"Defaults: face 0.6, face-cnn 7.2, "
"hist 0.3"})
argument_list.append({"opts": ('-b', '--bins'),
"action": Slider,
"min_max": (1, 100),
"rounding": 1,
"type": int,
"dest": 'num_bins',
"default": 5,
"help": "Integer value. "
"Number of folders that will be used "
"to group by blur and face-yaw. "
"For blur folder 0 will be the least "
"blurry, while the last folder will be "
"the blurriest. "
"For face-yaw the number of bins is by "
"how much 180 degrees is divided. So "
"if you use 18, then each folder will "
"be a 10 degree increment. Folder 0 "
"will contain faces looking the most "
"to the left whereas the last folder "
"will contain the faces looking the "
"most to the right. "
"If the number of images doesn't "
"divide evenly into the number of "
"bins, the remaining images get put in "
"the last bin."
"Default value: 5"})
argument_list.append({"opts": ('-l', '--log-changes'),
"action": 'store_true',
"dest": 'log_changes',
"default": False,
"help": "Logs file renaming changes if "
"grouping by renaming, or it logs the "
"file copying/movement if grouping by "
"folders. If no log file is specified "
"with '--log-file', then a "
"'sort_log.json' file will be created "
"in the input directory."})
argument_list.append({"opts": ('-lf', '--log-file'),
"action": SaveFileFullPaths,
"filetypes": "alignments",
"dest": 'log_file_path',
"default": 'sort_log.json',
"help": "Specify a log file to use for saving "
"the renaming or grouping information. "
"If specified extension isn't 'json' "
"or 'yaml', then json will be used as "
"the serializer, with the supplied "
"filename. "
"Default: sort_log.json"})
return argument_list