Skip to content

Commit

Permalink
feat: ForeachListBegin/End, WorklistToItemList
Browse files Browse the repository at this point in the history
  • Loading branch information
ltdrdata committed Nov 22, 2024
1 parent 3bcc1d4 commit 71a6865
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 7 deletions.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ This repository offers various extension nodes for ComfyUI. Nodes here have diff
* Save LoRA Block Weight: Save LBW_MODEL as a .lbw.safetensors file
* Load LoRA Block Weight: Load LBW_MODEL from .lbw.safetensors file


### SEGS Supports nodes - This is a node that supports ApplyControlNet (SEGS) from the Impact Pack.
* `OpenPose Preprocessor Provider (SEGS)`: OpenPose preprocessor is applied for the purpose of using OpenPose ControlNet in SEGS.
* You need to install [ControlNet Auxiliary Preprocessors](https://github.com/Fannovel16/comfyui_controlnet_aux) to use this.
Expand All @@ -34,6 +35,7 @@ This repository offers various extension nodes for ComfyUI. Nodes here have diff
`Color Preprocessor Provider (SEGS)`, `Inpaint Preprocessor Provider (SEGS)`, `Tile Preprocessor Provider (SEGS)`, `MeshGraphormer Depth Map Preprocessor Provider (SEGS)`
* `MediaPipeFaceMeshDetectorProvider`: This node provides `BBOX_DETECTOR` and `SEGM_DETECTOR` that can be used in Impact Pack's Detector using the `MediaPipe-FaceMesh Preprocessor` of ControlNet Auxiliary Preprocessors.


### A1111 Compatibility support - These nodes assists in replicating the creation of A1111 in ComfyUI exactly.
* `KSampler (Inspire)`: ComfyUI uses the CPU for generating random noise, while A1111 uses the GPU. One of the three factors that significantly impact reproducing A1111's results in ComfyUI can be addressed using `KSampler (Inspire)`.
* Other point #1 : Please make sure you haven't forgotten to include 'embedding:' in the embedding used in the prompt, like 'embedding:easynegative.'
Expand All @@ -48,6 +50,7 @@ This repository offers various extension nodes for ComfyUI. Nodes here have diff
* `variation_seed` and `variation_strength` - Initial noise generated by the seed is transformed to the shape of `variation_seed` by `variation_strength`. If `variation_strength` is 0, it only relies on the influence of the seed, and if `variation_strength` is 1.0, it is solely influenced by `variation_seed`.
* These parameters are used when you want to maintain the composition of an image generated by the seed but wish to introduce slight changes.


### Sampler nodes
* `KSampler Progress (Inspire)` - In KSampler, the sampling process generates latent batches. By using `Video Combine` node from [ComfyUI-VideoHelperSuite](https://github.com/Kosinkadink/ComfyUI-VideoHelperSuite), you can create a video from the progress.
* `Scheduled CFGGuider (Inspire)` - This is a CFGGuider that adjusts the schedule from from_cfg to to_cfg using linear, log, and exp methods.
Expand Down Expand Up @@ -154,8 +157,13 @@ This repository offers various extension nodes for ComfyUI. Nodes here have diff
* `IPAdapter Model Helper (Inspire)`: This provides presets that allow for easy loading of the IPAdapter related models. However, it is essential for the model's name to be accurate.
* You can download the appropriate model through ComfyUI-Manager.

### Util - Utilities
### List - Nodes for List processing
* `Float Range (Inspire)`: Create a float list that increases the value by `step` from `start` to `stop`. A list as large as the maximum limit is created, and when `ensure_end` is enabled, the last value of the list becomes the stop value.
* `Worklist To Item List (Inspire)`: The list in ComfyUI allows for repeated execution of a sub-workflow. This groups these repetitions (a.k.a. list) into a single ITEM_LIST output. ITEM_LIST can then be used in ForeachList.
* `▶Foreach List (Inspire)`: A starting node for performing iterative tasks by retrieving items one by one from the ITEM_LIST.\nGenerate a new intermediate_output using item and intermediate_output as inputs, then connect it to ForeachListEnd.\nNOTE:If initial_input is omitted, the first item in item_list is used as the initial value, and the processing starts from the second item in item_list.
* `Foreach List◀ (Inspire)`: A end node for performing iterative tasks by retrieving items one by one from the ITEM_LIST.\nNOTE:Directly connect the outputs of ForeachListBegin to 'flow_control' and 'remained_list'.

### Util - Utilities
* `ToIPAdapterPipe (Inspire)`, `FromIPAdapterPipe (Inspire)`: These nodes assists in conveniently using the bundled ipadapter_model, clip_vision, and model required for applying IPAdapter.
* `List Counter (Inspire)`: When each item in the list traverses through this node, it increments a counter by one, generating an integer value.
* `RGB Hex To HSV (Inspire)`: Convert an RGB hex string like `#FFD500` to HSV:
Expand All @@ -179,3 +187,5 @@ cubiq/[ComfyUI_IPAdapter_plus](https://github.com/cubiq/ComfyUI_IPAdapter_plus)
Davemane42/[ComfyUI_Dave_CustomNode](https://github.com/Davemane42/ComfyUI_Dave_CustomNode) - Original author of ConditioningStretch, ConditioningUpscale

BlenderNeko/[ComfyUI_Noise](https://github.com/BlenderNeko/ComfyUI_Noise) - slerp code for noise variation

BadCafeCode/[execution-inversion-demo-comfyui](https://github.com/BadCafeCode/execution-inversion-demo-comfyui) - reference loop implementation for ComfyUI
4 changes: 2 additions & 2 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
@author: Dr.Lt.Data
@title: Inspire Pack
@nickname: Inspire Pack
@description: This extension provides various nodes to support Lora Block Weight and the Impact Pack.
@description: This extension provides various nodes to support Lora Block Weight, Regional Nodes, Backend Cache, Prompt Utils, List Utils and the Impact Pack.
"""

import importlib

version_code = [1, 6, 1]
version_code = [1, 7]
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
print(f"### Loading: ComfyUI-Inspire-Pack ({version_str})")

Expand Down
167 changes: 165 additions & 2 deletions inspire/list_nodes.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from comfy_execution.graph_utils import GraphBuilder, is_link
from .libs.utils import any_typ

class FloatRange:
@classmethod
def INPUT_TYPES(s):
Expand All @@ -15,7 +18,7 @@ def INPUT_TYPES(s):

FUNCTION = "doit"

CATEGORY = "InspirePack/Util"
CATEGORY = "InspirePack/List"

def doit(self, start, stop, step, limit, ensure_end):
if start == stop or step == 0:
Expand Down Expand Up @@ -47,10 +50,170 @@ def doit(self, start, stop, step, limit, ensure_end):
return (res, )


class WorklistToItemList:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"item": (any_typ, ),
}
}

INPUT_IS_LIST = True

RETURN_TYPES = ("ITEM_LIST",)
RETURN_NAMES = ("item_list",)

FUNCTION = "doit"

DESCRIPTION = "The list in ComfyUI allows for repeated execution of a sub-workflow.\nThis groups these repetitions (a.k.a. list) into a single ITEM_LIST output.\nITEM_LIST can then be used in ForeachList."

CATEGORY = "InspirePack/List"

def doit(self, item):
return (item, )


# Loop nodes are implemented based on BadCafeCode's reference loop implementation
# https://github.com/BadCafeCode/execution-inversion-demo-comfyui/blob/main/flow_control.py

class ForeachListBegin:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"item_list": ("ITEM_LIST", {"tooltip": "ITEM_LIST containing items to be processed iteratively."}),
},
"optional": {
"initial_input": (any_typ, {"tooltip": "If initial_input is omitted, the first item in item_list is used as the initial value, and the processing starts from the second item in item_list."}),
}
}

RETURN_TYPES = ("FOREACH_LIST_CONTROL", "ITEM_LIST", any_typ, any_typ)
RETURN_NAMES = ("flow_control", "remained_list", "item", "intermediate_output")
OUTPUT_TOOLTIPS = (
"Pass ForeachListEnd as is to indicate the end of the iteration.",
"Output the ITEM_LIST containing the remaining items during the iteration, passing ForeachListEnd as is to indicate the end of the iteration.",
"Output the current item during the iteration.",
"Output the intermediate results during the iteration.")

FUNCTION = "doit"

DESCRIPTION = "A starting node for performing iterative tasks by retrieving items one by one from the ITEM_LIST.\nGenerate a new intermediate_output using item and intermediate_output as inputs, then connect it to ForeachListEnd.\nNOTE:If initial_input is omitted, the first item in item_list is used as the initial value, and the processing starts from the second item in item_list."

CATEGORY = "InspirePack/List"

def doit(self, item_list, initial_input=None):
if initial_input is None:
initial_input = item_list[0]
item_list = item_list[1:]

if len(item_list) > 0:
return ("stub", item_list[1:], item_list[0], initial_input)

return ("stub", [], None, initial_input)


class ForeachListEnd:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"flow_control": ("FOREACH_LIST_CONTROL", {"rawLink": True, "tooltip": "Directly connect the output of ForeachListBegin, the starting node of the iteration."}),
"remained_list": ("ITEM_LIST", {"tooltip":"Directly connect the output of ForeachListBegin, the starting node of the iteration."}),
"intermediate_output": (any_typ, {"tooltip":"Connect the intermediate outputs processed within the iteration here."}),
},
"hidden": {
"dynprompt": "DYNPROMPT",
"unique_id": "UNIQUE_ID",
}
}

RETURN_TYPES = (any_typ,)
RETURN_NAMES = ("result",)
OUTPUT_TOOLTIPS = ("This is the final output value.",)

FUNCTION = "doit"

DESCRIPTION = "A end node for performing iterative tasks by retrieving items one by one from the ITEM_LIST.\nNOTE:Directly connect the outputs of ForeachListBegin to 'flow_control' and 'remained_list'."

CATEGORY = "InspirePack/List"

def explore_dependencies(self, node_id, dynprompt, upstream):
node_info = dynprompt.get_node(node_id)
if "inputs" not in node_info:
return
for k, v in node_info["inputs"].items():
if is_link(v):
parent_id = v[0]
if parent_id not in upstream:
upstream[parent_id] = []
self.explore_dependencies(parent_id, dynprompt, upstream)
upstream[parent_id].append(node_id)

def collect_contained(self, node_id, upstream, contained):
if node_id not in upstream:
return
for child_id in upstream[node_id]:
if child_id not in contained:
contained[child_id] = True
self.collect_contained(child_id, upstream, contained)

def doit(self, flow_control, remained_list, intermediate_output, dynprompt, unique_id):
if len(remained_list) == 0:
return (intermediate_output,)

# We want to loop
this_node = dynprompt.get_node(unique_id)
upstream = {}

# Get the list of all nodes between the open and close nodes
self.explore_dependencies(unique_id, dynprompt, upstream)

contained = {}
open_node = flow_control[0]
self.collect_contained(open_node, upstream, contained)
contained[unique_id] = True
contained[open_node] = True

# We'll use the default prefix, but to avoid having node names grow exponentially in size,
# we'll use "Recurse" for the name of the recursively-generated copy of this node.
graph = GraphBuilder()
for node_id in contained:
original_node = dynprompt.get_node(node_id)
node = graph.node(original_node["class_type"], "Recurse" if node_id == unique_id else node_id)
node.set_override_display_id(node_id)

for node_id in contained:
original_node = dynprompt.get_node(node_id)
node = graph.lookup_node("Recurse" if node_id == unique_id else node_id)
for k, v in original_node["inputs"].items():
if is_link(v) and v[0] in contained:
parent = graph.lookup_node(v[0])
node.set_input(k, parent.out(v[1]))
else:
node.set_input(k, v)

new_open = graph.lookup_node(open_node)
new_open.set_input("item_list", remained_list)
new_open.set_input("initial_input", intermediate_output)

my_clone = graph.lookup_node("Recurse" )
result = (my_clone.out(0),)

return {
"result": result,
"expand": graph.finalize(),
}


NODE_CLASS_MAPPINGS = {
"FloatRange //Inspire": FloatRange,
"WorklistToItemList //Inspire": WorklistToItemList,
"ForeachListBegin //Inspire": ForeachListBegin,
"ForeachListEnd //Inspire": ForeachListEnd,
}

NODE_DISPLAY_NAME_MAPPINGS = {
"FloatRange //Inspire": "Float Range (Inspire)"
"FloatRange //Inspire": "Float Range (Inspire)",
"WorklistToItemList //Inspire": "Worklist To Item List (Inspire)",
"ForeachListBegin //Inspire": "▶Foreach List (Inspire)",
"ForeachListEnd //Inspire": "Foreach List◀ (Inspire)",
}
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
name = "comfyui-inspire-pack"
description = "This extension provides various nodes to support Lora Block Weight and the Impact Pack. Provides many easily applicable regional features and applications for Variation Seed."
version = "1.6.1"
description = "This extension provides various nodes to support Lora Block Weight, Regional Nodes, Backend Cache, Prompt Utils, List Utils, Noise(Seed) Utils, ... and the Impact Pack."
version = "1.7"
license = { file = "LICENSE" }
dependencies = ["matplotlib", "cachetools"]

Expand Down

0 comments on commit 71a6865

Please sign in to comment.