mirror of
https://github.com/zebrajr/faceswap.git
synced 2025-12-06 00:20:09 +01:00
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:
parent
42a010b17a
commit
98d01760e4
34
docs/full/tools/sort.rst
Normal file
34
docs/full/tools/sort.rst
Normal 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:
|
||||
|
|
@ -14,6 +14,7 @@ Subpackages
|
|||
:maxdepth: 1
|
||||
|
||||
manual
|
||||
sort
|
||||
|
||||
alignments module
|
||||
=================
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
23
lib/image.py
23
lib/image.py
|
|
@ -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.
|
|
@ -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."
|
||||
Binary file not shown.
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
|
||||
|
|
@ -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 "
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
5
tools.py
5
tools.py
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
1167
tools/sort/sort.py
1167
tools/sort/sort.py
File diff suppressed because it is too large
Load Diff
1031
tools/sort/sort_methods.py
Normal file
1031
tools/sort/sort_methods.py
Normal file
File diff suppressed because it is too large
Load Diff
370
tools/sort/sort_methods_aligned.py
Normal file
370
tools/sort/sort_methods_aligned.py
Normal 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)
|
||||
Loading…
Reference in New Issue
Block a user