diff --git a/README.md b/README.md index caa5343..9250789 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# πŸ”— Comfyui : Bjornulf_custom_nodes v1.1.7 πŸ”— +# πŸ”— Comfyui : Bjornulf_custom_nodes v1.1.8 πŸ”— A list of 170 custom nodes for Comfyui : Display, manipulate, create and edit text, images, videos, loras, generate characters and more. You can manage looping operations, generate randomized content, trigger logical conditions, pause and manually control your workflows and even work with external AI tools, like Ollama or Text To Speech. diff --git a/combine_background_overlay.py b/combine_background_overlay.py index 0ab1360..bf7568c 100644 --- a/combine_background_overlay.py +++ b/combine_background_overlay.py @@ -17,12 +17,14 @@ class CombineBackgroundOverlay: }, } - RETURN_TYPES = ("IMAGE",) + RETURN_TYPES = ("IMAGE", "MASK") + RETURN_NAMES = ("image", "mask") FUNCTION = "combine_background_overlay" CATEGORY = "Bjornulf" def combine_background_overlay(self, background, overlay, horizontal_position, vertical_position, mask=None): results = [] + output_masks = [] # Process the first background image bg = background[0].cpu().numpy() @@ -45,7 +47,7 @@ class CombineBackgroundOverlay: else: ov_img = Image.fromarray(ov, 'RGB') - # Apply mask if provided + # Apply mask if provided - INVERTED LOGIC: mask removes opacity if mask is not None: mask_idx = min(i, mask.shape[0] - 1) m = mask[mask_idx].cpu().numpy() @@ -56,15 +58,18 @@ class CombineBackgroundOverlay: if mask_img.size != ov_img.size: mask_img = mask_img.resize(ov_img.size, Image.LANCZOS) + # INVERT THE MASK - white areas in mask become transparent + inverted_mask = Image.eval(mask_img, lambda x: 255 - x) + if ov_img.mode == 'RGBA': - # Combine overlay’s alpha with mask + # Combine overlay's alpha with inverted mask ov_alpha = np.array(ov_img.split()[3], dtype=np.float32) / 255.0 - mask_alpha = np.array(mask_img, dtype=np.float32) / 255.0 - effective_alpha = (ov_alpha * mask_alpha * 255).astype(np.uint8) + inverted_mask_alpha = np.array(inverted_mask, dtype=np.float32) / 255.0 + effective_alpha = (ov_alpha * inverted_mask_alpha * 255).astype(np.uint8) ov_img.putalpha(Image.fromarray(effective_alpha, 'L')) else: - # Use mask as alpha for RGB overlay - ov_img.putalpha(mask_img) + # Use inverted mask as alpha for RGB overlay + ov_img.putalpha(inverted_mask) else: if ov_img.mode == 'RGB': # Add fully opaque alpha for RGB overlay @@ -82,26 +87,90 @@ class CombineBackgroundOverlay: result = Image.new('RGBA', bg_img.size, (0, 0, 0, 0)) result.paste(bg_img, (0, 0)) - # Paste overlay with alpha blending + # Create output mask - start with background alpha or white + if bg_has_alpha: + output_mask_img = bg_img.split()[3].copy() + else: + output_mask_img = Image.new('L', bg_img.size, 255) + + # Paste overlay directly on top (no alpha blending) if x + ov_img.width > 0 and y + ov_img.height > 0 and x < result.width and y < result.height: - temp = Image.new('RGBA', result.size, (0, 0, 0, 0)) - temp.paste(ov_img, (x, y), ov_img) - result = Image.alpha_composite(result.convert('RGBA'), temp) + # Convert overlay to RGB if needed for direct paste + if ov_img.mode == 'RGBA': + ov_rgb = Image.new('RGB', ov_img.size, (255, 255, 255)) + ov_rgb.paste(ov_img, mask=ov_img.split()[3]) + ov_paste = ov_rgb + paste_mask = ov_img.split()[3] + else: + ov_paste = ov_img + paste_mask = None + + # Apply input mask if provided - UPDATED LOGIC FOR INVERTED MASK + if mask is not None: + mask_idx = min(i, mask.shape[0] - 1) + m = mask[mask_idx].cpu().numpy() + m = np.clip(m * 255, 0, 255).astype(np.uint8) + input_mask = Image.fromarray(m, 'L') + if input_mask.size != ov_img.size: + input_mask = input_mask.resize(ov_img.size, Image.LANCZOS) + + # INVERT THE INPUT MASK + inverted_input_mask = Image.eval(input_mask, lambda x: 255 - x) + + if paste_mask is not None: + # Combine overlay alpha with inverted input mask + paste_mask_array = np.array(paste_mask, dtype=np.float32) / 255.0 + inverted_input_mask_array = np.array(inverted_input_mask, dtype=np.float32) / 255.0 + combined_mask_array = (paste_mask_array * inverted_input_mask_array * 255).astype(np.uint8) + paste_mask = Image.fromarray(combined_mask_array, 'L') + else: + # Use inverted input mask directly + paste_mask = inverted_input_mask + + # Paste overlay directly onto result + result.paste(ov_paste, (x, y), paste_mask) + + # Update output mask + if paste_mask is not None: + temp_mask = Image.new('L', result.size, 0) + temp_mask.paste(paste_mask, (x, y)) + + # Combine masks - overlay mask replaces background mask where it exists + output_mask_array = np.array(output_mask_img, dtype=np.float32) + temp_mask_array = np.array(temp_mask, dtype=np.float32) + combined_mask_array = np.maximum(output_mask_array, temp_mask_array).astype(np.uint8) + output_mask_img = Image.fromarray(combined_mask_array, 'L') + else: + # No mask - overlay covers background completely in paste area + temp_mask = Image.new('L', result.size, 0) + temp_mask.paste(Image.new('L', ov_paste.size, 255), (x, y)) + + output_mask_array = np.array(output_mask_img, dtype=np.float32) + temp_mask_array = np.array(temp_mask, dtype=np.float32) + combined_mask_array = np.maximum(output_mask_array, temp_mask_array).astype(np.uint8) + output_mask_img = Image.fromarray(combined_mask_array, 'L') # Convert result back to tensor result_np = np.array(result) - if bg_has_alpha: - result_tensor = torch.from_numpy(result_np).float() / 255.0 - else: - # Convert RGBA to RGB, blending with white only where needed - if result_np.shape[2] == 4: + if result_np.shape[2] == 4: + # Convert RGBA back to RGB if background was RGB + if not bg_has_alpha: alpha = result_np[:, :, 3:4] / 255.0 rgb = result_np[:, :, :3] white_bg = np.ones_like(rgb) * 255 result_np = (rgb * alpha + white_bg * (1 - alpha)).astype(np.uint8) + result_tensor = torch.from_numpy(result_np).float() / 255.0 + else: + result_tensor = torch.from_numpy(result_np).float() / 255.0 + else: result_tensor = torch.from_numpy(result_np).float() / 255.0 + # Convert output mask to tensor + output_mask_tensor = torch.from_numpy(np.array(output_mask_img)).float() / 255.0 + results.append(result_tensor) + output_masks.append(output_mask_tensor) final_result = torch.stack(results) - return (final_result,) \ No newline at end of file + final_masks = torch.stack(output_masks) + return (final_result, final_masks) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index e6085c4..2e350fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "bjornulf_custom_nodes" description = "170 ComfyUI nodes : Display, manipulate, and edit text, images, videos, loras, generate characters and more. Manage looping operations, generate randomized content, use logical conditions and work with external AI tools, like Ollama or Text To Speech, etc..." -version = "1.1.7" +version = "1.1.8" license = {file = "LICENSE"} [project.urls]