mirror of
https://github.com/deepfakes/faceswap
synced 2025-06-09 04:36:50 -04:00
* Add a new sorting/grouping feature based on the percentage of black pixels in images Context: - Faces are quite often moving and can get near borders of the image. - Because of that, the face image has the out of bound part replaced by black pixels. - Some are still good for training but others not. Also, one might want to keep an alignment file with the near borders faces but excluded (some of) them for training. - Having to manually move/delete those images in a dataset of thousands of them is painful and prone to mistakes. - Thus, having a way to sort and group those images would be helpful. At least, it is for me :-) Added features: - A new option in Tools/Sort for both Sort and Group By called Black-Pixels. - Sort feature: calculates the percentage of black pixels in each images and sort them from 0 to 100%. - Group By feature: Uses the Bins slider number selected and creates 100/Bins folders with the images percentage of black pixels. ie. a Bins of 5 will create 20 folders with the first one containing images with 0-5% of black pixels, the second one with 6-10%, etc. A static method as been added for the rounding error. * PEP8 fixes Co-authored-by: Olivier Gagnon <ogagnon@ludia.com>
171 lines
8.9 KiB
Python
171 lines
8.9 KiB
Python
#!/usr/bin/env python3
|
|
""" Command Line Arguments for tools """
|
|
import gettext
|
|
|
|
from lib.cli.args import FaceSwapArgs
|
|
from lib.cli.actions import DirFullPaths, SaveFileFullPaths, Radio, Slider
|
|
|
|
|
|
# LOCALES
|
|
_LANG = gettext.translation("tools.sort.cli", localedir="locales", fallback=True)
|
|
_ = _LANG.gettext
|
|
|
|
|
|
_HELPTEXT = _("This command lets you sort images using various methods.")
|
|
|
|
|
|
class SortArgs(FaceSwapArgs):
|
|
""" Class to parse the command line arguments for sort tool """
|
|
|
|
@staticmethod
|
|
def get_info():
|
|
""" Return command information """
|
|
return _("Sort faces using a number of different techniques")
|
|
|
|
@staticmethod
|
|
def get_argument_list():
|
|
""" Put the arguments in a list so that they are accessible from both argparse and gui """
|
|
argument_list = list()
|
|
argument_list.append(dict(
|
|
opts=('-i', '--input'),
|
|
action=DirFullPaths,
|
|
dest="input_dir",
|
|
group=_("data"),
|
|
help=_("Input directory of aligned faces."),
|
|
required=True))
|
|
argument_list.append(dict(
|
|
opts=('-o', '--output'),
|
|
action=DirFullPaths,
|
|
dest="output_dir",
|
|
group=_("data"),
|
|
help=_("Output directory for sorted aligned faces.")))
|
|
argument_list.append(dict(
|
|
opts=('-s', '--sort-by'),
|
|
action=Radio,
|
|
type=str,
|
|
choices=("blur", "blur-fft", "distance", "face", "face-cnn", "face-cnn-dissim",
|
|
"face-yaw", "hist", "hist-dissim", "color-gray", "color-luma", "color-green",
|
|
"color-orange", "size", "black-pixels"),
|
|
dest='sort_method',
|
|
group=_("sort settings"),
|
|
default="face",
|
|
help=_("R|Sort by method. Choose how images are sorted. "
|
|
"\nL|'blur': Sort faces by blurriness."
|
|
"\nL|'blur-fft': Sort faces by fft filtered blurriness."
|
|
"\nL|'distance' Sort faces by the estimated distance of the alignments from an "
|
|
"'average' face. This can be useful for eliminating misaligned faces."
|
|
"\nL|'face': Use VGG Face to sort by face similarity. This uses a pairwise "
|
|
"clustering algorithm to check the distances between 512 features on every "
|
|
"face in your set and order them appropriately."
|
|
"\nL|'face-cnn': Sort faces by their landmarks. You can adjust the threshold "
|
|
"with the '-t' (--ref_threshold) option."
|
|
"\nL|'face-cnn-dissim': Like 'face-cnn' but sorts by dissimilarity."
|
|
"\nL|'face-yaw': Sort faces by Yaw (rotation left to right)."
|
|
"\nL|'hist': Sort faces by their color histogram. You can adjust the threshold "
|
|
"with the '-t' (--ref_threshold) option."
|
|
"\nL|'hist-dissim': Like 'hist' but sorts by dissimilarity."
|
|
"\nL|'color-gray': Sort images by the average intensity of the converted "
|
|
"grayscale color channel."
|
|
"\nL|'color-luma': Sort images by the average intensity of the converted Y "
|
|
"color channel. Bright lighting and oversaturated images will be ranked first."
|
|
"\nL|'color-green': Sort images by the average intensity of the converted Cg "
|
|
"color channel. Green images will be ranked first and red images will be last."
|
|
"\nL|'color-orange': Sort images by the average intensity of the converted Co "
|
|
"color channel. Orange images will be ranked first and blue images will be "
|
|
"last."
|
|
"\nL|'size': Sort images by their size in the original frame. Faces closer to "
|
|
"the camera and from higher resolution sources will be sorted first, whilst "
|
|
"faces further from the camera and from lower resolution sources will be "
|
|
"sorted last."
|
|
"\nL|'black-pixels': Sort images by their number of black pixels. Useful when "
|
|
"faces are near borders and a large part of the image is black."
|
|
"\nDefault: face")))
|
|
argument_list.append(dict(
|
|
opts=('-k', '--keep'),
|
|
action='store_true',
|
|
dest='keep_original',
|
|
default=False,
|
|
group=_("output"),
|
|
help=_("Keeps the original files in the input directory. Be careful when using this "
|
|
"with rename grouping and no specified output directory as this would keep the "
|
|
"original and renamed files in the same directory.")))
|
|
argument_list.append(dict(
|
|
opts=('-t', '--ref_threshold'),
|
|
action=Slider,
|
|
min_max=(-1.0, 10.0),
|
|
rounding=2,
|
|
type=float,
|
|
dest='min_threshold',
|
|
group=_("sort settings"),
|
|
default=-1.0,
|
|
help=_("Float value. Minimum threshold to use for grouping comparison with 'face-cnn' "
|
|
"and 'hist' methods. The lower the value the more discriminating the grouping "
|
|
"is. Leaving -1.0 will allow the program set the default value automatically. "
|
|
"For face-cnn 7.2 should be enough, with 4 being very discriminating. For hist "
|
|
"0.3 should be enough, with 0.2 being very discriminating. Be careful setting "
|
|
"a value that's too low in a directory with many images, as this could result "
|
|
"in a lot of directories being created. Defaults: face-cnn 7.2, hist 0.3")))
|
|
argument_list.append(dict(
|
|
opts=('-fp', '--final-process'),
|
|
action=Radio,
|
|
type=str,
|
|
choices=("folders", "rename"),
|
|
dest='final_process',
|
|
default="rename",
|
|
group=_("output"),
|
|
help=_("R|Default: rename."
|
|
"\nL|'folders': files are sorted using the -s/--sort-by method, then they are "
|
|
"organized into folders using the -g/--group-by grouping method."
|
|
"\nL|'rename': files are sorted using the -s/--sort-by then they are "
|
|
"renamed.")))
|
|
argument_list.append(dict(
|
|
opts=('-g', '--group-by'),
|
|
action=Radio,
|
|
type=str,
|
|
choices=("blur", "blur-fft", "face-cnn", "face-yaw", "hist", "black-pixels"),
|
|
dest='group_method',
|
|
group=_("output"),
|
|
default="hist",
|
|
help=_("Group by method. When -fp/--final-processing by folders choose the how the "
|
|
"images are grouped after sorting. Default: hist")))
|
|
argument_list.append(dict(
|
|
opts=('-b', '--bins'),
|
|
action=Slider,
|
|
min_max=(1, 100),
|
|
rounding=1,
|
|
type=int,
|
|
dest='num_bins',
|
|
group=_("output"),
|
|
default=5,
|
|
help=_("Integer value. Number of folders that will be used to group by blur, "
|
|
"face-yaw and black-pixels. For blur folder 0 will be the least blurry, while "
|
|
"the last folder will be the blurriest. For face-yaw the number of bins is by "
|
|
"how much 180 degrees is divided. So if you use 18, then each folder will be "
|
|
"a 10 degree increment. Folder 0 will contain faces looking the most to the "
|
|
"left whereas the last folder will contain the faces looking the most to the "
|
|
"right. If the number of images doesn't divide evenly into the number of "
|
|
"bins, the remaining images get put in the last bin. For black-pixels it "
|
|
"represents the divider of the percentage of black pixels. For 10, first "
|
|
"folder will have the faces with 0 to 10% black pixels, second 11 to 20%, "
|
|
"etc. Default value: 5")))
|
|
argument_list.append(dict(
|
|
opts=('-l', '--log-changes'),
|
|
action='store_true',
|
|
group=_("settings"),
|
|
default=False,
|
|
help=_("Logs file renaming changes if grouping by renaming, or it logs the file "
|
|
"copying/movement if grouping by folders. If no log file is specified with "
|
|
"'--log-file', then a 'sort_log.json' file will be created in the input "
|
|
"directory.")))
|
|
argument_list.append(dict(
|
|
opts=('-lf', '--log-file'),
|
|
action=SaveFileFullPaths,
|
|
filetypes="alignments",
|
|
group=_("settings"),
|
|
dest='log_file_path',
|
|
default='sort_log.json',
|
|
help=_("Specify a log file to use for saving the renaming or grouping information. If "
|
|
"specified extension isn't 'json' or 'yaml', then json will be used as the "
|
|
"serializer, with the supplied filename. Default: sort_log.json")))
|
|
|
|
return argument_list
|