ddoc commited on
Commit
2b09f62
·
1 Parent(s): dd9c955

Upload ui.py

Browse files
Files changed (1) hide show
  1. ui.py +421 -0
ui.py ADDED
@@ -0,0 +1,421 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from functools import partial
4
+ from types import SimpleNamespace
5
+ from typing import Any
6
+
7
+ import gradio as gr
8
+
9
+ from adetailer import AFTER_DETAILER, __version__
10
+ from adetailer.args import AD_ENABLE, ALL_ARGS, MASK_MERGE_INVERT
11
+ from controlnet_ext import controlnet_exists, get_cn_inpaint_models
12
+
13
+
14
+ class Widgets(SimpleNamespace):
15
+ def tolist(self):
16
+ return [getattr(self, attr) for attr in ALL_ARGS.attrs]
17
+
18
+
19
+ def gr_interactive(value: bool = True):
20
+ return gr.update(interactive=value)
21
+
22
+
23
+ def ordinal(n: int) -> str:
24
+ d = {1: "st", 2: "nd", 3: "rd"}
25
+ return str(n) + ("th" if 11 <= n % 100 <= 13 else d.get(n % 10, "th"))
26
+
27
+
28
+ def suffix(n: int, c: str = " ") -> str:
29
+ return "" if n == 0 else c + ordinal(n + 1)
30
+
31
+
32
+ def on_widget_change(state: dict, value: Any, *, attr: str):
33
+ state[attr] = value
34
+ return state
35
+
36
+
37
+ def on_generate_click(state: dict, *values: Any):
38
+ for attr, value in zip(ALL_ARGS.attrs, values):
39
+ state[attr] = value
40
+ return state
41
+
42
+
43
+ def elem_id(item_id: str, n: int, is_img2img: bool) -> str:
44
+ tap = "img2img" if is_img2img else "txt2img"
45
+ suf = suffix(n, "_")
46
+ return f"script_{tap}_adetailer_{item_id}{suf}"
47
+
48
+
49
+ def adui(
50
+ num_models: int,
51
+ is_img2img: bool,
52
+ model_list: list[str],
53
+ t2i_button: gr.Button,
54
+ i2i_button: gr.Button,
55
+ ):
56
+ states = []
57
+ infotext_fields = []
58
+ eid = partial(elem_id, n=0, is_img2img=is_img2img)
59
+
60
+ with gr.Accordion(AFTER_DETAILER, open=False, elem_id=eid("ad_main_accordion")):
61
+ with gr.Row():
62
+ with gr.Column(scale=6):
63
+ ad_enable = gr.Checkbox(
64
+ label="Enable ADetailer",
65
+ value=False,
66
+ visible=True,
67
+ elem_id=eid("ad_enable"),
68
+ )
69
+
70
+ with gr.Column(scale=1, min_width=180):
71
+ gr.Markdown(
72
+ f"v{__version__}",
73
+ elem_id=eid("ad_version"),
74
+ )
75
+
76
+ infotext_fields.append((ad_enable, AD_ENABLE.name))
77
+
78
+ with gr.Group(), gr.Tabs():
79
+ for n in range(num_models):
80
+ with gr.Tab(ordinal(n + 1)):
81
+ state, infofields = one_ui_group(
82
+ n=n,
83
+ is_img2img=is_img2img,
84
+ model_list=model_list,
85
+ t2i_button=t2i_button,
86
+ i2i_button=i2i_button,
87
+ )
88
+
89
+ states.append(state)
90
+ infotext_fields.extend(infofields)
91
+
92
+ # components: [bool, dict, dict, ...]
93
+ components = [ad_enable] + states
94
+ return components, infotext_fields
95
+
96
+
97
+ def one_ui_group(
98
+ n: int,
99
+ is_img2img: bool,
100
+ model_list: list[str],
101
+ t2i_button: gr.Button,
102
+ i2i_button: gr.Button,
103
+ ):
104
+ w = Widgets()
105
+ state = gr.State({})
106
+ eid = partial(elem_id, n=n, is_img2img=is_img2img)
107
+
108
+ with gr.Row():
109
+ model_choices = model_list + ["None"] if n == 0 else ["None"] + model_list
110
+
111
+ w.ad_model = gr.Dropdown(
112
+ label="ADetailer model" + suffix(n),
113
+ choices=model_choices,
114
+ value=model_choices[0],
115
+ visible=True,
116
+ type="value",
117
+ elem_id=eid("ad_model"),
118
+ )
119
+
120
+ with gr.Group():
121
+ with gr.Row(elem_id=eid("ad_toprow_prompt")):
122
+ w.ad_prompt = gr.Textbox(
123
+ label="ad_prompt" + suffix(n),
124
+ show_label=False,
125
+ lines=3,
126
+ placeholder="ADetailer prompt" + suffix(n),
127
+ elem_id=eid("ad_prompt"),
128
+ )
129
+
130
+ with gr.Row(elem_id=eid("ad_toprow_negative_prompt")):
131
+ w.ad_negative_prompt = gr.Textbox(
132
+ label="ad_negative_prompt" + suffix(n),
133
+ show_label=False,
134
+ lines=2,
135
+ placeholder="ADetailer negative prompt" + suffix(n),
136
+ elem_id=eid("ad_negative_prompt"),
137
+ )
138
+
139
+ with gr.Group():
140
+ with gr.Accordion(
141
+ "Detection", open=False, elem_id=eid("ad_detection_accordion")
142
+ ):
143
+ detection(w, n, is_img2img)
144
+
145
+ with gr.Accordion(
146
+ "Mask Preprocessing",
147
+ open=False,
148
+ elem_id=eid("ad_mask_preprocessing_accordion"),
149
+ ):
150
+ mask_preprocessing(w, n, is_img2img)
151
+
152
+ with gr.Accordion(
153
+ "Inpainting", open=False, elem_id=eid("ad_inpainting_accordion")
154
+ ):
155
+ inpainting(w, n, is_img2img)
156
+
157
+ with gr.Group(), gr.Row(variant="panel"):
158
+ cn_inpaint_models = ["None"] + get_cn_inpaint_models()
159
+
160
+ w.ad_controlnet_model = gr.Dropdown(
161
+ label="ControlNet model" + suffix(n),
162
+ choices=cn_inpaint_models,
163
+ value="None",
164
+ visible=True,
165
+ type="value",
166
+ interactive=controlnet_exists,
167
+ elem_id=eid("ad_controlnet_model"),
168
+ )
169
+
170
+ w.ad_controlnet_weight = gr.Slider(
171
+ label="ControlNet weight" + suffix(n),
172
+ minimum=0.0,
173
+ maximum=1.0,
174
+ step=0.05,
175
+ value=1.0,
176
+ visible=True,
177
+ interactive=controlnet_exists,
178
+ elem_id=eid("ad_controlnet_weight"),
179
+ )
180
+
181
+ for attr in ALL_ARGS.attrs:
182
+ widget = getattr(w, attr)
183
+ on_change = partial(on_widget_change, attr=attr)
184
+ widget.change(
185
+ fn=on_change, inputs=[state, widget], outputs=[state], queue=False
186
+ )
187
+
188
+ all_inputs = [state] + w.tolist()
189
+ target_button = i2i_button if is_img2img else t2i_button
190
+ target_button.click(
191
+ fn=on_generate_click, inputs=all_inputs, outputs=state, queue=False
192
+ )
193
+
194
+ infotext_fields = [(getattr(w, attr), name + suffix(n)) for attr, name in ALL_ARGS]
195
+
196
+ return state, infotext_fields
197
+
198
+
199
+ def detection(w: Widgets, n: int, is_img2img: bool):
200
+ eid = partial(elem_id, n=n, is_img2img=is_img2img)
201
+
202
+ with gr.Row():
203
+ with gr.Column():
204
+ w.ad_confidence = gr.Slider(
205
+ label="Detection model confidence threshold" + suffix(n),
206
+ minimum=0.0,
207
+ maximum=1.0,
208
+ step=0.01,
209
+ value=0.3,
210
+ visible=True,
211
+ elem_id=eid("ad_confidence"),
212
+ )
213
+
214
+ with gr.Column(variant="compact"):
215
+ w.ad_mask_min_ratio = gr.Slider(
216
+ label="Mask min area ratio" + suffix(n),
217
+ minimum=0.0,
218
+ maximum=1.0,
219
+ step=0.001,
220
+ value=0.0,
221
+ visible=True,
222
+ elem_id=eid("ad_mask_min_ratio"),
223
+ )
224
+ w.ad_mask_max_ratio = gr.Slider(
225
+ label="Mask max area ratio" + suffix(n),
226
+ minimum=0.0,
227
+ maximum=1.0,
228
+ step=0.001,
229
+ value=1.0,
230
+ visible=True,
231
+ elem_id=eid("ad_mask_max_ratio"),
232
+ )
233
+
234
+
235
+ def mask_preprocessing(w: Widgets, n: int, is_img2img: bool):
236
+ eid = partial(elem_id, n=n, is_img2img=is_img2img)
237
+
238
+ with gr.Group():
239
+ with gr.Row():
240
+ with gr.Column(variant="compact"):
241
+ w.ad_x_offset = gr.Slider(
242
+ label="Mask x(→) offset" + suffix(n),
243
+ minimum=-200,
244
+ maximum=200,
245
+ step=1,
246
+ value=0,
247
+ visible=True,
248
+ elem_id=eid("ad_x_offset"),
249
+ )
250
+ w.ad_y_offset = gr.Slider(
251
+ label="Mask y(↑) offset" + suffix(n),
252
+ minimum=-200,
253
+ maximum=200,
254
+ step=1,
255
+ value=0,
256
+ visible=True,
257
+ elem_id=eid("ad_y_offset"),
258
+ )
259
+
260
+ with gr.Column(variant="compact"):
261
+ w.ad_dilate_erode = gr.Slider(
262
+ label="Mask erosion (-) / dilation (+)" + suffix(n),
263
+ minimum=-3200,
264
+ maximum=3200,
265
+ step=100,
266
+ value=100,
267
+ visible=True,
268
+ elem_id=eid("ad_dilate_erode"),
269
+ )
270
+
271
+ with gr.Row():
272
+ w.ad_mask_merge_invert = gr.Radio(
273
+ label="Mask merge mode" + suffix(n),
274
+ choices=MASK_MERGE_INVERT,
275
+ value="None",
276
+ elem_id=eid("ad_mask_merge_invert"),
277
+ )
278
+
279
+
280
+ def inpainting(w: Widgets, n: int, is_img2img: bool):
281
+ eid = partial(elem_id, n=n, is_img2img=is_img2img)
282
+
283
+ with gr.Group():
284
+ with gr.Row():
285
+ w.ad_mask_blur = gr.Slider(
286
+ label="Inpaint mask blur" + suffix(n),
287
+ minimum=0,
288
+ maximum=64,
289
+ step=1,
290
+ value=4,
291
+ visible=True,
292
+ elem_id=eid("ad_mask_blur"),
293
+ )
294
+
295
+ w.ad_denoising_strength = gr.Slider(
296
+ label="Inpaint denoising strength" + suffix(n),
297
+ minimum=0.0,
298
+ maximum=1.0,
299
+ step=0.01,
300
+ value=0.4,
301
+ visible=True,
302
+ elem_id=eid("ad_denoising_strength"),
303
+ )
304
+
305
+ with gr.Row():
306
+ with gr.Column(variant="compact"):
307
+ w.ad_inpaint_only_masked = gr.Checkbox(
308
+ label="Inpaint only masked" + suffix(n),
309
+ value=True,
310
+ visible=True,
311
+ elem_id=eid("ad_inpaint_full_res"),
312
+ )
313
+ w.ad_inpaint_only_masked_padding = gr.Slider(
314
+ label="Inpaint only masked padding, pixels" + suffix(n),
315
+ minimum=0,
316
+ maximum=256,
317
+ step=4,
318
+ value=32,
319
+ visible=True,
320
+ elem_id=eid("ad_inpaint_full_res_padding"),
321
+ )
322
+
323
+ w.ad_inpaint_only_masked.change(
324
+ gr_interactive,
325
+ inputs=w.ad_inpaint_only_masked,
326
+ outputs=w.ad_inpaint_only_masked_padding,
327
+ queue=False,
328
+ )
329
+
330
+ with gr.Column(variant="compact"):
331
+ w.ad_use_inpaint_width_height = gr.Checkbox(
332
+ label="Use separate width/height" + suffix(n),
333
+ value=False,
334
+ visible=True,
335
+ elem_id=eid("ad_use_inpaint_width_height"),
336
+ )
337
+
338
+ w.ad_inpaint_width = gr.Slider(
339
+ label="inpaint width" + suffix(n),
340
+ minimum=64,
341
+ maximum=2048,
342
+ step=4,
343
+ value=512,
344
+ visible=True,
345
+ elem_id=eid("ad_inpaint_width"),
346
+ )
347
+
348
+ w.ad_inpaint_height = gr.Slider(
349
+ label="inpaint height" + suffix(n),
350
+ minimum=64,
351
+ maximum=2048,
352
+ step=4,
353
+ value=512,
354
+ visible=True,
355
+ elem_id=eid("ad_inpaint_height"),
356
+ )
357
+
358
+ w.ad_use_inpaint_width_height.change(
359
+ lambda value: (gr_interactive(value), gr_interactive(value)),
360
+ inputs=w.ad_use_inpaint_width_height,
361
+ outputs=[w.ad_inpaint_width, w.ad_inpaint_height],
362
+ queue=False,
363
+ )
364
+
365
+ with gr.Row():
366
+ with gr.Column(variant="compact"):
367
+ w.ad_use_steps = gr.Checkbox(
368
+ label="Use separate steps" + suffix(n),
369
+ value=False,
370
+ visible=True,
371
+ elem_id=eid("ad_use_steps"),
372
+ )
373
+
374
+ w.ad_steps = gr.Slider(
375
+ label="ADetailer steps" + suffix(n),
376
+ minimum=1,
377
+ maximum=150,
378
+ step=1,
379
+ value=28,
380
+ visible=True,
381
+ elem_id=eid("ad_steps"),
382
+ )
383
+
384
+ w.ad_use_steps.change(
385
+ gr_interactive,
386
+ inputs=w.ad_use_steps,
387
+ outputs=w.ad_steps,
388
+ queue=False,
389
+ )
390
+
391
+ with gr.Column(variant="compact"):
392
+ w.ad_use_cfg_scale = gr.Checkbox(
393
+ label="Use separate CFG scale" + suffix(n),
394
+ value=False,
395
+ visible=True,
396
+ elem_id=eid("ad_use_cfg_scale"),
397
+ )
398
+
399
+ w.ad_cfg_scale = gr.Slider(
400
+ label="ADetailer CFG scale" + suffix(n),
401
+ minimum=0.0,
402
+ maximum=30.0,
403
+ step=0.5,
404
+ value=7.0,
405
+ visible=True,
406
+ elem_id=eid("ad_cfg_scale"),
407
+ )
408
+
409
+ w.ad_use_cfg_scale.change(
410
+ gr_interactive,
411
+ inputs=w.ad_use_cfg_scale,
412
+ outputs=w.ad_cfg_scale,
413
+ queue=False,
414
+ )
415
+
416
+ with gr.Row():
417
+ w.ad_restore_face = gr.Checkbox(
418
+ label="Restore faces after ADetailer" + suffix(n),
419
+ value=False,
420
+ elem_id=eid("ad_restore_face"),
421
+ )