mirror of
https://github.com/deepfakes/faceswap.git
synced 2026-10-24 10:46:24 +08:00
* Convert prints to logger. Further logging improvements. Tidy up * Fix system verbosity. Allow SystemExit * Fix reload extract bug * Child Traceback handling * Safer shutdown procedure * Add shutdown event to queue manager * landmarks_as_xy > property. GUI notes + linting. Aligner bugfix * fix FaceFilter. Enable nFilter when no Filter is supplied * Fix blurry face filter * Continue on IO error. Better error handling * Explicitly print stack trace tocrash log * Windows Multiprocessing bugfix * Add git info and conda version to crash log * Windows/Anaconda mp bugfix * Logging fixes for training
145 lines
5.1 KiB
Python
145 lines
5.1 KiB
Python
#!/usr/bin/env python3
|
|
""" Base class for Face Aligner plugins
|
|
Plugins should inherit from this class
|
|
|
|
See the override methods for which methods are
|
|
required.
|
|
|
|
The plugin will receive a dict containing:
|
|
{"filename": <filename of source frame>,
|
|
"image": <source image>,
|
|
"detected_faces": <list of DlibRectangles>}
|
|
|
|
For each source item, the plugin must pass a dict to finalize containing:
|
|
{"filename": <filename of source frame>,
|
|
"image": <source image>,
|
|
"detected_faces": <list of dlibRectangles>,
|
|
"landmarks": <list of landmarks>}
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
import traceback
|
|
|
|
from io import StringIO
|
|
|
|
from lib.aligner import Extract
|
|
from lib.gpu_stats import GPUStats
|
|
|
|
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
|
|
|
|
|
|
class Aligner():
|
|
""" Landmarks Aligner Object """
|
|
def __init__(self, loglevel):
|
|
logger.debug("Initializing %s", self.__class__.__name__)
|
|
self.loglevel = loglevel
|
|
self.cachepath = os.path.join(os.path.dirname(__file__), ".cache")
|
|
self.extract = Extract()
|
|
self.init = None
|
|
|
|
# The input and output queues for the plugin.
|
|
# See lib.queue_manager.QueueManager for getting queues
|
|
self.queues = {"in": None, "out": None}
|
|
|
|
# Path to model if required
|
|
self.model_path = self.set_model_path()
|
|
|
|
# Approximate VRAM required for aligner. Used to calculate
|
|
# how many parallel processes / batches can be run.
|
|
# Be conservative to avoid OOM.
|
|
self.vram = None
|
|
logger.debug("Initialized %s", self.__class__.__name__)
|
|
|
|
# <<< OVERRIDE METHODS >>> #
|
|
# These methods must be overriden when creating a plugin
|
|
@staticmethod
|
|
def set_model_path():
|
|
""" path to data file/models
|
|
override for specific detector """
|
|
raise NotImplementedError()
|
|
|
|
def initialize(self, *args, **kwargs):
|
|
""" Inititalize the aligner
|
|
Tasks to be run before any alignments are performed.
|
|
Override for specific detector """
|
|
logger_init = kwargs["log_init"]
|
|
log_queue = kwargs["log_queue"]
|
|
logger_init(self.loglevel, log_queue)
|
|
logger.debug("_base initialize %s: (PID: %s, args: %s, kwargs: %s)",
|
|
self.__class__.__name__, os.getpid(), args, kwargs)
|
|
self.init = kwargs["event"]
|
|
self.queues["in"] = kwargs["in_queue"]
|
|
self.queues["out"] = kwargs["out_queue"]
|
|
|
|
def align(self, *args, **kwargs):
|
|
""" Process landmarks
|
|
Override for specific detector
|
|
Must return a list of dlib rects"""
|
|
try:
|
|
if not self.init:
|
|
self.initialize(*args, **kwargs)
|
|
except ValueError as err:
|
|
logger.error(err)
|
|
exit(1)
|
|
logger.debug("Launching Align: (args: %s kwargs: %s)", args, kwargs)
|
|
|
|
# <<< DETECTION WRAPPER >>> #
|
|
def run(self, *args, **kwargs):
|
|
""" Parent align process.
|
|
This should always be called as the entry point so exceptions
|
|
are passed back to parent.
|
|
Do not override """
|
|
try:
|
|
self.align(*args, **kwargs)
|
|
except Exception: # pylint: disable=broad-except
|
|
logger.error("Caught exception in child process: %s", os.getpid())
|
|
tb_buffer = StringIO()
|
|
traceback.print_exc(file=tb_buffer)
|
|
exception = {"exception": (os.getpid(), tb_buffer)}
|
|
self.queues["out"].put(exception)
|
|
exit(1)
|
|
|
|
# <<< FINALIZE METHODS>>> #
|
|
def finalize(self, output):
|
|
""" This should be called as the final task of each plugin
|
|
aligns faces and puts to the out queue """
|
|
if output == "EOF":
|
|
logger.trace("Item out: %s", output)
|
|
self.queues["out"].put("EOF")
|
|
return
|
|
logger.trace("Item out: %s", {key: val
|
|
for key, val in output.items()
|
|
if key != "image"})
|
|
self.queues["out"].put((output))
|
|
|
|
# <<< MISC METHODS >>> #
|
|
@staticmethod
|
|
def get_vram_free():
|
|
""" Return free and total VRAM on card with most VRAM free"""
|
|
stats = GPUStats()
|
|
vram = stats.get_card_most_free()
|
|
logger.verbose("Using device %s with %sMB free of %sMB",
|
|
vram["device"],
|
|
int(vram["free"]),
|
|
int(vram["total"]))
|
|
return int(vram["card_id"]), int(vram["free"]), int(vram["total"])
|
|
|
|
def get_item(self):
|
|
""" Yield one item from the queue """
|
|
while True:
|
|
item = self.queues["in"].get()
|
|
if isinstance(item, dict):
|
|
logger.trace("Item in: %s", {key: val
|
|
for key, val in item.items()
|
|
if key != "image"})
|
|
# Pass Detector failures straight out and quit
|
|
if item.get("exception", None):
|
|
self.queues["out"].put(item)
|
|
exit(1)
|
|
else:
|
|
logger.trace("Item in: %s", item)
|
|
yield item
|
|
if item == "EOF":
|
|
break
|