295 lines
12 KiB
Python
295 lines
12 KiB
Python
from modules.sd_samplers_kdiffusion import KDiffusionSampler
|
|
import modules.scripts as scripts
|
|
import scripts.cc_const as const
|
|
from modules import shared
|
|
import gradio as gr
|
|
import random
|
|
|
|
from scripts.cc_noise import *
|
|
|
|
from scripts.cc_xyz import xyz_support
|
|
|
|
from scripts.cc_scaling import apply_scaling
|
|
|
|
from scripts.cc_version import VERSION
|
|
|
|
from scripts.cc_colorpicker import create_colorpicker
|
|
from scripts.cc_colorpicker import horizontal_js
|
|
from scripts.cc_colorpicker import vertical_js
|
|
|
|
from scripts.cc_style import StyleManager
|
|
style_manager = StyleManager()
|
|
style_manager.load_styles()
|
|
|
|
og_callback = KDiffusionSampler.callback_state
|
|
|
|
class VectorscopeCC(scripts.Script):
|
|
def __init__(self):
|
|
self.xyzCache = {}
|
|
xyz_support(self.xyzCache)
|
|
|
|
def title(self):
|
|
return "Vectorscope CC"
|
|
|
|
def show(self, is_img2img):
|
|
return scripts.AlwaysVisible
|
|
|
|
def ui(self, is_img2img):
|
|
|
|
with gr.Accordion(f"Vectorscope CC {VERSION}", elem_id='vec-cc-' + ('img' if is_img2img else 'txt'), open=False):
|
|
|
|
with gr.Row():
|
|
enable = gr.Checkbox(label="Enable")
|
|
latent = gr.Checkbox(label="Alt. (Stronger Effects)")
|
|
|
|
with gr.Row():
|
|
bri = gr.Slider(label="Brightness", minimum=const.Brightness.minimum, maximum=const.Brightness.maximum, step=0.1, value=const.Brightness.default)
|
|
con = gr.Slider(label="Contrast", minimum=const.Contrast.minimum, maximum=const.Contrast.maximum, step=0.05, value=const.Contrast.default)
|
|
sat = gr.Slider(label="Saturation", minimum=const.Saturation.minimum, maximum=const.Saturation.maximum, step=0.05, value=const.Saturation.default)
|
|
|
|
with gr.Row():
|
|
with gr.Column():
|
|
r = gr.Slider(label="R", info='Cyan | Red', minimum=const.R.minimum, maximum=const.R.maximum, step=0.05, value=const.R.default, elem_id='cc-r-' + ('img' if is_img2img else 'txt'))
|
|
g = gr.Slider(label="G", info='Magenta | Green',minimum=const.G.minimum, maximum=const.G.maximum, step=0.05, value=const.G.default, elem_id='cc-g-' + ('img' if is_img2img else 'txt'))
|
|
b = gr.Slider(label="B", info='Yellow | Blue',minimum=const.B.minimum, maximum=const.B.maximum, step=0.05, value=const.B.default, elem_id='cc-b-' + ('img' if is_img2img else 'txt'))
|
|
|
|
create_colorpicker(is_img2img)
|
|
|
|
for component in [r, g, b]:
|
|
component.change(None, inputs=[r, g, b], outputs=[], _js=horizontal_js(is_img2img))
|
|
component.change(None, inputs=[r, g, b], outputs=[], _js=vertical_js(is_img2img))
|
|
|
|
with gr.Accordion("Styles", open=False):
|
|
|
|
with gr.Row():
|
|
style_choice = gr.Dropdown(label="Styles", choices=style_manager.list_style(), scale = 3)
|
|
apply_btn = gr.Button(value="Apply Style", elem_id='cc-apply-' + ('img' if is_img2img else 'txt'), scale = 2)
|
|
refresh_btn = gr.Button(value="Refresh Style", scale = 2)
|
|
with gr.Row():
|
|
style_name = gr.Textbox(label="Style Name", scale = 3)
|
|
save_btn = gr.Button(value="Save Style", elem_id='cc-save-' + ('img' if is_img2img else 'txt'), scale = 2)
|
|
delete_btn = gr.Button(value="Delete Style", scale = 2)
|
|
|
|
apply_btn.click(fn=style_manager.get_style, inputs=style_choice, outputs=[latent, bri, con, sat, r, g, b])
|
|
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.")
|
|
scaling = gr.Radio(["Flat", "Cos", "Sin", "1 - Cos", "1 - Sin"], label="Scaling Settings", value="Flat")
|
|
|
|
with gr.Row():
|
|
reset_btn = gr.Button(value="Reset")
|
|
self.register_reset(reset_btn, enable, latent, bri, con, sat, r, g, b, doHR, method, scaling)
|
|
|
|
random_btn = gr.Button(value="Randomize")
|
|
self.register_random(random_btn, bri, con, sat, r, g, b)
|
|
|
|
self.infotext_fields = []
|
|
self.paste_field_names = []
|
|
self.infotext_fields = [
|
|
(enable, lambda d: enable.update(value=("Vec CC Enabled" in d))),
|
|
(latent, "Vec CC Alt"),
|
|
(bri, "Vec CC Brightness"),
|
|
(con, "Vec CC Contrast"),
|
|
(sat, "Vec CC Saturation"),
|
|
(r, "Vec CC R"),
|
|
(g, "Vec CC G"),
|
|
(b, "Vec CC B"),
|
|
(method, "Vec CC Noise"),
|
|
(doHR, "Vec CC Proc HrF"),
|
|
(scaling, "Vec CC Scaling"),
|
|
]
|
|
|
|
for _, name in self.infotext_fields:
|
|
self.paste_field_names.append(name)
|
|
|
|
return [enable, latent, bri, con, sat, r, g, b, doHR, method, scaling]
|
|
|
|
def register_reset(self, reset_btn, enable, latent, bri, con, sat, r, g, b, doHR, method, scaling):
|
|
for component in [enable, latent, doHR]:
|
|
reset_btn.click(fn=lambda _: gr.update(value=False), outputs=component)
|
|
for component in [bri, con, r, g, b]:
|
|
reset_btn.click(fn=lambda _: gr.update(value=0.0), outputs=component)
|
|
for component in [sat]:
|
|
reset_btn.click(fn=lambda _: gr.update(value=const.Saturation.default), outputs=component)
|
|
|
|
reset_btn.click(fn=lambda _: gr.update(value='Straight Abs.'), outputs=method)
|
|
reset_btn.click(fn=lambda _: gr.update(value='Flat'), outputs=scaling)
|
|
|
|
def register_random(self, random_btn, bri, con, sat, r, g, b):
|
|
for component in [bri, con, r, g, b]:
|
|
random_btn.click(fn=lambda _: gr.update(value=round(random.uniform(const.R.minimum, const.R.maximum), 2)), outputs=component)
|
|
for component in [sat]:
|
|
random_btn.click(fn=lambda _: gr.update(value=round(random.uniform(const.Saturation.minimum, const.Saturation.maximum), 2)), outputs=component)
|
|
|
|
def parse_bool(self, string:str):
|
|
if string.lower() == "true":
|
|
return True
|
|
if string.lower() == "false":
|
|
return False
|
|
|
|
raise ValueError(f"Invalid Value: {string}")
|
|
|
|
def process(self, p, enable:bool, latent:bool, bri:float, con:float, sat:float, r:float, g:float, b:float, doHR:bool, method:str, scaling:str):
|
|
KDiffusionSampler.isHR_Pass = False
|
|
if 'Enable' in self.xyzCache.keys():
|
|
enable = self.parse_bool(self.xyzCache['Enable'])
|
|
|
|
if not enable:
|
|
if 'Enable' not in self.xyzCache.keys():
|
|
if len(self.xyzCache) > 0:
|
|
print('\n[X/Y/Z Plot] x [Vec.CC] Extension is not Enabled!\n')
|
|
self.xyzCache.clear()
|
|
|
|
KDiffusionSampler.callback_state = og_callback
|
|
return p
|
|
|
|
if 'Random' in self.xyzCache.keys():
|
|
if len(self.xyzCache) > 1:
|
|
print('\n[X/Y/Z Plot] x [Vec.CC] Randomize is Enabled!\nSome settings will not apply!\n')
|
|
else:
|
|
print('\n[X/Y/Z Plot] x [Vec.CC] Randomize is Enabled!\n')
|
|
|
|
cc_seed = None
|
|
|
|
for k, v in self.xyzCache.items():
|
|
match k:
|
|
case 'Alt':
|
|
latent = self.parse_bool(v)
|
|
case 'Brightness':
|
|
bri = v
|
|
case 'Contrast':
|
|
con = v
|
|
case 'Saturation':
|
|
sat = v
|
|
case 'R':
|
|
r = v
|
|
case 'G':
|
|
g = v
|
|
case 'B':
|
|
b = v
|
|
case 'DoHR':
|
|
doHR = self.parse_bool(v)
|
|
case 'Method':
|
|
method = v
|
|
case 'Scaling':
|
|
scaling = v
|
|
case 'Random':
|
|
cc_seed = v
|
|
|
|
self.xyzCache.clear()
|
|
|
|
if method == 'Disabled':
|
|
KDiffusionSampler.callback_state = og_callback
|
|
return p
|
|
|
|
steps = p.steps
|
|
if not hasattr(p, 'enable_hr') and hasattr(p, 'denoising_strength') and not shared.opts.img2img_fix_steps:
|
|
steps = int(steps * p.denoising_strength)
|
|
|
|
if cc_seed is not None:
|
|
random.seed(cc_seed)
|
|
|
|
bri = round(random.uniform(const.Contrast.minimum, const.Contrast.maximum), 2)
|
|
con = round(random.uniform(const.Contrast.minimum, const.Contrast.maximum), 2)
|
|
r = round(random.uniform(const.R.minimum, const.R.maximum), 2)
|
|
g = round(random.uniform(const.G.minimum, const.G.maximum), 2)
|
|
b = round(random.uniform(const.B.minimum, const.B.maximum), 2)
|
|
|
|
sat = round(random.uniform(const.Saturation.minimum, const.Saturation.maximum), 2)
|
|
|
|
print(f'-> Seed: {cc_seed}')
|
|
print(f'Brightness:\t{bri}')
|
|
print(f'Contrast:\t{con}')
|
|
print(f'Saturation:\t{sat}')
|
|
print(f'R:\t\t{r}')
|
|
print(f'G:\t\t{g}')
|
|
print(f'B:\t\t{b}\n')
|
|
|
|
if hasattr(shared.opts, 'cc_metadata') and shared.opts.cc_metadata is True:
|
|
p.extra_generation_params['Vec CC Enabled'] = enable
|
|
p.extra_generation_params['Vec CC Alt'] = latent
|
|
p.extra_generation_params['Vec CC Brightness'] = bri
|
|
p.extra_generation_params['Vec CC Contrast'] = con
|
|
p.extra_generation_params['Vec CC Saturation'] = sat
|
|
p.extra_generation_params['Vec CC R'] = r
|
|
p.extra_generation_params['Vec CC G'] = g
|
|
p.extra_generation_params['Vec CC B'] = b
|
|
p.extra_generation_params['Vec CC Noise'] = method
|
|
p.extra_generation_params['Vec CC Proc HrF'] = doHR
|
|
p.extra_generation_params['Vec CC Scaling'] = scaling
|
|
p.extra_generation_params['Vec CC Version'] = VERSION
|
|
|
|
bri /= steps
|
|
con /= steps
|
|
sat = pow(sat, 1.0 / steps)
|
|
r /= steps
|
|
g /= steps
|
|
b /= steps
|
|
|
|
mode = 'x' if latent else 'denoised'
|
|
|
|
# Channel 0: Dark | Bright
|
|
# Channel 1: Purple | Green
|
|
# Channel 2: Red | Cyan
|
|
# Channel 2: Violet | Yellow
|
|
|
|
def callback_state(self, d):
|
|
if not doHR and self.isHR_Pass is True:
|
|
return og_callback(self, d)
|
|
|
|
source = d[mode]
|
|
|
|
# "Straight", "Straight Abs.", "Cross", "Cross Abs.", "Ones", "N.Random", "U.Random", "Multi-Res", "Multi-Res Abs."
|
|
if 'Straight' in method:
|
|
target = d[mode]
|
|
elif 'Cross' in method:
|
|
cross = 'x' if mode == 'denoised' else 'denoised'
|
|
target = d[cross]
|
|
elif 'Multi-Res' in method:
|
|
target = multires_noise(d[mode], 'Abs' in method)
|
|
elif method == 'Ones':
|
|
target = ones(d[mode])
|
|
elif method == 'N.Random':
|
|
target = normal_noise(d[mode])
|
|
elif method == 'U.Random':
|
|
target = gaussian_noise(d[mode])
|
|
|
|
if 'Abs' in method:
|
|
target = to_abs(target)
|
|
|
|
batchSize = d[mode].size(0)
|
|
|
|
mods = apply_scaling(scaling, d["i"], steps, bri, con, sat, r, g, b)
|
|
|
|
for i in range(batchSize):
|
|
BRIGHTNESS = [source[i, 0], target[i, 0]]
|
|
R = [source[i, 2], target[i, 2]]
|
|
G = [source[i, 1], target[i, 1]]
|
|
B = [source[i, 3], target[i, 3]]
|
|
|
|
BRIGHTNESS[0] += BRIGHTNESS[1] * mods[0]
|
|
BRIGHTNESS[0] += get_delta(BRIGHTNESS[0]) * mods[1]
|
|
|
|
R[0] -= R[1] * mods[3]
|
|
G[0] += G[1] * mods[4]
|
|
B[0] -= B[1] * mods[5]
|
|
|
|
R[0] *= mods[2]
|
|
G[0] *= mods[2]
|
|
B[0] *= mods[2]
|
|
|
|
return og_callback(self, d)
|
|
|
|
KDiffusionSampler.callback_state = callback_state
|
|
return p
|
|
|
|
def before_hr(self, p, *args):
|
|
KDiffusionSampler.isHR_Pass = True
|
|
|
|
def postprocess(self, p, processed, *args):
|
|
KDiffusionSampler.callback_state = og_callback
|