stable-diffusion-webui/modules/sd_emphasis.py

71 lines
2.1 KiB
Python

from __future__ import annotations
import torch
class Emphasis:
"""Emphasis class decides how to death with (emphasized:1.1) text in prompts"""
name: str = "Base"
description: str = ""
tokens: list[list[int]]
"""tokens from the chunk of the prompt"""
multipliers: torch.Tensor
"""tensor with multipliers, once for each token"""
z: torch.Tensor
"""output of cond transformers network (CLIP)"""
def after_transformers(self):
"""Called after cond transformers network has processed the chunk of the prompt; this function should modify self.z to apply the emphasis"""
pass
class EmphasisNone(Emphasis):
name = "None"
description = "disable the mechanism entirely and treat (:.1.1) as literal characters"
class EmphasisIgnore(Emphasis):
name = "Ignore"
description = "treat all empasised words as if they have no emphasis"
class EmphasisOriginal(Emphasis):
name = "Original"
description = "the original emphasis implementation"
def after_transformers(self):
original_mean = self.z.mean()
self.z = self.z * self.multipliers.reshape(self.multipliers.shape + (1,)).expand(self.z.shape)
# restoring original mean is likely not correct, but it seems to work well to prevent artifacts that happen otherwise
new_mean = self.z.mean()
self.z = self.z * (original_mean / new_mean)
class EmphasisOriginalNoNorm(EmphasisOriginal):
name = "No norm"
description = "same as original, but without normalization (seems to work better for SDXL)"
def after_transformers(self):
self.z = self.z * self.multipliers.reshape(self.multipliers.shape + (1,)).expand(self.z.shape)
def get_current_option(emphasis_option_name):
return next(iter([x for x in options if x.name == emphasis_option_name]), EmphasisOriginal)
def get_options_descriptions():
return ", ".join(f"{x.name}: {x.description}" for x in options)
options = [
EmphasisNone,
EmphasisIgnore,
EmphasisOriginal,
EmphasisOriginalNoNorm,
]