added license
This commit is contained in:
Antonio Cordero Balcazar
2023-05-11 21:37:42 +02:00
parent 0b7589225a
commit f2159d3df8
3 changed files with 166 additions and 28 deletions
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2023 Antonio Cordero Balcazar
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.
+25 -3
View File
@@ -1,7 +1,29 @@
# Send To Negative
# Send To Negative for Stable Diffusion WebUI
## Purpose
This extension allows the use of negative weights in the prompt and moves them to the negative prompt. This allows useful tricks when using a wildcard extension since you can add negative parts from the positive prompt. It will process recursive weights (one inside of another), but won't calculate the multiplied weights.
This extension allows the marking of parts of the prompt and moves them to the negative prompt. This allows useful tricks when using a wildcard extension since you can add negative parts from choices made in the positive prompt.
The extension must be loaded after the wildcard extension. With the "Dynamic Prompts" extension this happens by default due to both extensions default folder names.
The extension must be loaded after the wildcard extension. With the "Dynamic Prompts" extension this happens by default due to default folder names for both extensions.
## Format
The format is like this:
```text
<!content!>
```
And an optional position in the negative prompt can be specified like this:
```text
<!!position!content!>
```
Where position can be:
* s: at the start
* e: at the end
* pN: at the position of the insertion point "<!!iN!!>" with N being 0-9
The default position is the start, and also if the insertion point is not found.
+120 -25
View File
@@ -3,11 +3,126 @@ from modules.processing import StableDiffusionProcessing
import modules.scripts as scripts
import re
VERSION = '0.1'
VERSION = "0.2"
# TODO : support marking in the negative prompt to move to the positive
# TODO : add separator and ignoreRepeats to ui
# TODO : add tests
# TODO : replace regex with proper parsing to detect recursion
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
strStart = "<!"
strEnd = "!>"
strParamStart = "!"
strParamEnd = "!"
"""
Default format: <!content!> or <!!x!content!>
with x = s (start position) e (end position) p (specified position) i (insertion point)
both p and i have to be followed by a number 0-9
the insertion point does not accept content
"""
escapeSequence = r"(?<!\\)"
ignoreRepeats = True
separator = ", "
insertionPointTags = [
(strStart + strParamStart + "i" + str(x) + strParamEnd + strEnd) for x in range(10)
]
find = (
"("
+ escapeSequence
+ re.escape(strStart)
+ "(?:"
+ re.escape(strParamStart)
+ "([se]|(?:[pi][0-9]))"
+ re.escape(strParamEnd)
+ ")?(.*?)"
+ escapeSequence
+ re.escape(strEnd)
+ ")"
)
regex = re.compile(find, re.S)
def processPrompts(original_prompt, original_negative_prompt):
"""
Extract from the prompt the marked parts and add them to the negative prompt
"""
try:
prompt = original_prompt
negative_prompt = original_negative_prompt
insertionPointPositions = [negative_prompt.find(x) for x in insertionPointTags]
alreadyProcessed = []
addAtStart = []
addAtEnd = []
addAtInsertionPoint = [[] for x in range(10)]
matches = regex.findall(prompt)
for match in matches:
position = match[1] or "s"
content = match[2]
if len(content) > 0:
if content not in alreadyProcessed:
if ignoreRepeats:
alreadyProcessed.append(content)
logger.debug("Processing content: %s", content)
if position is "e":
addAtEnd.append(content)
elif position.startswith("p"):
n = int(position[1])
addAtInsertionPoint[n].append(content)
else: # position is "s" or invalid
addAtStart.append(content)
else:
logger.warn("Ignoring repeated content: %s", content)
prompt = prompt.replace(match[0], "")
# Add content to insertion points
for n in range(10):
if insertionPointPositions[n] >= 0:
ipp = insertionPointPositions[n]
ipl = len(insertionPointTags[n])
if negative_prompt[ipp - len(separator) : ipp] == separator:
ipp -= len(separator) # adjust for existing start separator
ipl += len(separator)
addAtInsertionPoint[n].insert(0, negative_prompt[:ipp])
if negative_prompt[ipp + ipl : ipp + ipl + len(separator)] == separator:
ipl += len(separator) # adjust for existing end separator
endPart = negative_prompt[ipp + ipl :]
if len(endPart) > 0:
addAtInsertionPoint[n].append(endPart)
negative_prompt = separator.join(addAtInsertionPoint[n])
else:
ipp = 0
if negative_prompt.startswith(separator):
ipp = len(separator)
addAtInsertionPoint[n].append(negative_prompt[ipp:])
negative_prompt = separator.join(addAtInsertionPoint[n])
# Add content to start
if len(addAtStart) > 0:
if len(negative_prompt) > 0:
ipp = 0
if negative_prompt.startswith(separator):
ipp = len(separator) # adjust for existing end separator
addAtStart.append(negative_prompt[ipp:])
negative_prompt = separator.join(addAtStart)
# Add content to end
if len(addAtEnd) > 0:
if len(negative_prompt) > 0:
ipl = len(negative_prompt)
if negative_prompt.endswith(separator):
ipl -= len(separator) # adjust for existing start separator
addAtEnd.insert(0, negative_prompt[:ipl])
negative_prompt = separator.join(addAtEnd)
return prompt, negative_prompt
except Exception as e:
logger.exception(e)
return original_prompt, original_negative_prompt
class Script(scripts.Script):
def __init__(self):
pass
@@ -19,27 +134,7 @@ class Script(scripts.Script):
return scripts.AlwaysVisible
def process(self, p: StableDiffusionProcessing, *args, **kwargs):
try:
find = r"(((?<!\\)\([^():]+:)-(\d+(?:\.\d+)?(?<!\\)\)))" # format (xxxx:-nnn) TODO : support [] ? accumulate recursive weights?
regex = re.compile(find, re.S)
for i in range(len(p.all_prompts)):
# Extract from the prompt the terms with negative weights and add them to the negative prompt
current_prompt = p.all_prompts[i]
current_negative_prompt = p.all_negative_prompts[i]
already_applied = []
while True:
matches = regex.findall(current_prompt)
if len(matches) == 0:
break
for match in matches:
if match[0] not in already_applied:
logger.debug("Found negative term: %s", match[0])
current_negative_prompt = match[1] + match[2] + " " + current_negative_prompt
current_prompt = current_prompt.replace(match[0], "")
already_applied.append(match[0])
else:
logger.debug("Found repeated negative term: %s", match[0])
p.all_prompts[i] = current_prompt
p.all_negative_prompts[i] = current_negative_prompt
except Exception as e:
logger.exception(e)
for i in range(len(p.all_prompts)):
p.all_prompts[i], p.all_negative_prompts[i] = processPrompts(
p.all_prompts[i], p.all_negative_prompts[i]
)