This commit is contained in:
DigitalIO
2023-10-10 17:45:02 -04:00
commit 0cc39784ca
6 changed files with 247 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
.idea/*
__pycache__/
+16
View File
@@ -0,0 +1,16 @@
The MIT License (MIT)
Copyright © 2023 Michael Wolfe (DigitalIO)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the “Software”), to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+50
View File
@@ -0,0 +1,50 @@
# ComfyUI Stable Wildcards
## Overview
ComfyUI has an amazing feature that saves the workflow to reproduce an image in the image itself. In theory, you can
import the workflow and reproduce the exact image. Unfortunately, this does not work with wildcards. The goal of this
node is to implement wildcard support using a seed to stabilize the output to allow greater reproducibility.
## Use
### Wildcards
Wildcards are supported via brackets and pipes.
Example:
`A painting of a {boat|fish} in the {sea|lake}`
The first pair of words will randomly select `boat` or `fish`, and the second will either be `sea` or `lake`.
The following is a list of possible random output using the above prompt:
* A painting of a boat in the sea
* A painting of a boat in the lake
* A painting of a fish in the sea
* A painting of a fish in the lake
_Note:_ Everything including the brackets are replaced with the final word.
Include spaces around your ` {} ` to prevent words from running together, or use it
to modify words.
Ex: `{cat|dog}fish` outputs `catfish` or `dogfish`.
### Node
The node requires three inputs
1) a string - Your prompt
2) a seed - This is what makes your prompt reproducible
3) a version - Allows for future changes to not break backwards compatibility
The node has one output
1) a string - Your final prompt with wildcards processed
### Bonus Node
An additional node is included that disabled the default dynamicPrompt behavior of multiline inputs.
## Opinions
Below are *my opinions* and as such will probably not be changed.
### CLIP
Many other implementations combine this feature with the CLIP node. I chose not to do this to keep the wildcard code and
CLIP implementation separate and more re-usable.
### Wildcard Files
Probably won't be implemented. Files are external to the workflow, so there is no guarantee that (1) the file exists on
another system and (2) the file content has not been modified. File wildcards are not an appropriate approach if you are
looking to create repeatable output.
+46
View File
@@ -0,0 +1,46 @@
# The MIT License (MIT)
#
# Copyright © 2023 Michael Wolfe (DigitalIO)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
# documentation files (the “Software”), to deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of
# the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
# BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
from .swnodes.StableWildcard import StableWildcard
from .swnodes.NonDynamicString import NondynamicString
# Nodes passed to ComfyUI.
# Node names must be unique
NODE_MAP = [
{
"name" : "Stable Wildcards",
"class": StableWildcard,
"disp" : "Stable Wildcard Output"
},
{
"name" : "NonDynamic String",
"class": NondynamicString,
"disp" : "NonDynamic String"
}
]
# Create the expected node maps for ComfyUI
NODE_CLASS_MAPPINGS = {}
NODE_DISPLAY_NAME_MAPPINGS = {}
for x in NODE_MAP:
x['class'].CATEGORY = 'StableWildcards'
NODE_CLASS_MAPPINGS[x['name']] = x['class']
NODE_DISPLAY_NAME_MAPPINGS[x['name']] = x['disp']
+47
View File
@@ -0,0 +1,47 @@
# The MIT License (MIT)
#
# Copyright © 2023 Michael Wolfe (DigitalIO)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
# documentation files (the “Software”), to deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of
# the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
class NondynamicString:
"""
ComfyUI Custom Node
A helper node that disable dynamic prompts
"""
def __init__(self):
pass
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("String",)
OUTPUT_NODE = False
FUNCTION = "execute"
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"string": ('STRING', {'default': '', 'multiline': True, 'dynamicPrompts': False}),
},
"optional": {}
}
def execute(self, string, **kwargs):
"""
Do nothing
"""
return string,
+86
View File
@@ -0,0 +1,86 @@
# The MIT License (MIT)
#
# Copyright © 2023 Michael Wolfe (DigitalIO)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
# documentation files (the “Software”), to deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of
# the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import sys
from random import Random
import re
class StableWildcard:
"""
ComfyUI Custom Node
Implements wildcards using a stable seed to make workflows reproducible after creation
"""
def __init__(self):
pass
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("Prompt String",)
OUTPUT_NODE = False
FUNCTION = "execute"
# A reusable wildcard pattern matches any string with brackets {}
# but will not select over other wildcards.
WILDCARD_PATTERN = re.compile('{[^{}]+}')
@classmethod
def INPUT_TYPES(s):
return {
"required": {
# Without dynamicPrompts set to false ComfyUI will randomize our prompt before we get to it!
"prompt": ('STRING', {'default': '', 'multiline': True, 'dynamicPrompts': False}),
"seed" : ('INT', {'default': 0, 'min': 0, 'max': sys.maxsize}),
# Currently unused, but if a breaking change must be introduced
# it can be used to provide backwards compatibility.
"version": ([1], {'default': 1})
},
"optional": {}
}
def execute(self, prompt, seed, **kwargs):
"""
Process wildcards using a seed to produce stable output.
To achieve stable results a new random object is created
using the given seed.
"""
rng = Random(int(seed))
# Search & replace matches
match = self.WILDCARD_PATTERN.search(prompt)
while match:
# Get the options - Remove the {}, split by | character
# Because the search pattern requires at least one character,
# ops is guaranteed to have at least one option
ops = match.group()[1:-1].split('|')
# Pick a random option.
pick = ops[rng.randint(0, len(ops)-1)]
# Replace the match and update the string
# Limit one match incase there are repeated wildcards
prompt = prompt.replace(match.group(), pick, 1)
# Search for more wildcards
match = self.WILDCARD_PATTERN.search(prompt)
print('\033[96m Stable Wildcard: ({}) "{}"\033[0m'.format(seed, prompt))
return prompt,