improved
added license
This commit is contained in:
+21
@@ -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.
|
||||
@@ -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
@@ -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]
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user