mirror of
https://github.com/deepfakes/faceswap
synced 2025-06-07 10:43:27 -04:00
* Remove tensorflow_probability requirement * setup.py - fix progress bars * requirements.txt: Remove pre python 3.9 packages * update apple requirements.txt * update INSTALL.md * Remove python<3.9 code * setup.py - fix Windows Installer * typing: python3.9 compliant * Update pytest and readthedocs python versions * typing fixes * Python Version updates - Reduce max version to 3.10 - Default to 3.10 in installers - Remove incompatible 3.11 tests * Update dependencies * Downgrade imageio dep for Windows * typing: merge optional unions and fixes * Updates - min python version 3.10 - typing to python 3.10 spec - remove pre-tf2.10 code - Add conda tests * train: re-enable optimizer saving * Update dockerfiles * Update setup.py - Apple Conda deps to setup.py - Better Cuda + dependency handling * bugfix: Patch logging to prevent Autograph errors * Update dockerfiles * Setup.py - Setup.py - stdout to utf-8 * Add more OSes to github Actions * suppress mac-os end to end test
419 lines
16 KiB
Python
419 lines
16 KiB
Python
#!/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
|
|
import typing as T
|
|
|
|
|
|
# << 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) -> 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: str | None = None, **kwargs) -> None:
|
|
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: str | None = None, **kwargs) -> None:
|
|
if kwargs.get("nargs", None) is None:
|
|
opt = kwargs["option_strings"]
|
|
raise ValueError(f"nargs must be provided for FilesFullPaths: {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))"
|
|
"""
|
|
|
|
|
|
class DirOrFilesFullPaths(FileFullPaths): # pylint: disable=too-few-public-methods
|
|
""" Adds support to the GUI to launch either a file browser for selecting multiple files
|
|
or a folder browser.
|
|
|
|
Some inputs (for example face filter) can come from a folder of images or from multiple
|
|
image file. This indicates to the GUI that it should place 2 buttons (one for a folder
|
|
browser, one for a multi-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))"
|
|
"""
|
|
def __call__(self, parser, namespace, values, option_string=None) -> None:
|
|
""" Override :class:`_FullPaths` __call__ function.
|
|
|
|
The input for this option can be a space separated list of files or a single folder.
|
|
Folders can have spaces in them, so we don't want to blindly expand the paths.
|
|
|
|
We check whether the input can be resolved to a folder first before expanding.
|
|
"""
|
|
assert isinstance(values, (list, tuple))
|
|
folder = os.path.abspath(os.path.expanduser(" ".join(values)))
|
|
if os.path.isdir(folder):
|
|
setattr(namespace, self.dest, [folder])
|
|
else: # file list so call parent method
|
|
super().__call__(parser, namespace, values, option_string)
|
|
|
|
|
|
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: str | None = None,
|
|
action_option: str | None = None,
|
|
**kwargs) -> None:
|
|
opt = kwargs["option_strings"]
|
|
if kwargs.get("nargs", None) is not None:
|
|
raise ValueError(f"nargs not allowed for ContextFullPaths: {opt}")
|
|
if filetypes is None:
|
|
raise ValueError(f"filetypes is required for ContextFullPaths: {opt}")
|
|
if action_option is None:
|
|
raise ValueError(f"action_option is required for ContextFullPaths: {opt}")
|
|
super().__init__(*args, filetypes=filetypes, **kwargs)
|
|
self.action_option = action_option
|
|
|
|
def _get_kwargs(self) -> list[tuple[str, T.Any]]:
|
|
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) -> None:
|
|
opt = kwargs["option_strings"]
|
|
if kwargs.get("nargs", None) is not None:
|
|
raise ValueError(f"nargs not allowed for Radio buttons: {opt}")
|
|
if not kwargs.get("choices", []):
|
|
raise ValueError(f"Choices must be provided for Radio buttons: {opt}")
|
|
super().__init__(*args, **kwargs)
|
|
|
|
def __call__(self, parser, namespace, values, option_string=None) -> 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) -> None:
|
|
opt = kwargs["option_strings"]
|
|
if not kwargs.get("nargs", []):
|
|
raise ValueError(f"nargs must be provided for MultiOption: {opt}")
|
|
if not kwargs.get("choices", []):
|
|
raise ValueError(f"Choices must be provided for MultiOption: {opt}")
|
|
super().__init__(*args, **kwargs)
|
|
|
|
def __call__(self, parser, namespace, values, option_string=None) -> 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: tuple[int, int] | tuple[float, float] | None = None,
|
|
rounding: int | None = None,
|
|
**kwargs) -> None:
|
|
opt = kwargs["option_strings"]
|
|
if kwargs.get("nargs", None) is not None:
|
|
raise ValueError(f"nargs not allowed for Slider: {opt}")
|
|
if kwargs.get("default", None) is None:
|
|
raise ValueError(f"A default value must be supplied for Slider: {opt}")
|
|
if kwargs.get("type", None) not in (int, float):
|
|
raise ValueError(f"Sliders only accept int and float data types: {opt}")
|
|
if min_max is None:
|
|
raise ValueError(f"min_max must be provided for Sliders: {opt}")
|
|
if rounding is None:
|
|
raise ValueError(f"rounding must be provided for Sliders: {opt}")
|
|
|
|
super().__init__(*args, **kwargs)
|
|
self.min_max = min_max
|
|
self.rounding = rounding
|
|
|
|
def _get_kwargs(self) -> list[tuple[str, T.Any]]:
|
|
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) -> None:
|
|
setattr(namespace, self.dest, values)
|