1
0
Fork 0
mirror of https://github.com/deepfakes/faceswap synced 2025-06-07 10:43:27 -04:00

Add mask dilation/erosion option for training

This commit is contained in:
torzdf 2024-03-26 18:06:48 +00:00
parent 2b08b8d2a6
commit 4502824481
6 changed files with 156 additions and 78 deletions

View file

@ -88,8 +88,8 @@ class DetectedFace():
landmarks_xy: np.ndarray | None = None,
mask: dict[str, "Mask"] | None = None,
filename: str | None = None) -> None:
logger.trace("Initializing %s: (image: %s, left: %s, width: %s, top: %s, " # type: ignore
"height: %s, landmarks_xy: %s, mask: %s, filename: %s)",
logger.trace("Initializing %s: (image: %s, left: %s, " # type:ignore[attr-defined]
"width: %s, top: %s, height: %s, landmarks_xy: %s, mask: %s, filename: %s)",
self.__class__.__name__,
image.shape if image is not None and image.any() else image, left, width, top,
height, landmarks_xy, mask, filename)
@ -105,7 +105,7 @@ class DetectedFace():
self._training_masks: tuple[bytes, tuple[int, int, int]] | None = None
self._aligned: AlignedFace | None = None
logger.trace("Initialized %s", self.__class__.__name__) # type: ignore
logger.trace("Initialized %s", self.__class__.__name__) # type:ignore[attr-defined]
@property
def aligned(self) -> AlignedFace:
@ -167,7 +167,7 @@ class DetectedFace():
The centering to store the mask at. One of `"legacy"`, `"face"`, `"head"`.
Default: `"face"`
"""
logger.trace("name: '%s', mask shape: %s, affine_matrix: %s, " # type: ignore
logger.trace("name: '%s', mask shape: %s, affine_matrix: %s, " # type:ignore[attr-defined]
"interpolator: %s, storage_size: %s, storage_centering: %s)", name,
mask.shape, affine_matrix, interpolator, storage_size, storage_centering)
fsmask = Mask(storage_size=storage_size, storage_centering=storage_centering)
@ -183,7 +183,7 @@ class DetectedFace():
landmarks: :class:`numpy.ndarray`
The 68 point face landmarks to add for the face
"""
logger.trace("landmarks shape: '%s'", landmarks.shape) # type: ignore
logger.trace("landmarks shape: '%s'", landmarks.shape) # type:ignore[attr-defined]
self._landmarks_xy = landmarks
def add_identity(self, name: str, embedding: np.ndarray, ) -> None:
@ -197,7 +197,7 @@ class DetectedFace():
embedding: numpy.ndarray
The identity embedding
"""
logger.trace("name: '%s', embedding shape: %s", # type: ignore
logger.trace("name: '%s', embedding shape: %s", # type:ignore[attr-defined]
name, embedding.shape)
assert name == "vggface2"
assert embedding.shape[0] == 512
@ -210,7 +210,7 @@ class DetectedFace():
def get_landmark_mask(self,
area: T.Literal["eye", "face", "mouth"],
blur_kernel: int,
dilation: int) -> np.ndarray:
dilation: float) -> np.ndarray:
""" Add a :class:`LandmarksMask` to this detected face
Landmark based masks are generated from face Aligned Face landmark points. An aligned
@ -224,8 +224,8 @@ class DetectedFace():
specific areas
blur_kernel: int
The size of the kernel for blurring the mask edges
dilation: int
The amount of dilation to apply to the mask. `0` for none. Default: `0`
dilation: float
The amount of dilation to apply to the mask. as a percentage of the mask size
Returns
-------
@ -233,7 +233,7 @@ class DetectedFace():
The generated landmarks mask for the selected area
"""
# TODO Face mask generation from landmarks
logger.trace("area: %s, dilation: %s", area, dilation) # type: ignore
logger.trace("area: %s, dilation: %s", area, dilation) # type:ignore[attr-defined]
areas = {"mouth": [slice(48, 60)], "eye": [slice(36, 42), slice(42, 48)]}
points = [self.aligned.landmarks[zone]
for zone in areas[area]]
@ -307,7 +307,7 @@ class DetectedFace():
for name, mask in self.mask.items()},
identity={k: v.tolist() for k, v in self._identity.items()},
thumb=self.thumbnail)
logger.trace("Returning: %s", alignment) # type: ignore
logger.trace("Returning: %s", alignment) # type:ignore[attr-defined]
return alignment
def from_alignment(self, alignment: AlignmentFileDict,
@ -332,8 +332,8 @@ class DetectedFace():
Default: ``False``
"""
logger.trace("Creating from alignment: (alignment: %s, has_image: %s)", # type: ignore
alignment, bool(image is not None))
logger.trace("Creating from alignment: (alignment: %s," # type:ignore[attr-defined]
" has_image: %s)", alignment, bool(image is not None))
self.left = alignment["x"]
self.width = alignment["w"]
self.top = alignment["y"]
@ -358,9 +358,9 @@ class DetectedFace():
self.mask[name].from_dict(mask_dict)
if image is not None and image.any():
self._image_to_face(image)
logger.trace("Created from alignment: (left: %s, width: %s, top: %s, " # type: ignore
"height: %s, landmarks: %s, mask: %s)", self.left, self.width, self.top,
self.height, self.landmarks_xy, self.mask)
logger.trace("Created from alignment: (left: %s, width: %s, " # type:ignore[attr-defined]
"top: %s, height: %s, landmarks: %s, mask: %s)",
self.left, self.width, self.top, self.height, self.landmarks_xy, self.mask)
def to_png_meta(self) -> PNGHeaderAlignmentsDict:
""" Return the detected face formatted for insertion into a png itxt header.
@ -403,14 +403,14 @@ class DetectedFace():
for key, val in alignment.get("identity", {}).items():
assert key in ["vggface2"]
self._identity[T.cast(T.Literal["vggface2"], key)] = np.array(val, dtype="float32")
logger.trace("Created from png exif header: (left: %s, width: %s, top: %s " # type: ignore
" height: %s, landmarks: %s, mask: %s, identity: %s)", self.left, self.width,
self.top, self.height, self.landmarks_xy, self.mask,
logger.trace("Created from png exif header: (left: %s, " # type:ignore[attr-defined]
"width: %s, top: %s height: %s, landmarks: %s, mask: %s, identity: %s)",
self.left, self.width, self.top, self.height, self.landmarks_xy, self.mask,
{k: v.shape for k, v in self._identity.items()})
def _image_to_face(self, image: np.ndarray) -> None:
""" set self.image to be the cropped face from detected bounding box """
logger.trace("Cropping face from image") # type: ignore
logger.trace("Cropping face from image") # type:ignore[attr-defined]
self.image = image[self.top: self.bottom,
self.left: self.right]
@ -467,10 +467,11 @@ class DetectedFace():
"""
if self._aligned and not force:
# Don't reload an already aligned face
logger.trace("Skipping alignment calculation for already aligned face") # type: ignore
logger.trace("Skipping alignment calculation for already " # type:ignore[attr-defined]
"aligned face")
else:
logger.trace("Loading aligned face: (size: %s, dtype: %s)", # type: ignore
size, dtype)
logger.trace("Loading aligned face: (size: %s, " # type:ignore[attr-defined]
"dtype: %s)", size, dtype)
self._aligned = AlignedFace(self.landmarks_xy,
image=image,
centering=centering,
@ -507,7 +508,8 @@ class Mask():
def __init__(self,
storage_size: int = 128,
storage_centering: CenteringType = "face") -> None:
logger.trace("Initializing: %s (storage_size: %s, storage_centering: %s)", # type: ignore
logger.trace("Initializing: %s (storage_size: %s, " # type:ignore[attr-defined]
"storage_centering: %s)",
self.__class__.__name__, storage_size, storage_centering)
self.stored_size = storage_size
self.stored_centering = storage_centering
@ -520,19 +522,21 @@ class Mask():
self._blur_passes: int = 0
self._blur_kernel: float | int = 0
self._threshold = 0.0
self._dilation: tuple[T.Literal["erode", "dilate"], np.ndarray | None] = ("erode", None)
self._sub_crop_size = 0
self._sub_crop_slices: dict[T.Literal["in", "out"], list[slice]] = {}
self.set_blur_and_threshold()
logger.trace("Initialized: %s", self.__class__.__name__) # type: ignore
logger.trace("Initialized: %s", self.__class__.__name__) # type:ignore[attr-defined]
@property
def mask(self) -> np.ndarray:
""" :class:`numpy.ndarray`: The mask at the size of :attr:`stored_size` with any requested
blurring, threshold amount and centering applied."""
mask = self.stored_mask
if self._threshold != 0.0 or self._blur_kernel != 0:
if self._dilation[-1] is not None or self._threshold != 0.0 or self._blur_kernel != 0:
mask = mask.copy()
self._dilate_mask(mask)
if self._threshold != 0.0:
mask[mask < self._threshold] = 0.0
mask[mask > 255.0 - self._threshold] = 255.0
@ -546,7 +550,7 @@ class Mask():
slice_in, slice_out = self._sub_crop_slices["in"], self._sub_crop_slices["out"]
out[slice_out[0], slice_out[1], :] = mask[slice_in[0], slice_in[1], :]
mask = out
logger.trace("mask shape: %s", mask.shape) # type: ignore
logger.trace("mask shape: %s", mask.shape) # type:ignore[attr-defined]
return mask
@property
@ -556,7 +560,7 @@ class Mask():
assert self._mask is not None
dims = (self.stored_size, self.stored_size, 1)
mask = np.frombuffer(decompress(self._mask), dtype="uint8").reshape(dims)
logger.trace("stored mask shape: %s", mask.shape) # type: ignore
logger.trace("stored mask shape: %s", mask.shape) # type:ignore[attr-defined]
return mask
@property
@ -567,9 +571,9 @@ class Mask():
[0, self.stored_size - 1],
[self.stored_size - 1, self.stored_size - 1],
[self.stored_size - 1, 0]], np.int32).reshape((-1, 1, 2))
matrix = cv2.invertAffineTransform(self._affine_matrix)
matrix = cv2.invertAffineTransform(self.affine_matrix)
roi = cv2.transform(points, matrix).reshape((4, 2))
logger.trace("Returning: %s", roi) # type: ignore
logger.trace("Returning: %s", roi) # type:ignore[attr-defined]
return roi
@property
@ -584,6 +588,22 @@ class Mask():
assert self._interpolator is not None
return self._interpolator
def _dilate_mask(self, mask: np.ndarray) -> None:
""" Erode/Dilate the mask. The action is performed in-place on the given mask.
No action is performed if a dilation amount has not been set
Parameters
----------
mask: :class:`numpy.ndarray`
The mask to be eroded/dilated
"""
if self._dilation[-1] is None:
return
func = cv2.erode if self._dilation[0] == "erode" else cv2.dilate
func(mask, self._dilation[-1], dst=mask, iterations=1)
def get_full_frame_mask(self, width: int, height: int) -> np.ndarray:
""" Return the stored mask in a full size frame of the given dimensions
@ -600,13 +620,13 @@ class Mask():
"""
frame = np.zeros((width, height, 1), dtype="uint8")
mask = cv2.warpAffine(self.mask,
self._affine_matrix,
self.affine_matrix,
(width, height),
frame,
flags=cv2.WARP_INVERSE_MAP | self._interpolator,
flags=cv2.WARP_INVERSE_MAP | self.interpolator,
borderMode=cv2.BORDER_CONSTANT)
logger.trace("mask shape: %s, mask dtype: %s, mask min: %s, mask max: %s", # type: ignore
mask.shape, mask.dtype, mask.min(), mask.max())
logger.trace("mask shape: %s, mask dtype: %s, mask min: %s, " # type:ignore[attr-defined]
"mask max: %s", mask.shape, mask.dtype, mask.min(), mask.max())
return mask
def add(self, mask: np.ndarray, affine_matrix: np.ndarray, interpolator: int) -> None:
@ -624,9 +644,9 @@ class Mask():
interpolator, int:
The CV2 interpolator required to transform this mask to it's original frame
"""
logger.trace("mask shape: %s, mask dtype: %s, mask min: %s, mask max: %s, " # type: ignore
"affine_matrix: %s, interpolator: %s)", mask.shape, mask.dtype, mask.min(),
affine_matrix, mask.max(), interpolator)
logger.trace("mask shape: %s, mask dtype: %s, mask min: %s, " # type:ignore[attr-defined]
"mask max: %s, affine_matrix: %s, interpolator: %s)",
mask.shape, mask.dtype, mask.min(), affine_matrix, mask.max(), interpolator)
self._affine_matrix = self._adjust_affine_matrix(mask.shape[0], affine_matrix)
self._interpolator = interpolator
self.replace_mask(mask)
@ -645,6 +665,26 @@ class Mask():
interpolation=cv2.INTER_AREA) * 255.0).astype("uint8")
self._mask = compress(mask.tobytes())
def set_dilation(self, amount: float) -> None:
""" Set the internal dilation object for returned masks
Parameters
----------
amount: float
The amount of erosion/dilation to apply as a percentage of the total mask size.
Negative values erode the mask. Positive values dilate the mask
"""
if amount == 0:
self._dilation = ("erode", None)
return
action: T.Literal["erode", "dilate"] = "erode" if amount < 0 else "dilate"
kernel = int(round(self.stored_size * abs(amount / 100.), 0))
self._dilation = (action, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernel, kernel)))
logger.trace("action: '%s', amount: %s, kernel: %s, ", # type:ignore[attr-defined]
action, amount, kernel)
def set_blur_and_threshold(self,
blur_kernel: int = 0,
blur_type: T.Literal["gaussian", "normalized"] | None = "gaussian",
@ -666,8 +706,9 @@ class Mask():
The threshold amount to minimize/maximize mask values to 0 and 100. Percentage value.
Default: 0
"""
logger.trace("blur_kernel: %s, blur_type: %s, blur_passes: %s, " # type: ignore
"threshold: %s", blur_kernel, blur_type, blur_passes, threshold)
logger.trace("blur_kernel: %s, blur_type: %s, " # type:ignore[attr-defined]
"blur_passes: %s, threshold: %s",
blur_kernel, blur_type, blur_passes, threshold)
if blur_type is not None:
blur_kernel += 0 if blur_kernel == 0 or blur_kernel % 2 == 1 else 1
self._blur_kernel = blur_kernel
@ -719,9 +760,9 @@ class Mask():
slice(max(roi[0] * -1, 0),
crop_size - min(crop_size, max(0, roi[2] - self.stored_size)))]
logger.trace("src_size: %s, coverage_ratio: %s, sub_crop_size: %s, " # type: ignore
"sub_crop_slices: %s", roi, coverage_ratio, self._sub_crop_size,
self._sub_crop_slices)
logger.trace("src_size: %s, coverage_ratio: %s, " # type:ignore[attr-defined]
"sub_crop_size: %s, sub_crop_slices: %s",
roi, coverage_ratio, self._sub_crop_size, self._sub_crop_slices)
def _adjust_affine_matrix(self, mask_size: int, affine_matrix: np.ndarray) -> np.ndarray:
""" Adjust the affine matrix for the mask's storage size
@ -741,7 +782,7 @@ class Mask():
zoom = self.stored_size / mask_size
zoom_mat = np.array([[zoom, 0, 0.], [0, zoom, 0.]])
adjust_mat = np.dot(zoom_mat, np.concatenate((affine_matrix, np.array([[0., 0., 1.]]))))
logger.trace("storage_size: %s, mask_size: %s, zoom: %s, " # type: ignore
logger.trace("storage_size: %s, mask_size: %s, zoom: %s, " # type:ignore[attr-defined]
"original matrix: %s, adjusted_matrix: %s", self.stored_size, mask_size, zoom,
affine_matrix.shape, adjust_mat.shape)
return adjust_mat
@ -768,7 +809,8 @@ class Mask():
interpolator=self.interpolator,
stored_size=self.stored_size,
stored_centering=self.stored_centering)
logger.trace({k: v if k != "mask" else type(v) for k, v in retval.items()}) # type: ignore
logger.trace({k: v if k != "mask" else type(v) # type:ignore[attr-defined]
for k, v in retval.items()})
return retval
def to_png_meta(self) -> MaskAlignmentsFileDict:
@ -799,7 +841,7 @@ class Mask():
self.stored_size = mask_dict["stored_size"]
centering = mask_dict.get("stored_centering")
self.stored_centering = "face" if centering is None else centering
logger.trace({k: v if k != "mask" else type(v) # type: ignore
logger.trace({k: v if k != "mask" else type(v) # type:ignore[attr-defined]
for k, v in mask_dict.items()})
@ -826,17 +868,17 @@ class LandmarksMask(Mask):
storage_centering, str (optional):
The centering to store the mask at. One of `"legacy"`, `"face"`, `"head"`.
Default: `"face"`
dilation: int, optional
The amount of dilation to apply to the mask. `0` for none. Default: `0`
dilation: float, optional
The amount of dilation to apply to the mask. as a percentage of the mask size. Default: 0.0
"""
def __init__(self,
points: list[np.ndarray],
storage_size: int = 128,
storage_centering: CenteringType = "face",
dilation: int = 0) -> None:
dilation: float = 0.0) -> None:
super().__init__(storage_size=storage_size, storage_centering=storage_centering)
self._points = points
self._dilation = dilation
self.set_dilation(dilation)
@property
def mask(self) -> np.ndarray:
@ -862,17 +904,15 @@ class LandmarksMask(Mask):
for landmarks in self._points:
lms = np.rint(landmarks).astype("int")
cv2.fillConvexPoly(mask, cv2.convexHull(lms), 1.0, lineType=cv2.LINE_AA)
if self._dilation != 0:
mask = cv2.dilate(mask,
cv2.getStructuringElement(cv2.MORPH_ELLIPSE,
(self._dilation, self._dilation)),
iterations=1)
if self._dilation[-1] is not None:
self._dilate_mask(mask)
if self._blur_kernel != 0 and self._blur_type is not None:
mask = BlurMask(self._blur_type,
mask,
self._blur_kernel,
passes=self._blur_passes).blurred
logger.trace("mask: (shape: %s, dtype: %s)", mask.shape, mask.dtype) # type: ignore
logger.trace("mask: (shape: %s, dtype: %s)", # type:ignore[attr-defined]
mask.shape, mask.dtype)
self.add(mask, affine_matrix, interpolator)
@ -911,15 +951,16 @@ class BlurMask(): # pylint:disable=too-few-public-methods
kernel: int | float,
is_ratio: bool = False,
passes: int = 1) -> None:
logger.trace("Initializing %s: (blur_type: '%s', mask_shape: %s, " # type: ignore
"kernel: %s, is_ratio: %s, passes: %s)", self.__class__.__name__, blur_type,
logger.trace("Initializing %s: (blur_type: '%s', " # type:ignore[attr-defined]
"mask_shape: %s, kernel: %s, is_ratio: %s, passes: %s)",
self.__class__.__name__, blur_type,
mask.shape, kernel, is_ratio, passes)
self._blur_type = blur_type
self._mask = mask
self._passes = passes
kernel_size = self._get_kernel_size(kernel, is_ratio)
self._kernel_size = self._get_kernel_tuple(kernel_size)
logger.trace("Initialized %s", self.__class__.__name__) # type: ignore
logger.trace("Initialized %s", self.__class__.__name__) # type:ignore[attr-defined]
@property
def blurred(self) -> np.ndarray:
@ -930,12 +971,14 @@ class BlurMask(): # pylint:disable=too-few-public-methods
for i in range(self._passes):
assert isinstance(kwargs["ksize"], tuple)
ksize = int(kwargs["ksize"][0])
logger.trace("Pass: %s, kernel_size: %s", i + 1, (ksize, ksize)) # type: ignore
logger.trace("Pass: %s, kernel_size: %s", # type:ignore[attr-defined]
i + 1, (ksize, ksize))
blurred = func(blurred, **kwargs)
ksize = int(round(ksize * self._multipass_factor))
kwargs["ksize"] = self._get_kernel_tuple(ksize)
blurred = blurred[..., None]
logger.trace("Returning blurred mask. Shape: %s", blurred.shape) # type: ignore
logger.trace("Returning blurred mask. Shape: %s", # type:ignore[attr-defined]
blurred.shape)
return blurred
@property
@ -991,7 +1034,7 @@ class BlurMask(): # pylint:disable=too-few-public-methods
mask_diameter = np.sqrt(np.sum(self._mask))
radius = round(max(1., mask_diameter * kernel / 100.))
kernel_size = int(radius * 2 + 1)
logger.trace("kernel_size: %s", kernel_size) # type: ignore
logger.trace("kernel_size: %s", kernel_size) # type:ignore[attr-defined]
return kernel_size
@staticmethod
@ -1010,14 +1053,14 @@ class BlurMask(): # pylint:disable=too-few-public-methods
"""
kernel_size += 1 if kernel_size % 2 == 0 else 0
retval = (kernel_size, kernel_size)
logger.trace(retval) # type: ignore
logger.trace(retval) # type:ignore[attr-defined]
return retval
def _get_kwargs(self) -> dict[str, int | tuple[int, int]]:
""" dict: the valid keyword arguments for the requested :attr:`_blur_type` """
retval = {kword: self._kwarg_mapping[kword]
for kword in self._kwarg_requirements[self._blur_type]}
logger.trace("BlurMask kwargs: %s", retval) # type: ignore
logger.trace("BlurMask kwargs: %s", retval) # type:ignore[attr-defined]
return retval

View file

@ -428,8 +428,10 @@ class _Cache():
f"The masks that exist for this face are: {list(detected_face.mask)}")
mask = detected_face.mask[str(self._config["mask_type"])]
assert isinstance(self._config["mask_dilation"], float)
assert isinstance(self._config["mask_blur_kernel"], int)
assert isinstance(self._config["mask_threshold"], int)
mask.set_dilation(self._config["mask_dilation"])
mask.set_blur_and_threshold(blur_kernel=self._config["mask_blur_kernel"],
threshold=self._config["mask_threshold"])
@ -467,7 +469,7 @@ class _Cache():
assert isinstance(multiplier, int)
if not self._config["penalized_mask_loss"] or multiplier <= 1:
return None
mask = detected_face.get_landmark_mask(area, self._size // 16, self._size // 32)
mask = detected_face.get_landmark_mask(area, self._size // 16, 2.5)
logger.trace("Caching localized '%s' mask for: %s %s", # type: ignore
area, filename, mask.shape)
return mask

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-08-20 14:54+0100\n"
"POT-Creation-Date: 2024-03-26 17:37+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"
@ -539,8 +539,9 @@ msgid ""
"attention on the core face area."
msgstr ""
#: plugins/train/_config.py:600 plugins/train/_config.py:642
#: plugins/train/_config.py:656 plugins/train/_config.py:665
#: plugins/train/_config.py:600 plugins/train/_config.py:643
#: plugins/train/_config.py:656 plugins/train/_config.py:671
#: plugins/train/_config.py:680
msgid "mask"
msgstr ""
@ -582,7 +583,14 @@ msgid ""
"performance."
msgstr ""
#: plugins/train/_config.py:644
#: plugins/train/_config.py:645
msgid ""
"Dilate or erode the mask. Negative values erode the mask (make it smaller). "
"Positive values dilate the mask (make it larger). The value given is a "
"percentage of the total mask size."
msgstr ""
#: plugins/train/_config.py:658
msgid ""
"Apply gaussian blur to the mask input. This has the effect of smoothing the "
"edges of the mask, which can help with poorly calculated masks and give less "
@ -592,13 +600,13 @@ msgid ""
"number."
msgstr ""
#: plugins/train/_config.py:658
#: plugins/train/_config.py:673
msgid ""
"Sets pixels that are near white to white and near black to black. Set to 0 "
"for off."
msgstr ""
#: plugins/train/_config.py:667
#: plugins/train/_config.py:682
msgid ""
"Dedicate a portion of the model to learning how to duplicate the input mask. "
"Increases VRAM usage in exchange for learning a quick ability to try to "

View file

@ -7,15 +7,15 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-08-20 14:54+0100\n"
"PO-Revision-Date: 2023-08-20 14:58+0100\n"
"POT-Creation-Date: 2024-03-26 17:37+0000\n"
"PO-Revision-Date: 2024-03-26 17:40+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: ru_RU\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.3.2\n"
"X-Generator: Poedit 3.4.2\n"
#: plugins/train/_config.py:17
msgid ""
@ -851,8 +851,9 @@ msgstr ""
"время как область лица с маской является приоритетной. Может повысить общее "
"качество за счет концентрации внимания на основной области лица."
#: plugins/train/_config.py:600 plugins/train/_config.py:642
#: plugins/train/_config.py:656 plugins/train/_config.py:665
#: plugins/train/_config.py:600 plugins/train/_config.py:643
#: plugins/train/_config.py:656 plugins/train/_config.py:671
#: plugins/train/_config.py:680
msgid "mask"
msgstr "маска"
@ -928,7 +929,16 @@ msgstr ""
"сообщества и для дальнейшего описания нуждается в тестировании. Профильные "
"лица могут иметь низкую производительность."
#: plugins/train/_config.py:644
#: plugins/train/_config.py:645
msgid ""
"Dilate or erode the mask. Negative values erode the mask (make it smaller). "
"Positive values dilate the mask (make it larger). The value given is a "
"percentage of the total mask size."
msgstr ""
"Расширяет или сужает маску. Отрицательные значения сужают маску (делают её "
"меньше). Положительные значения расширяют маску (делают её больше). "
#: plugins/train/_config.py:658
msgid ""
"Apply gaussian blur to the mask input. This has the effect of smoothing the "
"edges of the mask, which can help with poorly calculated masks and give less "
@ -944,7 +954,7 @@ msgstr ""
"должно быть нечетным, если передано четное число, то оно будет округлено до "
"следующего нечетного числа."
#: plugins/train/_config.py:658
#: plugins/train/_config.py:673
msgid ""
"Sets pixels that are near white to white and near black to black. Set to 0 "
"for off."
@ -952,7 +962,7 @@ msgstr ""
"Устанавливает пиксели, которые почти белые - в белые и которые почти черные "
"- в черные. Установите 0, чтобы выключить."
#: plugins/train/_config.py:667
#: plugins/train/_config.py:682
msgid ""
"Dedicate a portion of the model to learning how to duplicate the input mask. "
"Increases VRAM usage in exchange for learning a quick ability to try to "

View file

@ -632,6 +632,19 @@ class Config(FaceswapConfig):
"faces. The mask model has been trained by community members and will need "
"testing for further description. Profile faces may result in sub-par "
"performance."))
self.add_item(
section=section,
title="mask_dilation",
datatype=float,
min_max=(-5.0, 5.0),
rounding=1,
default=0,
fixed=False,
group=_("mask"),
info=_(
"Dilate or erode the mask. Negative values erode the mask (make it smaller). "
"Positive values dilate the mask (make it larger). The value given is a "
"percentage of the total mask size."))
self.add_item(
section=section,
title="mask_blur_kernel",
@ -639,6 +652,7 @@ class Config(FaceswapConfig):
min_max=(0, 9),
rounding=1,
default=3,
fixed=False,
group=_("mask"),
info=_(
"Apply gaussian blur to the mask input. This has the effect of smoothing the "
@ -653,6 +667,7 @@ class Config(FaceswapConfig):
default=4,
min_max=(0, 50),
rounding=1,
fixed=False,
group=_("mask"),
info=_(
"Sets pixels that are near white to white and near black to black. Set to 0 for "