mirror of
https://github.com/zebrajr/faceswap.git
synced 2025-12-06 12:20:27 +01:00
Unified CLI (#22)
* Created a single script to call the other ones.
Usage is ./faceswap.py {train|extract|convert}
* Improved the help from the commands.
* Added forgotten faceswap.py file.
* Changed gitignore to add the scripts.
* Updates gitignore.
* Added guarding not to execute code when imported.
* Removed useless script. Display help when no arguments are provided.
* Update README
This commit is contained in:
parent
f0c971e8a5
commit
59d234ae5e
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -5,3 +5,4 @@
|
||||||
!Dockerfile*
|
!Dockerfile*
|
||||||
!requirements*
|
!requirements*
|
||||||
!lib/*.py
|
!lib/*.py
|
||||||
|
!scripts
|
||||||
|
|
@ -47,13 +47,13 @@ The project has multiple entry points. You will have to:
|
||||||
- **Convert** your sources with the model
|
- **Convert** your sources with the model
|
||||||
|
|
||||||
### Extract
|
### Extract
|
||||||
From your setup folder, run `python extract.py`. This will take photos from `src` folder and extract faces into `extract` folder.
|
From your setup folder, run `python faceswap.py extract`. This will take photos from `src` folder and extract faces into `extract` folder.
|
||||||
|
|
||||||
### Train
|
### Train
|
||||||
From your setup folder, run `python train.py`. This will take photos from `data/trump` and `data/cage` folder and train a model that will be saved inside the `models` folder.
|
From your setup folder, run `python faceswap.py train`. This will take photos from two folders containing pictures of both faces and train a model that will be saved inside the `models` folder.
|
||||||
|
|
||||||
### Convert
|
### Convert
|
||||||
From your setup folder, run `python convert.py`. This will take photos from `original` folder and apply new faces into `modified` folder.
|
From your setup folder, run `python faceswap.py convert`. This will take photos from `original` folder and apply new faces into `modified` folder.
|
||||||
|
|
||||||
#### General notes:
|
#### General notes:
|
||||||
- All of the scripts mentioned have `-h`/`--help` options with a arguments that they will accept. You're smart, you can figure out how this works, right?!
|
- All of the scripts mentioned have `-h`/`--help` options with a arguments that they will accept. You're smart, you can figure out how this works, right?!
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
### Simple face extractor
|
|
||||||
|
|
||||||
Takes pictures in the 'src' folder and extracts faces in 'extract'
|
|
||||||
|
|
||||||
Note : the cropped area may have to be improved
|
|
||||||
|
|
||||||
From https://docs.opencv.org/3.3.0/d7/d8b/tutorial_py_face_detection.html
|
|
||||||
And https://stackoverflow.com/questions/13211745/detect-face-then-autocrop-pictures
|
|
||||||
|
|
||||||
You can also look tools like https://github.com/leblancfg/autocrop
|
|
||||||
17
faceswap.py
Executable file
17
faceswap.py
Executable file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
from scripts.extract import ExtractTrainingData
|
||||||
|
from lib.cli import TrainingProcessor
|
||||||
|
from scripts.convert import ConvertImage
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
subparser = parser.add_subparsers()
|
||||||
|
extract = ExtractTrainingData(subparser, "extract", "Extract the faces from a pictures.")
|
||||||
|
train = TrainingProcessor(subparser, "train", "This command trains the model for the two faces A and B.")
|
||||||
|
convert = ConvertImage(subparser, "convert", "Convert a source image to a new one with the face swapped.")
|
||||||
|
arguments = parser.parse_args()
|
||||||
|
try:
|
||||||
|
arguments.func(arguments)
|
||||||
|
except:
|
||||||
|
parser.print_help()
|
||||||
54
lib/cli.py
54
lib/cli.py
|
|
@ -22,10 +22,11 @@ class FullPaths(argparse.Action):
|
||||||
class TrainingProcessor(object):
|
class TrainingProcessor(object):
|
||||||
arguments = None
|
arguments = None
|
||||||
|
|
||||||
def __init__(self, description='default'):
|
def __init__(self, subparser, command, description='default'):
|
||||||
print('Initializing')
|
self.parse_arguments(description, subparser, command)
|
||||||
self.parse_arguments(description)
|
|
||||||
|
|
||||||
|
def process_arguments(self, arguments):
|
||||||
|
self.arguments = arguments
|
||||||
print("Model A Directory: {}".format(self.arguments.input_A))
|
print("Model A Directory: {}".format(self.arguments.input_A))
|
||||||
print("Model B Directory: {}".format(self.arguments.input_B))
|
print("Model B Directory: {}".format(self.arguments.input_B))
|
||||||
print("Training data directory: {}".format(self.arguments.model_dir))
|
print("Training data directory: {}".format(self.arguments.model_dir))
|
||||||
|
|
@ -41,8 +42,10 @@ class TrainingProcessor(object):
|
||||||
|
|
||||||
self.process()
|
self.process()
|
||||||
|
|
||||||
def parse_arguments(self, description):
|
def parse_arguments(self, description, subparser, command):
|
||||||
parser = argparse.ArgumentParser(
|
parser = subparser.add_parser(
|
||||||
|
command,
|
||||||
|
help="This command trains the model for the two faces A and B.",
|
||||||
description=description,
|
description=description,
|
||||||
epilog="Questions and feedback: \
|
epilog="Questions and feedback: \
|
||||||
https://github.com/deepfakes/faceswap-playground"
|
https://github.com/deepfakes/faceswap-playground"
|
||||||
|
|
@ -78,7 +81,7 @@ class TrainingProcessor(object):
|
||||||
default=False,
|
default=False,
|
||||||
help="Show verbose output")
|
help="Show verbose output")
|
||||||
parser = self.add_optional_arguments(parser)
|
parser = self.add_optional_arguments(parser)
|
||||||
self.arguments = parser.parse_args()
|
parser.set_defaults(func=self.process_arguments)
|
||||||
|
|
||||||
def add_optional_arguments(self, parser):
|
def add_optional_arguments(self, parser):
|
||||||
# Override this for custom arguments
|
# Override this for custom arguments
|
||||||
|
|
@ -150,6 +153,7 @@ class DirectoryProcessor(object):
|
||||||
and writes output to the specified folder
|
and writes output to the specified folder
|
||||||
'''
|
'''
|
||||||
arguments = None
|
arguments = None
|
||||||
|
parser = None
|
||||||
|
|
||||||
input_dir = None
|
input_dir = None
|
||||||
output_dir = None
|
output_dir = None
|
||||||
|
|
@ -159,10 +163,12 @@ class DirectoryProcessor(object):
|
||||||
images_processed = 0
|
images_processed = 0
|
||||||
faces_detected = 0
|
faces_detected = 0
|
||||||
|
|
||||||
def __init__(self, description='default'):
|
def __init__(self, subparser, command, description='default'):
|
||||||
print('Initializing')
|
self.create_parser(subparser, command, description)
|
||||||
self.parse_arguments(description)
|
self.parse_arguments(description, subparser, command)
|
||||||
|
|
||||||
|
def process_arguments(self, arguments):
|
||||||
|
self.arguments = arguments
|
||||||
print("Input Directory: {}".format(self.arguments.input_dir))
|
print("Input Directory: {}".format(self.arguments.input_dir))
|
||||||
print("Output Directory: {}".format(self.arguments.output_dir))
|
print("Output Directory: {}".format(self.arguments.output_dir))
|
||||||
print('Starting, this may take a while...')
|
print('Starting, this may take a while...')
|
||||||
|
|
@ -176,6 +182,9 @@ class DirectoryProcessor(object):
|
||||||
|
|
||||||
self.images_found = len(self.input_dir)
|
self.images_found = len(self.input_dir)
|
||||||
|
|
||||||
|
self.process_directory()
|
||||||
|
|
||||||
|
def process_directory(self):
|
||||||
for filename in self.input_dir:
|
for filename in self.input_dir:
|
||||||
if self.arguments.verbose:
|
if self.arguments.verbose:
|
||||||
print('Processing: {}'.format(os.path.basename(filename)))
|
print('Processing: {}'.format(os.path.basename(filename)))
|
||||||
|
|
@ -185,32 +194,35 @@ class DirectoryProcessor(object):
|
||||||
|
|
||||||
self.finalize()
|
self.finalize()
|
||||||
|
|
||||||
def parse_arguments(self, description):
|
def parse_arguments(self, description, subparser, command):
|
||||||
parser = argparse.ArgumentParser(
|
self.parser.add_argument('-i', '--input-dir',
|
||||||
description=description,
|
|
||||||
epilog="Questions and feedback: \
|
|
||||||
https://github.com/deepfakes/faceswap-playground"
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument('-i', '--input-dir',
|
|
||||||
action=FullPaths,
|
action=FullPaths,
|
||||||
dest="input_dir",
|
dest="input_dir",
|
||||||
default="input",
|
default="input",
|
||||||
help="Input directory. A directory containing the files \
|
help="Input directory. A directory containing the files \
|
||||||
you wish to process. Defaults to 'input'")
|
you wish to process. Defaults to 'input'")
|
||||||
parser.add_argument('-o', '--output-dir',
|
self.parser.add_argument('-o', '--output-dir',
|
||||||
action=FullPaths,
|
action=FullPaths,
|
||||||
dest="output_dir",
|
dest="output_dir",
|
||||||
default="output",
|
default="output",
|
||||||
help="Output directory. This is where the converted files will \
|
help="Output directory. This is where the converted files will \
|
||||||
be stored. Defaults to 'output'")
|
be stored. Defaults to 'output'")
|
||||||
parser.add_argument('-v', '--verbose',
|
self.parser.add_argument('-v', '--verbose',
|
||||||
action="store_true",
|
action="store_true",
|
||||||
dest="verbose",
|
dest="verbose",
|
||||||
default=False,
|
default=False,
|
||||||
help="Show verbose output")
|
help="Show verbose output")
|
||||||
parser = self.add_optional_arguments(parser)
|
self.parser = self.add_optional_arguments(self.parser)
|
||||||
self.arguments = parser.parse_args()
|
self.parser.set_defaults(func=self.process_arguments)
|
||||||
|
|
||||||
|
def create_parser(self, subparser, command, description):
|
||||||
|
parser = subparser.add_parser(
|
||||||
|
command,
|
||||||
|
description=description,
|
||||||
|
epilog="Questions and feedback: \
|
||||||
|
https://github.com/deepfakes/faceswap-playground"
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
def add_optional_arguments(self, parser):
|
def add_optional_arguments(self, parser):
|
||||||
# Override this for custom arguments
|
# Override this for custom arguments
|
||||||
|
|
|
||||||
0
scripts/__init__.py
Normal file
0
scripts/__init__.py
Normal file
|
|
@ -7,6 +7,14 @@ from lib.faces_detect import crop_faces
|
||||||
|
|
||||||
class ConvertImage(DirectoryProcessor):
|
class ConvertImage(DirectoryProcessor):
|
||||||
filename = ''
|
filename = ''
|
||||||
|
def create_parser(self, subparser, command, description):
|
||||||
|
self.parser = subparser.add_parser(
|
||||||
|
command,
|
||||||
|
help="Convert a source image to a new one with the face swapped.",
|
||||||
|
description=description,
|
||||||
|
epilog="Questions and feedback: \
|
||||||
|
https://github.com/deepfakes/faceswap-playground"
|
||||||
|
)
|
||||||
|
|
||||||
def add_optional_arguments(self, parser):
|
def add_optional_arguments(self, parser):
|
||||||
parser.add_argument('-m', '--model-dir',
|
parser.add_argument('-m', '--model-dir',
|
||||||
|
|
@ -34,4 +42,3 @@ class ConvertImage(DirectoryProcessor):
|
||||||
print('Failed to extract from image: {}. Reason: {}'.format(filename, e))
|
print('Failed to extract from image: {}. Reason: {}'.format(filename, e))
|
||||||
|
|
||||||
|
|
||||||
extract_cli = ConvertImage(description='Swaps faces for images in a directory')
|
|
||||||
|
|
@ -4,12 +4,17 @@ from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
class ExtractTrainingData(DirectoryProcessor):
|
class ExtractTrainingData(DirectoryProcessor):
|
||||||
|
def create_parser(self, subparser, command, description):
|
||||||
|
self.parser = subparser.add_parser(
|
||||||
|
command,
|
||||||
|
help="Extract the faces from a pictures.",
|
||||||
|
description=description,
|
||||||
|
epilog="Questions and feedback: \
|
||||||
|
https://github.com/deepfakes/faceswap-playground"
|
||||||
|
)
|
||||||
|
|
||||||
def process_face(self, face, index, filename):
|
def process_face(self, face, index, filename):
|
||||||
resized_image = cv2.resize(face.image, (256, 256))
|
resized_image = cv2.resize(face.image, (256, 256))
|
||||||
output_file = self.output_dir / Path(filename).stem
|
output_file = self.output_dir / Path(filename).stem
|
||||||
cv2.imwrite(str(output_file) + str(index) + Path(filename).suffix,
|
cv2.imwrite(str(output_file) + str(index) + Path(filename).suffix,
|
||||||
resized_image)
|
resized_image)
|
||||||
|
|
||||||
|
|
||||||
extract_cli = ExtractTrainingData(description='Extracts faces from a collection of pictures \
|
|
||||||
and saves them to a separate directory')
|
|
||||||
Loading…
Reference in New Issue
Block a user