1
0
Fork 0
mirror of https://github.com/deepfakes/faceswap synced 2025-06-08 20:13:52 -04:00
faceswap/lib/gui/display_page.py
torzdf d8557c1970
Faceswap 2.0 (#1045)
* Core Updates
    - Remove lib.utils.keras_backend_quiet and replace with get_backend() where relevant
    - Document lib.gpu_stats and lib.sys_info
    - Remove call to GPUStats.is_plaidml from convert and replace with get_backend()
    - lib.gui.menu - typofix

* Update Dependencies
Bump Tensorflow Version Check

* Port extraction to tf2

* Add custom import finder for loading Keras or tf.keras depending on backend

* Add `tensorflow` to KerasFinder search path

* Basic TF2 training running

* model.initializers - docstring fix

* Fix and pass tests for tf2

* Replace Keras backend tests with faceswap backend tests

* Initial optimizers update

* Monkey patch tf.keras optimizer

* Remove custom Adam Optimizers and Memory Saving Gradients

* Remove multi-gpu option. Add Distribution to cli

* plugins.train.model._base: Add Mirror, Central and Default distribution strategies

* Update tensorboard kwargs for tf2

* Penalized Loss - Fix for TF2 and AMD

* Fix syntax for tf2.1

* requirements typo fix

* Explicit None for clipnorm if using a distribution strategy

* Fix penalized loss for distribution strategies

* Update Dlight

* typo fix

* Pin to TF2.2

* setup.py - Install tensorflow from pip if not available in Conda

* Add reduction options and set default for mirrored distribution strategy

* Explicitly use default strategy rather than nullcontext

* lib.model.backup_restore documentation

* Remove mirrored strategy reduction method and default based on OS

* Initial restructure - training

* Remove PingPong
Start model.base refactor

* Model saving and resuming enabled

* More tidying up of model.base

* Enable backup and snapshotting

* Re-enable state file
Remove loss names from state file
Fix print loss function
Set snapshot iterations correctly

* Revert original model to Keras Model structure rather than custom layer
Output full model and sub model summary
Change NNBlocks to callables rather than custom keras layers

* Apply custom Conv2D layer

* Finalize NNBlock restructure
Update Dfaker blocks

* Fix reloading model under a different distribution strategy

* Pass command line arguments through to trainer

* Remove training_opts from model and reference params directly

* Tidy up model __init__

* Re-enable tensorboard logging
Suppress "Model Not Compiled" warning

* Fix timelapse

* lib.model.nnblocks - Bugfix residual block
Port dfaker
bugfix original

* dfl-h128 ported

* DFL SAE ported

* IAE Ported

* dlight ported

* port lightweight

* realface ported

* unbalanced ported

* villain ported

* lib.cli.args - Update Batchsize + move allow_growth to config

* Remove output shape definition
Get image sizes per side rather than globally

* Strip mask input from encoder

* Fix learn mask and output learned mask to preview

* Trigger Allow Growth prior to setting strategy

* Fix GUI Graphing

* GUI - Display batchsize correctly + fix training graphs

* Fix penalized loss

* Enable mixed precision training

* Update analysis displayed batch to match input

* Penalized Loss - Multi-GPU Fix

* Fix all losses for TF2

* Fix Reflect Padding

* Allow different input size for each side of the model

* Fix conv-aware initialization on reload

* Switch allow_growth order

* Move mixed_precision to cli

* Remove distrubution strategies

* Compile penalized loss sub-function into LossContainer

* Bump default save interval to 250
Generate preview on first iteration but don't save
Fix iterations to start at 1 instead of 0
Remove training deprecation warnings
Bump some scripts.train loglevels

* Add ability to refresh preview on demand on pop-up window

* Enable refresh of training preview from GUI

* Fix Convert
Debug logging in Initializers

* Fix Preview Tool

* Update Legacy TF1 weights to TF2
Catch stats error on loading stats with missing logs

* lib.gui.popup_configure - Make more responsive + document

* Multiple Outputs supported in trainer
Original Model - Mask output bugfix

* Make universal inference model for convert
Remove scaling from penalized mask loss (now handled at input to y_true)

* Fix inference model to work properly with all models

* Fix multi-scale output for convert

* Fix clipnorm issue with distribution strategies
Edit error message on OOM

* Update plaidml losses

* Add missing file

* Disable gmsd loss for plaidnl

* PlaidML - Basic training working

* clipnorm rewriting for mixed-precision

* Inference model creation bugfixes

* Remove debug code

* Bugfix: Default clipnorm to 1.0

* Remove all mask inputs from training code

* Remove mask inputs from convert

* GUI - Analysis Tab - Docstrings

* Fix rate in totals row

* lib.gui - Only update display pages if they have focus

* Save the model on first iteration

* plaidml - Fix SSIM loss with penalized loss

* tools.alignments - Remove manual and fix jobs

* GUI - Remove case formatting on help text

* gui MultiSelect custom widget - Set default values on init

* vgg_face2 - Move to plugins.extract.recognition and use plugins._base base class
cli - Add global GPU Exclude Option
tools.sort - Use global GPU Exlude option for backend
lib.model.session - Exclude all GPUs when running in CPU mode
lib.cli.launcher - Set backend to CPU mode when all GPUs excluded

* Cascade excluded devices to GPU Stats

* Explicit GPU selection for Train and Convert

* Reduce Tensorflow Min GPU Multiprocessor Count to 4

* remove compat.v1 code from extract

* Force TF to skip mixed precision compatibility check if GPUs have been filtered

* Add notes to config for non-working AMD losses

* Rasie error if forcing extract to CPU mode

* Fix loading of legace dfl-sae weights + dfl-sae typo fix

* Remove unused requirements
Update sphinx requirements
Fix broken rst file locations

* docs: lib.gui.display

* clipnorm amd condition check

* documentation - gui.display_analysis

* Documentation - gui.popup_configure

* Documentation - lib.logger

* Documentation - lib.model.initializers

* Documentation - lib.model.layers

* Documentation - lib.model.losses

* Documentation - lib.model.nn_blocks

* Documetation - lib.model.normalization

* Documentation - lib.model.session

* Documentation - lib.plaidml_stats

* Documentation: lib.training_data

* Documentation: lib.utils

* Documentation: plugins.train.model._base

* GUI Stats: prevent stats from using GPU

* Documentation - Original Model

* Documentation: plugins.model.trainer._base

* linting

* unit tests: initializers + losses

* unit tests: nn_blocks

* bugfix - Exclude gpu devices in train, not include

* Enable Exclude-Gpus in Extract

* Enable exclude gpus in tools

* Disallow multiple plugin types in a single model folder

* Automatically add exclude_gpus argument in for cpu backends

* Cpu backend fixes

* Relax optimizer test threshold

* Default Train settings - Set mask to Extended

* Update Extractor cli help text
Update to Python 3.8

* Fix FAN to run on CPU

* lib.plaidml_tools - typofix

* Linux installer - check for curl

* linux installer - typo fix
2020-08-12 10:36:41 +01:00

295 lines
10 KiB
Python

#!/usr/bin python3
""" Display Page parent classes for display section of the Faceswap GUI """
import logging
import tkinter as tk
from tkinter import ttk
from .custom_widgets import Tooltip
from .utils import get_images
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
class DisplayPage(ttk.Frame): # pylint: disable=too-many-ancestors
""" Parent frame holder for each tab.
Defines uniform structure for each tab to inherit from """
def __init__(self, parent, tab_name, helptext):
logger.debug("Initializing %s: (tab_name: '%s', helptext: %s)",
self.__class__.__name__, tab_name, helptext)
ttk.Frame.__init__(self, parent)
self._parent = parent
self.runningtask = parent.runningtask
self.helptext = helptext
self.tabname = tab_name
self.vars = {"info": tk.StringVar()}
self.add_optional_vars(self.set_vars())
self.subnotebook = self.add_subnotebook()
self.optsframe = self.add_options_frame()
self.add_options_info()
self.add_frame_separator()
self.set_mainframe_single_tab_style()
self.pack(fill=tk.BOTH, side=tk.TOP, anchor=tk.NW)
parent.add(self, text=self.tabname.title())
logger.debug("Initialized %s", self.__class__.__name__,)
@property
def _tab_is_active(self):
""" bool: ``True`` if the tab currently has focus otherwise ``False`` """
return self._parent.tab(self._parent.select(), "text").lower() == self.tabname.lower()
def add_optional_vars(self, varsdict):
""" Add page specific variables """
if isinstance(varsdict, dict):
for key, val in varsdict.items():
logger.debug("Adding: (%s: %s)", key, val)
self.vars[key] = val
@staticmethod
def set_vars():
""" Override to return a dict of page specific variables """
return dict()
def on_tab_select(self): # pylint:disable=no-self-use
""" Override for specific actions when the current tab is selected """
logger.debug("Returning as 'on_tab_select' not implemented for %s",
self.__class__.__name__)
def add_subnotebook(self):
""" Add the main frame notebook """
logger.debug("Adding subnotebook")
notebook = ttk.Notebook(self)
notebook.pack(side=tk.TOP, anchor=tk.NW, fill=tk.BOTH, expand=True)
return notebook
def add_options_frame(self):
""" Add the display tab options """
logger.debug("Adding options frame")
optsframe = ttk.Frame(self)
optsframe.pack(side=tk.BOTTOM, padx=5, pady=5, fill=tk.X)
return optsframe
def add_options_info(self):
""" Add the info bar """
logger.debug("Adding options info")
lblinfo = ttk.Label(self.optsframe,
textvariable=self.vars["info"],
anchor=tk.W,
width=70)
lblinfo.pack(side=tk.LEFT, padx=5, pady=5, anchor=tk.W)
def set_info(self, msg):
""" Set the info message """
logger.debug("Setting info: %s", msg)
self.vars["info"].set(msg)
def add_frame_separator(self):
""" Add a separator between top and bottom frames """
logger.debug("Adding frame seperator")
sep = ttk.Frame(self, height=2, relief=tk.RIDGE)
sep.pack(fill=tk.X, pady=(5, 0), side=tk.BOTTOM)
@staticmethod
def set_mainframe_single_tab_style():
""" Configure ttk notebook style to represent a single frame """
logger.debug("Setting main frame single tab style")
nbstyle = ttk.Style()
nbstyle.configure("single.TNotebook", borderwidth=0)
nbstyle.layout("single.TNotebook.Tab", [])
def subnotebook_add_page(self, tabtitle, widget=None):
""" Add a page to the sub notebook """
logger.debug("Adding subnotebook page: %s", tabtitle)
frame = widget if widget else ttk.Frame(self.subnotebook)
frame.pack(padx=5, pady=5, fill=tk.BOTH, expand=True)
self.subnotebook.add(frame, text=tabtitle)
self.subnotebook_configure()
return frame
def subnotebook_configure(self):
""" Configure notebook to display or hide tabs """
if len(self.subnotebook.children) == 1:
logger.debug("Setting single page style")
self.subnotebook.configure(style="single.TNotebook")
else:
logger.debug("Setting multi page style")
self.subnotebook.configure(style="TNotebook")
def subnotebook_hide(self):
""" Hide the subnotebook. Used for hiding
Optional displays """
if self.subnotebook and self.subnotebook.winfo_ismapped():
logger.debug("Hiding subnotebook")
self.subnotebook.pack_forget()
self.subnotebook.destroy()
self.subnotebook = None
def subnotebook_show(self):
""" Show subnotebook. Used for displaying
Optional displays """
if not self.subnotebook:
logger.debug("Showing subnotebook")
self.subnotebook = self.add_subnotebook()
def subnotebook_get_widgets(self):
""" Return each widget that sits within each
subnotebook frame """
logger.debug("Getting subnotebook widgets")
for child in self.subnotebook.winfo_children():
for widget in child.winfo_children():
yield widget
def subnotebook_get_titles_ids(self):
""" Return tabs ids and titles """
tabs = dict()
for tab_id in range(0, self.subnotebook.index("end")):
tabs[self.subnotebook.tab(tab_id, "text")] = tab_id
logger.debug(tabs)
return tabs
def subnotebook_page_from_id(self, tab_id):
""" Return subnotebook tab widget from it's ID """
tab_name = self.subnotebook.tabs()[tab_id].split(".")[-1]
logger.debug(tab_name)
return self.subnotebook.children[tab_name]
class DisplayOptionalPage(DisplayPage): # pylint: disable=too-many-ancestors
""" Parent Context Sensitive Display Tab """
def __init__(self, parent, tab_name, helptext, waittime, command=None):
logger.debug("%s: OptionalPage args: (waittime: %s, command: %s)",
self.__class__.__name__, waittime, command)
DisplayPage.__init__(self, parent, tab_name, helptext)
self._waittime = waittime
self.command = command
self.display_item = None
self.set_info_text()
self.add_options()
parent.select(self)
self.update_idletasks()
self._update_page()
@staticmethod
def set_vars():
""" Analysis specific vars """
enabled = tk.BooleanVar()
enabled.set(True)
ready = tk.BooleanVar()
ready.set(False)
modified = tk.DoubleVar()
modified.set(None)
tk_vars = {"enabled": enabled,
"ready": ready,
"modified": modified}
logger.debug(tk_vars)
return tk_vars
def on_tab_select(self):
""" Callback for when the optional tab is selected.
Run the tab's update code when the tab is selected.
"""
logger.debug("Callback received for '%s' tab", self.tabname)
self._update_page()
# INFO LABEL
def set_info_text(self):
""" Set waiting for display text """
if not self.vars["enabled"].get():
msg = "{} disabled".format(self.tabname.title())
elif self.vars["enabled"].get() and not self.vars["ready"].get():
msg = "Waiting for {}...".format(self.tabname)
else:
msg = "Displaying {}".format(self.tabname)
logger.debug(msg)
self.set_info(msg)
# DISPLAY OPTIONS BAR
def add_options(self):
""" Add the additional options """
self.add_option_save()
self.add_option_enable()
def add_option_save(self):
""" Add save button to save page output to file """
logger.debug("Adding save option")
btnsave = ttk.Button(self.optsframe,
image=get_images().icons["save"],
command=self.save_items)
btnsave.pack(padx=2, side=tk.RIGHT)
Tooltip(btnsave,
text="Save {}(s) to file".format(self.tabname),
wraplength=200)
def add_option_enable(self):
""" Add check-button to enable/disable page """
logger.debug("Adding enable option")
chkenable = ttk.Checkbutton(self.optsframe,
variable=self.vars["enabled"],
text="Enable {}".format(self.tabname),
command=self.on_chkenable_change)
chkenable.pack(side=tk.RIGHT, padx=5, anchor=tk.W)
Tooltip(chkenable,
text="Enable or disable {} display".format(self.tabname),
wraplength=200)
def save_items(self):
""" Save items. Override for display specific saving """
raise NotImplementedError()
def on_chkenable_change(self):
""" Update the display immediately on a check-button change """
logger.debug("Enabled checkbox changed")
if self.vars["enabled"].get():
self.subnotebook_show()
else:
self.subnotebook_hide()
self.set_info_text()
def _update_page(self):
""" Update the latest preview item """
if not self.runningtask.get() or not self._tab_is_active:
return
if self.vars["enabled"].get():
logger.trace("Updating page")
self.display_item_set()
self.load_display()
self.after(self._waittime, self._update_page)
def display_item_set(self):
""" Override for display specific loading """
raise NotImplementedError()
def load_display(self):
""" Load the display """
if not self.display_item or not self._tab_is_active:
return
logger.debug("Loading display for tab: %s", self.tabname)
self.display_item_process()
self.vars["ready"].set(True)
self.set_info_text()
def display_item_process(self):
""" Override for display specific loading """
raise NotImplementedError()
def close(self):
""" Called when the parent notebook is shutting down
Children must be destroyed as forget only hides display
Override for page specific shutdown """
for child in self.winfo_children():
logger.debug("Destroying child: %s", child)
child.destroy()