diff --git a/Comfyui_FSL_Nodes/8WayImgSwitch.py b/Comfyui_FSL_Nodes/8WayImgSwitch.py new file mode 100644 index 0000000..dc432b4 --- /dev/null +++ b/Comfyui_FSL_Nodes/8WayImgSwitch.py @@ -0,0 +1,64 @@ +# fsl_nodes.py + +# 8-Way Image Switch Node for ComfyUI +class Image_Switch_8Way: + CATEGORY = "FSL Nodes/Switches" # This controls sidebar grouping in ComfyUI + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("img_out",) + FUNCTION = "get_image" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "select": ("INT", {"default": 1, "min": 1, "max": 8}), + }, + "optional": { + "img_1": ("IMAGE",), + "img_2": ("IMAGE",), + "img_3": ("IMAGE",), + "img_4": ("IMAGE",), + "img_5": ("IMAGE",), + "img_6": ("IMAGE",), + "img_7": ("IMAGE",), + "img_8": ("IMAGE",), + } + } + + def get_image(self, select, img_1=None, img_2=None, img_3=None, img_4=None, + img_5=None, img_6=None, img_7=None, img_8=None): + # Default to img_1 if selection is invalid or missing + images = [img_1, img_2, img_3, img_4, img_5, img_6, img_7, img_8] + idx = max(1, min(select, 8)) - 1 # Clamp select to 1-8, convert to 0-based index + img_out = images[idx] + return (img_out,) + +# Example: Another custom node for demonstration (can be removed or replaced) +class FSL_Image_Passthrough: + CATEGORY = "FSL Nodes/Switches" + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("img_out",) + FUNCTION = "passthrough" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "img_in": ("IMAGE",), + } + } + + def passthrough(self, img_in): + return (img_in,) + +# Node registration dictionaries +NODE_CLASS_MAPPINGS = { + "FSL Image Switch 8Way": Image_Switch_8Way, + "FSL Image Passthrough": FSL_Image_Passthrough, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + # Optional: Controls how the node appears in the sidebar (can use emojis, etc.) + "FSL Image Switch 8Way": "🔀 8-Way Image Switch (FSL)", + "FSL Image Passthrough": "➡️ Image Passthrough (FSL)", +} diff --git a/Comfyui_FSL_Nodes/__init__.py b/Comfyui_FSL_Nodes/__init__.py index 18bbb16..05c979a 100644 --- a/Comfyui_FSL_Nodes/__init__.py +++ b/Comfyui_FSL_Nodes/__init__.py @@ -4,14 +4,20 @@ import os NODE_CLASS_MAPPINGS = {} NODE_DISPLAY_NAME_MAPPINGS = {} -# Automatically load all .py files in this folder except __init__.py +# Load all node modules current_dir = os.path.dirname(__file__) for filename in os.listdir(current_dir): if filename.endswith(".py") and filename != "__init__.py": module_name = filename[:-3] module = importlib.import_module(f".{module_name}", __package__) + if hasattr(module, "NODE_CLASS_MAPPINGS"): + # Add category override here + for cls_name, cls in module.NODE_CLASS_MAPPINGS.items(): + cls.CATEGORY = "FSL Nodes" # Your unified category name + NODE_CLASS_MAPPINGS.update(module.NODE_CLASS_MAPPINGS) + if hasattr(module, "NODE_DISPLAY_NAME_MAPPINGS"): NODE_DISPLAY_NAME_MAPPINGS.update(module.NODE_DISPLAY_NAME_MAPPINGS) diff --git a/Comfyui_FSL_Nodes/fsl_composite_with_mask_cropped.py b/Comfyui_FSL_Nodes/fsl_composite_with_mask_cropped.py new file mode 100644 index 0000000..80378d5 --- /dev/null +++ b/Comfyui_FSL_Nodes/fsl_composite_with_mask_cropped.py @@ -0,0 +1,93 @@ +import torch +import numpy as np +from PIL import Image +import os + +class FSL_CompositeWithMask: + CATEGORY = "FSL Nodes/Utils" + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("composited_rgb",) + FUNCTION = "process" + + @classmethod + def INPUT_TYPES(cls): + return { + "optional": { + "foreground": ("IMAGE",), + "mask": ("MASK",), + "background": ("IMAGE",), + "filename": ("STRING", {"default": "composited_rgb.png"}), + "save_dir": ("STRING", {"default": "./outputs"}), + } + } + + def process(self, foreground=None, mask=None, background=None, filename="composited_rgb.png", save_dir="./outputs"): + # Ensure filename has valid extension + valid_extensions = [".png", ".jpg", ".jpeg", ".webp"] + if not any(filename.lower().endswith(ext) for ext in valid_extensions): + filename += ".png" # Default to PNG + + # Set default size + DEFAULT_SIZE = (512, 512) + + # Create default background if not provided (black) + if background is None: + bg_np = np.zeros((DEFAULT_SIZE[1], DEFAULT_SIZE[0], 3), dtype=np.uint8) + else: + bg_np = (background.cpu().numpy()[0] * 255).astype(np.uint8) + if bg_np.shape[-1] == 4: + bg_np = bg_np[..., :3] + + h, w = bg_np.shape[:2] + + # Create default mask if not provided (all white) + if mask is None: + mask_np = np.ones((h, w), dtype=np.uint8) * 255 + else: + mask_np = (mask.cpu().numpy()[0] * 255).astype(np.uint8) + if mask_np.shape != (h, w): + mask_np = np.array(Image.fromarray(mask_np).resize((w, h), Image.BILINEAR)) + + # Prepare mask for blending (0.0-1.0 float, shape HxWx1) + mask_f = (mask_np.astype(np.float32) / 255.0)[..., None] + + # If foreground is provided, blend; else just use background + if foreground is not None: + fg_np = (foreground.cpu().numpy()[0] * 255).astype(np.uint8) + if fg_np.shape[-1] == 4: + fg_np = fg_np[..., :3] + if fg_np.shape[:2] != (h, w): + fg_np = np.array(Image.fromarray(fg_np).resize((w, h), Image.BILINEAR)) + composited = fg_np.astype(np.float32) * mask_f + bg_np.astype(np.float32) * (1.0 - mask_f) + else: + composited = bg_np.astype(np.float32) * (1.0 - mask_f) + + composited = composited.astype(np.uint8) + + # Convert composited image and mask to PIL + pil_img = Image.fromarray(composited, mode="RGB") + pil_mask = Image.fromarray(mask_np, mode="L") + + # Calculate bounding box of mask (nonzero areas) + bbox = pil_mask.getbbox() + if bbox: + pil_img = pil_img.crop(bbox) + + # Save cropped RGB image + os.makedirs(save_dir, exist_ok=True) + save_path = os.path.join(save_dir, filename) + pil_img.save(save_path) + + # Convert back to tensor + out_np = np.array(pil_img).astype(np.float32) / 255.0 + out_tensor = torch.from_numpy(out_np).unsqueeze(0) + return (out_tensor,) + + +NODE_CLASS_MAPPINGS = { + "FSL Composite With Mask": FSL_CompositeWithMask, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "FSL Composite With Mask": "🖼️ Composite With Mask Cropped (FSL)", +} diff --git a/Comfyui_FSL_Nodes/fsl_save_and_strip_alpha.py b/Comfyui_FSL_Nodes/fsl_save_and_strip_alpha.py new file mode 100644 index 0000000..aea233d --- /dev/null +++ b/Comfyui_FSL_Nodes/fsl_save_and_strip_alpha.py @@ -0,0 +1,45 @@ +import torch +import numpy as np +from PIL import Image +import os + +class FSL_SaveAndStripAlpha: + CATEGORY = "FSL Nodes/Utils" + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("image_rgb",) + FUNCTION = "process" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "image_rgba": ("IMAGE",), + "filename": ("STRING", {"default": "output_rgb.png"}), + "save_dir": ("STRING", {"default": "./outputs"}), + } + } + + def process(self, image_rgba, filename, save_dir): + # Convert tensor to numpy array + np_img = (image_rgba.cpu().numpy()[0] * 255).astype(np.uint8) # shape: (H, W, 4) + pil_img = Image.fromarray(np_img, mode="RGBA") + pil_rgb = pil_img.convert("RGB") # Remove alpha channel[5] + + # Ensure save directory exists + os.makedirs(save_dir, exist_ok=True) + save_path = os.path.join(save_dir, filename) + pil_rgb.save(save_path) + + # Convert back to tensor for ComfyUI workflow (float32, 0-1, shape [1, H, W, 3]) + rgb_np = np.array(pil_rgb).astype(np.float32) / 255.0 + rgb_tensor = torch.from_numpy(rgb_np).unsqueeze(0) + + return (rgb_tensor,) + +NODE_CLASS_MAPPINGS = { + "FSL Save And Strip Alpha": FSL_SaveAndStripAlpha, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "FSL Save And Strip Alpha": "💾 Save & Strip Alpha (FSL)", +} diff --git a/Comfyui_FSL_Nodes/manual_alpha_mask_painter.py b/Comfyui_FSL_Nodes/manual_alpha_mask_painter.py index 09f082b..3cede12 100644 --- a/Comfyui_FSL_Nodes/manual_alpha_mask_painter.py +++ b/Comfyui_FSL_Nodes/manual_alpha_mask_painter.py @@ -1,21 +1,25 @@ -#You’ll need to create a folder in custom_nodes/ and place this as manual_alpha_mask_painter.py. - """ -CustomManualAlphaMaskPainter for Tshirt Designer Workflow +ManualAlphaMaskPainter for FSL Nodes + Version: 1.0.0 Author: Fred LeFevre Date: 2025-04-27 + Description: Custom MaskPainter Node to convert a black mask to alpha. """ __version__ = "1.0.0" - import numpy as np from PIL import Image import torch -class ManualAlphaMaskPainter: +class FSL_ManualAlphaMaskPainter: + CATEGORY = "FSL Nodes/Masking" + RETURN_TYPES = ("IMAGE", "MASK") + RETURN_NAMES = ("image_out", "mask_out") + FUNCTION = "paint_mask" + @classmethod def INPUT_TYPES(cls): return { @@ -25,25 +29,20 @@ class ManualAlphaMaskPainter: } } - RETURN_TYPES = ("IMAGE", "MASK") - FUNCTION = "paint_mask" - CATEGORY = "image/masking" - def paint_mask(self, image, mask): # Convert tensors to numpy arrays image_np = image.cpu().numpy()[0] * 255 image_np = image_np.astype(np.uint8) - mask_np = mask.cpu().numpy()[0] * 255 mask_np = mask_np.astype(np.uint8) # Create RGBA image img_rgba = Image.fromarray(image_np).convert("RGBA") rgba_array = np.array(img_rgba) - + # Apply mask to alpha channel rgba_array[:, :, 3] = mask_np - + # Convert back to tensor format result_image = torch.from_numpy(rgba_array.astype(np.float32) / 255.0).unsqueeze(0) result_mask = torch.from_numpy(mask_np.astype(np.float32) / 255.0).unsqueeze(0) @@ -51,9 +50,9 @@ class ManualAlphaMaskPainter: return (result_image, result_mask) NODE_CLASS_MAPPINGS = { - "ManualAlphaMaskPainter": ManualAlphaMaskPainter + "FSL Manual Alpha Mask Painter": FSL_ManualAlphaMaskPainter, } NODE_DISPLAY_NAME_MAPPINGS = { - "ManualAlphaMaskPainter": "Manual Alpha Mask Painter" + "FSL Manual Alpha Mask Painter": "🖌️ Manual Alpha Mask Painter (FSL)", } diff --git a/Comfyui_FSL_Nodes/requirements.txt b/Comfyui_FSL_Nodes/requirements.txt new file mode 100644 index 0000000..0130a0d --- /dev/null +++ b/Comfyui_FSL_Nodes/requirements.txt @@ -0,0 +1,2 @@ +pillow +numpy diff --git a/Comfyui_FSL_Nodes/sampler_config_hub.py b/Comfyui_FSL_Nodes/sampler_config_hub.py index 2589370..703d22c 100644 --- a/Comfyui_FSL_Nodes/sampler_config_hub.py +++ b/Comfyui_FSL_Nodes/sampler_config_hub.py @@ -1,17 +1,13 @@ -# ───────────────────────────────────────────────────────────────────────────── -# Sampler Config Hub Node -# Created by: Fred & Mycroft (ChatGPT) -# Date: 2025-04-21 -# Purpose: Outputs a consistent sampler config for KSampler nodes. -# Workflow: Used in Tshirt-SDXL-Gold-*.json workflows. -# ───────────────────────────────────────────────────────────────────────────── - class SamplerConfigHub: """ Custom node that emits a unified sampler config dictionary. This allows centralized control over sampler settings when using multiple KSampler nodes in a workflow (e.g., for SDXL base and refiner). """ + CATEGORY = "FSL Nodes/Utils" # Changed to match your preferred category + RETURN_TYPES = ("SAMPLER_CONFIG",) + RETURN_NAMES = ("sampler_config",) + FUNCTION = "build_config" @classmethod def INPUT_TYPES(cls): @@ -26,17 +22,12 @@ class SamplerConfigHub: "ddim", "plms" ],), "scheduler": (["normal", "karras", "exponential", "sgm_uniform"],), - "steps": (int, {"default": 30, "min": 1, "max": 150}), - "cfg": (float, {"default": 7.5, "min": 0.0, "max": 20.0}), - "seed": (int, {"default": 42}), + "steps": ("INT", {"default": 30, "min": 1, "max": 150}), + "cfg": ("FLOAT", {"default": 7.5, "min": 0.0, "max": 20.0}), + "seed": ("INT", {"default": 42}), } } - RETURN_TYPES = ("SAMPLER_CONFIG",) - RETURN_NAMES = ("sampler_config",) - FUNCTION = "build_config" - CATEGORY = "utils" - def build_config(self, sampler_name, scheduler, steps, cfg, seed): return ({ "sampler_name": sampler_name, @@ -47,9 +38,9 @@ class SamplerConfigHub: },) NODE_CLASS_MAPPINGS = { - "SamplerConfigHub": SamplerConfigHub + "FSL SamplerConfigHub": SamplerConfigHub, # Added prefix and comma } NODE_DISPLAY_NAME_MAPPINGS = { - "SamplerConfigHub": "Sampler Config Hub" -} \ No newline at end of file + "FSL SamplerConfigHub": "🎛️ Sampler Config Hub (FSL)", # Added prefix +} diff --git a/Comfyui_FSL_Nodes/sampler_scheduler_config.py b/Comfyui_FSL_Nodes/sampler_scheduler_config.py index a5b38ea..377fd0c 100644 --- a/Comfyui_FSL_Nodes/sampler_scheduler_config.py +++ b/Comfyui_FSL_Nodes/sampler_scheduler_config.py @@ -1,16 +1,21 @@ """ -CustomSamplerNode for Tshirt Designer Workflow +FSL Sampler & Scheduler Config Node + Version: 1.0.0 Author: Fred LeFevre Date: 2025-04-27 + Description: Custom Sampler Node to feed settings to metadata. """ __version__ = "1.0.0" +class FSL_SamplerSchedulerConfig: + CATEGORY = "FSL Nodes/Utils" + RETURN_TYPES = ("SAMPLER", "SCHEDULER") + RETURN_NAMES = ("sampler", "scheduler") + FUNCTION = "bundle" - -class SamplerSchedulerConfig: @classmethod def INPUT_TYPES(cls): return { @@ -33,20 +38,13 @@ class SamplerSchedulerConfig: } } - RETURN_TYPES = ("SAMPLER", "SCHEDULER") - RETURN_NAMES = ("sampler", "scheduler") - - FUNCTION = "bundle" - CATEGORY = "utils" - def bundle(self, sampler_name, scheduler_name): return (sampler_name, scheduler_name) - NODE_CLASS_MAPPINGS = { - "SamplerSchedulerConfig": SamplerSchedulerConfig + "FSL Sampler & Scheduler Config": FSL_SamplerSchedulerConfig, } NODE_DISPLAY_NAME_MAPPINGS = { - "SamplerSchedulerConfig": "Sampler & Scheduler Config" + "FSL Sampler & Scheduler Config": "🛠️ Sampler & Scheduler Config (FSL)", } diff --git a/README.md b/README.md index 1a76df0..ea62e10 100644 --- a/README.md +++ b/README.md @@ -6,20 +6,28 @@ A collection of custom ComfyUI workflows and nodes created by **Fred LeFevre**. ### 🧩 Custom Nodes: Comfyui_FSL_Nodes - **sampler_config_hub.py** - One of two custom nodes to configure sampler and scheduler settings for workflows. + One of two custom nodes to configure sampler and scheduler settings for workflows. Used to feed data to the meta node - **sampler_scheduler_config.py** - Two of two custom nodes to configure sampler and scheduler settings for workflows. + Two of two custom nodes to configure sampler and scheduler settings for workflows. Used to feed data to the meta node - **manual_alpha_mask_painter.py** - Custom node to manually convert black mask areas into alpha transparency. -- **place this node folder into the custom_nodes folder - no additional action is necessary. These two nodes support the ability to feed data to the meta node. + Custom node to manually convert black mask areas into alpha transparency. Useful with GRIPTAPE when inpainting and creating the mask by right clicking on the loaded image and select load mask editor. Griptape requires an alpha channel mask. +- **8WayImgSwitch** + Image switch with eight inputs +- **fsl_composite_with_mask_cropped** + Designed to remove the Alpha Channel But Keep Transparent Parts as Background Color (not currently used) +- **fsl_save_and_strip_alpha** + Strips alpha channel from RGBA image. (not currently used) +- **How to use** + place this node folder into the custom_nodes folder - no additional action is necessary. Nodes can be found in the comfyui onscreen nodes folder by searching for FSL Located in the [`Comfyui_FSL_Nodes/`](./Comfyui_FSL_Nodes) directory. --- ### 🎨 Workflow: T-shirt Designer -- **Tshirt-Designer-ver_1.0.json** +- **Tshirt-Designer-ver_2.0.json** Workflow designed for generating masked transparent images for T-shirt and object printing. +- Many corrections and updates - Full workflow details and notes are available in [`tshirt_designer/`](./tshirt_designer). --- diff --git a/image.png b/image.png new file mode 100644 index 0000000..514292b Binary files /dev/null and b/image.png differ diff --git a/repos_mask/README.md b/repos_mask/README.md new file mode 100644 index 0000000..abc133e --- /dev/null +++ b/repos_mask/README.md @@ -0,0 +1,16 @@ +# Repos Mask Workflow + + + This is a simple workflow that allows you to reposition either the mask or the image to change what parts of the image are exposed by the mask. + + +## License + +This project and all sub-projects are licensed under the terms of the [LICENSE](./LICENSE) provided at the repository root. + +--- + +## Author + +Created and maintained by **Fred LeFevre**. + diff --git a/repos_mask/Repos_Mask.json b/repos_mask/Repos_Mask.json new file mode 100644 index 0000000..0796fe5 --- /dev/null +++ b/repos_mask/Repos_Mask.json @@ -0,0 +1 @@ +{"id":"cefda2df-471e-4cd1-8020-aee156ea9a56","revision":0,"last_node_id":10,"last_link_id":17,"nodes":[{"id":1,"type":"LoadImage","pos":[2161.431640625,593.9169921875],"size":[452.3467102050781,498.7640380859375],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"image","name":"image","type":"COMBO","widget":{"name":"image"},"link":null}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","links":[1]},{"localized_name":"MASK","name":"MASK","type":"MASK","links":[2]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.29","Node name for S&R":"LoadImage","widget_ue_connectable":{}},"widgets_values":["UPSMask_00008_.png","image"]},{"id":2,"type":"OffsetImage","pos":[2668.897705078125,599.7833862304688],"size":[329.9867858886719,100.77959442138672],"flags":{},"order":1,"mode":0,"inputs":[{"localized_name":"pixels","name":"pixels","type":"IMAGE","link":1},{"localized_name":"x_percent","name":"x_percent","type":"FLOAT","widget":{"name":"x_percent"},"link":null},{"localized_name":"y_percent","name":"y_percent","type":"FLOAT","widget":{"name":"y_percent"},"link":null}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","links":[14]}],"properties":{"cnr_id":"comfyui-seamless-tiling","ver":"1.0.0","Node name for S&R":"OffsetImage","widget_ue_connectable":{}},"widgets_values":[90,0]},{"id":3,"type":"OffsetMask","pos":[2669.20263671875,767.8871459960938],"size":[315,202],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"mask","name":"mask","type":"MASK","link":2},{"localized_name":"x","name":"x","type":"INT","widget":{"name":"x"},"link":null},{"localized_name":"y","name":"y","type":"INT","widget":{"name":"y"},"link":null},{"localized_name":"angle","name":"angle","type":"INT","widget":{"name":"angle"},"link":null},{"localized_name":"duplication_factor","name":"duplication_factor","type":"INT","widget":{"name":"duplication_factor"},"link":null},{"localized_name":"roll","name":"roll","type":"BOOLEAN","widget":{"name":"roll"},"link":null},{"localized_name":"incremental","name":"incremental","type":"BOOLEAN","widget":{"name":"incremental"},"link":null},{"localized_name":"padding_mode","name":"padding_mode","type":"COMBO","widget":{"name":"padding_mode"},"link":null}],"outputs":[{"localized_name":"mask","name":"mask","type":"MASK","links":[8]}],"properties":{"cnr_id":"comfyui-kjnodes","ver":"b7e5b6f1e2b7c79b3f1e4b4bfe5e1687715803ab","Node name for S&R":"OffsetMask","widget_ue_connectable":{}},"widgets_values":[0,0,0,1,false,false,"empty"]},{"id":9,"type":"JoinImageWithAlpha","pos":[3030.108642578125,909.9120483398438],"size":[264.5999755859375,46],"flags":{},"order":3,"mode":0,"inputs":[{"localized_name":"image","name":"image","type":"IMAGE","link":14},{"localized_name":"alpha","name":"alpha","type":"MASK","link":8}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","links":[15]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.29","Node name for S&R":"JoinImageWithAlpha","widget_ue_connectable":{}},"widgets_values":[]},{"id":10,"type":"ImageResizeKJv2","pos":[3036.46728515625,589.3678588867188],"size":[315,242],"flags":{},"order":4,"mode":0,"inputs":[{"localized_name":"image","name":"image","type":"IMAGE","link":15},{"localized_name":"width","name":"width","type":"INT","widget":{"name":"width"},"link":null},{"localized_name":"height","name":"height","type":"INT","widget":{"name":"height"},"link":null},{"localized_name":"upscale_method","name":"upscale_method","type":"COMBO","widget":{"name":"upscale_method"},"link":null},{"localized_name":"keep_proportion","name":"keep_proportion","type":"COMBO","widget":{"name":"keep_proportion"},"link":null},{"localized_name":"pad_color","name":"pad_color","type":"STRING","widget":{"name":"pad_color"},"link":null},{"localized_name":"crop_position","name":"crop_position","type":"COMBO","widget":{"name":"crop_position"},"link":null},{"localized_name":"divisible_by","name":"divisible_by","type":"INT","widget":{"name":"divisible_by"},"link":null}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","links":[16,17]},{"localized_name":"width","name":"width","type":"INT","links":null},{"localized_name":"height","name":"height","type":"INT","links":null}],"properties":{"cnr_id":"comfyui-kjnodes","ver":"b7e5b6f1e2b7c79b3f1e4b4bfe5e1687715803ab","Node name for S&R":"ImageResizeKJv2","widget_ue_connectable":{}},"widgets_values":[1500,1500,"lanczos","stretch","0, 0, 0","top",2]},{"id":5,"type":"PreviewImage","pos":[3410.33056640625,344.7284851074219],"size":[210,246],"flags":{},"order":5,"mode":0,"inputs":[{"localized_name":"images","name":"images","type":"IMAGE","link":16}],"outputs":[],"properties":{"cnr_id":"comfy-core","ver":"0.3.29","Node name for S&R":"PreviewImage","widget_ue_connectable":{}},"widgets_values":[]},{"id":6,"type":"SaveImage","pos":[3412.1025390625,665.0047607421875],"size":[571.5828857421875,492.433349609375],"flags":{},"order":6,"mode":0,"inputs":[{"localized_name":"images","name":"images","type":"IMAGE","link":17},{"localized_name":"filename_prefix","name":"filename_prefix","type":"STRING","widget":{"name":"filename_prefix"},"link":null}],"outputs":[],"properties":{"cnr_id":"comfy-core","ver":"0.3.29","Node name for S&R":"SaveImage","widget_ue_connectable":{}},"widgets_values":["ComfyUI"]}],"links":[[1,1,0,2,0,"IMAGE"],[2,1,1,3,0,"MASK"],[8,3,0,9,1,"MASK"],[14,2,0,9,0,"IMAGE"],[15,9,0,10,0,"IMAGE"],[16,10,0,5,0,"IMAGE"],[17,10,0,6,0,"IMAGE"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.8954302432552402,"offset":[-2037.021317807537,-269.99633782838026]},"ue_links":[],"links_added_by_ue":[],"frontendVersion":"1.16.8","VHS_latentpreview":false,"VHS_latentpreviewrate":0,"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/tshirt_designer/README.md b/tshirt_designer/README.md index a739488..bda2a88 100644 --- a/tshirt_designer/README.md +++ b/tshirt_designer/README.md @@ -1,4 +1,6 @@ -# Tshirt Designer Workflow v1.0 for ComfyUI +# Tshirt Designer Workflow v2git clone https://github.com/comfyanonymous/ComfyUI.git +git clone https://github.com/comfyanonymous/ComfyUI.git +.0 for ComfyUI ## Overview This project provides a specialized **ComfyUI** workflow designed to create **masked images** for T-shirts, apparel, and other printable products. @@ -16,15 +18,17 @@ Originally developed for internal use, the workflow has grown into a versatile s - **Supports Griptape API** (OpenAI + Black Forest Labs) for automated image generation. - **Designed for upscaling** (e.g., with Topaz Photo AI). - **Ready-to-print transparent PNG output**. +- **Automatic masking based on mask selection** +- **Upscaling module is included** --- ## Requirements - **ComfyUI** installed and operational. -- **Custom nodes included** in this repository: - - `CustomSamplerNode` - - `CustomSchedulerNode` - - (List any others you included) +- **FSL Custom nodes included** in this workflow: + - **`Sampler Config Hub`** - supports sampler data to metadata node + - **`Manual Alpha Mask Painter`** - when creating a mask directly in the "load image" node the Mask Painter node converts the mask to Alpha hannel (required by Griptape). + - **`8 Way Image Switch`** - **API keys** for: - **OpenAI** @@ -35,8 +39,25 @@ Originally developed for internal use, the workflow has grown into a versatile s --- +## Griptape Groups Explained + +- **Griptape Loader** + - Text to image prompt using Griptape nodes. It is set for the Open AI driver but there are several other options available. +- **Griptape Variations** + - This standard Griptape group allows for minor variations in an image. I am not yet sure about it's value but have included it. +- **GT Image w/Mask - Inpainting** + - Use this group when doing inpainting with an image where the mask has been prepared. Make sure to use an alpha-channel mask for Griptape. +- **GT Create Mask - Inpainting** + - Use this group to upload an image and then create a mask. Right click on the uploaded image and select 'open in maskeditor'. This will create a black mask on the image. The custom node - 'Manual Alpha Mask Painter' will convert the mask to an alpha channel. +- **NOTE OF CAUTION** + - Be sure disable any API groups you aren't using to avoid using API tokens unnecessarily. + +--- + + + ## Installation -1. Clone or download this repository. +1. Clone or download this repository.git clone https://github.com/comfyanonymous/ComfyUI.git 2. Copy the `custom_nodes` into your ComfyUI `custom_nodes` directory. 3. Copy the workflow `.json` file into your `users/default/workflows` directory. 4. Configure your Griptape API keys inside ComfyUI Settings -> Griptape -> API Keys. @@ -58,6 +79,32 @@ Originally developed for internal use, the workflow has grown into a versatile s --- +## Workflow Switches Explained + +![alt text](image.png) + +This switch determined what path is used for image creation. For example item 1 bypassed Griptape groups and uses the image loaded in the Image Path Group. This makes it a pure SDXL workflow. + +![](image-1.png) + +This switch determines whether an image or a prompt will be used (or both together). It is used in conjunction with the denoise setting as defined in this table below - + +![](image-2.png) + + + + +![ +](image-3.png) + +This switch determines if a Griptape image should be processed through the SDXL workflow for further refinement or being reinserted downstream to start the masking process. + +![alt text](image-4.png) + +This switch determines which image gets processed through the upscaler. + +--- + ## License [MIT License](LICENSE) You are free to use, modify, and distribute this workflow with attribution. diff --git a/tshirt_designer/image-1.png b/tshirt_designer/image-1.png new file mode 100644 index 0000000..b9fa89b Binary files /dev/null and b/tshirt_designer/image-1.png differ diff --git a/tshirt_designer/image-2.png b/tshirt_designer/image-2.png new file mode 100644 index 0000000..f6b4415 Binary files /dev/null and b/tshirt_designer/image-2.png differ diff --git a/tshirt_designer/image-3.png b/tshirt_designer/image-3.png new file mode 100644 index 0000000..5be525a Binary files /dev/null and b/tshirt_designer/image-3.png differ diff --git a/tshirt_designer/image-4.png b/tshirt_designer/image-4.png new file mode 100644 index 0000000..183079c Binary files /dev/null and b/tshirt_designer/image-4.png differ diff --git a/tshirt_designer/image.png b/tshirt_designer/image.png new file mode 100644 index 0000000..903fdf5 Binary files /dev/null and b/tshirt_designer/image.png differ