1
0
Fork 0
mirror of https://github.com/deepfakes/faceswap synced 2025-06-09 04:36:50 -04:00
faceswap/tools/sort/cli.py
Olivier Gagnon acf1fc5612
Add a new sorting/grouping feature based on black pixels in images (#1169)
* 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>
2021-08-07 12:24:41 +01:00

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