Implement Styles Preset!

- Implement #7
- Finally separate logics into their own scripts
- Version bump
- Lint code
pull/8/head
Haoming 2023-07-06 13:30:02 +08:00
parent 6baa27b97c
commit b0da134bf5
5 changed files with 169 additions and 59 deletions

View File

@ -53,6 +53,13 @@ refer to the parameters and sample images below and play around with the values.
- **Reset:** Revert all settings to the default values
- **Randomize:** Randomize `Brightness`, `Contrast`, `Saturation`, `R`, `G`, `B`
#### Styles Preset
- Use the `Dropdown` to select a Style to apply
- To save a Style, enter a name in the `Textbox` then click **Save Style**
- To delete a Style, enter the name in the `Textbox` then click **Delete Style**
- *Deleted Style is still in the `styles.json` in case you wish to retrieve it*
- Click **Manual Refresh** to update the `Dropdown` the if you edited the `styles.json` directly
#### Advanced Settings
- **Process Hires. fix:** By default, this Extension only functions during the **txt2img** phase, so that **Hires. fix** may "fix" the artifacts introduced during **txt2img**. Enable this to process **Hires. fix** phase too.
@ -128,6 +135,7 @@ refer to the parameters and sample images below and play around with the values.
- [X] Add Support for **X/Y/Z Plot**
- [X] Implement different Noise functions
- [X] Add Randomize functions
- [X] Styles Preset
- [ ] Implement a better scaling algorithm
- [ ] Fix the Brightness issues
- [ ] Add Gradient feature

View File

@ -1,64 +1,19 @@
from modules.sd_samplers_kdiffusion import KDiffusionSampler
import modules.scripts as scripts
from modules import devices
from modules import shared
import gradio as gr
import random
import torch
import json
VERSION = 'v1.2.0'
from scripts.cc_version import *
from scripts.cc_noise import *
from scripts.cc_style import StyleManager
def clean_outdated(EXT_NAME:str):
with open(scripts.basedir() + '/' + 'ui-config.json', 'r') as json_file:
configs = json.loads(json_file.read())
cleaned_configs = {key: value for key, value in configs.items() if EXT_NAME not in key}
with open(scripts.basedir() + '/' + 'ui-config.json', 'w') as json_file:
json.dump(cleaned_configs, json_file)
def ones(latent):
return torch.ones_like(latent)
def gaussian_noise(latent):
return torch.rand_like(latent)
def normal_noise(latent):
return torch.randn_like(latent)
def multires_noise(latent, use_zero:bool, iterations=8, discount=0.4):
"""
Reference: https://wandb.ai/johnowhitaker/multires_noise/reports/Multi-Resolution-Noise-for-Diffusion-Model-Training--VmlldzozNjYyOTU2
Credit: Kohya_SS
"""
noise = torch.zeros_like(latent) if use_zero else ones(latent)
batchSize = noise.size(0)
height = noise.size(2)
width = noise.size(3)
device = devices.get_optimal_device()
upsampler = torch.nn.Upsample(size=(height, width), mode="bilinear").to(device)
for b in range(batchSize):
for i in range(iterations):
r = random.random() * 2 + 2
wn = max(1, int(width / (r**i)))
hn = max(1, int(height / (r**i)))
for c in range(4):
noise[b, c] += upsampler(torch.randn(1, 1, hn, wn).to(device))[0, 0] * discount**i
if wn == 1 or hn == 1:
break
return noise / noise.std()
style_manager = StyleManager()
class VectorscopeCC(scripts.Script):
def __init__(self):
clean_outdated('cc.py')
style_manager.load_styles()
global og_callback
og_callback = KDiffusionSampler.callback_state
@ -73,7 +28,7 @@ class VectorscopeCC(scripts.Script):
return _
def choices_bool():
return ["True", "False"]
return ["False", "True"]
def choices_method():
return ["Disabled", "Straight", "Straight Abs.", "Cross", "Cross Abs.", "Ones", "N.Random", "U.Random", "Multi-Res", "Multi-Res Abs."]
@ -107,6 +62,7 @@ class VectorscopeCC(scripts.Script):
return scripts.AlwaysVisible
def ui(self, is_img2img):
with gr.Accordion(f"Vectorscope CC {VERSION}", open=False):
with gr.Row():
@ -124,6 +80,25 @@ class VectorscopeCC(scripts.Script):
g = gr.Slider(label="G", info='Magenta | Green',minimum=-2.5, maximum=2.5, step=0.05, value=0.0)
b = gr.Slider(label="B", info='Yellow | Blue',minimum=-2.5, maximum=2.5, step=0.05, value=0.0)
with gr.Accordion("Styles", open=False):
with gr.Row():
with gr.Column():
style_choice = gr.Dropdown(label="Apply Style", choices=style_manager.list_style())
style_name = gr.Textbox(label="Style Name")
style_choice.input(fn=style_manager.get_style, inputs=style_choice, outputs=[latent, bri, con, sat, r, g, b])
with gr.Column(variant="compact"):
save_btn = gr.Button(value="Save Style")
delete_btn = gr.Button(value="Delete Style")
refresh_btn = gr.Button(value="Manual Refresh")
save_btn.click(fn=lambda *args: gr.update(choices=style_manager.save_style(*args)), inputs=[style_name, latent, bri, con, sat, r, g, b], outputs=style_choice)
delete_btn.click(fn=lambda name: gr.update(choices=style_manager.delete_style(name)), inputs=style_name, outputs=style_choice)
refresh_btn.click(fn=lambda _: gr.update(choices=style_manager.list_style()), outputs=style_choice)
with gr.Accordion("Advanced Settings", open=False):
doHR = gr.Checkbox(label="Process Hires. fix")
method = gr.Radio(["Straight", "Straight Abs.", "Cross", "Cross Abs.", "Ones", "N.Random", "U.Random", "Multi-Res", "Multi-Res Abs."], label="Noise Settings", value="Straight Abs.")
@ -139,19 +114,19 @@ class VectorscopeCC(scripts.Script):
def register_reset(self, reset_btn, enable, latent, bri, con, sat, early, r, g, b, doHR, method):
for component in [enable, latent, doHR]:
reset_btn.click(fn=lambda x: gr.update(value=False), outputs=component)
reset_btn.click(fn=lambda _: gr.update(value=False), outputs=component)
for component in [early, bri, r, g, b]:
reset_btn.click(fn=lambda x: gr.update(value=0.0), outputs=component)
reset_btn.click(fn=lambda _: gr.update(value=0.0), outputs=component)
for component in [con, sat]:
reset_btn.click(fn=lambda x: gr.update(value=1.0), outputs=component)
reset_btn.click(fn=lambda _: gr.update(value=1.0), outputs=component)
reset_btn.click(fn=lambda x: gr.update(value='Straight Abs.'), outputs=method)
reset_btn.click(fn=lambda _: gr.update(value='Straight Abs.'), outputs=method)
def register_random(self, random_btn, bri, con, sat, r, g, b):
for component in [bri, r, g, b]:
random_btn.click(fn=lambda x: gr.update(value=round(random.uniform(-2.5, 2.5), 2)), outputs=component)
random_btn.click(fn=lambda _: gr.update(value=round(random.uniform(-2.5, 2.5), 2)), outputs=component)
for component in [con, sat]:
random_btn.click(fn=lambda x: gr.update(value=round(random.uniform(0.5, 1.5), 2)), outputs=component)
random_btn.click(fn=lambda _: gr.update(value=round(random.uniform(0.5, 1.5), 2)), outputs=component)
def parse_bool(self, string:str):
if string.lower() == "true":
@ -218,7 +193,7 @@ class VectorscopeCC(scripts.Script):
stop = steps * (1.0 - early)
if not cc_seed == None:
if cc_seed is not None:
random.seed(cc_seed)
bri = round(random.uniform(-2.5, 2.5), 2)
@ -292,7 +267,7 @@ class VectorscopeCC(scripts.Script):
target = gaussian_noise(d[mode])
if 'Abs' in method:
target = torch.abs(target)
abs_cvt(target)
batchSize = d[mode].size(0)

43
scripts/cc_noise.py Normal file
View File

@ -0,0 +1,43 @@
from modules import devices
import torch
def abs_cvt(latent):
return torch.abs(latent)
def ones(latent):
return torch.ones_like(latent)
def gaussian_noise(latent):
return torch.rand_like(latent)
def normal_noise(latent):
return torch.randn_like(latent)
def multires_noise(latent, use_zero:bool, iterations=8, discount=0.4):
"""
Reference: https://wandb.ai/johnowhitaker/multires_noise/reports/Multi-Resolution-Noise-for-Diffusion-Model-Training--VmlldzozNjYyOTU2
Credit: Kohya_SS
"""
noise = torch.zeros_like(latent) if use_zero else ones(latent)
batchSize = noise.size(0)
height = noise.size(2)
width = noise.size(3)
device = devices.get_optimal_device()
upsampler = torch.nn.Upsample(size=(height, width), mode="bilinear").to(device)
for b in range(batchSize):
for i in range(iterations):
r = torch.rand(1).item() * 2 + 2
wn = max(1, int(width / (r**i)))
hn = max(1, int(height / (r**i)))
for c in range(4):
noise[b, c] += upsampler(torch.randn(1, 1, hn, wn).to(device))[0, 0] * discount**i
if wn == 1 or hn == 1:
break
return noise / noise.std()

71
scripts/cc_style.py Normal file
View File

@ -0,0 +1,71 @@
import modules.scripts as scripts
import json
STYLE_FILE = scripts.basedir() + '/' + 'styles.json'
EMPTY_STYLE = {
'styles' : {},
'deleted' : {}
}
class StyleManager():
def __init__(self):
self.STYLE_SHEET = None
def load_styles(self):
if self.STYLE_SHEET is not None:
return
try:
with open(STYLE_FILE, 'r') as json_file:
self.STYLE_SHEET = json.loads(json_file.read())
print('[Vec. CC] Style Sheet Loaded...')
except IOError:
with open(STYLE_FILE, 'w+') as json_file:
self.STYLE_SHEET = EMPTY_STYLE
json_file.write(json.dumps(self.STYLE_SHEET))
print('[Vec. CC] Creating Empty Style Sheet...')
def list_style(self):
return list(self.STYLE_SHEET['styles'].keys())
def get_style(self, style_name):
style = self.STYLE_SHEET['styles'][style_name]
return style['alt'], style['brightness'], style['contrast'], style['saturation'], style['rgb'][0], style['rgb'][1], style['rgb'][2]
def save_style(self, style_name, latent, bri, con, sat, r, g, b):
if style_name in self.STYLE_SHEET['styles'].keys():
print(f'\n[Warning] Duplicated Style Name "{style_name}" Detected! Values are not saved!\n')
return self.list_style()
style = {
'alt' : latent,
'brightness' : bri,
'contrast' : con,
'saturation' : sat,
'rgb' : [r, g, b]
}
self.STYLE_SHEET['styles'].update({style_name:style})
with open(STYLE_FILE, 'w+') as json_file:
json_file.write(json.dumps(self.STYLE_SHEET))
print(f'\nStyle of Name "{style_name}" Saved!\n')
return self.list_style()
def delete_style(self, style_name):
try:
style = self.STYLE_SHEET['styles'][style_name]
del self.STYLE_SHEET['styles'][style_name]
except KeyError:
print(f'\n[Warning] No Style of Name "{style_name}" Found!\n')
return self.list_style()
self.STYLE_SHEET['deleted'].update({style_name:style})
with open(STYLE_FILE, 'w+') as json_file:
json_file.write(json.dumps(self.STYLE_SHEET))
print(f'\nStyle of Name "{style_name}" Deleted!\n')
return self.list_style()

13
scripts/cc_version.py Normal file
View File

@ -0,0 +1,13 @@
import modules.scripts as scripts
import json
VERSION = 'v1.3.0'
def clean_outdated(EXT_NAME:str):
with open(scripts.basedir() + '/' + 'ui-config.json', 'r') as json_file:
configs = json.loads(json_file.read())
cleaned_configs = {key: value for key, value in configs.items() if EXT_NAME not in key}
with open(scripts.basedir() + '/' + 'ui-config.json', 'w') as json_file:
json.dump(cleaned_configs, json_file)