Compare commits

...

5 Commits

2 changed files with 79 additions and 0 deletions

View File

@ -15,6 +15,7 @@ from .baseinvocation import (
InvocationConfig,
)
import cv2
class PILInvocationConfig(BaseModel):
"""Helper class to provide all PIL invocations with additional config"""
@ -650,3 +651,55 @@ class ImageInverseLerpInvocation(BaseInvocation, PILInvocationConfig):
width=image_dto.width,
height=image_dto.height,
)
class MaskEdgeInvocation(BaseInvocation, PILInvocationConfig):
"""Applies an edge mask to an image"""
# fmt: off
type: Literal["mask_edge"] = "mask_edge"
# Inputs
image: Optional[ImageField] = Field(default=None, description="The image to apply the mask to")
edge_size: int = Field(description="The size of the edge")
edge_blur: int = Field(description="The amount of blur on the edge")
low_threshold: int = Field(description="First threshold for the hysteresis procedure in Canny edge detection")
high_threshold: int = Field(description="Second threshold for the hysteresis procedure in Canny edge detection")
# fmt: on
def invoke(self, context: InvocationContext) -> MaskOutput:
mask = context.services.images.get_pil_image(self.image.image_name)
npimg = numpy.asarray(mask, dtype=np.uint8)
npgradient = numpy.uint8(
255 * (1.0 - numpy.floor(numpy.abs(0.5 - numpy.float32(npimg) / 255.0) * 2.0))
)
npedge = cv2.Canny(npimg, threshold1=self.low_threshold, threshold2=self.high_threshold)
npmask = npgradient + npedge
npmask = cv2.dilate(
npmask, numpy.ones((3, 3), numpy.uint8), iterations=int(self.edge_size / 2)
)
new_mask = Image.fromarray(npmask)
if self.edge_blur > 0:
new_mask = new_mask.filter(ImageFilter.BoxBlur(self.edge_blur))
new_mask = ImageOps.invert(new_mask)
image_dto = context.services.images.create(
image=new_mask,
image_origin=ResourceOrigin.INTERNAL,
image_category=ImageCategory.MASK,
node_id=self.id,
session_id=context.graph_execution_state_id,
is_intermediate=self.is_intermediate,
)
return MaskOutput(
mask=ImageField(image_name=image_dto.image_name),
width=image_dto.width,
height=image_dto.height,
)

View File

@ -771,3 +771,29 @@ class ImageToLatentsInvocation(BaseInvocation):
latents = latents.to("cpu")
context.services.latents.save(name, latents)
return build_latents_output(latents_name=name, latents=latents)
class LatentsAlphaBlendingInvocation(BaseInvocation):
"""Blends two Latents objects using an alpha/mask"""
# fmt: off
type: Literal["latents_alpha_blending"] = "latents_alpha_blending"
# Inputs
latents1: Optional[LatentsField] = Field(description="The first Latents object to blend")
latents2: Optional[LatentsField] = Field(description="The second Latents object to blend")
alphaLatents: Optional[LatentsField] = Field(description="The alpha/mask to use for blending in latents form")
# fmt: on
def invoke(self, context: InvocationContext) -> LatentsOutput:
latents1 = context.services.latents.get(self.latents1.latents_name)
latents2 = context.services.latents.get(self.latents2.latents_name)
alpha = context.services.latents.get(self.alpha.latents_name)
# Perform the alpha-blending
blended_latents = alpha * latents1 + (1 - alpha) * latents2
# Save the blended Latents object
name = f"{context.graph_execution_state_id}__{self.id}"
context.services.latents.save(name, blended_latents)
return build_latents_output(latents_name=name, latents=blended_latents)