1
0
Fork 0
mirror of https://github.com/deepfakes/faceswap synced 2025-06-09 04:36:50 -04:00
faceswap/lib/gui/display_analysis.py
torzdf be8e235eab
Gui v3.0b (#436)
* GUI version 3 (#411)

GUI version 3.0a

* Required for Shaonlu mode (#416)

Added two modes - Original and Shaonlu.
The later requires this file to function.

* model update (#417)

New, functional Original 128 model

* OriginalHighRes 128 model update (#418)

Required for OriginalHighRes Model to function

* Add OriginalHighRes 128 update to gui branch (#421)

* Required for Shaonlu mode (#416)

Added two modes - Original and Shaonlu.
The later requires this file to function.

* model update (#417)

New, functional Original 128 model

* OriginalHighRes 128 model update (#418)

Required for OriginalHighRes Model to function

* Dev gui (#420)

* reduce singletons

* Fix tooltips and screen boundaries on popup

* Remove dpi fix. Fix context filebrowsers

* fix tools.py execution and context filebrowser bugs

* Bugfixes (#422)

* Bump matplotlib requirement. Fix polyfit. Fix TQDM on sort

* Fixed memory usage at 6GB cards. (#423)

- Switched default encoder to ORIGINAL
- Fixed memory consumption. Tested with geforce gtx 9800 ti with 6Gb; batch_size 8 no OOM or memory warnings now.

* Staging (#426)

* altered trainer (#425)

altered trainer to accommodate with model change

* Update Model.py (#424)

- Added saving state (currently only saved epoch number, to be extended in future)
- Changed saving to ThreadPoolExecutor

* Add DPI Scaling (#428)

* Add dpi scaling

* Hotfix for effmpeg. (#429)

effmpeg fixed so it works both in cli and gui.
Initial work done to add previewing feature to effmpeg (currently does nothing).
Some small spacing changes in other files to improve PEP8 conformity.

* PEP8 Linting (#430)

* pep8 linting

* Requirements version bump (#432)

* altered trainer (#425)

altered trainer to accommodate with model change

* Update Model.py (#424)

- Added saving state (currently only saved epoch number, to be extended in future)
- Changed saving to ThreadPoolExecutor

* Requirements version bump (#431)

This bumps the versions of:

    scandir
    h5py
    Keras
    opencv-python

to their latest vesions.

Virtual Environment will need to be setup again to make use of these.

* High DPI Fixes (#433)

* dpi scaling

* DPI Fixes

* Fix and improve context manager. (#434)

effmpeg tool:
Context manager for GUI fixed.

Context manager in general:
Functionality extended to allow configuring the context with both:
  command -> action
  command -> variable (cli argument) -> action

* Change epoch option to iterations

* Change epochs to iterations
2018-06-20 19:25:31 +02:00

505 lines
18 KiB
Python

#!/usr/bin python3
""" Analysis tab of Display Frame of the Faceswap GUI """
import csv
import tkinter as tk
from tkinter import ttk
from .display_graph import SessionGraph
from .display_page import DisplayPage
from .stats import Calculations, SavedSessions, SessionsSummary, SessionsTotals
from .tooltip import Tooltip
from .utils import Images, FileHandler
class Analysis(DisplayPage):
""" Session analysis tab """
def __init__(self, parent, tabname, helptext, scaling_factor):
DisplayPage.__init__(self, parent, tabname, helptext)
self.summary = None
self.add_options()
self.add_main_frame(scaling_factor)
def set_vars(self):
""" Analysis specific vars """
selected_id = tk.StringVar()
filename = tk.StringVar()
return {"selected_id": selected_id,
"filename": filename}
def add_main_frame(self, scaling_factor):
""" Add the main frame to the subnotebook
to hold stats and session data """
mainframe = self.subnotebook_add_page("stats")
self.stats = StatsData(mainframe,
self.vars["filename"],
self.vars["selected_id"],
self.helptext["stats"],
scaling_factor)
def add_options(self):
""" Add the options bar """
self.reset_session_info()
options = Options(self)
options.add_options()
def reset_session_info(self):
""" Reset the session info status to default """
self.vars["filename"].set(None)
self.set_info("No session data loaded")
def load_session(self):
""" Load previously saved sessions """
self.clear_session()
filename = FileHandler("open", "session").retfile
if not filename:
return
filename = filename.name
loaded_data = SavedSessions(filename).sessions
msg = filename
if len(filename) > 70:
msg = "...{}".format(filename[-70:])
self.set_session_summary(loaded_data, msg)
self.vars["filename"].set(filename)
def reset_session(self):
""" Load previously saved sessions """
self.clear_session()
if self.session.stats["iterations"] == 0:
print("Training not running")
return
loaded_data = self.session.historical.sessions
msg = "Currently running training session"
self.set_session_summary(loaded_data, msg)
self.vars["filename"].set("Currently running training session")
def set_session_summary(self, data, message):
""" Set the summary data and info message """
self.summary = SessionsSummary(data).summary
self.set_info("Session: {}".format(message))
self.stats.loaded_data = data
self.stats.tree_insert_data(self.summary)
def clear_session(self):
""" Clear sessions stats """
self.summary = None
self.stats.loaded_data = None
self.stats.tree_clear()
self.reset_session_info()
def save_session(self):
""" Save sessions stats to csv """
if not self.summary:
print("No summary data loaded. Nothing to save")
return
savefile = FileHandler("save", "csv").retfile
if not savefile:
return
write_dicts = [val for val in self.summary.values()]
fieldnames = sorted(key for key in write_dicts[0].keys())
with savefile as outfile:
csvout = csv.DictWriter(outfile, fieldnames)
csvout.writeheader()
for row in write_dicts:
csvout.writerow(row)
class Options(object):
""" Options bar of Analysis tab """
def __init__(self, parent):
self.optsframe = parent.optsframe
self.parent = parent
def add_options(self):
""" Add the display tab options """
self.add_buttons()
def add_buttons(self):
""" Add the option buttons """
for btntype in ("reset", "clear", "save", "load"):
cmd = getattr(self.parent, "{}_session".format(btntype))
btn = ttk.Button(self.optsframe,
image=Images().icons[btntype],
command=cmd)
btn.pack(padx=2, side=tk.RIGHT)
hlp = self.set_help(btntype)
Tooltip(btn, text=hlp, wraplength=200)
@staticmethod
def set_help(btntype):
""" Set the helptext for option buttons """
hlp = ""
if btntype == "reset":
hlp = "Load/Refresh stats for the currently training session"
elif btntype == "clear":
hlp = "Clear currently displayed session stats"
elif btntype == "save":
hlp = "Save session stats to csv"
elif btntype == "load":
hlp = "Load saved session stats"
return hlp
class StatsData(ttk.Frame):
""" Stats frame of analysis tab """
def __init__(self,
parent,
filename,
selected_id,
helptext,
scaling_factor):
ttk.Frame.__init__(self, parent)
self.pack(side=tk.TOP,
padx=5,
pady=5,
expand=True,
fill=tk.X,
anchor=tk.N)
self.filename = filename
self.loaded_data = None
self.selected_id = selected_id
self.popup_positions = list()
self.scaling_factor = scaling_factor
self.add_label()
self.tree = ttk.Treeview(self, height=1, selectmode=tk.BROWSE)
self.scrollbar = ttk.Scrollbar(self,
orient="vertical",
command=self.tree.yview)
self.columns = self.tree_configure(helptext)
def add_label(self):
""" Add Treeview Title """
lbl = ttk.Label(self, text="Session Stats", anchor=tk.CENTER)
lbl.pack(side=tk.TOP, expand=True, fill=tk.X, padx=5, pady=5)
def tree_configure(self, helptext):
""" Build a treeview widget to hold the sessions stats """
self.tree.configure(yscrollcommand=self.scrollbar.set)
self.tree.tag_configure("total",
background="black",
foreground="white")
self.tree.pack(side=tk.LEFT, expand=True, fill=tk.X)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.tree.bind("<ButtonRelease-1>", self.select_item)
Tooltip(self.tree, text=helptext, wraplength=200)
return self.tree_columns()
def tree_columns(self):
""" Add the columns to the totals treeview """
columns = (("session", 40, "#"),
("start", 130, None),
("end", 130, None),
("elapsed", 90, None),
("batch", 50, None),
("iterations", 90, None),
("rate", 60, "EGs/sec"))
self.tree["columns"] = [column[0] for column in columns]
for column in columns:
text = column[2] if column[2] else column[0].title()
self.tree.heading(column[0], text=text)
self.tree.column(column[0],
width=column[1],
anchor=tk.E,
minwidth=40)
self.tree.column("#0", width=40)
self.tree.heading("#0", text="Graphs")
return [column[0] for column in columns]
def tree_insert_data(self, sessions):
""" Insert the data into the totals treeview """
self.tree.configure(height=len(sessions))
for item in sessions:
values = [item[column] for column in self.columns]
kwargs = {"values": values, "image": Images().icons["graph"]}
if values[0] == "Total":
kwargs["tags"] = "total"
self.tree.insert("", "end", **kwargs)
def tree_clear(self):
""" Clear the totals tree """
self.tree.delete(* self.tree.get_children())
self.tree.configure(height=1)
def select_item(self, event):
""" Update the session summary info with
the selected item or launch graph """
region = self.tree.identify("region", event.x, event.y)
selection = self.tree.focus()
values = self.tree.item(selection, "values")
if values:
self.selected_id.set(values[0])
if region == "tree":
self.data_popup()
def data_popup(self):
""" Pop up a window and control it's position """
toplevel = SessionPopUp(self.loaded_data, self.selected_id.get())
toplevel.title(self.data_popup_title())
position = self.data_popup_get_position()
height = int(720 * self.scaling_factor)
width = int(400 * self.scaling_factor)
toplevel.geometry("{}x{}+{}+{}".format(str(height),
str(width),
str(position[0]),
str(position[1])))
toplevel.update()
def data_popup_title(self):
""" Set the data popup title """
selected_id = self.selected_id.get()
title = "All Sessions"
if selected_id != "Total":
title = "Session #{}".format(selected_id)
return "{} - {}".format(title, self.filename.get())
def data_popup_get_position(self):
""" Get the position of the next window """
init_pos = [120, 120]
pos = init_pos
while True:
if pos not in self.popup_positions:
self.popup_positions.append(pos)
break
pos = [item + 200 for item in pos]
init_pos, pos = self.data_popup_check_boundaries(init_pos, pos)
return pos
def data_popup_check_boundaries(self, initial_position, position):
""" Check that the popup remains within the screen boundaries """
boundary_x = self.winfo_screenwidth() - 120
boundary_y = self.winfo_screenheight() - 120
if position[0] >= boundary_x or position[1] >= boundary_y:
initial_position = [initial_position[0] + 50, initial_position[1]]
position = initial_position
return initial_position, position
class SessionPopUp(tk.Toplevel):
""" Pop up for detailed grap/stats for selected session """
def __init__(self, data, session_id):
tk.Toplevel.__init__(self)
self.is_totals = True if session_id == "Total" else False
self.data = self.set_session_data(data, session_id)
self.graph = None
self.display_data = None
self.vars = dict()
self.graph_initialised = False
self.build()
def set_session_data(self, sessions, session_id):
""" Set the correct list index based on the passed in session is """
if self.is_totals:
data = SessionsTotals(sessions).stats
else:
data = sessions[int(session_id) - 1]
return data
def build(self):
""" Build the popup window """
optsframe, graphframe = self.layout_frames()
self.opts_build(optsframe)
self.compile_display_data()
self.graph_build(graphframe)
def layout_frames(self):
""" Top level container frames """
leftframe = ttk.Frame(self)
leftframe.pack(side=tk.LEFT, expand=False, fill=tk.BOTH, pady=5)
sep = ttk.Frame(self, width=2, relief=tk.RIDGE)
sep.pack(fill=tk.Y, side=tk.LEFT)
rightframe = ttk.Frame(self)
rightframe.pack(side=tk.RIGHT, fill=tk.BOTH, pady=5, expand=True)
return leftframe, rightframe
def opts_build(self, frame):
""" Options in options to the optsframe """
self.opts_combobox(frame)
self.opts_checkbuttons(frame)
self.opts_entry(frame)
self.opts_buttons(frame)
sep = ttk.Frame(frame, height=2, relief=tk.RIDGE)
sep.pack(fill=tk.X, pady=(5, 0), side=tk.BOTTOM)
def opts_combobox(self, frame):
""" Add the options combo boxes """
choices = {"Display": ("Loss", "Rate"),
"Scale": ("Linear", "Log")}
for item in ["Display", "Scale"]:
var = tk.StringVar()
cmd = self.optbtn_reset if item == "Display" else self.graph_scale
var.trace("w", cmd)
cmbframe = ttk.Frame(frame)
cmbframe.pack(fill=tk.X, pady=5, padx=5, side=tk.TOP)
lblcmb = ttk.Label(cmbframe,
text="{}:".format(item),
width=7,
anchor=tk.W)
lblcmb.pack(padx=(0, 2), side=tk.LEFT)
cmb = ttk.Combobox(cmbframe, textvariable=var, width=10)
cmb["values"] = choices[item]
cmb.current(0)
cmb.pack(fill=tk.X, side=tk.RIGHT)
self.vars[item.lower().strip()] = var
hlp = self.set_help(item)
Tooltip(cmbframe, text=hlp, wraplength=200)
def opts_checkbuttons(self, frame):
""" Add the options check buttons """
for item in ("raw", "trend", "avg", "outliers"):
if item == "avg":
text = "Show Rolling Average"
elif item == "outliers":
text = "Flatten Outliers"
else:
text = "Show {}".format(item.title())
var = tk.BooleanVar()
if item == "raw":
var.set(True)
var.trace("w", self.optbtn_reset)
self.vars[item] = var
ctl = ttk.Checkbutton(frame, variable=var, text=text)
ctl.pack(side=tk.TOP, padx=5, pady=5, anchor=tk.W)
hlp = self.set_help(item)
Tooltip(ctl, text=hlp, wraplength=200)
def opts_entry(self, frame):
""" Add the options entry boxes """
for item in ("avgiterations", ):
if item == "avgiterations":
text = "Iterations to Average:"
default = "10"
entframe = ttk.Frame(frame)
entframe.pack(fill=tk.X, pady=5, padx=5, side=tk.TOP)
lbl = ttk.Label(entframe, text=text, anchor=tk.W)
lbl.pack(padx=(0, 2), side=tk.LEFT)
ctl = ttk.Entry(entframe, width=4, justify=tk.RIGHT)
ctl.pack(side=tk.RIGHT, anchor=tk.W)
ctl.insert(0, default)
hlp = self.set_help(item)
Tooltip(entframe, text=hlp, wraplength=200)
self.vars[item] = ctl
def opts_buttons(self, frame):
""" Add the option buttons """
btnframe = ttk.Frame(frame)
btnframe.pack(fill=tk.X, pady=5, padx=5, side=tk.BOTTOM)
for btntype in ("reset", "save"):
cmd = getattr(self, "optbtn_{}".format(btntype))
btn = ttk.Button(btnframe,
image=Images().icons[btntype],
command=cmd)
btn.pack(padx=2, side=tk.RIGHT)
hlp = self.set_help(btntype)
Tooltip(btn, text=hlp, wraplength=200)
def optbtn_save(self):
""" Action for save button press """
savefile = FileHandler("save", "csv").retfile
if not savefile:
return
save_data = self.display_data.stats
fieldnames = sorted(key for key in save_data.keys())
with savefile as outfile:
csvout = csv.writer(outfile, delimiter=",")
csvout.writerow(fieldnames)
csvout.writerows(zip(*[save_data[key] for key in fieldnames]))
def optbtn_reset(self, *args):
""" Action for reset button press and checkbox changes"""
if not self.graph_initialised:
return
self.compile_display_data()
self.graph.refresh(self.display_data,
self.vars["display"].get(),
self.vars["scale"].get())
def graph_scale(self, *args):
""" Action for changing graph scale """
if not self.graph_initialised:
return
self.graph.set_yscale_type(self.vars["scale"].get())
@staticmethod
def set_help(control):
""" Set the helptext for option buttons """
hlp = ""
control = control.lower()
if control == "reset":
hlp = "Refresh graph"
elif control == "save":
hlp = "Save display data to csv"
elif control == "avgiterations":
hlp = "Number of data points to sample for rolling average"
elif control == "outliers":
hlp = "Flatten data points that fall more than 1 standard " \
"deviation from the mean to the mean value."
elif control == "avg":
hlp = "Display rolling average of the data"
elif control == "raw":
hlp = "Display raw data"
elif control == "trend":
hlp = "Display polynormal data trend"
elif control == "display":
hlp = "Set the data to display"
elif control == "scale":
hlp = "Change y-axis scale"
return hlp
def compile_display_data(self):
""" Compile the data to be displayed """
self.display_data = Calculations(self.data,
self.vars["display"].get(),
self.selections_to_list(),
self.vars["avgiterations"].get(),
self.vars["outliers"].get(),
self.is_totals)
def selections_to_list(self):
""" Compile checkbox selections to list """
selections = list()
for key, val in self.vars.items():
if (isinstance(val, tk.BooleanVar)
and key != "outliers"
and val.get()):
selections.append(key)
return selections
def graph_build(self, frame):
""" Build the graph in the top right paned window """
self.graph = SessionGraph(frame,
self.display_data,
self.vars["display"].get(),
self.vars["scale"].get())
self.graph.pack(expand=True, fill=tk.BOTH)
self.graph.build()
self.graph_initialised = True