|
|
|
|
@ -47,10 +47,8 @@ class LoadImageDataSetFromFolderNode(io.ComfyNode):
|
|
|
|
|
def define_schema(cls):
|
|
|
|
|
return io.Schema(
|
|
|
|
|
node_id="LoadImageDataSetFromFolder",
|
|
|
|
|
search_aliases=["load folder", "load from folder", "load dataset", "load images", "import dataset"],
|
|
|
|
|
display_name="Load Image (from Folder)",
|
|
|
|
|
category="image",
|
|
|
|
|
description="Load a dataset of images from a specified folder and return a list of images. Supported formats: PNG, JPG, JPEG, WEBP.",
|
|
|
|
|
display_name="Load Image Dataset from Folder",
|
|
|
|
|
category="dataset",
|
|
|
|
|
is_experimental=True,
|
|
|
|
|
inputs=[
|
|
|
|
|
io.Combo.Input(
|
|
|
|
|
@ -86,16 +84,14 @@ class LoadImageTextDataSetFromFolderNode(io.ComfyNode):
|
|
|
|
|
def define_schema(cls):
|
|
|
|
|
return io.Schema(
|
|
|
|
|
node_id="LoadImageTextDataSetFromFolder",
|
|
|
|
|
search_aliases=["load folder", "load from folder", "load dataset", "load images", "import dataset"],
|
|
|
|
|
display_name="Load Image-Text (from Folder)",
|
|
|
|
|
category="image",
|
|
|
|
|
description="Load a dataset of pairs of images and text captions from a specified folder and return them as a list. Supported formats: PNG, JPG, JPEG, WEBP.",
|
|
|
|
|
display_name="Load Image and Text Dataset from Folder",
|
|
|
|
|
category="dataset",
|
|
|
|
|
is_experimental=True,
|
|
|
|
|
inputs=[
|
|
|
|
|
io.Combo.Input(
|
|
|
|
|
"folder",
|
|
|
|
|
options=folder_paths.get_input_subfolders(),
|
|
|
|
|
tooltip="The folder to load images and text captions from.",
|
|
|
|
|
tooltip="The folder to load images from.",
|
|
|
|
|
)
|
|
|
|
|
],
|
|
|
|
|
outputs=[
|
|
|
|
|
@ -210,10 +206,8 @@ class SaveImageDataSetToFolderNode(io.ComfyNode):
|
|
|
|
|
def define_schema(cls):
|
|
|
|
|
return io.Schema(
|
|
|
|
|
node_id="SaveImageDataSetToFolder",
|
|
|
|
|
search_aliases=["save folder", "save to folder", "save dataset", "save images", "export dataset"],
|
|
|
|
|
display_name="Save Image (to Folder) (DEPRECATED)",
|
|
|
|
|
category="image",
|
|
|
|
|
description="Save a dataset of images to a specified folder. Supported formats: PNG.",
|
|
|
|
|
display_name="Save Image Dataset to Folder",
|
|
|
|
|
category="dataset",
|
|
|
|
|
is_experimental=True,
|
|
|
|
|
is_output_node=True,
|
|
|
|
|
is_input_list=True, # Receive images as list
|
|
|
|
|
@ -232,7 +226,6 @@ class SaveImageDataSetToFolderNode(io.ComfyNode):
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
outputs=[],
|
|
|
|
|
is_deprecated=True, # This node is redundant and superseded by existing Save Image nodes where the target folder can be specified in the filename_prefix
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
@ -253,20 +246,14 @@ class SaveImageTextDataSetToFolderNode(io.ComfyNode):
|
|
|
|
|
def define_schema(cls):
|
|
|
|
|
return io.Schema(
|
|
|
|
|
node_id="SaveImageTextDataSetToFolder",
|
|
|
|
|
search_aliases=["save folder", "save to folder", "save dataset", "save images", "save text", "export dataset"],
|
|
|
|
|
display_name="Save Image-Text (to Folder)",
|
|
|
|
|
category="image",
|
|
|
|
|
description="Save a dataset of pairs of images and text captions to a specified folder. Images are saved as PNG files and captions are saved as TXT files with the same filename_prefix.",
|
|
|
|
|
display_name="Save Image and Text Dataset to Folder",
|
|
|
|
|
category="dataset",
|
|
|
|
|
is_experimental=True,
|
|
|
|
|
is_output_node=True,
|
|
|
|
|
is_input_list=True, # Receive both images and texts as lists
|
|
|
|
|
inputs=[
|
|
|
|
|
io.Image.Input("images", tooltip="List of images to save."),
|
|
|
|
|
io.String.Input("texts",
|
|
|
|
|
optional=True,
|
|
|
|
|
force_input=True,
|
|
|
|
|
tooltip="List of text captions to save."
|
|
|
|
|
),
|
|
|
|
|
io.String.Input("texts", tooltip="List of text captions to save."),
|
|
|
|
|
io.String.Input(
|
|
|
|
|
"folder_name",
|
|
|
|
|
default="dataset",
|
|
|
|
|
@ -283,7 +270,7 @@ class SaveImageTextDataSetToFolderNode(io.ComfyNode):
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def execute(cls, images, folder_name, filename_prefix, texts=None):
|
|
|
|
|
def execute(cls, images, texts, folder_name, filename_prefix):
|
|
|
|
|
# Extract scalar values
|
|
|
|
|
folder_name = folder_name[0]
|
|
|
|
|
filename_prefix = filename_prefix[0]
|
|
|
|
|
@ -292,12 +279,11 @@ class SaveImageTextDataSetToFolderNode(io.ComfyNode):
|
|
|
|
|
saved_files = save_images_to_folder(images, output_dir, filename_prefix)
|
|
|
|
|
|
|
|
|
|
# Save captions
|
|
|
|
|
if texts:
|
|
|
|
|
for idx, (filename, caption) in enumerate(zip(saved_files, texts)):
|
|
|
|
|
caption_filename = filename.replace(".png", ".txt")
|
|
|
|
|
caption_path = os.path.join(output_dir, caption_filename)
|
|
|
|
|
with open(caption_path, "w", encoding="utf-8") as f:
|
|
|
|
|
f.write(caption)
|
|
|
|
|
for idx, (filename, caption) in enumerate(zip(saved_files, texts)):
|
|
|
|
|
caption_filename = filename.replace(".png", ".txt")
|
|
|
|
|
caption_path = os.path.join(output_dir, caption_filename)
|
|
|
|
|
with open(caption_path, "w", encoding="utf-8") as f:
|
|
|
|
|
f.write(caption)
|
|
|
|
|
|
|
|
|
|
logging.info(f"Saved {len(saved_files)} images and captions to {output_dir}.")
|
|
|
|
|
return io.NodeOutput()
|
|
|
|
|
@ -328,13 +314,11 @@ class ImageProcessingNode(io.ComfyNode):
|
|
|
|
|
|
|
|
|
|
Child classes should set:
|
|
|
|
|
node_id: Unique node identifier (required)
|
|
|
|
|
search_aliases: List of search aliases (optional)
|
|
|
|
|
display_name: Display name (optional, defaults to node_id)
|
|
|
|
|
description: Node description (optional)
|
|
|
|
|
extra_inputs: List of additional io.Input objects beyond "images" (optional)
|
|
|
|
|
is_group_process: None (auto-detect), True (group), or False (individual) (optional)
|
|
|
|
|
is_output_list: True (list output) or False (single output) (optional, default True)
|
|
|
|
|
is_deprecated: True if the node is deprecated (optional, default False)
|
|
|
|
|
|
|
|
|
|
Child classes must implement ONE of:
|
|
|
|
|
_process(cls, image, **kwargs) -> tensor (for single-item processing)
|
|
|
|
|
@ -342,13 +326,12 @@ class ImageProcessingNode(io.ComfyNode):
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
node_id = None
|
|
|
|
|
search_aliases = []
|
|
|
|
|
display_name = None
|
|
|
|
|
description = None
|
|
|
|
|
extra_inputs = []
|
|
|
|
|
is_group_process = None # None = auto-detect, True/False = explicit
|
|
|
|
|
is_output_list = None # None = auto-detect based on processing mode
|
|
|
|
|
is_deprecated = False
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def _detect_processing_mode(cls):
|
|
|
|
|
"""Detect whether this node uses group or individual processing.
|
|
|
|
|
@ -419,10 +402,8 @@ class ImageProcessingNode(io.ComfyNode):
|
|
|
|
|
|
|
|
|
|
return io.Schema(
|
|
|
|
|
node_id=cls.node_id,
|
|
|
|
|
search_aliases=cls.search_aliases,
|
|
|
|
|
display_name=cls.display_name or cls.node_id,
|
|
|
|
|
category=cls.category,
|
|
|
|
|
description=cls.description,
|
|
|
|
|
category="dataset/image",
|
|
|
|
|
is_experimental=True,
|
|
|
|
|
is_input_list=is_group, # True for group, False for individual
|
|
|
|
|
inputs=inputs,
|
|
|
|
|
@ -491,13 +472,11 @@ class TextProcessingNode(io.ComfyNode):
|
|
|
|
|
|
|
|
|
|
Child classes should set:
|
|
|
|
|
node_id: Unique node identifier (required)
|
|
|
|
|
search_aliases: List of search aliases (optional)
|
|
|
|
|
display_name: Display name (optional, defaults to node_id)
|
|
|
|
|
description: Node description (optional)
|
|
|
|
|
extra_inputs: List of additional io.Input objects beyond "texts" (optional)
|
|
|
|
|
is_group_process: None (auto-detect), True (group), or False (individual) (optional)
|
|
|
|
|
is_output_list: True (list output) or False (single output) (optional, default True)
|
|
|
|
|
is_deprecated: True if the node is deprecated (optional, default False)
|
|
|
|
|
|
|
|
|
|
Child classes must implement ONE of:
|
|
|
|
|
_process(cls, text, **kwargs) -> str (for single-item processing)
|
|
|
|
|
@ -505,13 +484,12 @@ class TextProcessingNode(io.ComfyNode):
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
node_id = None
|
|
|
|
|
search_aliases = []
|
|
|
|
|
display_name = None
|
|
|
|
|
description = None
|
|
|
|
|
extra_inputs = []
|
|
|
|
|
is_group_process = None # None = auto-detect, True/False = explicit
|
|
|
|
|
is_output_list = None # None = auto-detect based on processing mode
|
|
|
|
|
is_deprecated = False
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def _detect_processing_mode(cls):
|
|
|
|
|
"""Detect whether this node uses group or individual processing.
|
|
|
|
|
@ -649,17 +627,15 @@ class TextProcessingNode(io.ComfyNode):
|
|
|
|
|
|
|
|
|
|
class ResizeImagesByShorterEdgeNode(ImageProcessingNode):
|
|
|
|
|
node_id = "ResizeImagesByShorterEdge"
|
|
|
|
|
display_name = "Resize Images by Shorter Edge (DEPRECATED)"
|
|
|
|
|
category = "image/transform"
|
|
|
|
|
description = "Resize images so that the shorter edge matches the specified dimension while preserving aspect ratio."
|
|
|
|
|
is_deprecated = True # This node is superseded by Resize Image/Mask with resize_type = scale shorter dimension
|
|
|
|
|
display_name = "Resize Images by Shorter Edge"
|
|
|
|
|
description = "Resize images so that the shorter edge matches the specified length while preserving aspect ratio."
|
|
|
|
|
extra_inputs = [
|
|
|
|
|
io.Int.Input(
|
|
|
|
|
"shorter_edge",
|
|
|
|
|
default=512,
|
|
|
|
|
min=1,
|
|
|
|
|
max=8192,
|
|
|
|
|
tooltip="Target dimension for the shorter edge.",
|
|
|
|
|
tooltip="Target length for the shorter edge.",
|
|
|
|
|
),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
@ -679,17 +655,15 @@ class ResizeImagesByShorterEdgeNode(ImageProcessingNode):
|
|
|
|
|
|
|
|
|
|
class ResizeImagesByLongerEdgeNode(ImageProcessingNode):
|
|
|
|
|
node_id = "ResizeImagesByLongerEdge"
|
|
|
|
|
display_name = "Resize Images by Longer Edge (DEPRECATED)"
|
|
|
|
|
category = "image/transform"
|
|
|
|
|
description = "Resize images so that the longer edge matches the specified dimension while preserving aspect ratio."
|
|
|
|
|
is_deprecated = True # This node is superseded by Resize Image/Mask with resize_type = scale longer dimension
|
|
|
|
|
display_name = "Resize Images by Longer Edge"
|
|
|
|
|
description = "Resize images so that the longer edge matches the specified length while preserving aspect ratio."
|
|
|
|
|
extra_inputs = [
|
|
|
|
|
io.Int.Input(
|
|
|
|
|
"longer_edge",
|
|
|
|
|
default=1024,
|
|
|
|
|
min=1,
|
|
|
|
|
max=8192,
|
|
|
|
|
tooltip="Target dimension for the longer edge.",
|
|
|
|
|
tooltip="Target length for the longer edge.",
|
|
|
|
|
),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
@ -712,10 +686,8 @@ class ResizeImagesByLongerEdgeNode(ImageProcessingNode):
|
|
|
|
|
|
|
|
|
|
class CenterCropImagesNode(ImageProcessingNode):
|
|
|
|
|
node_id = "CenterCropImages"
|
|
|
|
|
search_aliases=["crop", "cut", "trim"]
|
|
|
|
|
display_name="Crop Image (Center)"
|
|
|
|
|
category="image/transform"
|
|
|
|
|
description = "Center crop an image to the specified dimensions."
|
|
|
|
|
display_name = "Center Crop Images"
|
|
|
|
|
description = "Center crop all images to the specified dimensions."
|
|
|
|
|
extra_inputs = [
|
|
|
|
|
io.Int.Input("width", default=512, min=1, max=8192, tooltip="Crop width."),
|
|
|
|
|
io.Int.Input("height", default=512, min=1, max=8192, tooltip="Crop height."),
|
|
|
|
|
@ -734,11 +706,10 @@ class CenterCropImagesNode(ImageProcessingNode):
|
|
|
|
|
|
|
|
|
|
class RandomCropImagesNode(ImageProcessingNode):
|
|
|
|
|
node_id = "RandomCropImages"
|
|
|
|
|
search_aliases=["crop", "cut", "trim"]
|
|
|
|
|
display_name = "Crop Image (Random)"
|
|
|
|
|
category="image/transform"
|
|
|
|
|
description = "Randomly crop an image to the specified dimensions."
|
|
|
|
|
|
|
|
|
|
display_name = "Random Crop Images"
|
|
|
|
|
description = (
|
|
|
|
|
"Randomly crop all images to the specified dimensions (for data augmentation)."
|
|
|
|
|
)
|
|
|
|
|
extra_inputs = [
|
|
|
|
|
io.Int.Input("width", default=512, min=1, max=8192, tooltip="Crop width."),
|
|
|
|
|
io.Int.Input("height", default=512, min=1, max=8192, tooltip="Crop height."),
|
|
|
|
|
@ -763,9 +734,7 @@ class RandomCropImagesNode(ImageProcessingNode):
|
|
|
|
|
|
|
|
|
|
class NormalizeImagesNode(ImageProcessingNode):
|
|
|
|
|
node_id = "NormalizeImages"
|
|
|
|
|
search_aliases=["normalize", "normalize colors"]
|
|
|
|
|
display_name = "Normalize Image Colors"
|
|
|
|
|
category = "image/color"
|
|
|
|
|
display_name = "Normalize Images"
|
|
|
|
|
description = "Normalize images using mean and standard deviation."
|
|
|
|
|
extra_inputs = [
|
|
|
|
|
io.Float.Input(
|
|
|
|
|
@ -793,10 +762,8 @@ class NormalizeImagesNode(ImageProcessingNode):
|
|
|
|
|
|
|
|
|
|
class AdjustBrightnessNode(ImageProcessingNode):
|
|
|
|
|
node_id = "AdjustBrightness"
|
|
|
|
|
search_aliases=["brightness"]
|
|
|
|
|
display_name = "Adjust Brightness"
|
|
|
|
|
category="image/adjustments"
|
|
|
|
|
description = "Adjust the brightness of an image."
|
|
|
|
|
description = "Adjust brightness of all images."
|
|
|
|
|
extra_inputs = [
|
|
|
|
|
io.Float.Input(
|
|
|
|
|
"factor",
|
|
|
|
|
@ -814,10 +781,8 @@ class AdjustBrightnessNode(ImageProcessingNode):
|
|
|
|
|
|
|
|
|
|
class AdjustContrastNode(ImageProcessingNode):
|
|
|
|
|
node_id = "AdjustContrast"
|
|
|
|
|
search_aliases=["contrast"]
|
|
|
|
|
display_name = "Adjust Contrast"
|
|
|
|
|
category="image/adjustments"
|
|
|
|
|
description = "Adjust the contrast of an image."
|
|
|
|
|
description = "Adjust contrast of all images."
|
|
|
|
|
extra_inputs = [
|
|
|
|
|
io.Float.Input(
|
|
|
|
|
"factor",
|
|
|
|
|
@ -835,10 +800,8 @@ class AdjustContrastNode(ImageProcessingNode):
|
|
|
|
|
|
|
|
|
|
class ShuffleDatasetNode(ImageProcessingNode):
|
|
|
|
|
node_id = "ShuffleDataset"
|
|
|
|
|
search_aliases=["shuffle", "randomize", "mix"]
|
|
|
|
|
display_name = "Shuffle Images List"
|
|
|
|
|
category = "image/batch"
|
|
|
|
|
description = "Randomly shuffle the order of images in a list."
|
|
|
|
|
display_name = "Shuffle Image Dataset"
|
|
|
|
|
description = "Randomly shuffle the order of images in the dataset."
|
|
|
|
|
is_group_process = True # Requires full list to shuffle
|
|
|
|
|
extra_inputs = [
|
|
|
|
|
io.Int.Input(
|
|
|
|
|
@ -860,15 +823,13 @@ class ShuffleImageTextDatasetNode(io.ComfyNode):
|
|
|
|
|
def define_schema(cls):
|
|
|
|
|
return io.Schema(
|
|
|
|
|
node_id="ShuffleImageTextDataset",
|
|
|
|
|
search_aliases=["shuffle", "randomize", "mix"],
|
|
|
|
|
display_name = "Shuffle Pairs of Image-Text",
|
|
|
|
|
category = "image/batch",
|
|
|
|
|
description = "Randomly shuffle the order of pairs of image-text in a list.",
|
|
|
|
|
display_name="Shuffle Image-Text Dataset",
|
|
|
|
|
category="dataset/image",
|
|
|
|
|
is_experimental=True,
|
|
|
|
|
is_input_list=True,
|
|
|
|
|
inputs=[
|
|
|
|
|
io.Image.Input("images", tooltip="List of images to shuffle."),
|
|
|
|
|
io.String.Input("texts", tooltip="List of texts to shuffle.", force_input=True),
|
|
|
|
|
io.String.Input("texts", tooltip="List of texts to shuffle."),
|
|
|
|
|
io.Int.Input(
|
|
|
|
|
"seed",
|
|
|
|
|
default=0,
|
|
|
|
|
@ -904,11 +865,8 @@ class ShuffleImageTextDatasetNode(io.ComfyNode):
|
|
|
|
|
|
|
|
|
|
class TextToLowercaseNode(TextProcessingNode):
|
|
|
|
|
node_id = "TextToLowercase"
|
|
|
|
|
search_aliases=["lowercase"]
|
|
|
|
|
display_name = "Convert Text to Lowercase (DEPRECATED)"
|
|
|
|
|
category = "text"
|
|
|
|
|
description = "Convert text to lowercase."
|
|
|
|
|
is_deprecated = True # This node is superseded by the Convert Text Case node
|
|
|
|
|
display_name = "Text to Lowercase"
|
|
|
|
|
description = "Convert all texts to lowercase."
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def _process(cls, text):
|
|
|
|
|
@ -917,11 +875,8 @@ class TextToLowercaseNode(TextProcessingNode):
|
|
|
|
|
|
|
|
|
|
class TextToUppercaseNode(TextProcessingNode):
|
|
|
|
|
node_id = "TextToUppercase"
|
|
|
|
|
search_aliases=["uppercase"]
|
|
|
|
|
display_name = "Convert Text to Uppercase (DEPRECATED)"
|
|
|
|
|
category = "text"
|
|
|
|
|
description = "Convert text to uppercase."
|
|
|
|
|
is_deprecated = True # This node is superseded by the Convert Text Case node
|
|
|
|
|
display_name = "Text to Uppercase"
|
|
|
|
|
description = "Convert all texts to uppercase."
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def _process(cls, text):
|
|
|
|
|
@ -930,10 +885,8 @@ class TextToUppercaseNode(TextProcessingNode):
|
|
|
|
|
|
|
|
|
|
class TruncateTextNode(TextProcessingNode):
|
|
|
|
|
node_id = "TruncateText"
|
|
|
|
|
search_aliases=["truncate", "cut", "shorten"]
|
|
|
|
|
display_name = "Truncate Text"
|
|
|
|
|
category = "text"
|
|
|
|
|
description = "Truncate text to a maximum length."
|
|
|
|
|
description = "Truncate all texts to a maximum length."
|
|
|
|
|
extra_inputs = [
|
|
|
|
|
io.Int.Input(
|
|
|
|
|
"max_length", default=77, min=1, max=10000, tooltip="Maximum text length."
|
|
|
|
|
@ -947,10 +900,8 @@ class TruncateTextNode(TextProcessingNode):
|
|
|
|
|
|
|
|
|
|
class AddTextPrefixNode(TextProcessingNode):
|
|
|
|
|
node_id = "AddTextPrefix"
|
|
|
|
|
display_name = "Add Text Prefix (DEPRECATED)"
|
|
|
|
|
category = "text"
|
|
|
|
|
display_name = "Add Text Prefix"
|
|
|
|
|
description = "Add a prefix to all texts."
|
|
|
|
|
is_deprecated = True # This node is superseded by the Concatenate Text node
|
|
|
|
|
extra_inputs = [
|
|
|
|
|
io.String.Input("prefix", default="", tooltip="Prefix to add."),
|
|
|
|
|
]
|
|
|
|
|
@ -962,10 +913,8 @@ class AddTextPrefixNode(TextProcessingNode):
|
|
|
|
|
|
|
|
|
|
class AddTextSuffixNode(TextProcessingNode):
|
|
|
|
|
node_id = "AddTextSuffix"
|
|
|
|
|
display_name = "Add Text Suffix (DEPRECATED)"
|
|
|
|
|
category = "text"
|
|
|
|
|
display_name = "Add Text Suffix"
|
|
|
|
|
description = "Add a suffix to all texts."
|
|
|
|
|
is_deprecated = True # This node is superseded by the Concatenate Text node
|
|
|
|
|
extra_inputs = [
|
|
|
|
|
io.String.Input("suffix", default="", tooltip="Suffix to add."),
|
|
|
|
|
]
|
|
|
|
|
@ -977,10 +926,8 @@ class AddTextSuffixNode(TextProcessingNode):
|
|
|
|
|
|
|
|
|
|
class ReplaceTextNode(TextProcessingNode):
|
|
|
|
|
node_id = "ReplaceText"
|
|
|
|
|
display_name = "Replace Text (DEPRECATED)"
|
|
|
|
|
category = "text"
|
|
|
|
|
display_name = "Replace Text"
|
|
|
|
|
description = "Replace text in all texts."
|
|
|
|
|
is_deprecated = True # This node is superseded by the other Replace Text node
|
|
|
|
|
extra_inputs = [
|
|
|
|
|
io.String.Input("find", default="", tooltip="Text to find."),
|
|
|
|
|
io.String.Input("replace", default="", tooltip="Text to replace with."),
|
|
|
|
|
@ -993,10 +940,8 @@ class ReplaceTextNode(TextProcessingNode):
|
|
|
|
|
|
|
|
|
|
class StripWhitespaceNode(TextProcessingNode):
|
|
|
|
|
node_id = "StripWhitespace"
|
|
|
|
|
display_name = "Strip Whitespace (DEPRECATED)"
|
|
|
|
|
category = "text"
|
|
|
|
|
display_name = "Strip Whitespace"
|
|
|
|
|
description = "Strip leading and trailing whitespace from all texts."
|
|
|
|
|
is_deprecated = True # This node is superseded by the Trim Text node
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def _process(cls, text):
|
|
|
|
|
@ -1007,13 +952,11 @@ class StripWhitespaceNode(TextProcessingNode):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ImageDeduplicationNode(ImageProcessingNode):
|
|
|
|
|
"""Remove duplicate or very similar images from a list using perceptual hashing."""
|
|
|
|
|
"""Remove duplicate or very similar images from the dataset using perceptual hashing."""
|
|
|
|
|
|
|
|
|
|
node_id = "ImageDeduplication"
|
|
|
|
|
search_aliases=["deduplicate", "remove duplicates", "similarity filter"]
|
|
|
|
|
display_name = "Deduplicate Images"
|
|
|
|
|
category = "image/batch"
|
|
|
|
|
description = "Remove duplicate or very similar images from a list."
|
|
|
|
|
display_name = "Image Deduplication"
|
|
|
|
|
description = "Remove duplicate or very similar images from the dataset."
|
|
|
|
|
is_group_process = True # Requires full list to compare images
|
|
|
|
|
extra_inputs = [
|
|
|
|
|
io.Float.Input(
|
|
|
|
|
@ -1083,9 +1026,7 @@ class ImageGridNode(ImageProcessingNode):
|
|
|
|
|
"""Combine multiple images into a single grid/collage."""
|
|
|
|
|
|
|
|
|
|
node_id = "ImageGrid"
|
|
|
|
|
search_aliases=["grid", "collage", "combine"]
|
|
|
|
|
display_name = "Make Image Grid"
|
|
|
|
|
category="image/batch"
|
|
|
|
|
display_name = "Image Grid"
|
|
|
|
|
description = "Arrange multiple images into a grid layout."
|
|
|
|
|
is_group_process = True # Requires full list to create grid
|
|
|
|
|
is_output_list = False # Outputs single grid image
|
|
|
|
|
@ -1161,12 +1102,9 @@ class MergeImageListsNode(ImageProcessingNode):
|
|
|
|
|
"""Merge multiple image lists into a single list."""
|
|
|
|
|
|
|
|
|
|
node_id = "MergeImageLists"
|
|
|
|
|
search_aliases=["list", "merge list", "make list"]
|
|
|
|
|
display_name = "Merge Image Lists (DEPRECATED)"
|
|
|
|
|
category = "image/batch"
|
|
|
|
|
display_name = "Merge Image Lists"
|
|
|
|
|
description = "Concatenate multiple image lists into one."
|
|
|
|
|
is_group_process = True # Receives images as list
|
|
|
|
|
is_deprecated = True # This node is superseded by the Create List node
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def _group_process(cls, images):
|
|
|
|
|
@ -1181,11 +1119,9 @@ class MergeTextListsNode(TextProcessingNode):
|
|
|
|
|
"""Merge multiple text lists into a single list."""
|
|
|
|
|
|
|
|
|
|
node_id = "MergeTextLists"
|
|
|
|
|
display_name = "Merge Text Lists (DEPRECATED)"
|
|
|
|
|
category = "text"
|
|
|
|
|
display_name = "Merge Text Lists"
|
|
|
|
|
description = "Concatenate multiple text lists into one."
|
|
|
|
|
is_group_process = True # Receives texts as list
|
|
|
|
|
is_deprecated = True # This node is superseded by the Create List node
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def _group_process(cls, texts):
|
|
|
|
|
@ -1206,10 +1142,8 @@ class ResolutionBucket(io.ComfyNode):
|
|
|
|
|
def define_schema(cls):
|
|
|
|
|
return io.Schema(
|
|
|
|
|
node_id="ResolutionBucket",
|
|
|
|
|
search_aliases=["bucket by resolution", "group by resolution", "batch by resolution"],
|
|
|
|
|
display_name="Resolution Bucket",
|
|
|
|
|
category="training",
|
|
|
|
|
description="Group latents and conditionings into buckets",
|
|
|
|
|
category="dataset",
|
|
|
|
|
is_experimental=True,
|
|
|
|
|
is_input_list=True,
|
|
|
|
|
inputs=[
|
|
|
|
|
@ -1302,8 +1236,7 @@ class MakeTrainingDataset(io.ComfyNode):
|
|
|
|
|
node_id="MakeTrainingDataset",
|
|
|
|
|
search_aliases=["encode dataset"],
|
|
|
|
|
display_name="Make Training Dataset",
|
|
|
|
|
category="training",
|
|
|
|
|
description="Encode images with VAE and texts with CLIP to create a training dataset of latents and conditionings.",
|
|
|
|
|
category="dataset",
|
|
|
|
|
is_experimental=True,
|
|
|
|
|
is_input_list=True, # images and texts as lists
|
|
|
|
|
inputs=[
|
|
|
|
|
@ -1318,7 +1251,6 @@ class MakeTrainingDataset(io.ComfyNode):
|
|
|
|
|
"texts",
|
|
|
|
|
optional=True,
|
|
|
|
|
tooltip="List of text captions. Can be length n (matching images), 1 (repeated for all), or omitted (uses empty string).",
|
|
|
|
|
force_input=True
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
outputs=[
|
|
|
|
|
@ -1388,10 +1320,9 @@ class SaveTrainingDataset(io.ComfyNode):
|
|
|
|
|
def define_schema(cls):
|
|
|
|
|
return io.Schema(
|
|
|
|
|
node_id="SaveTrainingDataset",
|
|
|
|
|
search_aliases=["export dataset", "save dataset"],
|
|
|
|
|
search_aliases=["export training data"],
|
|
|
|
|
display_name="Save Training Dataset",
|
|
|
|
|
category="training",
|
|
|
|
|
description="Save encoded training dataset (latents + conditioning) to disk for efficient loading during training.",
|
|
|
|
|
category="dataset",
|
|
|
|
|
is_experimental=True,
|
|
|
|
|
is_output_node=True,
|
|
|
|
|
is_input_list=True, # Receive lists
|
|
|
|
|
@ -1493,8 +1424,7 @@ class LoadTrainingDataset(io.ComfyNode):
|
|
|
|
|
node_id="LoadTrainingDataset",
|
|
|
|
|
search_aliases=["import dataset", "training data"],
|
|
|
|
|
display_name="Load Training Dataset",
|
|
|
|
|
category="training",
|
|
|
|
|
description="Load encoded training dataset (latents + conditioning) from disk for use in training.",
|
|
|
|
|
category="dataset",
|
|
|
|
|
is_experimental=True,
|
|
|
|
|
inputs=[
|
|
|
|
|
io.String.Input(
|
|
|
|
|
|