Skip to content

Commit

Permalink
bugfix: Alignment tool, auto-detect alignments
Browse files Browse the repository at this point in the history
- Random linting and typing
  • Loading branch information
torzdf committed Apr 19, 2024
1 parent d75898f commit 2bad105
Show file tree
Hide file tree
Showing 11 changed files with 258 additions and 210 deletions.
8 changes: 4 additions & 4 deletions lib/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ def encode_image(image: np.ndarray,
Returns
-------
encoded_image: bytes
The image encoded into the correct file format
The image encoded into the correct file format as bytes
Example
-------
Expand All @@ -591,10 +591,10 @@ def encode_image(image: np.ndarray,
raise ValueError("Metadata is only supported for .png and .tif images")
args = tuple() if encoding_args is None else encoding_args

retval = cv2.imencode(extension, image, args)[1]
retval = cv2.imencode(extension, image, args)[1].tobytes()
if metadata:
func = {".png": png_write_meta, ".tif": tiff_write_meta}[extension]
retval = func(retval.tobytes(), metadata) # type:ignore[arg-type]
retval = func(retval, metadata)
return retval


Expand Down Expand Up @@ -624,7 +624,7 @@ def png_write_meta(image: bytes, data: PNGHeaderDict | dict[str, T.Any] | bytes)
return retval


def tiff_write_meta(image: bytes, data: dict[str, T.Any] | bytes) -> bytes:
def tiff_write_meta(image: bytes, data: PNGHeaderDict | dict[str, T.Any] | bytes) -> bytes:
""" Write Faceswap information to a tiff's image_description field.
Parameters
Expand Down
313 changes: 176 additions & 137 deletions lib/training/augmentation.py

Large diffs are not rendered by default.

51 changes: 30 additions & 21 deletions lib/training/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def __init__(self,
side: T.Literal["a", "b"],
images: list[str],
batch_size: int) -> None:
logger.debug("Initializing %s: (model: %s, side: %s, images: %s , " # type: ignore
logger.debug("Initializing %s: (model: %s, side: %s, images: %s , "
"batch_size: %s, config: %s)", self.__class__.__name__, model.name, side,
len(images), batch_size, config)
self._config = config
Expand Down Expand Up @@ -243,8 +243,9 @@ def _get_images_with_meta(self, filenames: list[str]) -> tuple[np.ndarray, list[
raw_faces = read_image_batch(filenames)

detected_faces = self._face_cache.get_items(filenames)
logger.trace("filenames: %s, raw_faces: '%s', detected_faces: %s", # type: ignore
filenames, raw_faces.shape, len(detected_faces))
logger.trace( # type:ignore[attr-defined]
"filenames: %s, raw_faces: '%s', detected_faces: %s",
filenames, raw_faces.shape, len(detected_faces))
return raw_faces, detected_faces

def _crop_to_coverage(self,
Expand All @@ -271,8 +272,8 @@ def _crop_to_coverage(self,
batch: :class:`np.ndarray`
The pre-allocated array to hold this batch
"""
logger.trace("Cropping training images info: (filenames: %s, side: '%s')", # type: ignore
filenames, self._side)
logger.trace( # type:ignore[attr-defined]
"Cropping training images info: (filenames: %s, side: '%s')", filenames, self._side)

with futures.ThreadPoolExecutor() as executor:
proc = {executor.submit(face.aligned.extract_face, img): idx
Expand Down Expand Up @@ -304,7 +305,7 @@ def _apply_mask(self, detected_faces: list[DetectedFace], batch: np.ndarray) ->
masks = np.array([face.get_training_masks() for face in detected_faces])
batch[..., 3:] = masks

logger.trace("side: %s, masks: %s, batch: %s", # type: ignore
logger.trace("side: %s, masks: %s, batch: %s", # type:ignore[attr-defined]
self._side, masks.shape, batch.shape)

def _process_batch(self, filenames: list[str]) -> BatchType:
Expand Down Expand Up @@ -333,9 +334,9 @@ def _process_batch(self, filenames: list[str]) -> BatchType:
self._apply_mask(detected_faces, batch)
feed, targets = self.process_batch(filenames, raw_faces, detected_faces, batch)

logger.trace("Processed %s batch side %s. (filenames: %s, feed: %s, " # type: ignore
"targets: %s)", self.__class__.__name__, self._side, filenames,
feed.shape, [t.shape for t in targets])
logger.trace( # type:ignore[attr-defined]
"Processed %s batch side %s. (filenames: %s, feed: %s, targets: %s)",
self.__class__.__name__, self._side, filenames, feed.shape, [t.shape for t in targets])

return feed, targets

Expand Down Expand Up @@ -450,15 +451,19 @@ def _create_targets(self, batch: np.ndarray) -> list[np.ndarray]:
List of 4-dimensional target images, at all model output sizes, with masks compiled
into channels 4+ for each output size
"""
logger.trace("Compiling targets: batch shape: %s", batch.shape) # type: ignore
logger.trace("Compiling targets: batch shape: %s", # type:ignore[attr-defined]
batch.shape)
if len(self._output_sizes) == 1 and self._output_sizes[0] == self._process_size:
# Rolling buffer here makes next to no difference, so just create array on the fly
retval = [self._to_float32(batch)]
else:
retval = [self._to_float32(np.array([cv2.resize(image, (size, size), cv2.INTER_AREA)
retval = [self._to_float32(np.array([cv2.resize(image,
(size, size),
interpolation=cv2.INTER_AREA)
for image in batch]))
for size in self._output_sizes]
logger.trace("Processed targets: %s", [t.shape for t in retval]) # type: ignore
logger.trace("Processed targets: %s", # type:ignore[attr-defined]
[t.shape for t in retval])
return retval

def process_batch(self,
Expand Down Expand Up @@ -533,7 +538,7 @@ def process_batch(self,
feed = self._to_float32(np.array([cv2.resize(image,
(self._model_input_size,
self._model_input_size),
cv2.INTER_AREA)
interpolation=cv2.INTER_AREA)
for image in warped]))
else:
feed = self._to_float32(warped)
Expand All @@ -556,8 +561,9 @@ def _get_closest_match(self, filenames: list[str], batch_src_points: np.ndarray)
:class:`np.ndarray`
Randomly selected closest matches from the other side's landmarks
"""
logger.trace("Retrieving closest matched landmarks: (filenames: '%s', " # type: ignore
"src_points: '%s')", filenames, batch_src_points)
logger.trace( # type:ignore[attr-defined]
"Retrieving closest matched landmarks: (filenames: '%s', src_points: '%s')",
filenames, batch_src_points)
lm_side: T.Literal["a", "b"] = "a" if self._side == "b" else "b"
other_cache = get_cache(lm_side)
landmarks = other_cache.aligned_landmarks
Expand All @@ -575,7 +581,8 @@ def _get_closest_match(self, filenames: list[str], batch_src_points: np.ndarray)
closest_matches = self._cache_closest_matches(filenames, batch_src_points, landmarks)

batch_dst_points = np.array([landmarks[choice(fname)] for fname in closest_matches])
logger.trace("Returning: (batch_dst_points: %s)", batch_dst_points.shape) # type: ignore
logger.trace("Returning: (batch_dst_points: %s)", # type:ignore[attr-defined]
batch_dst_points.shape)
return batch_dst_points

def _cache_closest_matches(self,
Expand Down Expand Up @@ -648,8 +655,9 @@ def _create_samples(self,
list
List of 4-dimensional target images, at final model output size
"""
logger.trace("Compiling samples: images shape: %s, detected_faces: %s ", # type: ignore
images.shape, len(detected_faces))
logger.trace( # type:ignore[attr-defined]
"Compiling samples: images shape: %s, detected_faces: %s ",
images.shape, len(detected_faces))
output_size = self._output_sizes[-1]
full_size = 2 * int(np.rint((output_size / self._coverage_ratio) / 2))

Expand All @@ -665,7 +673,7 @@ def _create_samples(self,
is_aligned=True).face
for idx, face in enumerate(detected_faces)]))

logger.trace("Processed samples: %s", retval.shape) # type: ignore
logger.trace("Processed samples: %s", retval.shape) # type:ignore[attr-defined]
return [retval]

def process_batch(self,
Expand Down Expand Up @@ -840,8 +848,9 @@ def get_batch(self) -> tuple[list[list[np.ndarray]], ...]:
side_feed, side_targets = next(self._feeds[side])
if self._model.config["learn_mask"]: # Add the face mask as it's own target
side_targets += [side_targets[-1][..., 3][..., None]]
logger.trace("side: %s, input_shapes: %s, target_shapes: %s", # type: ignore
side, side_feed.shape, [i.shape for i in side_targets])
logger.trace( # type:ignore[attr-defined]
"side: %s, input_shapes: %s, target_shapes: %s",
side, side_feed.shape, [i.shape for i in side_targets])
model_inputs.append([side_feed])
model_targets.append(side_targets)

Expand Down
Binary file modified locales/es/LC_MESSAGES/tools.alignments.cli.mo
Binary file not shown.
24 changes: 12 additions & 12 deletions locales/es/LC_MESSAGES/tools.alignments.cli.po
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ msgid ""
msgstr ""
"Project-Id-Version: faceswap.spanish\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-12 12:10+0100\n"
"PO-Revision-Date: 2024-04-12 12:14+0100\n"
"POT-Creation-Date: 2024-04-19 11:28+0100\n"
"PO-Revision-Date: 2024-04-19 11:29+0100\n"
"Last-Translator: \n"
"Language-Team: tokafondo\n"
"Language: es_ES\n"
Expand Down Expand Up @@ -37,29 +37,29 @@ msgstr ""
"archivo de alineación."

#: tools/alignments/cli.py:43
msgid " Must Pass in a frames folder/source video file (-fr)."
msgid " Must Pass in a frames folder/source video file (-r)."
msgstr ""
" Debe indicar una carpeta de fotogramas o archivo de vídeo de origen (-fr)."
" Debe indicar una carpeta de fotogramas o archivo de vídeo de origen (-r)."

#: tools/alignments/cli.py:44
msgid " Must Pass in a faces folder (-fc)."
msgstr " Debe indicar una carpeta de caras (-fc)."
msgid " Must Pass in a faces folder (-c)."
msgstr " Debe indicar una carpeta de caras (-c)."

#: tools/alignments/cli.py:45
msgid ""
" Must Pass in either a frames folder/source video file OR a faces folder (-"
"fr or -fc)."
" Must Pass in either a frames folder/source video file OR a faces folder (-r "
"or -c)."
msgstr ""
" Debe indicar una carpeta de fotogramas o archivo de vídeo de origen, o una "
"carpeta de caras (-fr o -fc)."
"carpeta de caras (-r o -c)."

#: tools/alignments/cli.py:47
msgid ""
" Must Pass in a frames folder/source video file AND a faces folder (-fr and -"
"fc)."
" Must Pass in a frames folder/source video file AND a faces folder (-r and -"
"c)."
msgstr ""
" Debe indicar una carpeta de fotogramas o archivo de vídeo de origen, y una "
"carpeta de caras (-fr y -fc)."
"carpeta de caras (-r y -c)."

#: tools/alignments/cli.py:49
msgid " Use the output option (-o) to process results."
Expand Down
Binary file modified locales/kr/LC_MESSAGES/tools.alignments.cli.mo
Binary file not shown.
24 changes: 12 additions & 12 deletions locales/kr/LC_MESSAGES/tools.alignments.cli.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-12 12:10+0100\n"
"PO-Revision-Date: 2024-04-12 12:17+0100\n"
"POT-Creation-Date: 2024-04-19 11:28+0100\n"
"PO-Revision-Date: 2024-04-19 11:30+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: ko_KR\n"
Expand All @@ -35,29 +35,29 @@ msgstr ""
"용하거나 여러 작업을 수행할 수 있습니다."

#: tools/alignments/cli.py:43
msgid " Must Pass in a frames folder/source video file (-fr)."
msgid " Must Pass in a frames folder/source video file (-r)."
msgstr ""
" 프레임들이 저장된 폴더나 원본 비디오 파일을 무조건 전달해야 합니다 (-fr)."
" 프레임들이 저장된 폴더나 원본 비디오 파일을 무조건 전달해야 합니다 (-r)."

#: tools/alignments/cli.py:44
msgid " Must Pass in a faces folder (-fc)."
msgstr " 얼굴 폴더를 무조건 전달해야 합니다 (-fc)."
msgid " Must Pass in a faces folder (-c)."
msgstr " 얼굴 폴더를 무조건 전달해야 합니다 (-c)."

#: tools/alignments/cli.py:45
msgid ""
" Must Pass in either a frames folder/source video file OR a faces folder (-"
"fr or -fc)."
" Must Pass in either a frames folder/source video file OR a faces folder (-r "
"or -c)."
msgstr ""
" 프레임 폴더나 원본 비디오 파일 또는 얼굴 폴더중 하나를 무조건 전달해야 합니"
"다 (-fr and -fc)."
"다 (-r and -c)."

#: tools/alignments/cli.py:47
msgid ""
" Must Pass in a frames folder/source video file AND a faces folder (-fr and -"
"fc)."
" Must Pass in a frames folder/source video file AND a faces folder (-r and -"
"c)."
msgstr ""
" 프레임 폴더나 원본 비디오 파일 그리고 얼굴 폴더를 무조건 전달해야 합니다 (-"
"fr and -fc)."
"r and -c)."

#: tools/alignments/cli.py:49
msgid " Use the output option (-o) to process results."
Expand Down
Binary file modified locales/ru/LC_MESSAGES/tools.alignments.cli.mo
Binary file not shown.
24 changes: 12 additions & 12 deletions locales/ru/LC_MESSAGES/tools.alignments.cli.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-12 12:10+0100\n"
"PO-Revision-Date: 2024-04-12 12:13+0100\n"
"POT-Creation-Date: 2024-04-19 11:28+0100\n"
"PO-Revision-Date: 2024-04-19 11:31+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: ru\n"
Expand Down Expand Up @@ -38,28 +38,28 @@ msgstr ""
"кадров."

#: tools/alignments/cli.py:43
msgid " Must Pass in a frames folder/source video file (-fr)."
msgstr " Должен проходить в папке с кадрами/исходным видеофайлом (-fr)."
msgid " Must Pass in a frames folder/source video file (-r)."
msgstr " Должен проходить в папке с кадрами/исходным видеофайлом (-r)."

#: tools/alignments/cli.py:44
msgid " Must Pass in a faces folder (-fc)."
msgstr " Должен проходить в папке с лицами (-fc)."
msgid " Must Pass in a faces folder (-c)."
msgstr " Должен проходить в папке с лицами (-c)."

#: tools/alignments/cli.py:45
msgid ""
" Must Pass in either a frames folder/source video file OR a faces folder (-"
"fr or -fc)."
" Must Pass in either a frames folder/source video file OR a faces folder (-r "
"or -c)."
msgstr ""
" Должно передаваться либо в папку с кадрами/исходным видеофайлом, либо в "
"папку с лицами (-fr или -fc)."
"папку с лицами (-r или -c)."

#: tools/alignments/cli.py:47
msgid ""
" Must Pass in a frames folder/source video file AND a faces folder (-fr and -"
"fc)."
" Must Pass in a frames folder/source video file AND a faces folder (-r and -"
"c)."
msgstr ""
" Должно передаваться либо в папку с кадрами/исходным видеофайлом И в папку с "
"лицами (-fr и -fc)."
"лицами (-r и -c)."

#: tools/alignments/cli.py:49
msgid " Use the output option (-o) to process results."
Expand Down
14 changes: 7 additions & 7 deletions locales/tools.alignments.cli.pot
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-12 12:10+0100\n"
"POT-Creation-Date: 2024-04-19 11:28+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
Expand All @@ -30,23 +30,23 @@ msgid ""
msgstr ""

#: tools/alignments/cli.py:43
msgid " Must Pass in a frames folder/source video file (-fr)."
msgid " Must Pass in a frames folder/source video file (-r)."
msgstr ""

#: tools/alignments/cli.py:44
msgid " Must Pass in a faces folder (-fc)."
msgid " Must Pass in a faces folder (-c)."
msgstr ""

#: tools/alignments/cli.py:45
msgid ""
" Must Pass in either a frames folder/source video file OR a faces folder (-"
"fr or -fc)."
" Must Pass in either a frames folder/source video file OR a faces folder (-r "
"or -c)."
msgstr ""

#: tools/alignments/cli.py:47
msgid ""
" Must Pass in a frames folder/source video file AND a faces folder (-fr and -"
"fc)."
" Must Pass in a frames folder/source video file AND a faces folder (-r and -"
"c)."
msgstr ""

#: tools/alignments/cli.py:49
Expand Down
10 changes: 5 additions & 5 deletions tools/alignments/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ def get_argument_list() -> list[dict[str, T.Any]]:
dict
The argparse command line options for processing by argparse
"""
frames_dir = _(" Must Pass in a frames folder/source video file (-fr).")
faces_dir = _(" Must Pass in a faces folder (-fc).")
frames_dir = _(" Must Pass in a frames folder/source video file (-r).")
faces_dir = _(" Must Pass in a faces folder (-c).")
frames_or_faces_dir = _(" Must Pass in either a frames folder/source video file OR a "
"faces folder (-fr or -fc).")
"faces folder (-r or -c).")
frames_and_faces_dir = _(" Must Pass in a frames folder/source video file AND a faces "
"folder (-fr and -fc).")
"folder (-r and -c).")
output_opts = _(" Use the output option (-o) to process results.")
argument_list = []
argument_list.append({
Expand Down Expand Up @@ -118,7 +118,7 @@ def get_argument_list() -> list[dict[str, T.Any]]:
"group": _("data"),
# hacky solution to not require alignments file if creating alignments from faces:
"required": not any(val in sys.argv for val in ["from-faces",
"-fr",
"-r",
"-frames_folder"]),
"filetypes": "alignments",
"help": _(
Expand Down

0 comments on commit 2bad105

Please sign in to comment.