diff --git a/invokeai/app/invocations/image.py b/invokeai/app/invocations/image.py index 7fc92199540..3f26f169f86 100644 --- a/invokeai/app/invocations/image.py +++ b/invokeai/app/invocations/image.py @@ -1089,7 +1089,7 @@ def invoke(self, context: InvocationContext) -> ImageOutput: @invocation( - "expand_mask_with_fade", title="Expand Mask with Fade", tags=["image", "mask"], category="image", version="1.0.0" + "expand_mask_with_fade", title="Expand Mask with Fade", tags=["image", "mask"], category="image", version="1.0.1" ) class ExpandMaskWithFadeInvocation(BaseInvocation, WithMetadata, WithBoard): """Expands a mask with a fade effect. The mask uses black to indicate areas to keep from the generated image and white for areas to discard. @@ -1147,8 +1147,21 @@ def invoke(self, context: InvocationContext) -> ImageOutput: coeffs = numpy.polyfit(x_control, y_control, 3) poly = numpy.poly1d(coeffs) - # Evaluate and clip the smooth mapping - feather = numpy.clip(poly(d_norm), 0, 1) + # Evaluate the polynomial + feather = poly(d_norm) + + # The polynomial fit isn't perfect. Points beyond the fade distance are likely to be slightly less than 1.0, + # even though the control points indicate that they should be exactly 1.0. This is due to the nature of the + # polynomial fit, which is a best approximation of the control points but not an exact match. + + # When this occurs, the area outside the mask and fade-out will not be 100% transparent. For example, it may + # have an alpha value of 1 instead of 0. So we must force pixels at or beyond the fade distance to exactly 1.0. + + # Force pixels at or beyond the fade distance to exactly 1.0 + feather = numpy.where(d_norm >= 1.0, 1.0, feather) + + # Clip any other values to ensure they're in the valid range [0,1] + feather = numpy.clip(feather, 0, 1) # Build final image. np_result = numpy.where(black_mask == 1, 0, (feather * 255).astype(numpy.uint8))