Init
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
.idea/*
|
||||
__pycache__/
|
||||
@@ -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.
|
||||
@@ -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
@@ -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']
|
||||
@@ -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,
|
||||
@@ -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,
|
||||
Reference in New Issue
Block a user