mirror of
https://github.com/zebrajr/faceswap.git
synced 2025-12-06 00:20:09 +01:00
bugfix: Alignment tool, auto-detect alignments
- Random linting and typing
This commit is contained in:
parent
d75898f718
commit
2bad105dc8
|
|
@ -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
|
||||
-------
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
""" Processes the augmentation of images for feeding into a Faceswap model. """
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
import typing as T
|
||||
|
||||
|
|
@ -11,6 +10,7 @@ import numpy as np
|
|||
from scipy.interpolate import griddata
|
||||
|
||||
from lib.image import batch_convert_color
|
||||
from lib.logger import parse_class_init
|
||||
|
||||
if T.TYPE_CHECKING:
|
||||
from lib.config import ConfigValueType
|
||||
|
|
@ -18,49 +18,152 @@ if T.TYPE_CHECKING:
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class AugConstants:
|
||||
class AugConstants: # pylint:disable=too-many-instance-attributes,too-few-public-methods
|
||||
""" Dataclass for holding constants for Image Augmentation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
clahe_base_contrast: int
|
||||
The base number for Contrast Limited Adaptive Histogram Equalization
|
||||
clahe_chance: float
|
||||
Probability to perform Contrast Limited Adaptive Histogram Equilization
|
||||
clahe_max_size: int
|
||||
Maximum clahe window size
|
||||
lab_adjust: np.ndarray
|
||||
Adjustment amounts for L*A*B augmentation
|
||||
transform_rotation: int
|
||||
Rotation range for transformations
|
||||
transform_zoom: float
|
||||
Zoom range for transformations
|
||||
transform_shift: float
|
||||
Shift range for transformations
|
||||
warp_maps: :class:`numpy.ndarray`
|
||||
The stacked (x, y) mappings for image warping
|
||||
warp_pads: tuple
|
||||
The padding to apply for image warping
|
||||
warp_slices: slice
|
||||
The slices for extracting a warped image
|
||||
warp_lm_edge_anchors: :class:`numpy.ndarray`
|
||||
The edge anchors for landmark based warping
|
||||
warp_lm_grids: :class:`numpy.ndarray`
|
||||
The grids for landmark based warping
|
||||
config: dict[str, ConfigValueType]
|
||||
The user training configuration options
|
||||
pricessing_size: int:
|
||||
The size of image to augment the data for
|
||||
batch_size: int
|
||||
The batch size that augmented data is being prepared for
|
||||
"""
|
||||
clahe_base_contrast: int
|
||||
clahe_chance: float
|
||||
clahe_max_size: int
|
||||
lab_adjust: np.ndarray
|
||||
transform_rotation: int
|
||||
transform_zoom: float
|
||||
transform_shift: float
|
||||
warp_maps: np.ndarray
|
||||
warp_pad: tuple[int, int]
|
||||
warp_slices: slice
|
||||
warp_lm_edge_anchors: np.ndarray
|
||||
warp_lm_grids: np.ndarray
|
||||
def __init__(self,
|
||||
config: dict[str, ConfigValueType],
|
||||
processing_size: int,
|
||||
batch_size: int) -> None:
|
||||
logger.debug(parse_class_init(locals()))
|
||||
self.clahe_base_contrast: int = 0
|
||||
"""int: The base number for Contrast Limited Adaptive Histogram Equalization"""
|
||||
self.clahe_chance: float = 0.0
|
||||
"""float: Probability to perform Contrast Limited Adaptive Histogram Equilization"""
|
||||
self.clahe_max_size: int = 0
|
||||
"""int: Maximum clahe window size"""
|
||||
|
||||
self.lab_adjust: np.ndarray
|
||||
""":class:`numpy.ndarray`: Adjustment amounts for L*A*B augmentation"""
|
||||
self.transform_rotation: int = 0
|
||||
"""int: Rotation range for transformations"""
|
||||
self.transform_zoom: float = 0.0
|
||||
"""float: Zoom range for transformations"""
|
||||
self.transform_shift: float = 0.0
|
||||
"""float: Shift range for transformations"""
|
||||
self.warp_maps: np.ndarray
|
||||
""":class:`numpy.ndarray`The stacked (x, y) mappings for image warping"""
|
||||
self.warp_pad: tuple[int, int] = (0, 0)
|
||||
""":tuple[int, int]: The padding to apply for image warping"""
|
||||
self.warp_slices: slice
|
||||
""":slice: The slices for extracting a warped image"""
|
||||
self.warp_lm_edge_anchors: np.ndarray
|
||||
"""::class:`numpy.ndarray`: The edge anchors for landmark based warping"""
|
||||
self.warp_lm_grids: np.ndarray
|
||||
"""::class:`numpy.ndarray`: The grids for landmark based warping"""
|
||||
|
||||
self._config = config
|
||||
self._size = processing_size
|
||||
self._load_config(batch_size)
|
||||
logger.debug("Initialized: %s", self.__class__.__name__)
|
||||
|
||||
def _load_clahe(self) -> None:
|
||||
""" Load the CLAHE constants from user config """
|
||||
color_clahe_chance = self._config.get("color_clahe_chance", 50)
|
||||
color_clahe_max_size = self._config.get("color_clahe_max_size", 4)
|
||||
assert isinstance(color_clahe_chance, int)
|
||||
assert isinstance(color_clahe_max_size, int)
|
||||
|
||||
self.clahe_base_contrast = max(2, self._size // 128)
|
||||
self.clahe_chance = color_clahe_chance / 100
|
||||
self.clahe_max_size = color_clahe_max_size
|
||||
logger.debug("clahe_base_contrast: %s, clahe_chance: %s, clahe_max_size: %s",
|
||||
self.clahe_base_contrast, self.clahe_chance, self.clahe_max_size)
|
||||
|
||||
def _load_lab(self) -> None:
|
||||
""" Load the random L*A*B augmentation constants """
|
||||
color_lightness = self._config.get("color_lightness", 30)
|
||||
color_ab = self._config.get("color_ab", 8)
|
||||
assert isinstance(color_lightness, int)
|
||||
assert isinstance(color_ab, int)
|
||||
|
||||
amount_l = int(color_lightness) / 100
|
||||
amount_ab = int(color_ab) / 100
|
||||
|
||||
self.lab_adjust = np.array([amount_l, amount_ab, amount_ab], dtype="float32")
|
||||
logger.debug("lab_adjust: %s", self.lab_adjust)
|
||||
|
||||
def _load_transform(self) -> None:
|
||||
""" Load the random transform constants """
|
||||
shift_range = self._config.get("shift_range", 5)
|
||||
rotation_range = self._config.get("rotation_range", 10)
|
||||
zoom_amount = self._config.get("zoom_amount", 5)
|
||||
assert isinstance(shift_range, int)
|
||||
assert isinstance(rotation_range, int)
|
||||
assert isinstance(zoom_amount, int)
|
||||
|
||||
self.transform_shift = (shift_range / 100) * self._size
|
||||
self.transform_rotation = rotation_range
|
||||
self.transform_zoom = zoom_amount / 100
|
||||
logger.debug("transform_shift: %s, transform_rotation: %s, transform_zoom: %s",
|
||||
self.transform_shift, self.transform_rotation, self.transform_zoom)
|
||||
|
||||
def _load_warp(self, batch_size: int) -> None:
|
||||
""" Load the warp augmentation constants
|
||||
|
||||
Parameters
|
||||
----------
|
||||
batch_size: int
|
||||
The batch size that augmented data is being prepared for
|
||||
"""
|
||||
warp_range = np.linspace(0, self._size, 5, dtype='float32')
|
||||
warp_mapx = np.broadcast_to(warp_range, (batch_size, 5, 5)).astype("float32")
|
||||
warp_mapy = np.broadcast_to(warp_mapx[0].T, (batch_size, 5, 5)).astype("float32")
|
||||
warp_pad = int(1.25 * self._size)
|
||||
|
||||
self.warp_maps = np.stack((warp_mapx, warp_mapy), axis=1)
|
||||
self.warp_pad = (warp_pad, warp_pad)
|
||||
self.warp_slices = slice(warp_pad // 10, -warp_pad // 10)
|
||||
logger.debug("warp_maps: (%s, %s), warp_pad: %s, warp_slices: %s",
|
||||
self.warp_maps.shape, self.warp_maps.dtype,
|
||||
self.warp_pad, self.warp_slices)
|
||||
|
||||
def _load_warp_to_landmarks(self, batch_size: int) -> None:
|
||||
""" Load the warp-to-landmarks augmentation constants
|
||||
|
||||
Parameters
|
||||
----------
|
||||
batch_size: int
|
||||
The batch size that augmented data is being prepared for
|
||||
"""
|
||||
p_mx = self._size - 1
|
||||
p_hf = (self._size // 2) - 1
|
||||
edge_anchors = np.array([(0, 0), (0, p_mx), (p_mx, p_mx), (p_mx, 0),
|
||||
(p_hf, 0), (p_hf, p_mx), (p_mx, p_hf), (0, p_hf)]).astype("int32")
|
||||
edge_anchors = np.broadcast_to(edge_anchors, (batch_size, 8, 2))
|
||||
grids = np.mgrid[0: p_mx: complex(self._size), # type:ignore[misc]
|
||||
0: p_mx: complex(self._size)] # type:ignore[misc]
|
||||
|
||||
self.warp_lm_edge_anchors = edge_anchors
|
||||
self.warp_lm_grids = grids
|
||||
logger.debug("warp_lm_edge_anchors: (%s, %s), warp_lm_grids: (%s, %s)",
|
||||
self.warp_lm_edge_anchors.shape, self.warp_lm_edge_anchors.dtype,
|
||||
self.warp_lm_grids.shape, self.warp_lm_grids.dtype)
|
||||
|
||||
def _load_config(self, batch_size: int) -> None:
|
||||
""" Load the constants into the class from user config
|
||||
|
||||
Parameters
|
||||
----------
|
||||
batch_size: int
|
||||
The batch size that augmented data is being prepared for
|
||||
"""
|
||||
logger.debug("Loading augmentation constants")
|
||||
self._load_clahe()
|
||||
self._load_lab()
|
||||
self._load_transform()
|
||||
self._load_warp(batch_size)
|
||||
self._load_warp_to_landmarks(batch_size)
|
||||
logger.debug("Loaded augmentation constants")
|
||||
|
||||
|
||||
class ImageAugmentation():
|
||||
|
|
@ -68,7 +171,7 @@ class ImageAugmentation():
|
|||
|
||||
Parameters
|
||||
----------
|
||||
batchsize: int
|
||||
batch_size: int
|
||||
The number of images that will be fed through the augmentation functions at once.
|
||||
processing_size: int
|
||||
The largest input or output size of the model. This is the size that images are processed
|
||||
|
|
@ -78,88 +181,25 @@ class ImageAugmentation():
|
|||
plugin configuration options.
|
||||
"""
|
||||
def __init__(self,
|
||||
batchsize: int,
|
||||
batch_size: int,
|
||||
processing_size: int,
|
||||
config: dict[str, ConfigValueType]) -> None:
|
||||
logger.debug("Initializing %s: (batchsize: %s, processing_size: %s, "
|
||||
"config: %s)",
|
||||
self.__class__.__name__, batchsize, processing_size, config)
|
||||
|
||||
logger.debug(parse_class_init(locals()))
|
||||
self._processing_size = processing_size
|
||||
self._batchsize = batchsize
|
||||
self._config = config
|
||||
self._batch_size = batch_size
|
||||
|
||||
# flip_args
|
||||
flip_chance = config.get("random_flip", 50)
|
||||
assert isinstance(flip_chance, int)
|
||||
self._flip_chance = flip_chance
|
||||
|
||||
# Warp args
|
||||
self._warp_scale = 5 / 256 * self._processing_size # Normal random variable scale
|
||||
self._warp_lm_scale = 2 / 256 * self._processing_size # Normal random variable scale
|
||||
|
||||
self._constants = self._get_constants()
|
||||
self._constants = AugConstants(config, processing_size, batch_size)
|
||||
logger.debug("Initialized %s", self.__class__.__name__)
|
||||
|
||||
def _get_constants(self) -> AugConstants:
|
||||
""" Initializes the caching of constants for use in various image augmentations.
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict
|
||||
Cached constants that are used for various augmentations
|
||||
"""
|
||||
logger.debug("Initializing constants.")
|
||||
|
||||
# Config variables typing check
|
||||
shift_range = self._config.get("shift_range", 5)
|
||||
color_lightness = self._config.get("color_lightness", 30)
|
||||
color_ab = self._config.get("color_ab", 8)
|
||||
color_clahe_chance = self._config.get("color_clahe_chance", 50)
|
||||
color_clahe_max_size = self._config.get("color_clahe_max_size", 4)
|
||||
rotation_range = self._config.get("rotation_range", 10)
|
||||
zoom_amount = self._config.get("zoom_amount", 5)
|
||||
|
||||
assert isinstance(shift_range, int)
|
||||
assert isinstance(color_lightness, int)
|
||||
assert isinstance(color_ab, int)
|
||||
assert isinstance(color_clahe_chance, int)
|
||||
assert isinstance(color_clahe_max_size, int)
|
||||
assert isinstance(rotation_range, int)
|
||||
assert isinstance(zoom_amount, int)
|
||||
|
||||
# Transform
|
||||
tform_shift = (shift_range / 100) * self._processing_size
|
||||
|
||||
# Color Aug
|
||||
amount_l = int(color_lightness) / 100
|
||||
amount_ab = int(color_ab) / 100
|
||||
lab_adjust = np.array([amount_l, amount_ab, amount_ab], dtype="float32")
|
||||
|
||||
# Random Warp
|
||||
warp_range = np.linspace(0, self._processing_size, 5, dtype='float32')
|
||||
warp_mapx = np.broadcast_to(warp_range, (self._batchsize, 5, 5)).astype("float32")
|
||||
warp_mapy = np.broadcast_to(warp_mapx[0].T, (self._batchsize, 5, 5)).astype("float32")
|
||||
warp_pad = int(1.25 * self._processing_size)
|
||||
|
||||
# Random Warp Landmarks
|
||||
p_mx = self._processing_size - 1
|
||||
p_hf = (self._processing_size // 2) - 1
|
||||
edge_anchors = np.array([(0, 0), (0, p_mx), (p_mx, p_mx), (p_mx, 0),
|
||||
(p_hf, 0), (p_hf, p_mx), (p_mx, p_hf), (0, p_hf)]).astype("int32")
|
||||
edge_anchors = np.broadcast_to(edge_anchors, (self._batchsize, 8, 2))
|
||||
grids = np.mgrid[0: p_mx: complex(self._processing_size), # type: ignore
|
||||
0: p_mx: complex(self._processing_size)] # type: ignore
|
||||
retval = AugConstants(clahe_base_contrast=max(2, self._processing_size // 128),
|
||||
clahe_chance=color_clahe_chance / 100,
|
||||
clahe_max_size=color_clahe_max_size,
|
||||
lab_adjust=lab_adjust,
|
||||
transform_rotation=rotation_range,
|
||||
transform_zoom=zoom_amount / 100,
|
||||
transform_shift=tform_shift,
|
||||
warp_maps=np.stack((warp_mapx, warp_mapy), axis=1),
|
||||
warp_pad=(warp_pad, warp_pad),
|
||||
warp_slices=slice(warp_pad // 10, -warp_pad // 10),
|
||||
warp_lm_edge_anchors=edge_anchors,
|
||||
warp_lm_grids=grids)
|
||||
logger.debug("Initialized constants: %s", retval)
|
||||
return retval
|
||||
|
||||
# <<< COLOR AUGMENTATION >>> #
|
||||
def color_adjust(self, batch: np.ndarray) -> np.ndarray:
|
||||
""" Perform color augmentation on the passed in batch.
|
||||
|
|
@ -178,7 +218,7 @@ class ImageAugmentation():
|
|||
A 4-dimensional array of the same shape as :attr:`batch` with color augmentation
|
||||
applied.
|
||||
"""
|
||||
logger.trace("Augmenting color") # type: ignore
|
||||
logger.trace("Augmenting color") # type:ignore[attr-defined]
|
||||
batch = batch_convert_color(batch, "BGR2LAB")
|
||||
self._random_lab(batch)
|
||||
self._random_clahe(batch)
|
||||
|
|
@ -190,7 +230,7 @@ class ImageAugmentation():
|
|||
a batch of images """
|
||||
base_contrast = self._constants.clahe_base_contrast
|
||||
|
||||
batch_random = np.random.rand(self._batchsize)
|
||||
batch_random = np.random.rand(self._batch_size)
|
||||
indices = np.where(batch_random < self._constants.clahe_chance)[0]
|
||||
if not np.any(indices):
|
||||
return
|
||||
|
|
@ -198,9 +238,9 @@ class ImageAugmentation():
|
|||
size=indices.shape[0],
|
||||
dtype="uint8")
|
||||
grid_sizes = (grid_bases * (base_contrast // 2)) + base_contrast
|
||||
logger.trace("Adjusting Contrast. Grid Sizes: %s", grid_sizes) # type: ignore
|
||||
logger.trace("Adjusting Contrast. Grid Sizes: %s", grid_sizes) # type:ignore[attr-defined]
|
||||
|
||||
clahes = [cv2.createCLAHE(clipLimit=2.0, # pylint:disable=no-member
|
||||
clahes = [cv2.createCLAHE(clipLimit=2.0,
|
||||
tileGridSize=(grid_size, grid_size))
|
||||
for grid_size in grid_sizes]
|
||||
|
||||
|
|
@ -212,8 +252,8 @@ class ImageAugmentation():
|
|||
images """
|
||||
randoms = np.random.uniform(-self._constants.lab_adjust,
|
||||
self._constants.lab_adjust,
|
||||
size=(self._batchsize, 1, 1, 3)).astype("float32")
|
||||
logger.trace("Random LAB adjustments: %s", randoms) # type: ignore
|
||||
size=(self._batch_size, 1, 1, 3)).astype("float32")
|
||||
logger.trace("Random LAB adjustments: %s", randoms) # type:ignore[attr-defined]
|
||||
# Iterating through the images and channels is much faster than numpy.where and slightly
|
||||
# faster than numexpr.where.
|
||||
for image, rand in zip(batch, randoms):
|
||||
|
|
@ -236,18 +276,18 @@ class ImageAugmentation():
|
|||
The batch should be a 4-dimensional array of shape (`batchsize`, `height`, `width`,
|
||||
`channels`) and in `BGR` format.
|
||||
"""
|
||||
logger.trace("Randomly transforming image") # type: ignore
|
||||
logger.trace("Randomly transforming image") # type:ignore[attr-defined]
|
||||
|
||||
rotation = np.random.uniform(-self._constants.transform_rotation,
|
||||
self._constants.transform_rotation,
|
||||
size=self._batchsize).astype("float32")
|
||||
size=self._batch_size).astype("float32")
|
||||
scale = np.random.uniform(1 - self._constants.transform_zoom,
|
||||
1 + self._constants.transform_zoom,
|
||||
size=self._batchsize).astype("float32")
|
||||
size=self._batch_size).astype("float32")
|
||||
|
||||
tform = np.random.uniform(-self._constants.transform_shift,
|
||||
self._constants.transform_shift,
|
||||
size=(self._batchsize, 2)).astype("float32")
|
||||
size=(self._batch_size, 2)).astype("float32")
|
||||
mats = np.array(
|
||||
[cv2.getRotationMatrix2D((self._processing_size // 2, self._processing_size // 2),
|
||||
rot,
|
||||
|
|
@ -262,7 +302,7 @@ class ImageAugmentation():
|
|||
dst=image,
|
||||
borderMode=cv2.BORDER_REPLICATE)
|
||||
|
||||
logger.trace("Randomly transformed image") # type: ignore
|
||||
logger.trace("Randomly transformed image") # type:ignore[attr-defined]
|
||||
|
||||
def random_flip(self, batch: np.ndarray):
|
||||
""" Perform random horizontal flipping on the passed in batch.
|
||||
|
|
@ -275,14 +315,12 @@ class ImageAugmentation():
|
|||
The batch should be a 4-dimensional array of shape (`batchsize`, `height`, `width`,
|
||||
`channels`) and in `BGR` format.
|
||||
"""
|
||||
logger.trace("Randomly flipping image") # type: ignore
|
||||
randoms = np.random.rand(self._batchsize)
|
||||
flip_chance = self._config.get("random_flip", 50)
|
||||
assert isinstance(flip_chance, int)
|
||||
indices = np.where(randoms > flip_chance / 100)[0]
|
||||
logger.trace("Randomly flipping image") # type:ignore[attr-defined]
|
||||
randoms = np.random.rand(self._batch_size)
|
||||
indices = np.where(randoms <= self._flip_chance / 100)[0]
|
||||
batch[indices] = batch[indices, :, ::-1]
|
||||
logger.trace("Randomly flipped %s images of %s", # type: ignore
|
||||
len(indices), self._batchsize)
|
||||
logger.trace("Randomly flipped %s images of %s", # type:ignore[attr-defined]
|
||||
len(indices), self._batch_size)
|
||||
|
||||
def warp(self, batch: np.ndarray, to_landmarks: bool = False, **kwargs) -> np.ndarray:
|
||||
""" Perform random warping on the passed in batch by one of two methods.
|
||||
|
|
@ -329,9 +367,9 @@ class ImageAugmentation():
|
|||
:class:`numpy.ndarray`
|
||||
A 4-dimensional array of the same shape as :attr:`batch` with warping applied.
|
||||
"""
|
||||
logger.trace("Randomly warping batch") # type: ignore
|
||||
logger.trace("Randomly warping batch") # type:ignore[attr-defined]
|
||||
slices = self._constants.warp_slices
|
||||
rands = np.random.normal(size=(self._batchsize, 2, 5, 5),
|
||||
rands = np.random.normal(size=(self._batch_size, 2, 5, 5),
|
||||
scale=self._warp_scale).astype("float32")
|
||||
batch_maps = ne.evaluate("m + r", local_dict={"m": self._constants.warp_maps, "r": rands})
|
||||
batch_interp = np.array([[cv2.resize(map_, self._constants.warp_pad)[slices, slices]
|
||||
|
|
@ -340,7 +378,7 @@ class ImageAugmentation():
|
|||
warped_batch = np.array([cv2.remap(image, interp[0], interp[1], cv2.INTER_LINEAR)
|
||||
for image, interp in zip(batch, batch_interp)])
|
||||
|
||||
logger.trace("Warped image shape: %s", warped_batch.shape) # type: ignore
|
||||
logger.trace("Warped image shape: %s", warped_batch.shape) # type:ignore[attr-defined]
|
||||
return warped_batch
|
||||
|
||||
def _random_warp_landmarks(self,
|
||||
|
|
@ -364,7 +402,7 @@ class ImageAugmentation():
|
|||
:class:`numpy.ndarray`
|
||||
A 4-dimensional array of the same shape as :attr:`batch` with warping applied.
|
||||
"""
|
||||
logger.trace("Randomly warping landmarks") # type: ignore
|
||||
logger.trace("Randomly warping landmarks") # type:ignore[attr-defined]
|
||||
edge_anchors = self._constants.warp_lm_edge_anchors
|
||||
grids = self._constants.warp_lm_grids
|
||||
|
||||
|
|
@ -389,15 +427,16 @@ class ImageAugmentation():
|
|||
|
||||
grid_z = np.array([griddata(dst, src, (grids[0], grids[1]), method="linear")
|
||||
for src, dst in zip(lbatch_src, lbatch_dst)])
|
||||
maps = grid_z.reshape((self._batchsize,
|
||||
maps = grid_z.reshape((self._batch_size,
|
||||
self._processing_size,
|
||||
self._processing_size,
|
||||
2)).astype("float32")
|
||||
|
||||
warped_batch = np.array([cv2.remap(image,
|
||||
map_[..., 1],
|
||||
map_[..., 0],
|
||||
cv2.INTER_LINEAR,
|
||||
cv2.BORDER_TRANSPARENT)
|
||||
borderMode=cv2.BORDER_TRANSPARENT)
|
||||
for image, map_ in zip(batch, maps)])
|
||||
logger.trace("Warped batch shape: %s", warped_batch.shape) # type: ignore
|
||||
logger.trace("Warped batch shape: %s", warped_batch.shape) # type:ignore[attr-defined]
|
||||
return warped_batch
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ class DataGenerator():
|
|||
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
|
||||
|
|
@ -243,8 +243,9 @@ class DataGenerator():
|
|||
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,
|
||||
|
|
@ -271,8 +272,8 @@ class DataGenerator():
|
|||
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
|
||||
|
|
@ -304,7 +305,7 @@ class DataGenerator():
|
|||
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:
|
||||
|
|
@ -333,9 +334,9 @@ class DataGenerator():
|
|||
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
|
||||
|
||||
|
|
@ -450,15 +451,19 @@ class TrainingDataGenerator(DataGenerator):
|
|||
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,
|
||||
|
|
@ -533,7 +538,7 @@ class TrainingDataGenerator(DataGenerator):
|
|||
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)
|
||||
|
|
@ -556,8 +561,9 @@ class TrainingDataGenerator(DataGenerator):
|
|||
: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
|
||||
|
|
@ -575,7 +581,8 @@ class TrainingDataGenerator(DataGenerator):
|
|||
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,
|
||||
|
|
@ -648,8 +655,9 @@ class PreviewDataGenerator(DataGenerator):
|
|||
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))
|
||||
|
||||
|
|
@ -665,7 +673,7 @@ class PreviewDataGenerator(DataGenerator):
|
|||
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,
|
||||
|
|
@ -840,8 +848,9 @@ class Feeder():
|
|||
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)
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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"
|
||||
|
|
@ -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."
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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"
|
||||
|
|
@ -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."
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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"
|
||||
|
|
@ -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."
|
||||
|
|
|
|||
|
|
@ -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 <LL@li.org>\n"
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -40,12 +40,12 @@ class AlignmentsArgs(FaceSwapArgs):
|
|||
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({
|
||||
|
|
@ -118,7 +118,7 @@ class AlignmentsArgs(FaceSwapArgs):
|
|||
"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": _(
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user