Overhaul sort:

- Standardize image data reading and writing
  - Optimize loading (just one pass required)
  - Make all sort groups binnable (to greater or lesser results)
  - Add sort by pitch
  - Deprecate multiple options
  - linting, docs + locales
This commit is contained in:
torzdf 2022-09-13 13:08:59 +01:00
parent 42a010b17a
commit 98d01760e4
16 changed files with 2635 additions and 1384 deletions

34
docs/full/tools/sort.rst Normal file
View File

@ -0,0 +1,34 @@
************
sort package
************
.. contents:: Contents
:local:
sort module
===========
The Sort Module is the main entry point into the Sort Tool.
.. automodule:: tools.sort.sort
:members:
:undoc-members:
:show-inheritance:
sort_methods module
===================
.. automodule:: tools.sort.sort_methods
:members:
:undoc-members:
:show-inheritance:
sort_methods_algigned module
============================
.. automodule:: tools.sort.sort_methods_algigned
:members:
:undoc-members:
:show-inheritance:

View File

@ -14,6 +14,7 @@ Subpackages
:maxdepth: 1
manual
sort
alignments module
=================

View File

@ -418,7 +418,7 @@ class AlignedFace():
roi = np.rint(self.transform_points(roi, invert=True)).astype("int32")
logger.trace("original roi: %s", roi) # type: ignore
self._cache.original_roi = roi
return self._cache.original_roi[0]
return self._cache.original_roi
@property
def landmarks(self) -> np.ndarray:

View File

@ -1227,6 +1227,29 @@ class FacesLoader(ImagesLoader):
path, count)
super().__init__(path, queue_size=8, skip_list=skip_list, count=count)
def _get_count_and_filelist(self, fast_count, count):
""" Override default implementation to only return png files from the source folder
Parameters
----------
fast_count: bool
Not used for faces loader
count: int
The number of images that the loader will encounter if already known, otherwise
``None``
"""
if isinstance(self.location, (list, tuple)):
file_list = self.location
else:
file_list = get_image_paths(self.location)
self._file_list = [fname for fname in file_list
if os.path.splitext(fname)[-1].lower() == ".png"]
self._count = len(self.file_list) if count is None else count
logger.debug("count: %s", self.count)
logger.trace("filelist: %s", self.file_list)
def _from_folder(self):
""" Generator for loading images from a folder
Faces will only ever be loaded from a folder, so this is the only function requiring

Binary file not shown.

View File

@ -1,27 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: faceswap.spanish\n"
"POT-Creation-Date: 2021-02-18 23:49-0000\n"
"PO-Revision-Date: 2021-02-19 18:00+0000\n"
"Language-Team: tokafondo\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 2.3\n"
"Last-Translator: \n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Language: es_ES\n"
#: tools.py:46
msgid ""
"Please backup your data and/or test the tool you want to use with a smaller "
"data set to make sure you understand how it works."
msgstr ""
"Por favor, haga una copia de seguridad de sus datos, y pruebe la "
"herramienta que quiere utilizar con un conjunto de datos más pequeño para "
"asegurarse de que entiende cómo funciona."

View File

@ -6,8 +6,8 @@ msgid ""
msgstr ""
"Project-Id-Version: faceswap.spanish\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-09-07 12:34+0100\n"
"PO-Revision-Date: 2022-09-07 12:35+0100\n"
"POT-Creation-Date: 2022-09-13 12:49+0100\n"
"PO-Revision-Date: 2022-09-13 12:54+0100\n"
"Last-Translator: \n"
"Language-Team: tokafondo\n"
"Language: es_ES\n"
@ -23,199 +23,342 @@ msgid "This command lets you sort images using various methods."
msgstr ""
"Este comando le permite ordenar las imágenes utilizando varios métodos."
#: tools/sort/cli.py:23
msgid "Sort faces using a number of different techniques"
msgstr "Clasificar los rostros mediante diferentes técnicas"
#: tools/sort/cli.py:20
msgid ""
" Adjust the '-t' ('--threshold') parameter to control the strength of "
"grouping."
msgstr ""
" Ajuste el parámetro '-t' ('--threshold') para controlar la fuerza de la "
"agrupación."
#: tools/sort/cli.py:33 tools/sort/cli.py:40 tools/sort/cli.py:47
msgid "data"
msgstr "datos"
#: tools/sort/cli.py:21
msgid ""
" Adjust the '-b' ('--bins') parameter to control the number of bins for "
"grouping. Each image is allocated to a bin by the percentage of color pixels "
"that appear in the image."
msgstr ""
" Ajuste el parámetro '-b' ('--bins') para controlar el número de "
"contenedores para agrupar. Cada imagen se asigna a un contenedor por el "
"porcentaje de píxeles de color que aparecen en la imagen."
#: tools/sort/cli.py:34
msgid "Input directory of aligned faces."
msgstr "Directorio de entrada de caras alineadas."
#: tools/sort/cli.py:24
msgid ""
" Adjust the '-b' ('--bins') parameter to control the number of bins for "
"grouping. Each image is allocated to a bin by the number of degrees the face "
"is orientated from center."
msgstr ""
" Ajuste el parámetro '-b' ('--bins') para controlar el número de "
"contenedores para agrupar. Cada imagen se asigna a un contenedor por el "
"número de grados que la cara está orientada desde el centro."
#: tools/sort/cli.py:27
msgid ""
" Adjust the '-b' ('--bins') parameter to control the number of bins for "
"grouping. The minimum and maximum values are taken for the chosen sort "
"metric. The bins are then populated with the results from the group sorting."
msgstr ""
" Ajuste el parámetro '-b' ('--bins') para controlar el número de "
"contenedores para agrupar. Los valores mínimo y máximo se toman para la "
"métrica de clasificación elegida. Luego, los contenedores se llenan con los "
"resultados de la clasificación de grupos."
#: tools/sort/cli.py:31
msgid "faces by blurriness."
msgstr "rostros por desenfoque."
#: tools/sort/cli.py:32
msgid "faces by fft filtered blurriness."
msgstr "caras por borrosidad filtrada fft."
#: tools/sort/cli.py:33
msgid ""
"faces by the estimated distance of the alignments from an 'average' face. "
"This can be useful for eliminating misaligned faces. Sorts from most like an "
"average face to least like an average face."
msgstr ""
"caras por la distancia estimada de las alineaciones desde una cara "
"'promedio'. Esto puede ser útil para eliminar caras desalineadas. Ordena de "
"más parecido a un rostro promedio a menos parecido a un rostro promedio."
#: tools/sort/cli.py:36
msgid ""
"faces using VGG Face2 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."
msgstr ""
"caras usando VGG Face2 por similitud de caras. Esto utiliza un algoritmo de "
"agrupamiento por pares para verificar las distancias entre 512 "
"características en cada cara de su conjunto y ordenarlas apropiadamente."
#: tools/sort/cli.py:39
msgid "faces by their landmarks."
msgstr "caras por sus puntos de referencia."
#: tools/sort/cli.py:40
msgid "Like 'face-cnn' but sorts by dissimilarity."
msgstr "Como 'face-cnn' pero ordenada por la similitud."
#: tools/sort/cli.py:41
msgid "Output directory for sorted aligned faces."
msgstr "Directorio de salida para las caras alineadas ordenadas."
msgid "faces by Yaw (rotation left to right)."
msgstr "caras por guiñada (rotación de izquierda a derecha)."
#: tools/sort/cli.py:42
msgid "faces by Pitch (rotation up and down)."
msgstr "caras por Pitch (rotación arriba y abajo)."
#: tools/sort/cli.py:43
msgid "faces by their color histogram."
msgstr "caras por su histograma de color."
#: tools/sort/cli.py:44
msgid "Like 'hist' but sorts by dissimilarity."
msgstr "Como 'hist' pero ordenada por la disimilitud."
#: tools/sort/cli.py:45
msgid ""
"images by the average intensity of the converted grayscale color channel."
msgstr ""
"imágenes por la intensidad media del canal de color en escala de grises "
"convertido."
#: tools/sort/cli.py:46
msgid ""
"images by their number of black pixels. Useful when faces are near borders "
"and a large part of the image is black."
msgstr ""
"imágenes por su número de píxeles negros. Útil cuando las caras están cerca "
"de los bordes y una gran parte de la imagen es negra."
#: tools/sort/cli.py:48
msgid ""
"images by the average intensity of the converted Y color channel. Bright "
"lighting and oversaturated images will be ranked first."
msgstr ""
"imágenes por la intensidad media del canal de color Y convertido. La "
"iluminación brillante y las imágenes sobresaturadas se clasificarán en "
"primer lugar."
#: tools/sort/cli.py:50
msgid ""
"images by the average intensity of the converted Cg color channel. Green "
"images will be ranked first and red images will be last."
msgstr ""
"imágenes por la intensidad media del canal de color Cg convertido. Las "
"imágenes verdes se clasificarán primero y las imágenes rojas serán las "
"últimas."
#: tools/sort/cli.py:52
msgid ""
"images by the average intensity of the converted Co color channel. Orange "
"images will be ranked first and blue images will be last."
msgstr ""
"imágenes por la intensidad media del canal de color Co convertido. Las "
"imágenes naranjas se clasificarán en primer lugar y las imágenes azules en "
"último lugar."
#: tools/sort/cli.py:54
msgid ""
"images by their size in the original frame. Faces further from the camera "
"and from lower resolution sources will be sorted first, whilst faces closer "
"to the camera and from higher resolution sources will be sorted last."
msgstr ""
"imágenes por su tamaño en el marco original. Las caras más alejadas de la "
"cámara y de fuentes de menor resolución se ordenarán primero, mientras que "
"las caras más cercanas a la cámara y de fuentes de mayor resolución se "
"ordenarán en último lugar."
#: tools/sort/cli.py:57
msgid " option is deprecated. Use 'yaw'"
msgstr " la opción está en desuso. Usa 'yaw'"
#: tools/sort/cli.py:58
msgid " option is deprecated. Use 'color-black'"
msgstr " la opción está en desuso. Usa 'color-black'"
#: tools/sort/cli.py:80
msgid "Sort faces using a number of different techniques"
msgstr "Clasificar los rostros mediante diferentes técnicas"
#: tools/sort/cli.py:90 tools/sort/cli.py:97 tools/sort/cli.py:108
#: tools/sort/cli.py:146
msgid "data"
msgstr "datos"
#: tools/sort/cli.py:91
msgid "Input directory of aligned faces."
msgstr "Directorio de entrada de caras alineadas."
#: tools/sort/cli.py:98
msgid ""
"Output directory for sorted aligned faces. If not provided and 'keep' is "
"selected then a new folder called 'sorted' will be created within the input "
"folder to house the output. If not provided and 'keep' is not selected then "
"the images will be sorted in-place, overwriting the original contents of the "
"'input_dir'"
msgstr ""
"Directorio de salida para caras alineadas ordenadas. Si no se proporciona y "
"se selecciona 'keep', se creará una nueva carpeta llamada 'sorted' dentro de "
"la carpeta de entrada para albergar la salida. Si no se proporciona y no se "
"selecciona 'keep', las imágenes se ordenarán en el lugar, sobrescribiendo el "
"contenido original de 'input_dir'"
#: tools/sort/cli.py:109
msgid ""
"R|If selected then the input_dir should be a parent folder containing "
"multiple folders of faces you wish to sort. The faces will be output to "
"separate sub-folders in the output_dir if 'rename' has been selected"
"separate sub-folders in the output_dir"
msgstr ""
"R|Si se selecciona, input_dir debe ser una carpeta principal que contenga "
"varias carpetas de caras que desea ordenar. Las caras se enviarán a "
"subcarpetas separadas en output_dir si se ha seleccionado 'cambiar nombre'"
"subcarpetas separadas en output_dir"
#: tools/sort/cli.py:60 tools/sort/cli.py:109
#: tools/sort/cli.py:118
msgid "sort settings"
msgstr "ajustes de ordenación"
#: tools/sort/cli.py:62
#: tools/sort/cli.py:120
msgid ""
"R|Sort by method. Choose how images are sorted. \n"
"L|'blur': Sort faces by blurriness.\n"
"L|'blur-fft': Sort faces by fft filtered blurriness.\n"
"L|'distance' Sort faces by the estimated distance of the alignments from an "
"'average' face. This can be useful for eliminating misaligned faces.\n"
"L|'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.\n"
"L|'face-cnn': Sort faces by their landmarks. You can adjust the threshold "
"with the '-t' (--ref_threshold) option.\n"
"L|'face-cnn-dissim': Like 'face-cnn' but sorts by dissimilarity.\n"
"L|'face-yaw': Sort faces by Yaw (rotation left to right).\n"
"L|'hist': Sort faces by their color histogram. You can adjust the threshold "
"with the '-t' (--ref_threshold) option.\n"
"L|'hist-dissim': Like 'hist' but sorts by dissimilarity.\n"
"L|'color-gray': Sort images by the average intensity of the converted "
"grayscale color channel.\n"
"L|'color-luma': Sort images by the average intensity of the converted Y "
"color channel. Bright lighting and oversaturated images will be ranked "
"first.\n"
"L|'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.\n"
"L|'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.\n"
"L|'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.\n"
"L|'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.\n"
"Default: face"
"R|Choose how images are sorted. Selecting a sort method gives the images a "
"new filename based on the order the image appears within the given method.\n"
"L|'none': Don't sort the images. When a 'group-by' method is selected, "
"selecting 'none' means that the files will be moved/copied into their "
"respective bins, but the files will keep their original filenames. Selecting "
"'none' for both 'sort-by' and 'group-by' will do nothing"
msgstr ""
"R|Método de ordenación. Elige cómo se ordenan las imágenes. \n"
"L|'blur': Ordena las caras por desenfoque.\n"
"L|'blur-fft': Ordena las caras por fft filtrado desenfoque.\n"
"L|'distance' Ordene las caras por la distancia estimada de las alineaciones "
"desde una cara \"promedio\". Esto puede resultar útil para eliminar caras "
"desalineadas.\n"
"L|'face': Utiliza VGG Face para ordenar por similitud de caras. Esto utiliza "
"un algoritmo de agrupación por pares para comprobar las distancias entre 512 "
"características en cada cara en su conjunto y ordenarlos adecuadamente.\n"
"L|'face-cnn': Ordena las caras por sus puntos de referencia. Puedes ajustar "
"el umbral con la opción '-t' (--ref_threshold).\n"
"L|'face-cnn-dissim': Como 'face-cnn' pero ordena por disimilitud.\n"
"L|'face-yaw': Ordena las caras por Yaw (rotación de izquierda a derecha).\n"
"L|'hist': Ordena las caras por su histograma de color. Puedes ajustar el "
"umbral con la opción '-t' (--ref_threshold).\n"
"L|'hist-dissim': Como 'hist' pero ordena por disimilitud.\n"
"L|'color-gray': Ordena las imágenes por la intensidad media del canal de "
"color previa conversión a escala de grises convertido.\n"
"L|'color-luma': Ordena las imágenes por la intensidad media del canal de "
"color Y. Las imágenes muy brillantes y sobresaturadas se clasificarán "
"primero.\n"
"L|'color-green': Ordena las imágenes por la intensidad media del canal de "
"color Cg. Las imágenes verdes serán clasificadas primero y las rojas serán "
"las últimas.\n"
"L|'color-orange': Ordena las imágenes por la intensidad media del canal de "
"color Co. Las imágenes naranjas serán clasificadas primero y las azules "
"serán las últimas.\n"
"L|'size': Ordena las imágenes por su tamaño en el marco original. Los "
"rostros más cercanos a la cámara y de fuentes de mayor resolución se "
"ordenarán primero, mientras que los rostros más alejados de la cámara y de "
"fuentes de menor resolución se ordenarán en último lugar.\n"
"\vL|'black-pixels': Ordene las imágenes por su número de píxeles negros. "
"Útil cuando los rostros están cerca de los bordes y una gran parte de la "
"imagen es negra .\n"
"Por defecto: face"
"R|Elige cómo se ordenan las imágenes. Al seleccionar un método de "
"clasificación, las imágenes reciben un nuevo nombre de archivo basado en el "
"orden en que aparece la imagen dentro del método dado.\n"
"L|'none': No ordenar las imágenes. Cuando se selecciona un método de "
"'agrupar por', seleccionar 'none' significa que los archivos se moverán/"
"copiarán en sus contenedores respectivos, pero los archivos mantendrán sus "
"nombres de archivo originales. Seleccionar 'none' para 'sort-by' y 'group-"
"by' no hará nada"
#: tools/sort/cli.py:98 tools/sort/cli.py:125 tools/sort/cli.py:137
#: tools/sort/cli.py:148
#: tools/sort/cli.py:133 tools/sort/cli.py:160 tools/sort/cli.py:189
msgid "group settings"
msgstr "ajustes de grupo"
#: tools/sort/cli.py:135
msgid ""
"R|Selecting a group by method will move/copy files into numbered bins based "
"on the selected method.\n"
"L|'none': Don't bin the images. Folders will be sorted by the selected 'sort-"
"by' but will not be binned, instead they will be sorted into a single "
"folder. Selecting 'none' for both 'sort-by' and 'group-by' will do nothing"
msgstr ""
"R|Al seleccionar un grupo por método, los archivos se moverán/copiarán en "
"contenedores numerados según el método seleccionado.\n"
"L|'none': No agrupar las imágenes. Las carpetas se ordenarán por el 'sort-"
"by' seleccionado, pero no se agruparán, sino que se ordenarán en una sola "
"carpeta. Seleccionar 'none' para 'sort-by' y 'group-by' no hará nada"
#: tools/sort/cli.py:147
msgid ""
"Whether to keep the original files in their original location. Choosing a "
"'sort-by' method means that the files have to be renamed. Selecting 'keep' "
"means that the original files will be kept, and the renamed files will be "
"created in the specified output folder. Unselecting keep means that the "
"original files will be moved and renamed based on the selected sort/group "
"criteria."
msgstr ""
"Ya sea para mantener los archivos originales en su ubicación original. "
"Elegir un método de 'sort-by' significa que los archivos tienen que ser "
"renombrados. Seleccionar 'keep' significa que los archivos originales se "
"mantendrán y los archivos renombrados se crearán en la carpeta de salida "
"especificada. Deseleccionar 'keep' significa que los archivos originales se "
"moverán y cambiarán de nombre en función de los criterios de clasificación/"
"grupo seleccionados."
#: tools/sort/cli.py:162
msgid ""
"R|Float value. Minimum threshold to use for grouping comparison with 'face-"
"cnn' 'hist' and 'face' methods.\n"
"The lower the value the more discriminating the grouping is. Leaving -1.0 "
"will allow Faceswap to choose the default value.\n"
"L|For 'face-cnn' 7.2 should be enough, with 4 being very discriminating. \n"
"L|For 'hist' 0.3 should be enough, with 0.2 being very discriminating. \n"
"L|For 'face' between 0.1 (few bins) to 0.4 (more bins) should be about "
"right.\n"
"Be careful setting a value that's too extrene in a directory with many "
"images, as this could result in a lot of folders being created. Defaults: "
"face-cnn 7.2, hist 0.3, face 0.25"
msgstr ""
"R|Valor flotante. Umbral mínimo a usar para agrupar la comparación con los "
"métodos 'face-cnn' 'hist' y 'face'.\n"
"Cuanto más bajo es el valor, más discriminatoria es la agrupación. Dejar "
"-1.0 permitirá que Faceswap elija el valor predeterminado.\n"
"L|Para 'face-cnn' 7.2 debería ser suficiente, siendo 4 muy discriminatorio.\n"
"L|Para 'hist' 0.3 debería ser suficiente, siendo 0.2 muy discriminatorio.\n"
"L|Para 'face', entre 0,1 (pocos contenedores) y 0,4 (más contenedores) "
"debería ser correcto.\n"
"Tenga cuidado al establecer un valor que sea demasiado extremo en un "
"directorio con muchas imágenes, ya que esto podría resultar en la creación "
"de muchas carpetas. Valores predeterminados: face-cnn 7.2, hist 0.3, face "
"0.25"
#: tools/sort/cli.py:179
msgid "output"
msgstr "salida"
#: tools/sort/cli.py:99
#: tools/sort/cli.py:180
msgid ""
"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."
"Deprecated and no longer used. The final processing will be dictated by the "
"sort/group by methods and whether 'keep_original' is selected."
msgstr ""
"Mantiene los archivos originales en el directorio de entrada. Tenga cuidado "
"al usar esto con la agrupación de renombre y sin especificar el directorio "
"de salida, ya que esto mantendría los archivos originales y renombrados en "
"el mismo directorio."
"En desuso y ya no se usa. El procesamiento final será dictado por los "
"métodos de ordenación/agrupación y si se selecciona 'keepl'."
#: tools/sort/cli.py:111
msgid ""
"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"
msgstr ""
"Valor flotante. Umbral mínimo a utilizar para la comparación de agrupaciones "
"con los métodos 'face-cnn' e 'hist'. Cuanto más bajo sea el valor, más "
"discriminante será la agrupación. Si se deja -1.0, el programa establecerá "
"el valor por defecto automáticamente. Para 'face-cnn' 7.2 debería ser "
"suficiente, siendo 4 muy discriminante. Para 'hist' 0.3 debería ser "
"suficiente, siendo 0,2 muy discriminante. Tenga cuidado al establecer un "
"valor demasiado bajo en un directorio con muchas imágenes, ya que esto "
"podría resultar en la creación de muchos directorios. Por defecto: 'face-"
"cnn' = 7.2, 'hist' = 0.3"
#: tools/sort/cli.py:126
msgid ""
"R|Default: rename.\n"
"L|'folders': files are sorted using the -s/--sort-by method, then they are "
"organized into folders using the -g/--group-by grouping method.\n"
"L|'rename': files are sorted using the -s/--sort-by then they are renamed."
msgstr ""
"R|Por defecto: renombrar.\n"
"L|'folders': los archivos se ordenan utilizando el método -s/--sort-by, y "
"luego se organizan en carpetas utilizando el método de agrupación -g/--group-"
"by.\n"
"L|'rename': los archivos se ordenan utilizando el método -s/--sort-by y "
"luego se renombran."
#: tools/sort/cli.py:139
msgid ""
"Group by method. When -fp/--final-processing by folders choose the how the "
"images are grouped after sorting. Default: hist"
msgstr ""
"Método de agrupamiento. Elija la forma de agrupar las imágenes, en el caso "
"de hacerlo por carpetas, después de la clasificación. Por defecto: hist"
#: tools/sort/cli.py:150
#: tools/sort/cli.py:191
#, python-format
msgid ""
"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"
"R|Integer value. Used to control the number of bins created for grouping by: "
"any 'blur' methods, 'color' methods or 'face metric' methods ('distance', "
"'size') and 'orientation; methods ('yaw', 'pitch'). For any other grouping "
"methods see the '-t' ('--threshold') option.\n"
"L|For 'face metric' methods the bins are filled, according the the "
"distribution of faces between the minimum and maximum chosen metric.\n"
"L|For 'color' methods the number of bins represents the divider of the "
"percentage of colored pixels. Eg. For a bin number of '5': The first folder "
"will have the faces with 0%% to 20%% colored pixels, second 21%% to 40%%, "
"etc. Any empty bins will be deleted, so you may end up with fewer bins than "
"selected.\n"
"L|For 'blur' methods folder 0 will be the least blurry, while the last "
"folder will be the blurriest.\n"
"L|For 'orientation' methods the number of bins is dictated by how much 180 "
"degrees is divided. Eg. If 18 is selected, then each folder will be a 10 "
"degree increment. Folder 0 will contain faces looking the most to the left/"
"down whereas the last folder will contain the faces looking the most to the "
"right/up. NB: Some bins may be empty if faces do not fit the criteria.\n"
"Default value: 5"
msgstr ""
"Valor entero. Número de carpetas que se utilizarán al agrupar por 'blur' y "
"'face-yaw'. Para 'blur' la carpeta 0 será la menos borrosa, mientras que la "
"última carpeta será la más borrosa. Para 'face-yaw' el número de carpetas es "
"por cuanto se dividen los 180 grados. Así que si usas 18, entonces cada "
"carpeta será un incremento de 10 grados. La carpeta 0 contendrá las caras "
"que miren más a la izquierda, mientras que la última carpeta contendrá las "
"caras que miren más a la derecha. Si el número de imágenes no se divide "
"uniformemente en el número de carpetas, las imágenes restantes se colocan en "
"la última carpeta. Para píxeles negros, representa el divisor del porcentaje "
"de píxeles negros. Para 10, la primera carpeta tendrá las caras con 0 a 10%% "
"de píxeles negros, la segunda de 11 a 20%%, etc. Valor por defecto: 5"
"R|Valor entero. Se utiliza para controlar el número de contenedores creados "
"para agrupar por: cualquier método de 'blur', método de 'color' o método de "
"'face metric' ('distance', 'size') y 'orientación; métodos ('yaw', 'pitch'). "
"Para cualquier otro método de agrupación, consulte la opción '-t' ('--"
"threshold').\n"
"L|Para los métodos de 'face metric', los contenedores se llenan de acuerdo "
"con la distribución de caras entre la métrica mínima y máxima elegida.\n"
"L|Para los métodos de 'color', el número de contenedores representa el "
"divisor del porcentaje de píxeles coloreados. P.ej. Para un número de "
"contenedor de '5': la primera carpeta tendrá las caras con 0%% a 20%% "
"píxeles de color, la segunda 21%% a 40%%, etc. Se eliminarán todos los "
"contenedores vacíos, por lo que puede terminar con menos contenedores que "
"los seleccionados.\n"
"L|Para los métodos 'blur', la carpeta 0 será la menos borrosa, mientras que "
"la última carpeta será la más borrosa.\n"
"L|Para los métodos de 'orientation', el número de contenedores está dictado "
"por cuánto se dividen 180 grados. P.ej. Si se selecciona 18, cada carpeta "
"tendrá un incremento de 10 grados. La carpeta 0 contendrá las caras que "
"miran más hacia la izquierda/abajo, mientras que la última carpeta contendrá "
"las caras que miran más hacia la derecha/arriba. NB: algunos contenedores "
"pueden estar vacíos si las caras no se ajustan a los criterios.\n"
"Valor predeterminado: 5"
#: tools/sort/cli.py:164 tools/sort/cli.py:174
#: tools/sort/cli.py:213 tools/sort/cli.py:223
msgid "settings"
msgstr "ajustes"
#: tools/sort/cli.py:166
#: tools/sort/cli.py:215
msgid ""
"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 "
@ -227,7 +370,7 @@ msgstr ""
"se especifica ningún archivo de registro con '--log-file', se creará un "
"archivo 'sort_log.json' en el directorio de entrada."
#: tools/sort/cli.py:177
#: tools/sort/cli.py:226
msgid ""
"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 "
@ -237,3 +380,137 @@ msgstr ""
"información de renombrado o agrupación. Si la extensión especificada no es "
"'json' o 'yaml', se utilizará json como serializador, con el nombre de "
"archivo suministrado. Por defecto: sort_log.json"
#~ msgid "Output directory for sorted aligned faces."
#~ msgstr "Directorio de salida para las caras alineadas ordenadas."
#~ msgid ""
#~ "R|Sort by method. Choose how images are sorted. \n"
#~ "L|'blur': Sort faces by blurriness.\n"
#~ "L|'blur-fft': Sort faces by fft filtered blurriness.\n"
#~ "L|'distance' Sort faces by the estimated distance of the alignments from "
#~ "an 'average' face. This can be useful for eliminating misaligned faces.\n"
#~ "L|'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.\n"
#~ "L|'face-cnn': Sort faces by their landmarks. You can adjust the threshold "
#~ "with the '-t' (--ref_threshold) option.\n"
#~ "L|'face-cnn-dissim': Like 'face-cnn' but sorts by dissimilarity.\n"
#~ "L|'face-yaw': Sort faces by Yaw (rotation left to right).\n"
#~ "L|'hist': Sort faces by their color histogram. You can adjust the "
#~ "threshold with the '-t' (--ref_threshold) option.\n"
#~ "L|'hist-dissim': Like 'hist' but sorts by dissimilarity.\n"
#~ "L|'color-gray': Sort images by the average intensity of the converted "
#~ "grayscale color channel.\n"
#~ "L|'color-luma': Sort images by the average intensity of the converted Y "
#~ "color channel. Bright lighting and oversaturated images will be ranked "
#~ "first.\n"
#~ "L|'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.\n"
#~ "L|'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.\n"
#~ "L|'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.\n"
#~ "L|'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.\n"
#~ "Default: face"
#~ msgstr ""
#~ "R|Método de ordenación. Elige cómo se ordenan las imágenes. \n"
#~ "L|'blur': Ordena las caras por desenfoque.\n"
#~ "L|'blur-fft': Ordena las caras por fft filtrado desenfoque.\n"
#~ "L|'distance' Ordene las caras por la distancia estimada de las "
#~ "alineaciones desde una cara \"promedio\". Esto puede resultar útil para "
#~ "eliminar caras desalineadas.\n"
#~ "L|'face': Utiliza VGG Face para ordenar por similitud de caras. Esto "
#~ "utiliza un algoritmo de agrupación por pares para comprobar las "
#~ "distancias entre 512 características en cada cara en su conjunto y "
#~ "ordenarlos adecuadamente.\n"
#~ "L|'face-cnn': Ordena las caras por sus puntos de referencia. Puedes "
#~ "ajustar el umbral con la opción '-t' (--ref_threshold).\n"
#~ "L|'face-cnn-dissim': Como 'face-cnn' pero ordena por disimilitud.\n"
#~ "L|'face-yaw': Ordena las caras por Yaw (rotación de izquierda a "
#~ "derecha).\n"
#~ "L|'hist': Ordena las caras por su histograma de color. Puedes ajustar el "
#~ "umbral con la opción '-t' (--ref_threshold).\n"
#~ "L|'hist-dissim': Como 'hist' pero ordena por disimilitud.\n"
#~ "L|'color-gray': Ordena las imágenes por la intensidad media del canal de "
#~ "color previa conversión a escala de grises convertido.\n"
#~ "L|'color-luma': Ordena las imágenes por la intensidad media del canal de "
#~ "color Y. Las imágenes muy brillantes y sobresaturadas se clasificarán "
#~ "primero.\n"
#~ "L|'color-green': Ordena las imágenes por la intensidad media del canal de "
#~ "color Cg. Las imágenes verdes serán clasificadas primero y las rojas "
#~ "serán las últimas.\n"
#~ "L|'color-orange': Ordena las imágenes por la intensidad media del canal "
#~ "de color Co. Las imágenes naranjas serán clasificadas primero y las "
#~ "azules serán las últimas.\n"
#~ "L|'size': Ordena las imágenes por su tamaño en el marco original. Los "
#~ "rostros más cercanos a la cámara y de fuentes de mayor resolución se "
#~ "ordenarán primero, mientras que los rostros más alejados de la cámara y "
#~ "de fuentes de menor resolución se ordenarán en último lugar.\n"
#~ "\vL|'black-pixels': Ordene las imágenes por su número de píxeles negros. "
#~ "Útil cuando los rostros están cerca de los bordes y una gran parte de la "
#~ "imagen es negra .\n"
#~ "Por defecto: face"
#~ msgid ""
#~ "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."
#~ msgstr ""
#~ "Mantiene los archivos originales en el directorio de entrada. Tenga "
#~ "cuidado al usar esto con la agrupación de renombre y sin especificar el "
#~ "directorio de salida, ya que esto mantendría los archivos originales y "
#~ "renombrados en el mismo directorio."
#~ msgid ""
#~ "R|Default: rename.\n"
#~ "L|'folders': files are sorted using the -s/--sort-by method, then they "
#~ "are organized into folders using the -g/--group-by grouping method.\n"
#~ "L|'rename': files are sorted using the -s/--sort-by then they are renamed."
#~ msgstr ""
#~ "R|Por defecto: renombrar.\n"
#~ "L|'folders': los archivos se ordenan utilizando el método -s/--sort-by, y "
#~ "luego se organizan en carpetas utilizando el método de agrupación -g/--"
#~ "group-by.\n"
#~ "L|'rename': los archivos se ordenan utilizando el método -s/--sort-by y "
#~ "luego se renombran."
#~ msgid ""
#~ "Group by method. When -fp/--final-processing by folders choose the how "
#~ "the images are grouped after sorting. Default: hist"
#~ msgstr ""
#~ "Método de agrupamiento. Elija la forma de agrupar las imágenes, en el "
#~ "caso de hacerlo por carpetas, después de la clasificación. Por defecto: "
#~ "hist"
#, python-format
#~ msgid ""
#~ "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"
#~ msgstr ""
#~ "Valor entero. Número de carpetas que se utilizarán al agrupar por 'blur' "
#~ "y 'face-yaw'. Para 'blur' la carpeta 0 será la menos borrosa, mientras "
#~ "que la última carpeta será la más borrosa. Para 'face-yaw' el número de "
#~ "carpetas es por cuanto se dividen los 180 grados. Así que si usas 18, "
#~ "entonces cada carpeta será un incremento de 10 grados. La carpeta 0 "
#~ "contendrá las caras que miren más a la izquierda, mientras que la última "
#~ "carpeta contendrá las caras que miren más a la derecha. Si el número de "
#~ "imágenes no se divide uniformemente en el número de carpetas, las "
#~ "imágenes restantes se colocan en la última carpeta. Para píxeles negros, "
#~ "representa el divisor del porcentaje de píxeles negros. Para 10, la "
#~ "primera carpeta tendrá las caras con 0 a 10%% de píxeles negros, la "
#~ "segunda de 11 a 20%%, etc. Valor por defecto: 5"

View File

@ -1,21 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2021-02-18 23:49-0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=cp1252\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
#: tools.py:46
msgid "Please backup your data and/or test the tool you want to use with a smaller data set to make sure you understand how it works."
msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-09-07 12:34+0100\n"
"POT-Creation-Date: 2022-09-13 12:49+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -21,129 +21,243 @@ msgstr ""
msgid "This command lets you sort images using various methods."
msgstr ""
#: tools/sort/cli.py:23
msgid "Sort faces using a number of different techniques"
#: tools/sort/cli.py:20
msgid ""
" Adjust the '-t' ('--threshold') parameter to control the strength of "
"grouping."
msgstr ""
#: tools/sort/cli.py:33 tools/sort/cli.py:40 tools/sort/cli.py:47
msgid "data"
#: tools/sort/cli.py:21
msgid ""
" Adjust the '-b' ('--bins') parameter to control the number of bins for "
"grouping. Each image is allocated to a bin by the percentage of color pixels "
"that appear in the image."
msgstr ""
#: tools/sort/cli.py:34
msgid "Input directory of aligned faces."
#: tools/sort/cli.py:24
msgid ""
" Adjust the '-b' ('--bins') parameter to control the number of bins for "
"grouping. Each image is allocated to a bin by the number of degrees the face "
"is orientated from center."
msgstr ""
#: tools/sort/cli.py:27
msgid ""
" Adjust the '-b' ('--bins') parameter to control the number of bins for "
"grouping. The minimum and maximum values are taken for the chosen sort "
"metric. The bins are then populated with the results from the group sorting."
msgstr ""
#: tools/sort/cli.py:31
msgid "faces by blurriness."
msgstr ""
#: tools/sort/cli.py:32
msgid "faces by fft filtered blurriness."
msgstr ""
#: tools/sort/cli.py:33
msgid ""
"faces by the estimated distance of the alignments from an 'average' face. "
"This can be useful for eliminating misaligned faces. Sorts from most like an "
"average face to least like an average face."
msgstr ""
#: tools/sort/cli.py:36
msgid ""
"faces using VGG Face2 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."
msgstr ""
#: tools/sort/cli.py:39
msgid "faces by their landmarks."
msgstr ""
#: tools/sort/cli.py:40
msgid "Like 'face-cnn' but sorts by dissimilarity."
msgstr ""
#: tools/sort/cli.py:41
msgid "Output directory for sorted aligned faces."
msgid "faces by Yaw (rotation left to right)."
msgstr ""
#: tools/sort/cli.py:42
msgid "faces by Pitch (rotation up and down)."
msgstr ""
#: tools/sort/cli.py:43
msgid "faces by their color histogram."
msgstr ""
#: tools/sort/cli.py:44
msgid "Like 'hist' but sorts by dissimilarity."
msgstr ""
#: tools/sort/cli.py:45
msgid ""
"images by the average intensity of the converted grayscale color channel."
msgstr ""
#: tools/sort/cli.py:46
msgid ""
"images by their number of black pixels. Useful when faces are near borders "
"and a large part of the image is black."
msgstr ""
#: tools/sort/cli.py:48
msgid ""
"R|If selected then the input_dir should be a parent folder containing "
"multiple folders of faces you wish to sort. The faces will be output to "
"separate sub-folders in the output_dir if 'rename' has been selected"
"images by the average intensity of the converted Y color channel. Bright "
"lighting and oversaturated images will be ranked first."
msgstr ""
#: tools/sort/cli.py:60 tools/sort/cli.py:109
#: tools/sort/cli.py:50
msgid ""
"images by the average intensity of the converted Cg color channel. Green "
"images will be ranked first and red images will be last."
msgstr ""
#: tools/sort/cli.py:52
msgid ""
"images by the average intensity of the converted Co color channel. Orange "
"images will be ranked first and blue images will be last."
msgstr ""
#: tools/sort/cli.py:54
msgid ""
"images by their size in the original frame. Faces further from the camera "
"and from lower resolution sources will be sorted first, whilst faces closer "
"to the camera and from higher resolution sources will be sorted last."
msgstr ""
#: tools/sort/cli.py:57
msgid " option is deprecated. Use 'yaw'"
msgstr ""
#: tools/sort/cli.py:58
msgid " option is deprecated. Use 'color-black'"
msgstr ""
#: tools/sort/cli.py:80
msgid "Sort faces using a number of different techniques"
msgstr ""
#: tools/sort/cli.py:90 tools/sort/cli.py:97 tools/sort/cli.py:108
#: tools/sort/cli.py:146
msgid "data"
msgstr ""
#: tools/sort/cli.py:91
msgid "Input directory of aligned faces."
msgstr ""
#: tools/sort/cli.py:98
msgid ""
"Output directory for sorted aligned faces. If not provided and 'keep' is "
"selected then a new folder called 'sorted' will be created within the input "
"folder to house the output. If not provided and 'keep' is not selected then "
"the images will be sorted in-place, overwriting the original contents of the "
"'input_dir'"
msgstr ""
#: tools/sort/cli.py:109
msgid ""
"R|If selected then the input_dir should be a parent folder containing "
"multiple folders of faces you wish to sort. The faces will be output to "
"separate sub-folders in the output_dir"
msgstr ""
#: tools/sort/cli.py:118
msgid "sort settings"
msgstr ""
#: tools/sort/cli.py:62
#: tools/sort/cli.py:120
msgid ""
"R|Sort by method. Choose how images are sorted. \n"
"L|'blur': Sort faces by blurriness.\n"
"L|'blur-fft': Sort faces by fft filtered blurriness.\n"
"L|'distance' Sort faces by the estimated distance of the alignments from an "
"'average' face. This can be useful for eliminating misaligned faces.\n"
"L|'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.\n"
"L|'face-cnn': Sort faces by their landmarks. You can adjust the threshold "
"with the '-t' (--ref_threshold) option.\n"
"L|'face-cnn-dissim': Like 'face-cnn' but sorts by dissimilarity.\n"
"L|'face-yaw': Sort faces by Yaw (rotation left to right).\n"
"L|'hist': Sort faces by their color histogram. You can adjust the threshold "
"with the '-t' (--ref_threshold) option.\n"
"L|'hist-dissim': Like 'hist' but sorts by dissimilarity.\n"
"L|'color-gray': Sort images by the average intensity of the converted "
"grayscale color channel.\n"
"L|'color-luma': Sort images by the average intensity of the converted Y "
"color channel. Bright lighting and oversaturated images will be ranked "
"first.\n"
"L|'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.\n"
"L|'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.\n"
"L|'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.\n"
"L|'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.\n"
"Default: face"
"R|Choose how images are sorted. Selecting a sort method gives the images a "
"new filename based on the order the image appears within the given method.\n"
"L|'none': Don't sort the images. When a 'group-by' method is selected, "
"selecting 'none' means that the files will be moved/copied into their "
"respective bins, but the files will keep their original filenames. Selecting "
"'none' for both 'sort-by' and 'group-by' will do nothing"
msgstr ""
#: tools/sort/cli.py:98 tools/sort/cli.py:125 tools/sort/cli.py:137
#: tools/sort/cli.py:148
#: tools/sort/cli.py:133 tools/sort/cli.py:160 tools/sort/cli.py:189
msgid "group settings"
msgstr ""
#: tools/sort/cli.py:135
msgid ""
"R|Selecting a group by method will move/copy files into numbered bins based "
"on the selected method.\n"
"L|'none': Don't bin the images. Folders will be sorted by the selected 'sort-"
"by' but will not be binned, instead they will be sorted into a single "
"folder. Selecting 'none' for both 'sort-by' and 'group-by' will do nothing"
msgstr ""
#: tools/sort/cli.py:147
msgid ""
"Whether to keep the original files in their original location. Choosing a "
"'sort-by' method means that the files have to be renamed. Selecting 'keep' "
"means that the original files will be kept, and the renamed files will be "
"created in the specified output folder. Unselecting keep means that the "
"original files will be moved and renamed based on the selected sort/group "
"criteria."
msgstr ""
#: tools/sort/cli.py:162
msgid ""
"R|Float value. Minimum threshold to use for grouping comparison with 'face-"
"cnn' 'hist' and 'face' methods.\n"
"The lower the value the more discriminating the grouping is. Leaving -1.0 "
"will allow Faceswap to choose the default value.\n"
"L|For 'face-cnn' 7.2 should be enough, with 4 being very discriminating. \n"
"L|For 'hist' 0.3 should be enough, with 0.2 being very discriminating. \n"
"L|For 'face' between 0.1 (few bins) to 0.4 (more bins) should be about "
"right.\n"
"Be careful setting a value that's too extrene in a directory with many "
"images, as this could result in a lot of folders being created. Defaults: "
"face-cnn 7.2, hist 0.3, face 0.25"
msgstr ""
#: tools/sort/cli.py:179
msgid "output"
msgstr ""
#: tools/sort/cli.py:99
#: tools/sort/cli.py:180
msgid ""
"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."
"Deprecated and no longer used. The final processing will be dictated by the "
"sort/group by methods and whether 'keep_original' is selected."
msgstr ""
#: tools/sort/cli.py:111
msgid ""
"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"
msgstr ""
#: tools/sort/cli.py:126
msgid ""
"R|Default: rename.\n"
"L|'folders': files are sorted using the -s/--sort-by method, then they are "
"organized into folders using the -g/--group-by grouping method.\n"
"L|'rename': files are sorted using the -s/--sort-by then they are renamed."
msgstr ""
#: tools/sort/cli.py:139
msgid ""
"Group by method. When -fp/--final-processing by folders choose the how the "
"images are grouped after sorting. Default: hist"
msgstr ""
#: tools/sort/cli.py:150
#: tools/sort/cli.py:191
#, python-format
msgid ""
"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"
"R|Integer value. Used to control the number of bins created for grouping by: "
"any 'blur' methods, 'color' methods or 'face metric' methods ('distance', "
"'size') and 'orientation; methods ('yaw', 'pitch'). For any other grouping "
"methods see the '-t' ('--threshold') option.\n"
"L|For 'face metric' methods the bins are filled, according the the "
"distribution of faces between the minimum and maximum chosen metric.\n"
"L|For 'color' methods the number of bins represents the divider of the "
"percentage of colored pixels. Eg. For a bin number of '5': The first folder "
"will have the faces with 0%% to 20%% colored pixels, second 21%% to 40%%, "
"etc. Any empty bins will be deleted, so you may end up with fewer bins than "
"selected.\n"
"L|For 'blur' methods folder 0 will be the least blurry, while the last "
"folder will be the blurriest.\n"
"L|For 'orientation' methods the number of bins is dictated by how much 180 "
"degrees is divided. Eg. If 18 is selected, then each folder will be a 10 "
"degree increment. Folder 0 will contain faces looking the most to the left/"
"down whereas the last folder will contain the faces looking the most to the "
"right/up. NB: Some bins may be empty if faces do not fit the criteria.\n"
"Default value: 5"
msgstr ""
#: tools/sort/cli.py:164 tools/sort/cli.py:174
#: tools/sort/cli.py:213 tools/sort/cli.py:223
msgid "settings"
msgstr ""
#: tools/sort/cli.py:166
#: tools/sort/cli.py:215
msgid ""
"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 "
@ -151,7 +265,7 @@ msgid ""
"directory."
msgstr ""
#: tools/sort/cli.py:177
#: tools/sort/cli.py:226
msgid ""
"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 "

View File

@ -2,10 +2,13 @@
""" VGG_Face2 inference and sorting """
import logging
import psutil
import sys
from typing import Dict, Generator, List, Tuple, Optional
import cv2
import numpy as np
import psutil
from fastcluster import linkage, linkage_vector
from lib.model.layers import L2_normalize
@ -13,6 +16,12 @@ from lib.model.session import KSession
from lib.utils import FaceswapError
from plugins.extract._base import Extractor
if sys.version_info < (3, 8):
from typing_extensions import Literal
else:
from typing import Literal
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
@ -41,8 +50,11 @@ class VGGFace2(Extractor): # pylint:disable=abstract-method
self._plugin_type = "recognition"
self.name = "VGG_Face2"
self.input_size = 224
self._bins = {}
# Average image provided in https://github.com/ox-vgg/vgg_face2
self._average_img = np.array([91.4953, 103.8827, 131.0912])
self._iterator = self._get_bin()
logger.debug("Initialized %s", self.__class__.__name__)
# <<< GET MODEL >>> #
@ -56,6 +68,26 @@ class VGGFace2(Extractor): # pylint:disable=abstract-method
exclude_gpus=self._exclude_gpus)
self.model.load_model()
@classmethod
def _get_bin(cls) -> Generator[int, None, None]:
""" Generator that yields incremented integers
Yields
------
int
An integer 1 larger than the last integer returned
"""
i = 0
while True:
yield i
i += 1
@property
def next_bin(self) -> int:
""" int: The next available bin id in the iterator """
return next(self._iterator)
def predict(self, batch):
""" Return encodings for given image from vgg_face2.
@ -115,45 +147,48 @@ class VGGFace2(Extractor): # pylint:disable=abstract-method
var_c = np.sum(np.multiply(test_face, test_face))
return 1 - (var_a / (np.sqrt(var_b) * np.sqrt(var_c)))
def sorted_similarity(self, predictions, method="ward"):
""" Sort a matrix of predictions by similarity.
Transforms a distance matrix into a sorted distance matrix according to the order implied
by the hierarchical tree (dendrogram).
class Cluster(): # pylint: disable=too-few-public-methods
""" Cluster the outputs from a VGG-Face 2 Model
Parameters
----------
predictions: numpy.ndarray
A stacked matrix of vgg_face2 predictions of the shape (`N`, `D`) where `N` is the
number of observations and `D` are the number of dimensions. NB: The given
:attr:`predictions` will be overwritten to save memory. If you still require the
original values you should take a copy prior to running this method
method: ['single','centroid','median','ward']
The clustering method to use.
Parameters
----------
predictions: numpy.ndarray
A stacked matrix of vgg_face2 predictions of the shape (`N`, `D`) where `N` is the
number of observations and `D` are the number of dimensions. NB: The given
:attr:`predictions` will be overwritten to save memory. If you still require the
original values you should take a copy prior to running this method
method: ['single','centroid','median','ward']
The clustering method to use.
threshold: float, optional
The threshold to start creating bins for. Set to ``None`` to disable binning
"""
Returns
-------
list:
List of indices with the order implied by the hierarchical tree
"""
logger.info("Sorting face distances. Depending on your dataset this may take some time...")
num_predictions, dims = predictions.shape
def __init__(self,
predictions: np.ndarray,
method: Literal["single", "centroid", "median", "ward"],
threshold: Optional[float] = None) -> None:
logger.debug("Initializing: %s (predictions: %s, method: %s, threshold: %s)",
self.__class__.__name__, predictions.shape, method, threshold)
self._num_predictions = predictions.shape[0]
kwargs = dict(method=method)
if self._use_vector_linkage(num_predictions, dims):
func = linkage_vector
else:
kwargs["preserve_input"] = False
func = linkage
self._should_output_bins = threshold is not None
self._threshold = 0.0 if threshold is None else threshold
self._bins: Dict[int, int] = {}
self._iterator = self._integer_iterator()
result_linkage = func(predictions, **kwargs)
result_order = self._seriation(result_linkage,
num_predictions,
num_predictions + num_predictions - 2)
return result_order
self._result_linkage = self._do_linkage(predictions, method)
logger.debug("Initialized %s", self.__class__.__name__)
@staticmethod
def _use_vector_linkage(item_count, dims):
@classmethod
def _integer_iterator(cls) -> Generator[int, None, None]:
""" Iterator that just yields consecutive integers """
i = -1
while True:
i += 1
yield i
def _use_vector_linkage(self, dims: int) -> bool:
""" Calculate the RAM that will be required to sort these images and select the appropriate
clustering method.
@ -167,8 +202,6 @@ class VGGFace2(Extractor): # pylint:disable=abstract-method
Parameters
----------
item_count: int
The number of images that are to be processed
dims: int
The number of dimensions in the vgg_face output
@ -181,13 +214,13 @@ class VGGFace2(Extractor): # pylint:disable=abstract-method
divider = 1024 * 1024 # bytes to MB
free_ram = psutil.virtual_memory().available / divider
linkage_required = (((item_count ** 2) * np_float) / 1.8) / divider
vector_required = ((item_count * dims) * np_float) / divider
linkage_required = (((self._num_predictions ** 2) * np_float) / 1.8) / divider
vector_required = ((self._num_predictions * dims) * np_float) / divider
logger.debug("free_ram: %sMB, linkage_required: %sMB, vector_required: %sMB",
int(free_ram), int(linkage_required), int(vector_required))
if linkage_required < free_ram:
logger.verbose("Using linkage method")
logger.verbose("Using linkage method") # type:ignore
retval = False
elif vector_required < free_ram:
logger.warning("Not enough RAM to perform linkage clustering. Using vector "
@ -197,12 +230,85 @@ class VGGFace2(Extractor): # pylint:disable=abstract-method
retval = True
else:
raise FaceswapError("Not enough RAM available to sort faces. Try reducing "
"the size of your dataset. Free RAM: {}MB. "
"Required RAM: {}MB".format(int(free_ram), int(vector_required)))
f"the size of your dataset. Free RAM: {int(free_ram)}MB. "
f"Required RAM: {int(vector_required)}MB")
logger.debug(retval)
return retval
def _seriation(self, tree, points, current_index):
def _do_linkage(self,
predictions: np.ndarray,
method: Literal["single", "centroid", "median", "ward"]) -> np.ndarray:
""" Use FastCluster to perform vector or standard linkage
Parameters
----------
predictions: :class:`numpy.ndarray`
A stacked matrix of vgg_face2 predictions of the shape (`N`, `D`) where `N` is the
number of observations and `D` are the number of dimensions.
method: ['single','centroid','median','ward']
The clustering method to use.
Returns
-------
:class:`numpy.ndarray`
The [`num_predictions`, 4] linkage vector
"""
dims = predictions.shape[-1]
if self._use_vector_linkage(dims):
retval = linkage_vector(predictions, method=method)
else:
retval = linkage(predictions, method=method, preserve_input=False)
logger.debug("Linkage shape: %s", retval.shape)
return retval
def _process_leaf_node(self,
current_index: int,
current_bin: int) -> List[Tuple[int, int]]:
""" Process the output when we have hit a leaf node """
if not self._should_output_bins:
return [(current_index, 0)]
if current_bin not in self._bins:
next_val = 0 if not self._bins else max(self._bins.values()) + 1
self._bins[current_bin] = next_val
return [(current_index, self._bins[current_bin])]
def _get_bin(self,
tree: np.ndarray,
points: int,
current_index: int,
current_bin: int) -> int:
""" Obtain the bin that we are currently in.
If we are not currently below the threshold for binning, get a new bin ID from the integer
iterator.
Parameters
----------
tree: numpy.ndarray
A hierarchical tree (dendrogram)
points: int
The number of points given to the clustering process
current_index: int
The position in the tree for the recursive traversal
current_bin int, optional
The ID for the bin we are currently in. Only used when binning is enabled
Returns
-------
int
The current bin ID for the node
"""
if tree[current_index - points, 2] >= self._threshold:
current_bin = next(self._iterator)
logger.debug("Creating new bin ID: %s", current_bin)
return current_bin
def _seriation(self,
tree: np.ndarray,
points: int,
current_index: int,
current_bin: int = 0) -> List[Tuple[int, int]]:
""" Seriation method for sorted similarity.
Seriation computes the order implied by a hierarchical tree (dendrogram).
@ -215,14 +321,44 @@ class VGGFace2(Extractor): # pylint:disable=abstract-method
The number of points given to the clustering process
current_index: int
The position in the tree for the recursive traversal
current_bin int, optional
The ID for the bin we are currently in. Only used when binning is enabled
Returns
-------
list:
The indices in the order implied by the hierarchical tree
"""
if current_index < points:
return [current_index]
if current_index < points: # Output the leaf node
return self._process_leaf_node(current_index, current_bin)
if self._should_output_bins:
current_bin = self._get_bin(tree, points, current_index, current_bin)
left = int(tree[current_index-points, 0])
right = int(tree[current_index-points, 1])
return self._seriation(tree, points, left) + self._seriation(tree, points, right)
serate_left = self._seriation(tree, points, left, current_bin=current_bin)
serate_right = self._seriation(tree, points, right, current_bin=current_bin)
return serate_left + serate_right # type: ignore
def __call__(self) -> List[Tuple[int, int]]:
""" Process the linkages.
Transforms a distance matrix into a sorted distance matrix according to the order implied
by the hierarchical tree (dendrogram).
Returns
-------
list:
List of indices with the order implied by the hierarchical tree or list of tuples of
(`index`, `bin`) if a binning threshold was provided
"""
logger.info("Sorting face distances. Depending on your dataset this may take some time...")
if self._threshold:
self._threshold = self._result_linkage[:, 2].max() * self._threshold
result_order = self._seriation(self._result_linkage,
self._num_predictions,
self._num_predictions + self._num_predictions - 2)
return result_order

View File

@ -37,15 +37,12 @@ def _get_cli_opts():
if os.path.exists(cli_file):
mod = ".".join(("tools", tool_name, "cli"))
module = import_module(mod)
cliarg_class = getattr(module, "{}Args".format(tool_name.title()))
cliarg_class = getattr(module, f"{tool_name.title()}Args")
help_text = getattr(module, "_HELPTEXT")
yield tool_name, help_text, cliarg_class
if __name__ == "__main__":
print(_("Please backup your data and/or test the tool you want to use with a smaller data set "
"to make sure you understand how it works."))
PARSER = FullHelpArgumentParser()
SUBPARSER = PARSER.add_subparsers()
for tool, helptext, cli_args in _get_cli_opts():

View File

@ -12,6 +12,63 @@ _ = _LANG.gettext
_HELPTEXT = _("This command lets you sort images using various methods.")
_SORT_METHODS = (
"none", "blur", "blur-fft", "distance", "face", "face-cnn", "face-cnn-dissim",
"yaw", "pitch", "hist", "hist-dissim", "color-black", "color-gray", "color-luma",
"color-green", "color-orange", "size", "face-yaw", "black-pixels")
_GPTHRESHOLD = _(" Adjust the '-t' ('--threshold') parameter to control the strength of grouping.")
_GPCOLOR = _(" Adjust the '-b' ('--bins') parameter to control the number of bins for grouping. "
"Each image is allocated to a bin by the percentage of color pixels that appear in "
"the image.")
_GPDEGREES = _(" Adjust the '-b' ('--bins') parameter to control the number of bins for grouping. "
"Each image is allocated to a bin by the number of degrees the face is orientated "
"from center.")
_GPLINEAR = _(" Adjust the '-b' ('--bins') parameter to control the number of bins for grouping. "
"The minimum and maximum values are taken for the chosen sort metric. The bins "
"are then populated with the results from the group sorting.")
_METHOD_TEXT = {
"blur": _("faces by blurriness."),
"blur-fft": _("faces by fft filtered blurriness."),
"distance": _("faces by the estimated distance of the alignments from an 'average' face. This "
"can be useful for eliminating misaligned faces. Sorts from most like an "
"average face to least like an average face."),
"face": _("faces using VGG Face2 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."),
"face-cnn": _("faces by their landmarks."),
"face-cnn-dissim": _("Like 'face-cnn' but sorts by dissimilarity."),
"yaw": _("faces by Yaw (rotation left to right)."),
"pitch": _("faces by Pitch (rotation up and down)."),
"hist": _("faces by their color histogram."),
"hist-dissim": _("Like 'hist' but sorts by dissimilarity."),
"color-gray": _("images by the average intensity of the converted grayscale color channel."),
"color-black": _("images by their number of black pixels. Useful when faces are near borders "
"and a large part of the image is black."),
"color-luma": _("images by the average intensity of the converted Y color channel. Bright "
"lighting and oversaturated images will be ranked first."),
"color-green": _("images by the average intensity of the converted Cg color channel. Green "
"images will be ranked first and red images will be last."),
"color-orange": _("images by the average intensity of the converted Co color channel. Orange "
"images will be ranked first and blue images will be last."),
"size": _("images by their size in the original frame. Faces further from the camera and from "
"lower resolution sources will be sorted first, whilst faces closer to the camera "
"and from higher resolution sources will be sorted last."),
"face-yaw": _(" option is deprecated. Use 'yaw'"),
"black-pixels": _(" option is deprecated. Use 'color-black'")}
_BIN_TYPES = [
(("face", "face-cnn", "face-cnn-dissim", "hist", "hist-dissim"), _GPTHRESHOLD),
(("color-black", "color-gray", "color-luma", "color-green", "color-orange"), _GPCOLOR),
(("yaw", "pitch"), _GPDEGREES),
(("blur", "blur-fft", "distance", "size"), _GPLINEAR)]
_SORT_HELP = ""
_GROUP_HELP = ""
for method in sorted(_METHOD_TEXT):
_SORT_HELP += f"\nL|{method}: {_('Sort')} {_METHOD_TEXT[method]}"
_GROUP_HELP += (f"\nL|{method}: {_('Group')} {_METHOD_TEXT[method]} "
f"{next((x[1] for x in _BIN_TYPES if method in x[0]), '')}")
class SortArgs(FaceSwapArgs):
@ -25,7 +82,7 @@ class SortArgs(FaceSwapArgs):
@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 = []
argument_list.append(dict(
opts=('-i', '--input'),
action=DirFullPaths,
@ -38,7 +95,11 @@ class SortArgs(FaceSwapArgs):
action=DirFullPaths,
dest="output_dir",
group=_("data"),
help=_("Output directory for sorted aligned faces.")))
help=_("Output directory for sorted aligned faces. If not provided and 'keep' is "
"selected then a new folder called 'sorted' will be created within the input "
"folder to house the output. If not provided and 'keep' is not selected then "
"the images will be sorted in-place, overwriting the original contents of the "
"'input_dir'")))
argument_list.append(dict(
opts=("-B", "--batch-mode"),
action="store_true",
@ -47,97 +108,77 @@ class SortArgs(FaceSwapArgs):
group=_("data"),
help=_("R|If selected then the input_dir should be a parent folder containing "
"multiple folders of faces you wish to sort. The faces "
"will be output to separate sub-folders in the output_dir if 'rename' has been "
"selected")))
"will be output to separate sub-folders in the output_dir")))
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"),
choices=_SORT_METHODS,
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."
help=_("R|Choose how images are sorted. Selecting a sort method gives the images a "
"new filename based on the order the image appears within the given method."
"\nL|'none': Don't sort the images. When a 'group-by' method is selected, "
"selecting 'none' means that the files will be moved/copied into their "
"respective bins, but the files will keep their original filenames. Selecting "
"'none' for both 'sort-by' and 'group-by' will do nothing" + _SORT_HELP +
"\nDefault: face")))
argument_list.append(dict(
opts=('-g', '--group-by'),
action=Radio,
type=str,
choices=_SORT_METHODS,
dest='group_method',
group=_("group settings"),
default="none",
help=_("R|Selecting a group by method will move/copy files into numbered bins based "
"on the selected method."
"\nL|'none': Don't bin the images. Folders will be sorted by the selected "
"'sort-by' but will not be binned, instead they will be sorted into a single "
"folder. Selecting 'none' for both 'sort-by' and 'group-by' will do nothing" +
_GROUP_HELP + "\nDefault: none")))
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.")))
group=_("data"),
help=_("Whether to keep the original files in their original location. Choosing a "
"'sort-by' method means that the files have to be renamed. Selecting 'keep' "
"means that the original files will be kept, and the renamed files will be "
"created in the specified output folder. Unselecting keep means that the "
"original files will be moved and renamed based on the selected sort/group "
"criteria.")))
argument_list.append(dict(
opts=('-t', '--ref_threshold'),
opts=('-t', '--threshold'),
action=Slider,
min_max=(-1.0, 10.0),
rounding=2,
type=float,
dest='min_threshold',
group=_("sort settings"),
dest='threshold',
group=_("group 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")))
help=_("R|Float value. Minimum threshold to use for grouping comparison with "
"'face-cnn' 'hist' and 'face' methods."
"\nThe lower the value the more discriminating the grouping is. Leaving "
"-1.0 will allow Faceswap to choose the default value."
"\nL|For 'face-cnn' 7.2 should be enough, with 4 being very discriminating. "
"\nL|For 'hist' 0.3 should be enough, with 0.2 being very discriminating. "
"\nL|For 'face' between 0.1 (few bins) to 0.4 (more bins) should "
"be about right."
"\nBe careful setting a value that's too extrene in a directory "
"with many images, as this could result in a lot of folders being created. "
"Defaults: face-cnn 7.2, hist 0.3, face 0.25")))
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")))
help=_("Deprecated and no longer used. The final processing will be dictated by the "
"sort/group by methods and whether 'keep_original' is selected.")))
argument_list.append(dict(
opts=('-b', '--bins'),
action=Slider,
@ -145,19 +186,27 @@ class SortArgs(FaceSwapArgs):
rounding=1,
type=int,
dest='num_bins',
group=_("output"),
group=_("group settings"),
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")))
help=_("R|Integer value. Used to control the number of bins created for grouping by: "
"any 'blur' methods, 'color' methods or 'face metric' methods ('distance', "
"'size') and 'orientation; methods ('yaw', 'pitch'). For any other grouping "
"methods see the '-t' ('--threshold') option."
"\nL|For 'face metric' methods the bins are filled, according the the "
"distribution of faces between the minimum and maximum chosen metric."
"\nL|For 'color' methods the number of bins represents the divider of the "
"percentage of colored pixels. Eg. For a bin number of '5': The first folder "
"will have the faces with 0%% to 20%% colored pixels, second 21%% to 40%%, "
"etc. Any empty bins will be deleted, so you may end up with fewer bins than "
"selected."
"\nL|For 'blur' methods folder 0 will be the least blurry, while "
"the last folder will be the blurriest."
"\nL|For 'orientation' methods the number of bins is dictated by how much 180 "
"degrees is divided. Eg. If 18 is selected, then each folder will be a 10 "
"degree increment. Folder 0 will contain faces looking the most to the "
"left/down whereas the last folder will contain the faces looking the most to "
"the right/up. NB: Some bins may be empty if faces do not fit the criteria."
"\nDefault value: 5")))
argument_list.append(dict(
opts=('-l', '--log-changes'),
action='store_true',

File diff suppressed because it is too large Load Diff

1031
tools/sort/sort_methods.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,370 @@
#!/usr/bin/env python3
""" Sorting methods that use the properties of a :class:`lib.align.AlignedFace` object to obtain
their sorting metrics.
"""
import logging
import operator
import sys
from typing import Dict, Generator, List, Optional, Tuple, TYPE_CHECKING, Union
import numpy as np
from tqdm import tqdm
from lib.align import AlignedFace
from lib.utils import FaceswapError
from .sort_methods import SortMethod
if TYPE_CHECKING:
from argparse import Namespace
from lib.align.alignments import PNGHeaderAlignmentsDict
logger = logging.getLogger(__name__)
ImgMetaType = Generator[Tuple[str,
Optional[np.ndarray],
Optional["PNGHeaderAlignmentsDict"]], None, None]
class SortAlignedMetric(SortMethod): # pylint:disable=too-few-public-methods
""" Sort by comparison of metrics stored in an Aligned Face objects. This is a parent class
for sort by aligned metrics methods. Individual methods should inherit from this class
Parameters:
----------
arguments: :class:`argparse.Namespace`
The command line arguments passed to the sort process
sort_reverse: bool, optional
``True`` if the sorted results should be in reverse order. Default: ``True``
is_group: bool, optional
Set to ``True`` if this class is going to be called exclusively for binning.
Default: ``False``
"""
def _get_metric(self, aligned_face: AlignedFace) -> Union[np.ndarray, float]:
""" Obtain the correct metric for the given sort method"
Parameters
----------
aligned_face: :class:`lib.align.AlignedFace`
The aligned face to extract the metric from
Returns
-------
float or :class:`numpy.ndarray`
The metric for the current face based on chosen sort method
"""
raise NotImplementedError
def sort(self) -> None:
""" Sort by metric score. Order in reverse for distance sort. """
logger.info("Sorting...")
self._result = sorted(self._result, key=operator.itemgetter(1), reverse=True)
def score_image(self,
filename: str,
image: Optional[np.ndarray],
alignments: Optional["PNGHeaderAlignmentsDict"]) -> None:
""" Score a single image for sort method: "distance", "yaw", "pitch" or "size" and add the
result to :attr:`_result`
Parameters
----------
filename: str
The filename of the currently processing image
image: :class:`np.ndarray` or ``None``
A face image loaded from disk or ``None``
alignments: dict or ``None``
The alignments dictionary for the aligned face or ``None``
"""
if self._log_once:
msg = "Grouping" if self._is_group else "Sorting"
logger.info("%s by %s...", msg, self._method)
self._log_once = False
if not alignments:
msg = ("The images to be sorted do not contain alignment data. Images must have "
"been generated by Faceswap's Extract process.\nIf you are sorting an "
"older faceset, then you should re-extract the faces from your source "
"alignments file to generate this data.")
raise FaceswapError(msg)
face = AlignedFace(np.array(alignments["landmarks_xy"], dtype="float32"))
self._result.append((filename, self._get_metric(face)))
class SortDistance(SortAlignedMetric):
""" Sorting mechanism for sorting faces from small to large """
def _get_metric(self, aligned_face: AlignedFace) -> float:
""" Obtain the distance from mean face metric for the given face
Parameters
----------
aligned_face: :class:`lib.align.AlignedFace`
The aligned face to extract the metric from
Returns
-------
float
The distance metric for the current face
"""
return aligned_face.average_distance
def sort(self) -> None:
""" Override default sort to sort in ascending order. """
logger.info("Sorting...")
self._result = sorted(self._result, key=operator.itemgetter(1), reverse=False)
def binning(self) -> List[List[str]]:
""" Create bins to split linearly from the lowest to the highest sample value
Returns
-------
list
List of bins of filenames
"""
return self._binning_linear_threshold(multiplier=100)
class SortPitch(SortAlignedMetric):
""" Sorting mechansim for sorting a face by pitch (down to up) """
def _get_metric(self, aligned_face: AlignedFace) -> float:
""" Obtain the pitch metric for the given face
Parameters
----------
aligned_face: :class:`lib.align.AlignedFace`
The aligned face to extract the metric from
Returns
-------
float
The pitch metric for the current face
"""
return aligned_face.pose.pitch
def binning(self) -> List[List[str]]:
""" Create bins from 0 degrees to 180 degrees based on number of bins
Allocate item to bin when it is in range of one of the pre-allocated bins
Returns
-------
list
List of bins of filenames
"""
thresholds = (np.linspace(90, -90, self._num_bins + 1))
# Start bin names from 0 for more intuitive experience
names = np.flip(thresholds.astype("int")) + 90
self._bin_names = [f"{self._method}_"
f"{idx:03d}_{int(names[idx])}"
f"degs_to_{int(names[idx + 1])}degs"
for idx in range(self._num_bins)]
bins: List[List[str]] = [[] for _ in range(self._num_bins)]
for filename, result in self._result:
result = np.clip(result, -90.0, 90.0)
bin_idx = next(bin_id for bin_id, thresh in enumerate(thresholds)
if result >= thresh) - 1
bins[bin_idx].append(filename)
return bins
class SortYaw(SortPitch):
""" Sorting mechansim for sorting a face by yaw (left to right). Same logic as sort yaw, but
with different metric """
def _get_metric(self, aligned_face: AlignedFace) -> float:
""" Obtain the yaw metric for the given face
Parameters
----------
aligned_face: :class:`lib.align.AlignedFace`
The aligned face to extract the metric from
Returns
-------
float
The yaw metric for the current face
"""
return aligned_face.pose.yaw
class SortSize(SortAlignedMetric):
""" Sorting mechanism for sorting faces from small to large """
def _get_metric(self, aligned_face: AlignedFace) -> float:
""" Obtain the size metric for the given face
Parameters
----------
aligned_face: :class:`lib.align.AlignedFace`
The aligned face to extract the metric from
Returns
-------
float
The size metric for the current face
"""
roi = aligned_face.original_roi
size = ((roi[1][0] - roi[0][0]) ** 2 + (roi[1][1] - roi[0][1]) ** 2) ** 0.5
return size
def binning(self) -> List[List[str]]:
""" Create bins to split linearly from the lowest to the highest sample value
Allocate item to bin when it is in range of one of the pre-allocated bins
Returns
-------
list
List of bins of filenames
"""
return self._binning_linear_threshold(units="px")
class SortFaceCNN(SortAlignedMetric):
""" Sort by landmark similarity or dissimilarity
Parameters:
----------
arguments: :class:`argparse.Namespace`
The command line arguments passed to the sort process
is_group: bool, optional
Set to ``True`` if this class is going to be called exclusively for binning.
Default: ``False``
"""
def __init__(self, arguments: "Namespace", is_group: bool = False) -> None:
super().__init__(arguments, is_group=is_group)
self._is_dissim = self._method == "face-cnn-dissim"
self._threshold: float = 7.2 if arguments.threshold < 1.0 else arguments.threshold
def _get_metric(self, aligned_face: AlignedFace) -> np.ndarray:
""" Obtain the xy aligned landmarks for the face"
Parameters
----------
aligned_face: :class:`lib.align.AlignedFace`
The aligned face to extract the metric from
Returns
-------
float
The metric for the current face based on chosen sort method
"""
return aligned_face.landmarks
def sort(self) -> None:
""" Sort by landmarks. """
logger.info("Comparing landmarks and sorting...")
if self._is_dissim:
self._sort_landmarks_dissim()
return
self._sort_landmarks_ssim()
def _sort_landmarks_ssim(self) -> None:
""" Sort landmarks by similarity """
img_list_len = len(self._result)
for i in tqdm(range(0, img_list_len - 1), desc="Comparing", file=sys.stdout, leave=False):
min_score = float("inf")
j_min_score = i + 1
for j in range(i + 1, img_list_len):
fl1 = self._result[i][1]
fl2 = self._result[j][1]
score = np.sum(np.absolute((fl2 - fl1).flatten()))
if score < min_score:
min_score = score
j_min_score = j
(self._result[i + 1], self._result[j_min_score]) = (self._result[j_min_score],
self._result[i + 1])
def _sort_landmarks_dissim(self) -> None:
""" Sort landmarks by dissimilarity """
logger.info("Comparing landmarks...")
img_list_len = len(self._result)
for i in tqdm(range(0, img_list_len - 1), desc="Comparing", file=sys.stdout, leave=False):
score_total = 0
for j in range(i + 1, img_list_len):
if i == j:
continue
fl1 = self._result[i][1]
fl2 = self._result[j][1]
score_total += np.sum(np.absolute((fl2 - fl1).flatten()))
self._result[i][2] = score_total
logger.info("Sorting...")
self._result = sorted(self._result, key=operator.itemgetter(2), reverse=True)
def binning(self) -> List[List[str]]:
""" Group into bins by CNN face similarity
Returns
-------
list
List of bins of filenames
"""
msg = "dissimilarity" if self._is_dissim else "similarity"
logger.info("Grouping by face-cnn %s...", msg)
# Groups are of the form: group_num -> reference faces
reference_groups: Dict[int, List[np.ndarray]] = {}
# Bins array, where index is the group number and value is
# an array containing the file paths to the images in that group.
bins: List[List[str]] = []
# Comparison threshold used to decide how similar
# faces have to be to be grouped together.
# It is multiplied by 1000 here to allow the cli option to use smaller
# numbers.
threshold = self._threshold * 1000
img_list_len = len(self._result)
for i in tqdm(range(0, img_list_len - 1),
desc="Grouping",
file=sys.stdout,
leave=False):
fl1 = self._result[i][1]
current_key = -1
current_score = float("inf")
for key, references in reference_groups.items():
try:
score = self._get_avg_score(fl1, references)
except TypeError:
score = float("inf")
except ZeroDivisionError:
score = float("inf")
if score < current_score:
current_key, current_score = key, score
if current_score < threshold:
reference_groups[current_key].append(fl1[0])
bins[current_key].append(self._result[i][0])
else:
reference_groups[len(reference_groups)] = [self._result[i][1]]
bins.append([self._result[i][0]])
return bins
@classmethod
def _get_avg_score(cls, face: np.ndarray, references: List[np.ndarray]) -> float:
""" Return the average CNN similarity score between a face and reference images
Parameters
----------
face: :class:`numpy.ndarray`
The face to check against reference images
references: list
List of reference arrays to compare the face against
Returns
-------
float
The average score between the face and the references
"""
scores = []
for ref in references:
score = np.sum(np.absolute((ref - face).flatten()))
scores.append(score)
return sum(scores) / len(scores)