mirror of
https://github.com/zebrajr/faceswap.git
synced 2025-12-06 00:20:09 +01:00
Cli Restructure + Multi-Mask Select on Extract (#1012)
- Split up cli.py to smaller modules - Enable Multi Mask Selection in Extraction - Handle multi option selection options in the GUI - Document lib/cli
This commit is contained in:
parent
add55ccb3f
commit
ff8d85118e
65
docs/full/lib/cli.rst
Normal file
65
docs/full/lib/cli.rst
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
***********
|
||||
cli package
|
||||
***********
|
||||
|
||||
The CLI Package handles the Command Line Arguments that act as the entry point into Faceswap.
|
||||
|
||||
.. contents:: Contents
|
||||
:local:
|
||||
|
||||
args module
|
||||
===========
|
||||
|
||||
.. rubric:: Module Summary
|
||||
|
||||
.. autosummary::
|
||||
:nosignatures:
|
||||
|
||||
~lib.cli.args.ConvertArgs
|
||||
~lib.cli.args.ExtractArgs
|
||||
~lib.cli.args.ExtractConvertArgs
|
||||
~lib.cli.args.FaceSwapArgs
|
||||
~lib.cli.args.FullHelpArgumentParser
|
||||
~lib.cli.args.GuiArgs
|
||||
~lib.cli.args.SmartFormatter
|
||||
~lib.cli.args.TrainArgs
|
||||
|
||||
.. rubric:: Module
|
||||
|
||||
.. automodule:: lib.cli.args
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
actions module
|
||||
==============
|
||||
|
||||
.. rubric:: Module Summary
|
||||
|
||||
.. autosummary::
|
||||
:nosignatures:
|
||||
|
||||
~lib.cli.actions.ContextFullPaths
|
||||
~lib.cli.actions.DirFullPaths
|
||||
~lib.cli.actions.DirOrFileFullPaths
|
||||
~lib.cli.actions.FileFullPaths
|
||||
~lib.cli.actions.FilesFullPaths
|
||||
~lib.cli.actions.MultiOption
|
||||
~lib.cli.actions.Radio
|
||||
~lib.cli.actions.SaveFileFullPaths
|
||||
~lib.cli.actions.Slider
|
||||
|
||||
.. rubric:: Module
|
||||
|
||||
.. automodule:: lib.cli.actions
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
launcher module
|
||||
===============
|
||||
|
||||
.. automodule:: lib.cli.launcher
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
|
@ -3,7 +3,7 @@ gui package
|
|||
***********
|
||||
|
||||
The GUI Package contains the entire code base for Faceswap's optional GUI. The GUI itself itself
|
||||
is largely self-generated from the command line options specified in :mod:`lib.cli`.
|
||||
is largely self-generated from the command line options specified in :mod:`lib.cli.args`.
|
||||
|
||||
.. contents:: Contents
|
||||
:local:
|
||||
|
|
@ -18,6 +18,7 @@ custom\_widgets module
|
|||
|
||||
~lib.gui.custom_widgets.ConsoleOut
|
||||
~lib.gui.custom_widgets.ContextMenu
|
||||
~lib.gui.custom_widgets.MultiOption
|
||||
~lib.gui.custom_widgets.RightClickMenu
|
||||
~lib.gui.custom_widgets.StatusBar
|
||||
~lib.gui.custom_widgets.Tooltip
|
||||
|
|
|
|||
53
faceswap.py
53
faceswap.py
|
|
@ -2,7 +2,7 @@
|
|||
""" The master faceswap.py script """
|
||||
import sys
|
||||
|
||||
import lib.cli as cli
|
||||
from lib.cli import args
|
||||
from lib.config import generate_configs
|
||||
|
||||
if sys.version_info[0] < 3:
|
||||
|
|
@ -11,28 +11,37 @@ if sys.version_info[0] == 3 and sys.version_info[1] < 6:
|
|||
raise Exception("This program requires at least python3.6")
|
||||
|
||||
|
||||
def bad_args(args):
|
||||
""" Print help on bad arguments """
|
||||
PARSER.print_help()
|
||||
_PARSER = args.FullHelpArgumentParser()
|
||||
|
||||
|
||||
def _bad_args():
|
||||
""" Print help to console when bad arguments are provided. """
|
||||
_PARSER.print_help()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
def _main():
|
||||
""" The main entry point into Faceswap.
|
||||
|
||||
- Generates the config files, if they don't pre-exist.
|
||||
- Compiles the :class:`~lib.cli.args.FullHelpArgumentParser` objects for each section of
|
||||
Faceswap.
|
||||
- Sets the default values and launches the relevant script.
|
||||
- Outputs help if invalid parameters are provided.
|
||||
"""
|
||||
generate_configs()
|
||||
PARSER = cli.FullHelpArgumentParser()
|
||||
SUBPARSER = PARSER.add_subparsers()
|
||||
EXTRACT = cli.ExtractArgs(SUBPARSER,
|
||||
"extract",
|
||||
"Extract the faces from pictures")
|
||||
TRAIN = cli.TrainArgs(SUBPARSER,
|
||||
"train",
|
||||
"This command trains the model for the two faces A and B")
|
||||
CONVERT = cli.ConvertArgs(SUBPARSER,
|
||||
"convert",
|
||||
"Convert a source image to a new one with the face swapped")
|
||||
GUI = cli.GuiArgs(SUBPARSER,
|
||||
"gui",
|
||||
"Launch the Faceswap Graphical User Interface")
|
||||
PARSER.set_defaults(func=bad_args)
|
||||
ARGUMENTS = PARSER.parse_args()
|
||||
ARGUMENTS.func(ARGUMENTS)
|
||||
|
||||
subparser = _PARSER.add_subparsers()
|
||||
args.ExtractArgs(subparser, "extract", "Extract the faces from pictures")
|
||||
args.TrainArgs(subparser, "train", "This command trains the model for the two faces A and B")
|
||||
args.ConvertArgs(subparser,
|
||||
"convert",
|
||||
"Convert a source image to a new one with the face swapped")
|
||||
args.GuiArgs(subparser, "gui", "Launch the Faceswap Graphical User Interface")
|
||||
_PARSER.set_defaults(func=_bad_args)
|
||||
arguments = _PARSER.parse_args()
|
||||
arguments.func(arguments)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
_main()
|
||||
|
|
|
|||
1309
lib/cli.py
1309
lib/cli.py
File diff suppressed because it is too large
Load Diff
0
lib/cli/__init__.py
Normal file
0
lib/cli/__init__.py
Normal file
367
lib/cli/actions.py
Normal file
367
lib/cli/actions.py
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
#!/usr/bin/env python3
|
||||
""" Custom :class:`argparse.Action` objects for Faceswap's Command Line Interface.
|
||||
|
||||
The custom actions within this module allow for custom manipulation of Command Line Arguments
|
||||
as well as adding a mechanism for indicating to the GUI how specific options should be rendered.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
|
||||
|
||||
# << FILE HANDLING >>
|
||||
|
||||
class _FullPaths(argparse.Action): # pylint: disable=too-few-public-methods
|
||||
""" Parent class for various file type and file path handling classes.
|
||||
|
||||
Expands out given paths to their full absolute paths. This class should not be
|
||||
called directly. It is the base class for the various different file handling
|
||||
methods.
|
||||
"""
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
if isinstance(values, (list, tuple)):
|
||||
vals = [os.path.abspath(os.path.expanduser(val)) for val in values]
|
||||
else:
|
||||
vals = os.path.abspath(os.path.expanduser(values))
|
||||
setattr(namespace, self.dest, vals)
|
||||
|
||||
|
||||
class DirFullPaths(_FullPaths):
|
||||
""" Adds support for a Directory browser in the GUI.
|
||||
|
||||
This is a standard :class:`argparse.Action` (with stock parameters) which indicates to the GUI
|
||||
that a dialog box should be opened in order to browse for a folder.
|
||||
|
||||
No additional parameters are required.
|
||||
|
||||
Example
|
||||
-------
|
||||
>>> argument_list = []
|
||||
>>> argument_list.append(dict(
|
||||
>>> opts=("-f", "--folder_location"),
|
||||
>>> action=DirFullPaths)),
|
||||
"""
|
||||
# pylint: disable=too-few-public-methods,unnecessary-pass
|
||||
pass
|
||||
|
||||
|
||||
class FileFullPaths(_FullPaths):
|
||||
""" Adds support for a File browser to select a single file in the GUI.
|
||||
|
||||
This extends the standard :class:`argparse.Action` and adds an additional parameter
|
||||
:attr:`filetypes`, indicating to the GUI that it should pop a file browser for opening a file
|
||||
and limit the results to the file types listed. As well as the standard parameters, the
|
||||
following parameter is required:
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filetypes: str
|
||||
The accepted file types for this option. This is the key for the GUIs lookup table which
|
||||
can be found in :class:`lib.gui.utils.FileHandler`
|
||||
|
||||
Example
|
||||
-------
|
||||
>>> argument_list = []
|
||||
>>> argument_list.append(dict(
|
||||
>>> opts=("-f", "--video_location"),
|
||||
>>> action=FileFullPaths,
|
||||
>>> filetypes="video))"
|
||||
"""
|
||||
# pylint: disable=too-few-public-methods
|
||||
def __init__(self, *args, filetypes=None, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.filetypes = filetypes
|
||||
|
||||
def _get_kwargs(self):
|
||||
names = ["option_strings",
|
||||
"dest",
|
||||
"nargs",
|
||||
"const",
|
||||
"default",
|
||||
"type",
|
||||
"choices",
|
||||
"help",
|
||||
"metavar",
|
||||
"filetypes"]
|
||||
return [(name, getattr(self, name)) for name in names]
|
||||
|
||||
|
||||
class FilesFullPaths(FileFullPaths): # pylint: disable=too-few-public-methods
|
||||
""" Adds support for a File browser to select multiple files in the GUI.
|
||||
|
||||
This extends the standard :class:`argparse.Action` and adds an additional parameter
|
||||
:attr:`filetypes`, indicating to the GUI that it should pop a file browser, and limit
|
||||
the results to the file types listed. Multiple files can be selected for opening, so the
|
||||
:attr:`nargs` parameter must be set. As well as the standard parameters, the following
|
||||
parameter is required:
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filetypes: str
|
||||
The accepted file types for this option. This is the key for the GUIs lookup table which
|
||||
can be found in :class:`lib.gui.utils.FileHandler`
|
||||
|
||||
Example
|
||||
-------
|
||||
>>> argument_list = []
|
||||
>>> argument_list.append(dict(
|
||||
>>> opts=("-f", "--images"),
|
||||
>>> action=FilesFullPaths,
|
||||
>>> filetypes="image",
|
||||
>>> nargs="+"))
|
||||
"""
|
||||
def __init__(self, *args, filetypes=None, **kwargs):
|
||||
if kwargs.get("nargs", None) is None:
|
||||
opt = kwargs["option_strings"]
|
||||
raise ValueError("nargs must be provided for FilesFullPaths: {}".format(opt))
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class DirOrFileFullPaths(FileFullPaths): # pylint: disable=too-few-public-methods
|
||||
""" Adds support to the GUI to launch either a file browser or a folder browser.
|
||||
|
||||
Some inputs (for example source frames) can come from a folder of images or from a
|
||||
video file. This indicates to the GUI that it should place 2 buttons (one for a folder
|
||||
browser, one for a file browser) for file/folder browsing.
|
||||
|
||||
The standard :class:`argparse.Action` is extended with the additional parameter
|
||||
:attr:`filetypes`, indicating to the GUI that it should pop a file browser, and limit
|
||||
the results to the file types listed. As well as the standard parameters, the following
|
||||
parameter is required:
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filetypes: str
|
||||
The accepted file types for this option. This is the key for the GUIs lookup table which
|
||||
can be found in :class:`lib.gui.utils.FileHandler`. NB: This parameter is only used for
|
||||
the file browser and not the folder browser
|
||||
|
||||
Example
|
||||
-------
|
||||
>>> argument_list = []
|
||||
>>> argument_list.append(dict(
|
||||
>>> opts=("-f", "--input_frames"),
|
||||
>>> action=DirOrFileFullPaths,
|
||||
>>> filetypes="video))"
|
||||
"""
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
|
||||
class SaveFileFullPaths(FileFullPaths):
|
||||
""" Adds support for a Save File dialog in the GUI.
|
||||
|
||||
This extends the standard :class:`argparse.Action` and adds an additional parameter
|
||||
:attr:`filetypes`, indicating to the GUI that it should pop a save file browser, and limit
|
||||
the results to the file types listed. As well as the standard parameters, the following
|
||||
parameter is required:
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filetypes: str
|
||||
The accepted file types for this option. This is the key for the GUIs lookup table which
|
||||
can be found in :class:`lib.gui.utils.FileHandler`
|
||||
|
||||
Example
|
||||
-------
|
||||
>>> argument_list = []
|
||||
>>> argument_list.append(dict(
|
||||
>>> opts=("-f", "--video_out"),
|
||||
>>> action=SaveFileFullPaths,
|
||||
>>> filetypes="video"))
|
||||
"""
|
||||
# pylint: disable=too-few-public-methods,unnecessary-pass
|
||||
pass
|
||||
|
||||
|
||||
class ContextFullPaths(FileFullPaths):
|
||||
""" Adds support for context sensitive browser dialog opening in the GUI.
|
||||
|
||||
For some tasks, the type of action (file load, folder open, file save etc.) can vary
|
||||
depending on the task to be performed (a good example of this is the effmpeg tool).
|
||||
Using this action indicates to the GUI that the type of dialog to be launched can change
|
||||
depending on another option. As well as the standard parameters, the below parameters are
|
||||
required. NB: :attr:`nargs` are explicitly disallowed.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filetypes: str
|
||||
The accepted file types for this option. This is the key for the GUIs lookup table which
|
||||
can be found in :class:`lib.gui.utils.FileHandler`
|
||||
action_option: str
|
||||
The command line option that dictates the context of the file dialog to be opened.
|
||||
Bespoke actions are set in :class:`lib.gui.utils.FileHandler`
|
||||
|
||||
Example
|
||||
-------
|
||||
Assuming an argument has already been set with option string `-a` indicating the action to be
|
||||
performed, the following will pop a different type of dialog depending on the action selected:
|
||||
|
||||
>>> argument_list = []
|
||||
>>> argument_list.append(dict(
|
||||
>>> opts=("-f", "--input_video"),
|
||||
>>> action=ContextFullPaths,
|
||||
>>> filetypes="video",
|
||||
>>> action_option="-a"))
|
||||
"""
|
||||
# pylint: disable=too-few-public-methods, too-many-arguments
|
||||
def __init__(self, *args, filetypes=None, action_option=None, **kwargs):
|
||||
opt = kwargs["option_strings"]
|
||||
if kwargs.get("nargs", None) is not None:
|
||||
raise ValueError("nargs not allowed for ContextFullPaths: {}".format(opt))
|
||||
if filetypes is None:
|
||||
raise ValueError("filetypes is required for ContextFullPaths: {}".format(opt))
|
||||
if action_option is None:
|
||||
raise ValueError("action_option is required for ContextFullPaths: {}".format(opt))
|
||||
super().__init__(*args, filetypes=filetypes, **kwargs)
|
||||
self.action_option = action_option
|
||||
|
||||
def _get_kwargs(self):
|
||||
names = ["option_strings",
|
||||
"dest",
|
||||
"nargs",
|
||||
"const",
|
||||
"default",
|
||||
"type",
|
||||
"choices",
|
||||
"help",
|
||||
"metavar",
|
||||
"filetypes",
|
||||
"action_option"]
|
||||
return [(name, getattr(self, name)) for name in names]
|
||||
|
||||
|
||||
# << GUI DISPLAY OBJECTS >>
|
||||
|
||||
class Radio(argparse.Action): # pylint: disable=too-few-public-methods
|
||||
""" Adds support for a GUI Radio options box.
|
||||
|
||||
This is a standard :class:`argparse.Action` (with stock parameters) which indicates to the GUI
|
||||
that the options passed should be rendered as a group of Radio Buttons rather than a combo box.
|
||||
|
||||
No additional parameters are required, but the :attr:`choices` parameter must be provided as
|
||||
these will be the Radio Box options. :attr:`nargs` are explicitly disallowed.
|
||||
|
||||
Example
|
||||
-------
|
||||
>>> argument_list = []
|
||||
>>> argument_list.append(dict(
|
||||
>>> opts=("-f", "--foobar"),
|
||||
>>> action=Radio,
|
||||
>>> choices=["foo", "bar"))
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
opt = kwargs["option_strings"]
|
||||
if kwargs.get("nargs", None) is not None:
|
||||
raise ValueError("nargs not allowed for Radio buttons: {}".format(opt))
|
||||
if not kwargs.get("choices", []):
|
||||
raise ValueError("Choices must be provided for Radio buttons: {}".format(opt))
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
setattr(namespace, self.dest, values)
|
||||
|
||||
|
||||
class MultiOption(argparse.Action): # pylint: disable=too-few-public-methods
|
||||
""" Adds support for multiple option checkboxes in the GUI.
|
||||
|
||||
This is a standard :class:`argparse.Action` (with stock parameters) which indicates to the GUI
|
||||
that the options passed should be rendered as a group of Radio Buttons rather than a combo box.
|
||||
|
||||
The :attr:`choices` parameter must be provided as this provides the valid option choices.
|
||||
|
||||
Example
|
||||
-------
|
||||
>>> argument_list = []
|
||||
>>> argument_list.append(dict(
|
||||
>>> opts=("-f", "--foobar"),
|
||||
>>> action=MultiOption,
|
||||
>>> choices=["foo", "bar"))
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
opt = kwargs["option_strings"]
|
||||
if not kwargs.get("nargs", []):
|
||||
raise ValueError("nargs must be provided for MultiOption: {}".format(opt))
|
||||
if not kwargs.get("choices", []):
|
||||
raise ValueError("Choices must be provided for MultiOption: {}".format(opt))
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
setattr(namespace, self.dest, values)
|
||||
|
||||
|
||||
class Slider(argparse.Action): # pylint: disable=too-few-public-methods
|
||||
""" Adds support for a slider in the GUI.
|
||||
|
||||
The standard :class:`argparse.Action` is extended with the additional parameters listed below.
|
||||
The :attr:`default` value must be supplied and the :attr:`type` must be either :class:`int` or
|
||||
:class:`float`. :attr:`nargs` are explicitly disallowed.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
min_max: tuple
|
||||
The (`min`, `max`) values that the slider's range should be set to. The values should be a
|
||||
pair of `float` or `int` data types, depending on the data type of the slider. NB: These
|
||||
min/max values are not enforced, they are purely for setting the slider range. Values
|
||||
outside of this range can still be explicitly passed in from the cli.
|
||||
rounding: int
|
||||
If the underlying data type for the option is a `float` then this value is the number of
|
||||
decimal places to round the slider values to. If the underlying data type for the option is
|
||||
an `int` then this is the step interval between each value for the slider.
|
||||
|
||||
Examples
|
||||
--------
|
||||
For integer values:
|
||||
|
||||
>>> argument_list = []
|
||||
>>> argument_list.append(dict(
|
||||
>>> opts=("-f", "--foobar"),
|
||||
>>> action=Slider,
|
||||
>>> min_max=(0, 10)
|
||||
>>> rounding=1
|
||||
>>> type=int,
|
||||
>>> default=5))
|
||||
|
||||
For floating point values:
|
||||
|
||||
>>> argument_list = []
|
||||
>>> argument_list.append(dict(
|
||||
>>> opts=("-f", "--foobar"),
|
||||
>>> action=Slider,
|
||||
>>> min_max=(0.00, 1.00)
|
||||
>>> rounding=2
|
||||
>>> type=float,
|
||||
>>> default=5.00))
|
||||
"""
|
||||
def __init__(self, *args, min_max=None, rounding=None, **kwargs):
|
||||
opt = kwargs["option_strings"]
|
||||
if kwargs.get("nargs", None) is not None:
|
||||
raise ValueError("nargs not allowed for Slider: {}".format(opt))
|
||||
if kwargs.get("default", None) is None:
|
||||
raise ValueError("A default value must be supplied for Slider: {}".format(opt))
|
||||
if kwargs.get("type", None) not in (int, float):
|
||||
raise ValueError("Sliders only accept int and float data types: {}".format(opt))
|
||||
if min_max is None:
|
||||
raise ValueError("min_max must be provided for Sliders: {}".format(opt))
|
||||
if rounding is None:
|
||||
raise ValueError("rounding must be provided for Sliders: {}".format(opt))
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
self.min_max = min_max
|
||||
self.rounding = rounding
|
||||
|
||||
def _get_kwargs(self):
|
||||
names = ["option_strings",
|
||||
"dest",
|
||||
"nargs",
|
||||
"const",
|
||||
"default",
|
||||
"type",
|
||||
"choices",
|
||||
"help",
|
||||
"metavar",
|
||||
"min_max", # Tuple containing min and max values of scale
|
||||
"rounding"] # Decimal places to round floats to or step interval for ints
|
||||
return [(name, getattr(self, name)) for name in names]
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
setattr(namespace, self.dest, values)
|
||||
1138
lib/cli/args.py
Normal file
1138
lib/cli/args.py
Normal file
File diff suppressed because it is too large
Load Diff
193
lib/cli/launcher.py
Normal file
193
lib/cli/launcher.py
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
#!/usr/bin/env python3
|
||||
""" Launches the correct script with the given Command Line Arguments """
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
|
||||
from importlib import import_module
|
||||
from lib.logger import crash_log, log_setup
|
||||
from lib.utils import FaceswapError, get_backend, safe_shutdown, set_system_verbosity
|
||||
|
||||
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
|
||||
|
||||
|
||||
class ScriptExecutor(): # pylint:disable=too-few-public-methods
|
||||
""" Loads the relevant script modules and executes the script.
|
||||
|
||||
This class is initialized in each of the argparsers for the relevant
|
||||
command, then execute script is called within their set_default
|
||||
function.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
command: str
|
||||
The faceswap command that is being executed
|
||||
"""
|
||||
def __init__(self, command):
|
||||
self._command = command.lower()
|
||||
|
||||
def _import_script(self):
|
||||
""" Imports the relevant script as indicated by :attr:`_command` from the scripts folder.
|
||||
|
||||
Returns
|
||||
-------
|
||||
class: Faceswap Script
|
||||
The uninitialized script from the faceswap scripts folder.
|
||||
"""
|
||||
self._test_for_tf_version()
|
||||
self._test_for_gui()
|
||||
cmd = os.path.basename(sys.argv[0])
|
||||
src = "tools.{}".format(self._command.lower()) if cmd == "tools.py" else "scripts"
|
||||
mod = ".".join((src, self._command.lower()))
|
||||
module = import_module(mod)
|
||||
script = getattr(module, self._command.title())
|
||||
return script
|
||||
|
||||
@staticmethod
|
||||
def _test_for_tf_version():
|
||||
""" Check that the required Tensorflow version is installed.
|
||||
|
||||
Raises
|
||||
------
|
||||
FaceswapError
|
||||
If Tensorflow is not found, or is not between versions 1.12 and 1.15
|
||||
"""
|
||||
min_ver = 1.12
|
||||
max_ver = 1.15
|
||||
try:
|
||||
# Ensure tensorflow doesn't pin all threads to one core when using Math Kernel Library
|
||||
os.environ["KMP_AFFINITY"] = "disabled"
|
||||
import tensorflow as tf # pylint:disable=import-outside-toplevel
|
||||
except ImportError as err:
|
||||
raise FaceswapError("There was an error importing Tensorflow. This is most likely "
|
||||
"because you do not have TensorFlow installed, or you are trying "
|
||||
"to run tensorflow-gpu on a system without an Nvidia graphics "
|
||||
"card. Original import error: {}".format(str(err)))
|
||||
tf_ver = float(".".join(tf.__version__.split(".")[:2])) # pylint:disable=no-member
|
||||
if tf_ver < min_ver:
|
||||
raise FaceswapError("The minimum supported Tensorflow is version {} but you have "
|
||||
"version {} installed. Please upgrade Tensorflow.".format(
|
||||
min_ver, tf_ver))
|
||||
if tf_ver > max_ver:
|
||||
raise FaceswapError("The maximumum supported Tensorflow is version {} but you have "
|
||||
"version {} installed. Please downgrade Tensorflow.".format(
|
||||
max_ver, tf_ver))
|
||||
logger.debug("Installed Tensorflow Version: %s", tf_ver)
|
||||
|
||||
def _test_for_gui(self):
|
||||
""" If running the gui, performs check to ensure necessary prerequisites are present. """
|
||||
if self._command != "gui":
|
||||
return
|
||||
self._test_tkinter()
|
||||
self._check_display()
|
||||
|
||||
@staticmethod
|
||||
def _test_tkinter():
|
||||
""" If the user is running the GUI, test whether the tkinter app is available on their
|
||||
machine. If not exit gracefully.
|
||||
|
||||
This avoids having to import every tkinter function within the GUI in a wrapper and
|
||||
potentially spamming traceback errors to console.
|
||||
|
||||
Raises
|
||||
------
|
||||
FaceswapError
|
||||
If tkinter cannot be imported
|
||||
"""
|
||||
try:
|
||||
# pylint: disable=unused-variable
|
||||
import tkinter # noqa pylint: disable=unused-import,import-outside-toplevel
|
||||
except ImportError:
|
||||
logger.error("It looks like TkInter isn't installed for your OS, so the GUI has been "
|
||||
"disabled. To enable the GUI please install the TkInter application. You "
|
||||
"can try:")
|
||||
logger.info("Anaconda: conda install tk")
|
||||
logger.info("Windows/macOS: Install ActiveTcl Community Edition from "
|
||||
"http://www.activestate.com")
|
||||
logger.info("Ubuntu/Mint/Debian: sudo apt install python3-tk")
|
||||
logger.info("Arch: sudo pacman -S tk")
|
||||
logger.info("CentOS/Redhat: sudo yum install tkinter")
|
||||
logger.info("Fedora: sudo dnf install python3-tkinter")
|
||||
raise FaceswapError("TkInter not found")
|
||||
|
||||
@staticmethod
|
||||
def _check_display():
|
||||
""" Check whether there is a display to output the GUI to.
|
||||
|
||||
If running on Windows then it is assumed that we are not running in headless mode
|
||||
|
||||
Raises
|
||||
------
|
||||
FaceswapError
|
||||
If a DISPLAY environmental cannot be found
|
||||
"""
|
||||
if not os.environ.get("DISPLAY", None) and os.name != "nt":
|
||||
if platform.system() == "Darwin":
|
||||
logger.info("macOS users need to install XQuartz. "
|
||||
"See https://support.apple.com/en-gb/HT201341")
|
||||
raise FaceswapError("No display detected. GUI mode has been disabled.")
|
||||
|
||||
def execute_script(self, arguments):
|
||||
""" Performs final set up and launches the requested :attr:`_command` with the given
|
||||
command line arguments.
|
||||
|
||||
Monitors for errors and attempts to shut down the process cleanly on exit.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
arguments: :class:`argparse.Namespace`
|
||||
The command line arguments to be passed to the executing script.
|
||||
"""
|
||||
set_system_verbosity(arguments.loglevel)
|
||||
is_gui = hasattr(arguments, "redirect_gui") and arguments.redirect_gui
|
||||
log_setup(arguments.loglevel, arguments.logfile, self._command, is_gui)
|
||||
logger.debug("Executing: %s. PID: %s", self._command, os.getpid())
|
||||
success = False
|
||||
if get_backend() == "amd":
|
||||
plaidml_found = self._setup_amd(arguments.loglevel)
|
||||
if not plaidml_found:
|
||||
safe_shutdown(got_error=True)
|
||||
return
|
||||
try:
|
||||
script = self._import_script()
|
||||
process = script(arguments)
|
||||
process.process()
|
||||
success = True
|
||||
except FaceswapError as err:
|
||||
for line in str(err).splitlines():
|
||||
logger.error(line)
|
||||
except KeyboardInterrupt: # pylint: disable=try-except-raise
|
||||
raise
|
||||
except SystemExit:
|
||||
pass
|
||||
except Exception: # pylint: disable=broad-except
|
||||
crash_file = crash_log()
|
||||
logger.exception("Got Exception on main handler:")
|
||||
logger.critical("An unexpected crash has occurred. Crash report written to '%s'. "
|
||||
"You MUST provide this file if seeking assistance. Please verify you "
|
||||
"are running the latest version of faceswap before reporting",
|
||||
crash_file)
|
||||
|
||||
finally:
|
||||
safe_shutdown(got_error=not success)
|
||||
|
||||
@staticmethod
|
||||
def _setup_amd(log_level):
|
||||
""" Test for plaidml and perform setup for AMD.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
log_level: str
|
||||
The requested log level to run at
|
||||
"""
|
||||
logger.debug("Setting up for AMD")
|
||||
try:
|
||||
import plaidml # noqa pylint:disable=unused-import,import-outside-toplevel
|
||||
except ImportError:
|
||||
logger.error("PlaidML not found. Run `pip install plaidml-keras` for AMD support")
|
||||
return False
|
||||
from lib.plaidml_tools import setup_plaidml # pylint:disable=import-outside-toplevel
|
||||
setup_plaidml(log_level)
|
||||
logger.debug("setup up for PlaidML")
|
||||
return True
|
||||
|
|
@ -10,8 +10,7 @@ from functools import partial
|
|||
|
||||
from _tkinter import Tcl_Obj, TclError
|
||||
|
||||
from .custom_widgets import ContextMenu
|
||||
from .custom_widgets import Tooltip
|
||||
from .custom_widgets import ContextMenu, MultiOption, Tooltip
|
||||
from .utils import FileHandler, get_config, get_images
|
||||
|
||||
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
|
||||
|
|
@ -95,6 +94,8 @@ class ControlPanelOption():
|
|||
Used for combo boxes and radio control option setting
|
||||
is_radio: bool, optional
|
||||
Specifies to use a Radio control instead of combobox if choices are passed
|
||||
is_multi_option:
|
||||
Specifies to use a Multi Check Button option group for the specified control
|
||||
rounding: int or float, optional
|
||||
For slider controls. Sets the stepping
|
||||
min_max: int or float, optional
|
||||
|
|
@ -113,13 +114,14 @@ class ControlPanelOption():
|
|||
|
||||
def __init__(self, title, dtype, # pylint:disable=too-many-arguments
|
||||
group=None, default=None, initial_value=None, choices=None, is_radio=False,
|
||||
rounding=None, min_max=None, sysbrowser=None, helptext=None,
|
||||
track_modified=False, command=None):
|
||||
is_multi_option=False, rounding=None, min_max=None, sysbrowser=None,
|
||||
helptext=None, track_modified=False, command=None):
|
||||
logger.debug("Initializing %s: (title: '%s', dtype: %s, group: %s, default: %s, "
|
||||
"initial_value: %s, choices: %s, is_radio: %s, rounding: %s, min_max: %s, "
|
||||
"sysbrowser: %s, helptext: '%s', track_modified: %s, command: '%s')",
|
||||
self.__class__.__name__, title, dtype, group, default, initial_value, choices,
|
||||
is_radio, rounding, min_max, sysbrowser, helptext, track_modified, command)
|
||||
"initial_value: %s, choices: %s, is_radio: %s, is_multi_option: %s, "
|
||||
"rounding: %s, min_max: %s, sysbrowser: %s, helptext: '%s', "
|
||||
"track_modified: %s, command: '%s')", self.__class__.__name__, title, dtype,
|
||||
group, default, initial_value, choices, is_radio, is_multi_option, rounding,
|
||||
min_max, sysbrowser, helptext, track_modified, command)
|
||||
|
||||
self.dtype = dtype
|
||||
self.sysbrowser = sysbrowser
|
||||
|
|
@ -130,6 +132,7 @@ class ControlPanelOption():
|
|||
initial_value=initial_value,
|
||||
choices=choices,
|
||||
is_radio=is_radio,
|
||||
is_multi_option=is_multi_option,
|
||||
rounding=rounding,
|
||||
min_max=min_max,
|
||||
helptext=helptext)
|
||||
|
|
@ -176,6 +179,12 @@ class ControlPanelOption():
|
|||
""" Return is_radio """
|
||||
return self._options["is_radio"]
|
||||
|
||||
@property
|
||||
def is_multi_option(self):
|
||||
""" bool: ``True`` if the control should be contained in a multi check button group,
|
||||
otherwise ``False``. """
|
||||
return self._options["is_multi_option"]
|
||||
|
||||
@property
|
||||
def rounding(self):
|
||||
""" Return rounding """
|
||||
|
|
@ -241,13 +250,15 @@ class ControlPanelOption():
|
|||
def get_control(self):
|
||||
""" Set the correct control type based on the datatype or for this option """
|
||||
if self.choices and self.is_radio:
|
||||
control = ttk.Radiobutton
|
||||
control = "radio"
|
||||
elif self.choices and self.is_multi_option:
|
||||
control = "multi"
|
||||
elif self.choices:
|
||||
control = ttk.Combobox
|
||||
elif self.dtype == bool:
|
||||
control = ttk.Checkbutton
|
||||
elif self.dtype in (int, float):
|
||||
control = ttk.Scale
|
||||
control = "scale"
|
||||
else:
|
||||
control = ttk.Entry
|
||||
logger.debug("Setting control '%s' to %s", self.title, control)
|
||||
|
|
@ -590,7 +601,9 @@ class AutoFillContainer():
|
|||
"pack_info": self.pack_config_cleaner(child),
|
||||
"name": child.winfo_name(),
|
||||
"config": self.config_cleaner(child),
|
||||
"children": self.get_all_children_config(child, [])}
|
||||
"children": self.get_all_children_config(child, []),
|
||||
# Some children have custom kwargs, so keep dicts in sync
|
||||
"custom_kwargs": dict()}
|
||||
for idx, child in enumerate(children)]
|
||||
logger.debug("Compiled AutoFillContainer children: %s", self._widget_config)
|
||||
|
||||
|
|
@ -599,6 +612,15 @@ class AutoFillContainer():
|
|||
for child in widget.winfo_children():
|
||||
if child.winfo_ismapped():
|
||||
id_ = str(child)
|
||||
if child.__class__.__name__ == "MultiOption":
|
||||
# MultiOption checkbox groups are a custom object with additional parameter
|
||||
# requirements.
|
||||
custom_kwargs = dict(
|
||||
value=child._value, # pylint:disable=protected-access
|
||||
variable=child._master_variable) # pylint:disable=protected-access
|
||||
else:
|
||||
custom_kwargs = dict()
|
||||
|
||||
child_list.append({
|
||||
"class": child.__class__,
|
||||
"id": id_,
|
||||
|
|
@ -607,7 +629,8 @@ class AutoFillContainer():
|
|||
"pack_info": self.pack_config_cleaner(child),
|
||||
"name": child.winfo_name(),
|
||||
"config": self.config_cleaner(child),
|
||||
"parent": child.winfo_parent()})
|
||||
"parent": child.winfo_parent(),
|
||||
"custom_kwargs": custom_kwargs})
|
||||
self.get_all_children_config(child, child_list)
|
||||
return child_list
|
||||
|
||||
|
|
@ -668,7 +691,9 @@ class AutoFillContainer():
|
|||
else:
|
||||
# Get the next sub-frame if this doesn't have a logged parent
|
||||
parent = self.subframe
|
||||
clone = widget_dict["class"](parent, name=widget_dict["name"])
|
||||
clone = widget_dict["class"](parent,
|
||||
name=widget_dict["name"],
|
||||
**widget_dict["custom_kwargs"])
|
||||
if widget_dict["config"] is not None:
|
||||
clone.configure(**widget_dict["config"])
|
||||
if widget_dict["tooltip"] is not None:
|
||||
|
|
@ -746,7 +771,7 @@ class ControlBuilder():
|
|||
def build_control(self):
|
||||
""" Build the correct control type for the option passed through """
|
||||
logger.debug("Build config option control")
|
||||
if self.option.control not in (ttk.Checkbutton, ttk.Radiobutton):
|
||||
if self.option.control not in (ttk.Checkbutton, "radio", "multi"):
|
||||
self.build_control_label()
|
||||
self.build_one_control()
|
||||
logger.debug("Built option control")
|
||||
|
|
@ -764,10 +789,10 @@ class ControlBuilder():
|
|||
def build_one_control(self):
|
||||
""" Build and place the option controls """
|
||||
logger.debug("Build control: '%s')", self.option.name)
|
||||
if self.option.control == ttk.Scale:
|
||||
if self.option.control == "scale":
|
||||
ctl = self.slider_control()
|
||||
elif self.option.control == ttk.Radiobutton:
|
||||
ctl = self.radio_control()
|
||||
elif self.option.control in ("radio", "multi"):
|
||||
ctl = self._multi_option_control(self.option.control)
|
||||
elif self.option.control == ttk.Checkbutton:
|
||||
ctl = self.control_to_checkframe()
|
||||
else:
|
||||
|
|
@ -779,35 +804,65 @@ class ControlBuilder():
|
|||
|
||||
logger.debug("Built control: '%s'", self.option.name)
|
||||
|
||||
def radio_control(self):
|
||||
""" Create a group of radio buttons """
|
||||
logger.debug("Adding radio group: %s", self.option.name)
|
||||
all_help = [line for line in self.option.helptext.splitlines()]
|
||||
if any(line.startswith(" - ") for line in all_help):
|
||||
intro = all_help[0]
|
||||
helpitems = {re.sub(r'[^A-Za-z0-9\-]+', '',
|
||||
line.split()[1].lower()): " ".join(line.split()[1:])
|
||||
for line in all_help
|
||||
if line.startswith(" - ")}
|
||||
def _multi_option_control(self, option_type):
|
||||
""" Create a group of buttons for single or multi-select
|
||||
|
||||
Parameters
|
||||
----------
|
||||
option_type: {"radio", "multi"}
|
||||
The type of boxes that this control should hold. "radio" for single item select,
|
||||
"multi" for multi item select.
|
||||
|
||||
"""
|
||||
logger.debug("Adding %s group: %s", option_type, self.option.name)
|
||||
help_intro, help_items = self._get_multi_help_items(self.option.helptext)
|
||||
ctl = ttk.LabelFrame(self.frame,
|
||||
text=self.option.title,
|
||||
name="radio_labelframe")
|
||||
radio_holder = AutoFillContainer(ctl, self.option_columns, self.option_columns)
|
||||
name="{}_labelframe".format(option_type))
|
||||
holder = AutoFillContainer(ctl, self.option_columns, self.option_columns)
|
||||
for choice in self.option.choices:
|
||||
radio = ttk.Radiobutton(radio_holder.subframe,
|
||||
text=choice.replace("_", " ").title(),
|
||||
value=choice,
|
||||
variable=self.option.tk_var)
|
||||
if choice.lower() in helpitems:
|
||||
ctl = ttk.Radiobutton if option_type == "radio" else MultiOption
|
||||
ctl = ctl(holder.subframe,
|
||||
text=choice.replace("_", " ").title(),
|
||||
value=choice,
|
||||
variable=self.option.tk_var)
|
||||
if choice.lower() in help_items:
|
||||
self.helpset = True
|
||||
helptext = helpitems[choice.lower()].capitalize()
|
||||
helptext = help_items[choice.lower()].capitalize()
|
||||
helptext = "{}\n\n - {}".format(
|
||||
'. '.join(item.capitalize() for item in helptext.split('. ')),
|
||||
intro)
|
||||
_get_tooltip(radio, text=helptext, wraplength=600)
|
||||
radio.pack(anchor=tk.W)
|
||||
logger.debug("Added radio option %s", choice)
|
||||
return radio_holder.parent
|
||||
help_intro)
|
||||
_get_tooltip(ctl, text=helptext, wraplength=600)
|
||||
ctl.pack(anchor=tk.W)
|
||||
logger.debug("Added %s option %s", option_type, choice)
|
||||
return holder.parent
|
||||
|
||||
@staticmethod
|
||||
def _get_multi_help_items(helptext):
|
||||
""" Split the help text up, for formatted help text, into the individual options
|
||||
for multi/radio buttons.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
helptext: str
|
||||
The raw help text for this cli. option
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple (`str`, `dict`)
|
||||
The help text intro and a dictionary containing the help text split into separate
|
||||
entries for each option choice
|
||||
"""
|
||||
logger.debug("raw help: %s", helptext)
|
||||
all_help = helptext.splitlines()
|
||||
intro = ""
|
||||
if any(line.startswith(" - ") for line in all_help):
|
||||
intro = all_help[0]
|
||||
retval = (intro, {re.sub(r'[^A-Za-z0-9\-]+', '',
|
||||
line.split()[1].lower()): " ".join(line.split()[1:])
|
||||
for line in all_help if line.startswith(" - ")})
|
||||
logger.debug("help items: %s", retval)
|
||||
return retval
|
||||
|
||||
def slider_control(self):
|
||||
""" A slider control with corresponding Entry box """
|
||||
|
|
@ -829,7 +884,7 @@ class ControlBuilder():
|
|||
d_type=self.option.dtype,
|
||||
round_to=self.option.rounding,
|
||||
min_max=self.option.min_max)
|
||||
ctl = self.option.control(self.frame, variable=self.option.tk_var, command=cmd)
|
||||
ctl = ttk.Scale(self.frame, variable=self.option.tk_var, command=cmd)
|
||||
_add_command(ctl.cget("command"), cmd)
|
||||
rc_menu = _get_contextmenu(tbox)
|
||||
rc_menu.cm_bind()
|
||||
|
|
@ -885,7 +940,7 @@ class ControlBuilder():
|
|||
rc_menu.cm_bind()
|
||||
if self.option.choices:
|
||||
logger.debug("Adding combo choices: %s", self.option.choices)
|
||||
ctl["values"] = [choice for choice in self.option.choices]
|
||||
ctl["values"] = self.option.choices
|
||||
ctl["state"] = "readonly"
|
||||
logger.debug("Added control to Options Frame: %s", self.option.name)
|
||||
return ctl
|
||||
|
|
|
|||
|
|
@ -679,3 +679,84 @@ class Tooltip:
|
|||
if topwidget:
|
||||
topwidget.destroy()
|
||||
self._topwidget = None
|
||||
|
||||
|
||||
class MultiOption(ttk.Checkbutton): # pylint: disable=too-many-ancestors
|
||||
""" Similar to the standard :class:`ttk.Radio` widget, but with the ability to select
|
||||
multiple pre-defined options. Selected options are generated as `nargs` for the argument
|
||||
parser to consume.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
parent: :class:`ttk.Frame`
|
||||
The tkinter parent widget for the check button
|
||||
value: str
|
||||
The raw option value for this check button
|
||||
variable: :class:`tkinter.StingVar`
|
||||
The master variable for the group of check buttons that this check button will belong to.
|
||||
The output of this variable will be a string containing a space separated list of the
|
||||
selected check button options
|
||||
"""
|
||||
def __init__(self, parent, value, variable, **kwargs):
|
||||
self._tk_var = tk.BooleanVar()
|
||||
self._tk_var.set(False)
|
||||
super().__init__(parent, variable=self._tk_var, **kwargs)
|
||||
self._value = value
|
||||
self._master_variable = variable
|
||||
self._tk_var.trace("w", self._on_update)
|
||||
self._master_variable.trace("w", self._on_master_update)
|
||||
|
||||
@property
|
||||
def _master_list(self):
|
||||
""" list: The contents of the check box group's :attr:`_master_variable` in list form.
|
||||
Selected check boxes will appear in this list. """
|
||||
retval = self._master_variable.get().split()
|
||||
logger.trace(retval)
|
||||
return retval
|
||||
|
||||
@property
|
||||
def _master_needs_update(self):
|
||||
""" bool: ``True`` if :attr:`_master_variable` requires updating otherwise ``False``. """
|
||||
active = self._tk_var.get()
|
||||
retval = ((active and self._value not in self._master_list) or
|
||||
(not active and self._value in self._master_list))
|
||||
logger.trace(retval)
|
||||
return retval
|
||||
|
||||
def _on_update(self, *args): # pylint: disable=unused-argument
|
||||
""" Update the master variable on a check button change.
|
||||
|
||||
The value for this checked option is added or removed from the :attr:`_master_variable`
|
||||
on a ``True``, ``False`` change for this check button.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
args: tuple
|
||||
Required for variable callback, but unused
|
||||
"""
|
||||
if not self._master_needs_update:
|
||||
return
|
||||
new_vals = self._master_list + [self._value] if self._tk_var.get() else [
|
||||
val
|
||||
for val in self._master_list
|
||||
if val != self._value]
|
||||
val = " ".join(new_vals)
|
||||
logger.trace("Setting master variable to: %s", val)
|
||||
self._master_variable.set(val)
|
||||
|
||||
def _on_master_update(self, *args): # pylint: disable=unused-argument
|
||||
""" Update the check button on a master variable change (e.g. load .fsw file in the GUI).
|
||||
|
||||
The value for this option is set to ``True`` or ``False`` depending on it's existence in
|
||||
the :attr:`_master_variable`
|
||||
|
||||
Parameters
|
||||
----------
|
||||
args: tuple
|
||||
Required for variable callback, but unused
|
||||
"""
|
||||
if not self._master_needs_update:
|
||||
return
|
||||
state = self._value in self._master_list
|
||||
logger.trace("Setting '%s' to %s", self._value, state)
|
||||
self._tk_var.set(state)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import re
|
|||
import sys
|
||||
from collections import OrderedDict
|
||||
|
||||
from lib import cli
|
||||
from lib.cli import actions, args as cli
|
||||
from .utils import get_images
|
||||
from .control_helper import ControlPanelOption
|
||||
|
||||
|
|
@ -121,7 +121,8 @@ class CliOptions():
|
|||
group=opt.get("group", None),
|
||||
default=opt.get("default", None),
|
||||
choices=opt.get("choices", None),
|
||||
is_radio=opt.get("action", "") == cli.Radio,
|
||||
is_radio=opt.get("action", "") == actions.Radio,
|
||||
is_multi_option=opt.get("action", "") == actions.MultiOption,
|
||||
rounding=self.get_rounding(opt),
|
||||
min_max=opt.get("min_max", None),
|
||||
sysbrowser=self.get_sysbrowser(opt, command_options, command),
|
||||
|
|
@ -167,13 +168,12 @@ class CliOptions():
|
|||
def get_sysbrowser(self, option, options, command):
|
||||
""" Return the system file browser and file types if required else None """
|
||||
action = option.get("action", None)
|
||||
if action not in (cli.FullPaths,
|
||||
cli.DirFullPaths,
|
||||
cli.FileFullPaths,
|
||||
cli.FilesFullPaths,
|
||||
cli.DirOrFileFullPaths,
|
||||
cli.SaveFileFullPaths,
|
||||
cli.ContextFullPaths):
|
||||
if action not in (actions.DirFullPaths,
|
||||
actions.FileFullPaths,
|
||||
actions.FilesFullPaths,
|
||||
actions.DirOrFileFullPaths,
|
||||
actions.SaveFileFullPaths,
|
||||
actions.ContextFullPaths):
|
||||
return None
|
||||
|
||||
retval = dict()
|
||||
|
|
@ -182,15 +182,15 @@ class CliOptions():
|
|||
self.expand_action_option(option, options)
|
||||
action_option = option["action_option"]
|
||||
retval["filetypes"] = option.get("filetypes", "default")
|
||||
if action == cli.FileFullPaths:
|
||||
if action == actions.FileFullPaths:
|
||||
retval["browser"] = ["load"]
|
||||
elif action == cli.FilesFullPaths:
|
||||
elif action == actions.FilesFullPaths:
|
||||
retval["browser"] = ["multi_load"]
|
||||
elif action == cli.SaveFileFullPaths:
|
||||
elif action == actions.SaveFileFullPaths:
|
||||
retval["browser"] = ["save"]
|
||||
elif action == cli.DirOrFileFullPaths:
|
||||
elif action == actions.DirOrFileFullPaths:
|
||||
retval["browser"] = ["folder", "load"]
|
||||
elif action == cli.ContextFullPaths and action_option:
|
||||
elif action == actions.ContextFullPaths and action_option:
|
||||
retval["browser"] = ["context"]
|
||||
retval["command"] = command
|
||||
retval["action_option"] = action_option
|
||||
|
|
|
|||
|
|
@ -43,8 +43,9 @@ class Extractor():
|
|||
The name of a detector plugin as exists in :mod:`plugins.extract.detect`
|
||||
aligner: str
|
||||
The name of an aligner plugin as exists in :mod:`plugins.extract.align`
|
||||
masker: str
|
||||
The name of a masker plugin as exists in :mod:`plugins.extract.mask`
|
||||
masker: str or list
|
||||
The name of a masker plugin(s) as exists in :mod:`plugins.extract.mask`.
|
||||
This can be a single masker or a list of multiple maskers
|
||||
configfile: str, optional
|
||||
The path to a custom ``extract.ini`` configfile. If ``None`` then the system
|
||||
:file:`config/extract.ini` file will be used.
|
||||
|
|
@ -65,7 +66,7 @@ class Extractor():
|
|||
images fed to the aligner.Default: ``None``
|
||||
image_is_aligned: bool, optional
|
||||
Used to set the :attr:`plugins.extract.mask.image_is_aligned` attribute. Indicates to the
|
||||
masker that the fed in image is an aligned face rather than a frame.Default: ``False``
|
||||
masker that the fed in image is an aligned face rather than a frame. Default: ``False``
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ class Convert(): # pylint:disable=too-few-public-methods
|
|||
def process(self):
|
||||
""" The entry point for triggering the Conversion Process.
|
||||
|
||||
Should only be called from :class:`lib.cli.ScriptExecutor`
|
||||
Should only be called from :class:`lib.cli.launcher.ScriptExecutor`
|
||||
"""
|
||||
logger.debug("Starting Conversion")
|
||||
# queue_manager.debug_monitor(5)
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ class Extract(): # pylint:disable=too-few-public-methods
|
|||
def __init__(self, arguments):
|
||||
logger.debug("Initializing %s: (args: %s", self.__class__.__name__, arguments)
|
||||
self._args = arguments
|
||||
|
||||
self._output_dir = str(get_folder(self._args.output_dir))
|
||||
|
||||
logger.info("Output Directory: %s", self._args.output_dir)
|
||||
|
|
@ -51,9 +50,12 @@ class Extract(): # pylint:disable=too-few-public-methods
|
|||
self._post_process = PostProcess(arguments)
|
||||
configfile = self._args.configfile if hasattr(self._args, "configfile") else None
|
||||
normalization = None if self._args.normalization == "none" else self._args.normalization
|
||||
|
||||
maskers = ["components", "extended"]
|
||||
maskers += self._args.masker if self._args.masker else []
|
||||
self._extractor = Extractor(self._args.detector,
|
||||
self._args.aligner,
|
||||
[self._args.masker, "components", "extended"],
|
||||
maskers,
|
||||
configfile=configfile,
|
||||
multiprocess=not self._args.singleprocess,
|
||||
rotate_images=self._args.rotate_images,
|
||||
|
|
@ -106,7 +108,7 @@ class Extract(): # pylint:disable=too-few-public-methods
|
|||
def process(self):
|
||||
""" The entry point for triggering the Extraction Process.
|
||||
|
||||
Should only be called from :class:`lib.cli.ScriptExecutor`
|
||||
Should only be called from :class:`lib.cli.launcher.ScriptExecutor`
|
||||
"""
|
||||
logger.info('Starting, this may take a while...')
|
||||
# from lib.queue_manager import queue_manager ; queue_manager.debug_monitor(3)
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ class Train():
|
|||
def process(self):
|
||||
""" The entry point for triggering the Training Process.
|
||||
|
||||
Should only be called from :class:`lib.cli.ScriptExecutor`
|
||||
Should only be called from :class:`lib.cli.launcher.ScriptExecutor`
|
||||
"""
|
||||
logger.debug("Starting Training Process")
|
||||
logger.info("Training data directory: %s", self._args.model_dir)
|
||||
|
|
|
|||
2
tools.py
2
tools.py
|
|
@ -6,7 +6,7 @@ import sys
|
|||
from importlib import import_module
|
||||
|
||||
# Importing the various tools
|
||||
from lib.cli import FullHelpArgumentParser
|
||||
from lib.cli.args import FullHelpArgumentParser
|
||||
|
||||
# Python version check
|
||||
if sys.version_info[0] < 3:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
""" Tools for manipulating the alignments seralized file """
|
||||
""" Tools for manipulating the alignments serialized file """
|
||||
import sys
|
||||
import logging
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
""" Command Line Arguments for tools """
|
||||
from lib.cli import FaceSwapArgs
|
||||
from lib.cli import DirOrFileFullPaths, DirFullPaths, FilesFullPaths, Radio, Slider
|
||||
from lib.cli.args import FaceSwapArgs
|
||||
from lib.cli.actions import DirOrFileFullPaths, DirFullPaths, FilesFullPaths, Radio, Slider
|
||||
|
||||
_HELPTEXT = "This command lets you perform various tasks pertaining to an alignments file."
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
""" Command Line Arguments for tools """
|
||||
from lib.cli import FaceSwapArgs
|
||||
from lib.cli import ContextFullPaths, FileFullPaths, Radio
|
||||
from lib.cli.args import FaceSwapArgs
|
||||
from lib.cli.actions import ContextFullPaths, FileFullPaths, Radio
|
||||
from lib.utils import _image_extensions
|
||||
|
||||
_HELPTEXT = "This command allows you to easily execute common ffmpeg tasks."
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
""" Command Line Arguments for tools """
|
||||
from lib.cli import FaceSwapArgs
|
||||
from lib.cli import (DirOrFileFullPaths, DirFullPaths, FileFullPaths, Radio, Slider)
|
||||
from lib.cli.args import FaceSwapArgs
|
||||
from lib.cli.actions import (DirOrFileFullPaths, DirFullPaths, FileFullPaths, Radio, Slider)
|
||||
from plugins.plugin_loader import PluginLoader
|
||||
|
||||
_HELPTEXT = "This command lets you generate masks for existing alignments."
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
""" Command Line Arguments for tools """
|
||||
from lib.cli import FaceSwapArgs
|
||||
from lib.cli import DirOrFileFullPaths, DirFullPaths, FileFullPaths
|
||||
from lib.cli.args import FaceSwapArgs
|
||||
from lib.cli.actions import DirOrFileFullPaths, DirFullPaths, FileFullPaths
|
||||
|
||||
_HELPTEXT = "This command allows you to preview swaps to tweak convert settings."
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import numpy as np
|
|||
from PIL import Image, ImageTk
|
||||
|
||||
from lib.aligner import Extract as AlignerExtract
|
||||
from lib.cli import ConvertArgs
|
||||
from lib.cli.args import ConvertArgs
|
||||
from lib.gui.utils import get_images, get_config, initialize_config, initialize_images
|
||||
from lib.gui.custom_widgets import Tooltip
|
||||
from lib.gui.control_helper import ControlPanel, ControlPanelOption
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
""" Command Line Arguments for tools """
|
||||
from lib.cli import FaceSwapArgs
|
||||
from lib.cli import DirFullPaths
|
||||
from lib.cli.args import FaceSwapArgs
|
||||
from lib.cli.actions import DirFullPaths
|
||||
|
||||
_HELPTEXT = "This command lets you restore models from backup."
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
""" Command Line Arguments for tools """
|
||||
from lib.cli import FaceSwapArgs
|
||||
from lib.cli import DirFullPaths, SaveFileFullPaths, Radio, Slider
|
||||
from lib.cli.args import FaceSwapArgs
|
||||
from lib.cli.actions import DirFullPaths, SaveFileFullPaths, Radio, Slider
|
||||
|
||||
_HELPTEXT = "This command lets you sort images using various methods."
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user