Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
automatic1111
GitHub Repository: automatic1111/stable-diffusion-webui
Path: blob/master/scripts/postprocessing_upscale.py
3055 views
1
import re
2
3
from PIL import Image
4
import numpy as np
5
6
from modules import scripts_postprocessing, shared
7
import gradio as gr
8
9
from modules.ui_components import FormRow, ToolButton, InputAccordion
10
from modules.ui import switch_values_symbol
11
12
upscale_cache = {}
13
14
15
def limit_size_by_one_dimention(w, h, limit):
16
if h > w and h > limit:
17
w = limit * w // h
18
h = limit
19
elif w > limit:
20
h = limit * h // w
21
w = limit
22
23
return int(w), int(h)
24
25
26
class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing):
27
name = "Upscale"
28
order = 1000
29
30
def ui(self):
31
selected_tab = gr.Number(value=0, visible=False)
32
33
with InputAccordion(True, label="Upscale", elem_id="extras_upscale") as upscale_enabled:
34
with FormRow():
35
extras_upscaler_1 = gr.Dropdown(label='Upscaler 1', elem_id="extras_upscaler_1", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name)
36
37
with FormRow():
38
extras_upscaler_2 = gr.Dropdown(label='Upscaler 2', elem_id="extras_upscaler_2", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name)
39
extras_upscaler_2_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Upscaler 2 visibility", value=0.0, elem_id="extras_upscaler_2_visibility")
40
41
with FormRow():
42
with gr.Tabs(elem_id="extras_resize_mode"):
43
with gr.TabItem('Scale by', elem_id="extras_scale_by_tab") as tab_scale_by:
44
with gr.Row():
45
with gr.Column(scale=4):
46
upscaling_resize = gr.Slider(minimum=1.0, maximum=8.0, step=0.05, label="Resize", value=4, elem_id="extras_upscaling_resize")
47
with gr.Column(scale=1, min_width=160):
48
max_side_length = gr.Number(label="Max side length", value=0, elem_id="extras_upscale_max_side_length", tooltip="If any of two sides of the image ends up larger than specified, will downscale it to fit. 0 = no limit.", min_width=160, step=8, minimum=0)
49
50
with gr.TabItem('Scale to', elem_id="extras_scale_to_tab") as tab_scale_to:
51
with FormRow():
52
with gr.Column(elem_id="upscaling_column_size", scale=4):
53
upscaling_resize_w = gr.Slider(minimum=64, maximum=8192, step=8, label="Width", value=512, elem_id="extras_upscaling_resize_w")
54
upscaling_resize_h = gr.Slider(minimum=64, maximum=8192, step=8, label="Height", value=512, elem_id="extras_upscaling_resize_h")
55
with gr.Column(elem_id="upscaling_dimensions_row", scale=1, elem_classes="dimensions-tools"):
56
upscaling_res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="upscaling_res_switch_btn", tooltip="Switch width/height")
57
upscaling_crop = gr.Checkbox(label='Crop to fit', value=True, elem_id="extras_upscaling_crop")
58
59
def on_selected_upscale_method(upscale_method):
60
if not shared.opts.set_scale_by_when_changing_upscaler:
61
return gr.update()
62
63
match = re.search(r'(\d)[xX]|[xX](\d)', upscale_method)
64
if not match:
65
return gr.update()
66
67
return gr.update(value=int(match.group(1) or match.group(2)))
68
69
upscaling_res_switch_btn.click(lambda w, h: (h, w), inputs=[upscaling_resize_w, upscaling_resize_h], outputs=[upscaling_resize_w, upscaling_resize_h], show_progress=False)
70
tab_scale_by.select(fn=lambda: 0, inputs=[], outputs=[selected_tab])
71
tab_scale_to.select(fn=lambda: 1, inputs=[], outputs=[selected_tab])
72
73
extras_upscaler_1.change(on_selected_upscale_method, inputs=[extras_upscaler_1], outputs=[upscaling_resize], show_progress="hidden")
74
75
return {
76
"upscale_enabled": upscale_enabled,
77
"upscale_mode": selected_tab,
78
"upscale_by": upscaling_resize,
79
"max_side_length": max_side_length,
80
"upscale_to_width": upscaling_resize_w,
81
"upscale_to_height": upscaling_resize_h,
82
"upscale_crop": upscaling_crop,
83
"upscaler_1_name": extras_upscaler_1,
84
"upscaler_2_name": extras_upscaler_2,
85
"upscaler_2_visibility": extras_upscaler_2_visibility,
86
}
87
88
def upscale(self, image, info, upscaler, upscale_mode, upscale_by, max_side_length, upscale_to_width, upscale_to_height, upscale_crop):
89
if upscale_mode == 1:
90
upscale_by = max(upscale_to_width/image.width, upscale_to_height/image.height)
91
info["Postprocess upscale to"] = f"{upscale_to_width}x{upscale_to_height}"
92
else:
93
info["Postprocess upscale by"] = upscale_by
94
if max_side_length != 0 and max(*image.size)*upscale_by > max_side_length:
95
upscale_mode = 1
96
upscale_crop = False
97
upscale_to_width, upscale_to_height = limit_size_by_one_dimention(image.width*upscale_by, image.height*upscale_by, max_side_length)
98
upscale_by = max(upscale_to_width/image.width, upscale_to_height/image.height)
99
info["Max side length"] = max_side_length
100
101
cache_key = (hash(np.array(image.getdata()).tobytes()), upscaler.name, upscale_mode, upscale_by, upscale_to_width, upscale_to_height, upscale_crop)
102
cached_image = upscale_cache.pop(cache_key, None)
103
104
if cached_image is not None:
105
image = cached_image
106
else:
107
image = upscaler.scaler.upscale(image, upscale_by, upscaler.data_path)
108
109
upscale_cache[cache_key] = image
110
if len(upscale_cache) > shared.opts.upscaling_max_images_in_cache:
111
upscale_cache.pop(next(iter(upscale_cache), None), None)
112
113
if upscale_mode == 1 and upscale_crop:
114
cropped = Image.new("RGB", (upscale_to_width, upscale_to_height))
115
cropped.paste(image, box=(upscale_to_width // 2 - image.width // 2, upscale_to_height // 2 - image.height // 2))
116
image = cropped
117
info["Postprocess crop to"] = f"{image.width}x{image.height}"
118
119
return image
120
121
def process_firstpass(self, pp: scripts_postprocessing.PostprocessedImage, upscale_enabled=True, upscale_mode=1, upscale_by=2.0, max_side_length=0, upscale_to_width=None, upscale_to_height=None, upscale_crop=False, upscaler_1_name=None, upscaler_2_name=None, upscaler_2_visibility=0.0):
122
if upscale_mode == 1:
123
pp.shared.target_width = upscale_to_width
124
pp.shared.target_height = upscale_to_height
125
else:
126
pp.shared.target_width = int(pp.image.width * upscale_by)
127
pp.shared.target_height = int(pp.image.height * upscale_by)
128
129
pp.shared.target_width, pp.shared.target_height = limit_size_by_one_dimention(pp.shared.target_width, pp.shared.target_height, max_side_length)
130
131
def process(self, pp: scripts_postprocessing.PostprocessedImage, upscale_enabled=True, upscale_mode=1, upscale_by=2.0, max_side_length=0, upscale_to_width=None, upscale_to_height=None, upscale_crop=False, upscaler_1_name=None, upscaler_2_name=None, upscaler_2_visibility=0.0):
132
if not upscale_enabled:
133
return
134
135
upscaler_1_name = upscaler_1_name
136
if upscaler_1_name == "None":
137
upscaler_1_name = None
138
139
upscaler1 = next(iter([x for x in shared.sd_upscalers if x.name == upscaler_1_name]), None)
140
assert upscaler1 or (upscaler_1_name is None), f'could not find upscaler named {upscaler_1_name}'
141
142
if not upscaler1:
143
return
144
145
upscaler_2_name = upscaler_2_name
146
if upscaler_2_name == "None":
147
upscaler_2_name = None
148
149
upscaler2 = next(iter([x for x in shared.sd_upscalers if x.name == upscaler_2_name and x.name != "None"]), None)
150
assert upscaler2 or (upscaler_2_name is None), f'could not find upscaler named {upscaler_2_name}'
151
152
upscaled_image = self.upscale(pp.image, pp.info, upscaler1, upscale_mode, upscale_by, max_side_length, upscale_to_width, upscale_to_height, upscale_crop)
153
pp.info["Postprocess upscaler"] = upscaler1.name
154
155
if upscaler2 and upscaler_2_visibility > 0:
156
second_upscale = self.upscale(pp.image, pp.info, upscaler2, upscale_mode, upscale_by, max_side_length, upscale_to_width, upscale_to_height, upscale_crop)
157
if upscaled_image.mode != second_upscale.mode:
158
second_upscale = second_upscale.convert(upscaled_image.mode)
159
upscaled_image = Image.blend(upscaled_image, second_upscale, upscaler_2_visibility)
160
161
pp.info["Postprocess upscaler 2"] = upscaler2.name
162
163
pp.image = upscaled_image
164
165
def image_changed(self):
166
upscale_cache.clear()
167
168
169
class ScriptPostprocessingUpscaleSimple(ScriptPostprocessingUpscale):
170
name = "Simple Upscale"
171
order = 900
172
173
def ui(self):
174
with FormRow():
175
upscaler_name = gr.Dropdown(label='Upscaler', choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name)
176
upscale_by = gr.Slider(minimum=0.05, maximum=8.0, step=0.05, label="Upscale by", value=2)
177
178
return {
179
"upscale_by": upscale_by,
180
"upscaler_name": upscaler_name,
181
}
182
183
def process_firstpass(self, pp: scripts_postprocessing.PostprocessedImage, upscale_by=2.0, upscaler_name=None):
184
pp.shared.target_width = int(pp.image.width * upscale_by)
185
pp.shared.target_height = int(pp.image.height * upscale_by)
186
187
def process(self, pp: scripts_postprocessing.PostprocessedImage, upscale_by=2.0, upscaler_name=None):
188
if upscaler_name is None or upscaler_name == "None":
189
return
190
191
upscaler1 = next(iter([x for x in shared.sd_upscalers if x.name == upscaler_name]), None)
192
assert upscaler1, f'could not find upscaler named {upscaler_name}'
193
194
pp.image = self.upscale(pp.image, pp.info, upscaler1, 0, upscale_by, 0, 0, 0, False)
195
pp.info["Postprocess upscaler"] = upscaler1.name
196
197