dikdimon commited on
Commit
9786d3d
·
verified ·
1 Parent(s): bd26012

Upload nrs_kohaku_enhanced (1).py

Browse files
negative_rejection_steering/scripts/nrs_kohaku_enhanced (1).py ADDED
@@ -0,0 +1,508 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import gradio as gr
3
+ from modules import scripts, script_callbacks, sd_samplers_cfg_denoiser, shared
4
+
5
+ # ==============================================================================
6
+ # УЛУЧШЕНИЕ NRS С ИСПОЛЬЗОВАНИЕМ ГЕОМЕТРИЧЕСКОГО ПРИНЦИПА KOHAKU_LONYU_YOG
7
+ #
8
+ # Оригинальная идея Kohaku: в N-мерном пространстве, если взять антиподальную
9
+ # точку -x и вычислить оттуда градиент d2, то среднее (d+d2)/2 указывает
10
+ # точнее на целевую область A. Применяем этот же принцип к NRS-векторам.
11
+ #
12
+ # Новая функция calc_nrs_antipodal:
13
+ # 1. Вычисляет NRS-вектор из точки x (стандартный NRS)
14
+ # 2. Вычисляет NRS-вектор из антиподальной точки -x
15
+ # 3. Усредняет результаты — убирает смещение, зависящее от позиции x
16
+ #
17
+ # Параметр antipodal_blend (0.0 = чистый NRS, 1.0 = полное антиподальное
18
+ # усреднение) позволяет плавно управлять степенью улучшения.
19
+ # ==============================================================================
20
+
21
+
22
+ def _nrs_core(x_orig, cond, uncond, sigma, skew, stretch, squash):
23
+ """
24
+ Базовое математическое ядро NRS. Принимает один тензор x_orig
25
+ и возвращает управляемый cond-вектор.
26
+ """
27
+ is_v_pred = False
28
+ if hasattr(shared.sd_model, 'parameterization'):
29
+ is_v_pred = shared.sd_model.parameterization == "v"
30
+
31
+ if isinstance(sigma, torch.Tensor):
32
+ sig_tens = sigma[0]
33
+ else:
34
+ sig_tens = torch.tensor(sigma, device=cond.device, dtype=cond.dtype)
35
+
36
+ if sig_tens.dtype != cond.dtype:
37
+ sig_tens = sig_tens.to(dtype=cond.dtype)
38
+
39
+ sig_tens = sig_tens.view(1, 1, 1, 1)
40
+ sig_root = (sig_tens ** 2 + 1).sqrt()
41
+
42
+ if is_v_pred:
43
+ nrs_cond, nrs_uncond = cond, uncond
44
+ x_div = None
45
+ else:
46
+ x_div = x_orig / (sig_tens ** 2 + 1)
47
+ factor = sig_tens / sig_root
48
+ nrs_cond = x_orig - (x_div - cond * factor)
49
+ nrs_uncond = x_orig - (x_div - uncond * factor)
50
+
51
+ def _dot(a, b): return (a * b).sum(dim=1, keepdim=True)
52
+ def _nrm2(v): return _dot(v, v)
53
+
54
+ eps_safe = 1e-6
55
+
56
+ c_dot_c = _nrm2(nrs_cond) + eps_safe
57
+ u_dot_c = _dot(nrs_uncond, nrs_cond)
58
+ u_on_c = (u_dot_c / c_dot_c) * nrs_cond
59
+
60
+ proj_diff = nrs_cond - u_on_c
61
+ stretched = nrs_cond + (stretch * proj_diff)
62
+
63
+ u_rej_c = nrs_uncond - u_on_c
64
+ skewed = stretched - (skew * u_rej_c)
65
+
66
+ cond_len = nrs_cond.norm(dim=1, keepdim=True)
67
+ nrs_len = skewed.norm(dim=1, keepdim=True) + eps_safe
68
+ squash_scale = (1 - squash) + (squash * (cond_len / nrs_len))
69
+ x_final = skewed * squash_scale
70
+
71
+ if is_v_pred:
72
+ return x_final
73
+ else:
74
+ return (x_div - (x_orig - x_final)) * (sig_root / sig_tens)
75
+
76
+
77
+ def calc_nrs(x_orig, cond, uncond, sigma, skew, stretch, squash):
78
+ """
79
+ Оригинальная функция NRS — обёртка над ядром, сохранена для совместимости.
80
+ """
81
+ return _nrs_core(x_orig, cond, uncond, sigma, skew, stretch, squash)
82
+
83
+
84
+ def calc_nrs_antipodal(x_orig, cond, uncond, sigma, skew, stretch, squash,
85
+ antipodal_blend=0.5, antipodal_steps_half=True,
86
+ current_step=0, total_steps=20):
87
+ """
88
+ NRS с антиподальным геометрическим улучшением по принципу Kohaku_LoNyu_Yog.
89
+
90
+ Принцип:
91
+ Kohaku показал, что в высокоразмерном пространстве точка -x и точка x
92
+ находятся "по разные стороны" от целевой области A, и среднее их
93
+ градиентов (d+d2)/2 даёт более точный вектор к A.
94
+
95
+ Здесь: вычисляем NRS-вектор из x И из -x, затем смешиваем.
96
+ NRS из -x имеет инвертированные знаки компонент cond/uncond
97
+ относительно пространства x, но их среднее устраняет позиционное
98
+ смещение и даёт более "центрированный" вектор управления.
99
+
100
+ Параметры:
101
+ antipodal_blend : 0.0 = чистый NRS, 1.0 = полное усреднение
102
+ antipodal_steps_half : True = применять только на первой половине
103
+ шагов (как в оригинале Kohaku)
104
+ current_step : текущий шаг сэмплера
105
+ total_steps : всего шагов
106
+ """
107
+ # Стандартный NRS из x
108
+ nrs_direct = _nrs_core(x_orig, cond, uncond, sigma, skew, stretch, squash)
109
+
110
+ # Если антиподальное улучшение выключено — возвращаем стандартный результат
111
+ if antipodal_blend <= 0.0:
112
+ return nrs_direct
113
+
114
+ # Применять только на первой половине шагов (согласно теории Kohaku)
115
+ if antipodal_steps_half and current_step > total_steps / 2:
116
+ return nrs_direct
117
+
118
+ # ----------------------------------------------------------------
119
+ # Вычисляем NRS из антиподальной точки -x
120
+ #
121
+ # Геометрически: нас интересует не буквально -x как латент, а то,
122
+ # как меняется пространство проекций cond/uncond при инверсии x.
123
+ # Мы инвертируем x_orig и пересчитываем cond/uncond относительно него.
124
+ #
125
+ # Примечание: cond и uncond здесь — это выходы денойзера (x_out),
126
+ # а не промпты. Они тоже имеют смысл только в контексте входа x.
127
+ # При -x логично инвертировать и их знаки.
128
+ # ----------------------------------------------------------------
129
+ x_neg = -x_orig
130
+ cond_neg = -cond
131
+ uncond_neg = -uncond
132
+
133
+ nrs_antipodal_raw = _nrs_core(x_neg, cond_neg, uncond_neg, sigma,
134
+ skew, stretch, squash)
135
+
136
+ # Антиподальный результат возвращаем в пространство x (инвертируем обратно)
137
+ nrs_antipodal = -nrs_antipodal_raw
138
+
139
+ # ----------------------------------------------------------------
140
+ # Двухшаговое уточнение (аналог d3 в Kohaku):
141
+ # x3 = x + (d+d2)/2 * dt → затем вычисляется d3 → итог (d+d3)/2
142
+ # Здесь: вычисляем "уточнённый" NRS из промежуточной точки
143
+ # x_mid = x + (nrs_direct + nrs_antipodal) / 2 * blend_factor
144
+ # ----------------------------------------------------------------
145
+ nrs_avg = (nrs_direct + nrs_antipodal) / 2
146
+
147
+ # Blend: плавное смешивание
148
+ result = nrs_direct + antipodal_blend * (nrs_avg - nrs_direct)
149
+
150
+ return result
151
+
152
+
153
+ # ==============================================================================
154
+ # УПРАВЛЕНИЕ ШАГАМИ (без изменений)
155
+ # ==============================================================================
156
+ def should_apply_at_step(current_step, total_steps, start_step, end_step,
157
+ start_frac, end_frac, step_mode):
158
+ if step_mode == "Absolute Steps":
159
+ effective_start = max(0, start_step)
160
+ effective_end = min(total_steps, end_step) if end_step > 0 else total_steps
161
+ return effective_start <= current_step < effective_end
162
+ else:
163
+ effective_start = int(total_steps * max(0.0, min(1.0, start_frac)))
164
+ effective_end = int(total_steps * max(0.0, min(1.0, end_frac)))
165
+ if effective_end == 0:
166
+ effective_end = total_steps
167
+ return effective_start <= current_step < effective_end
168
+
169
+
170
+ def get_param_value_at_step(base_value, current_step, total_steps, start_step, end_step,
171
+ start_frac, end_frac, step_mode, enabled):
172
+ if not enabled:
173
+ return base_value
174
+ if should_apply_at_step(current_step, total_steps, start_step, end_step,
175
+ start_frac, end_frac, step_mode):
176
+ return base_value
177
+ else:
178
+ return 0.0
179
+
180
+
181
+ # ==============================================================================
182
+ # ПЕРЕХВАТ (HOOKING)
183
+ # ==============================================================================
184
+ def hook_cfg_denoiser_params(params):
185
+ if hasattr(params.denoiser, 'p') and getattr(params.denoiser.p, '_nrs_enabled', False):
186
+ params.denoiser.p._nrs_current_sigma = params.sigma
187
+ params.denoiser.p._nrs_current_x_in = params.x
188
+ if hasattr(params, 'sampling_step'):
189
+ params.denoiser.p._nrs_current_step = params.sampling_step
190
+ elif hasattr(params.denoiser, 'step'):
191
+ params.denoiser.p._nrs_current_step = params.denoiser.step
192
+ else:
193
+ params.denoiser.p._nrs_current_step = getattr(params.denoiser.p, '_nrs_current_step', 0)
194
+
195
+
196
+ script_callbacks.on_cfg_denoiser(hook_cfg_denoiser_params)
197
+
198
+ if not hasattr(sd_samplers_cfg_denoiser.CFGDenoiser, 'original_combine_denoised_nrs_backup'):
199
+ sd_samplers_cfg_denoiser.CFGDenoiser.original_combine_denoised_nrs_backup = \
200
+ sd_samplers_cfg_denoiser.CFGDenoiser.combine_denoised
201
+
202
+
203
+ def hijacked_combine_denoised(self, x_out, conds_list, uncond, cond_scale):
204
+ if not getattr(self, 'p', None) or not getattr(self.p, '_nrs_enabled', False):
205
+ return sd_samplers_cfg_denoiser.CFGDenoiser.original_combine_denoised_nrs_backup(
206
+ self, x_out, conds_list, uncond, cond_scale)
207
+
208
+ if not hasattr(self.p, '_nrs_current_sigma') or not hasattr(self.p, '_nrs_current_x_in'):
209
+ return sd_samplers_cfg_denoiser.CFGDenoiser.original_combine_denoised_nrs_backup(
210
+ self, x_out, conds_list, uncond, cond_scale)
211
+
212
+ try:
213
+ base_skew, base_stretch, base_squash = self.p._nrs_params
214
+ step_control_enabled = getattr(self.p, '_nrs_step_control_enabled', False)
215
+ step_control_mode = getattr(self.p, '_nrs_step_control_mode', 'Global')
216
+ current_step = getattr(self.p, '_nrs_current_step', 0)
217
+ total_steps = getattr(self.p, 'steps', 20)
218
+
219
+ if step_control_enabled:
220
+ if step_control_mode == 'Global':
221
+ global_settings = getattr(self.p, '_nrs_global_step_settings', {})
222
+ start_step = global_settings.get('start_step', 0)
223
+ end_step = global_settings.get('end_step', total_steps)
224
+ start_frac = global_settings.get('start_frac', 0.0)
225
+ end_frac = global_settings.get('end_frac', 1.0)
226
+ step_mode = global_settings.get('step_mode', 'Absolute Steps')
227
+ if not should_apply_at_step(current_step, total_steps, start_step, end_step,
228
+ start_frac, end_frac, step_mode):
229
+ return sd_samplers_cfg_denoiser.CFGDenoiser.original_combine_denoised_nrs_backup(
230
+ self, x_out, conds_list, uncond, cond_scale)
231
+ skew, stretch, squash = base_skew, base_stretch, base_squash
232
+ else:
233
+ individual_settings = getattr(self.p, '_nrs_individual_step_settings', {})
234
+ skew_s = individual_settings.get('skew', {})
235
+ skew = get_param_value_at_step(
236
+ base_skew, current_step, total_steps,
237
+ skew_s.get('start_step', 0), skew_s.get('end_step', total_steps),
238
+ skew_s.get('start_frac', 0.0), skew_s.get('end_frac', 1.0),
239
+ skew_s.get('step_mode', 'Absolute Steps'), skew_s.get('enabled', True))
240
+ str_s = individual_settings.get('stretch', {})
241
+ stretch = get_param_value_at_step(
242
+ base_stretch, current_step, total_steps,
243
+ str_s.get('start_step', 0), str_s.get('end_step', total_steps),
244
+ str_s.get('start_frac', 0.0), str_s.get('end_frac', 1.0),
245
+ str_s.get('step_mode', 'Absolute Steps'), str_s.get('enabled', True))
246
+ sq_s = individual_settings.get('squash', {})
247
+ squash = get_param_value_at_step(
248
+ base_squash, current_step, total_steps,
249
+ sq_s.get('start_step', 0), sq_s.get('end_step', total_steps),
250
+ sq_s.get('start_frac', 0.0), sq_s.get('end_frac', 1.0),
251
+ sq_s.get('step_mode', 'Absolute Steps'), sq_s.get('enabled', True))
252
+ else:
253
+ skew, stretch, squash = base_skew, base_stretch, base_squash
254
+
255
+ # Антиподальные параметры
256
+ antipodal_blend = getattr(self.p, '_nrs_antipodal_blend', 0.0)
257
+ antipodal_steps_half = getattr(self.p, '_nrs_antipodal_steps_half', True)
258
+
259
+ denoised_uncond = x_out[-uncond.shape[0]:]
260
+ denoised = torch.clone(denoised_uncond)
261
+ x_orig_all = self.p._nrs_current_x_in
262
+ x_orig_uncond = x_orig_all[-uncond.shape[0]:]
263
+
264
+ for i, conds in enumerate(conds_list):
265
+ for idx, (cond_index, weight) in enumerate(conds):
266
+ current_cond = x_out[cond_index]
267
+ if idx == 0:
268
+ current_x_orig = x_orig_uncond[i].unsqueeze(0)
269
+ c_in = current_cond.unsqueeze(0)
270
+ u_in = denoised_uncond[i].unsqueeze(0)
271
+
272
+ # Используем новую функцию с антиподальным улучшением
273
+ nrs_result = calc_nrs_antipodal(
274
+ current_x_orig, c_in, u_in,
275
+ self.p._nrs_current_sigma,
276
+ skew, stretch, squash,
277
+ antipodal_blend=antipodal_blend,
278
+ antipodal_steps_half=antipodal_steps_half,
279
+ current_step=current_step,
280
+ total_steps=total_steps
281
+ )
282
+
283
+ if len(conds) == 1:
284
+ denoised[i] = nrs_result.squeeze(0)
285
+ else:
286
+ delta = nrs_result.squeeze(0) - denoised_uncond[i]
287
+ denoised[i] += delta * weight
288
+ else:
289
+ denoised[i] += (current_cond - denoised_uncond[i]) * (weight * cond_scale)
290
+
291
+ return denoised
292
+
293
+ except Exception as e:
294
+ print(f"!!! NRS Antipodal Error (Fallback): {e}")
295
+ return sd_samplers_cfg_denoiser.CFGDenoiser.original_combine_denoised_nrs_backup(
296
+ self, x_out, conds_list, uncond, cond_scale)
297
+
298
+
299
+ # ==============================================================================
300
+ # UI
301
+ # ==============================================================================
302
+ class NRSScript(scripts.Script):
303
+ def title(self):
304
+ return "NRS + Kohaku Antipodal (Enhanced)"
305
+
306
+ def show(self, is_img2img):
307
+ return scripts.AlwaysVisible
308
+
309
+ def ui(self, is_img2img):
310
+ with gr.Accordion("Negative Rejection Steering + Kohaku Antipodal", open=False):
311
+ with gr.Row():
312
+ enabled = gr.Checkbox(label="Включить NRS (Enable)", value=False)
313
+
314
+ with gr.Accordion("❓ Инструкция", open=False):
315
+ gr.Markdown("""
316
+ ### Что нового: Antipodal Blend (Антиподальное улучшение)
317
+
318
+ Основано на принципе **Kohaku_LoNyu_Yog**: в высокоразмерном пространстве
319
+ точки `x` и `-x` находятся по разные стороны от целевой области изображения.
320
+
321
+ Стандартный NRS вычисляет управляющий вектор только из текущего `x`.
322
+ Это создаёт **позиционное смещение** — вектор "перекошен" относительно
323
+ истинного направления к цели.
324
+
325
+ **Antipodal Blend** вычисляет NRS также из `-x` и усредняет результаты.
326
+ Это устраняет смещение и даёт более точное управление.
327
+
328
+ ### Параметры Antipodal:
329
+ - **Antipodal Blend** (0.0–1.0): Степень антиподального улучшения.
330
+ 0.0 = чистый NRS, 0.5 = рекомендуемое значение, 1.0 = полное усреднение.
331
+ - **Only first half of steps**: Применять только на первой половине шагов
332
+ (как в оригинале Kohaku — в конце шагов метод может отклоняться).
333
+
334
+ ### Советы:
335
+ - Начните с **Antipodal Blend = 0.3–0.5** с включённым "Only first half"
336
+ - Если результат слишком "мягкий" — уменьшите blend или увеличьте Skew
337
+ - Этот метод особенно эффективен при низком CFG / высоком Squash
338
+ """)
339
+
340
+ gr.HTML("<div style='margin-bottom: 0.5em; opacity: 0.8; font-size: 0.9em;"
341
+ " border-bottom: 1px solid #444;'>Основные настройки NRS</div>")
342
+
343
+ with gr.Row():
344
+ skew = gr.Slider(
345
+ label="Skew (Композиция)",
346
+ minimum=-30.0, maximum=30.0, step=0.05, value=4.0,
347
+ info="Отклонение от Negative prompt. Рекомендуется: 3.0–5.0"
348
+ )
349
+ stretch = gr.Slider(
350
+ label="Stretch (Цвета/Текстура)",
351
+ minimum=-30.0, maximum=30.0, step=0.05, value=2.0,
352
+ info="Притяжение к Positive prompt. Рекомендуется: 2.0–7.0"
353
+ )
354
+
355
+ squash = gr.Slider(
356
+ label="Squash (Защита от пережарки)",
357
+ minimum=0.0, maximum=1.0, step=0.01, value=0.0,
358
+ info="0.0 = максимальный эффект. 1.0 = больше деталей."
359
+ )
360
+
361
+ # --- НОВЫЙ БЛОК: АНТИПОДАЛЬНОЕ УЛУЧШЕНИЕ ---
362
+ gr.HTML("<div style='margin: 0.8em 0 0.5em 0; opacity: 0.8; font-size: 0.9em;"
363
+ " border-bottom: 1px solid #444;'>🔮 Kohaku Antipodal Enhancement</div>")
364
+
365
+ antipodal_blend = gr.Slider(
366
+ label="Antipodal Blend",
367
+ minimum=0.0, maximum=1.0, step=0.01, value=0.0,
368
+ info="0.0 = выкл (чистый NRS). 0.5 = рекомендуется. 1.0 = полное антиподальное усреднение."
369
+ )
370
+
371
+ antipodal_steps_half = gr.Checkbox(
372
+ label="Only first half of steps (рекомендуется)",
373
+ value=True,
374
+ info="Применять антиподальное улучшение только на первой половине шагов (как в Kohaku)"
375
+ )
376
+
377
+ # --- STEP CONTROL ---
378
+ with gr.Accordion("⏱️ Step Control", open=False):
379
+ with gr.Row():
380
+ step_control_enabled = gr.Checkbox(label="Включить Step Control", value=False)
381
+ step_control_mode = gr.Radio(
382
+ label="Режим", choices=["Global", "Individual"], value="Global")
383
+
384
+ with gr.Group(visible=True) as global_group:
385
+ gr.HTML("<div style='margin: 0.5em 0; font-weight: bold;'>Глобальные настройки</div>")
386
+ global_step_mode = gr.Radio(
387
+ label="Режим шагов",
388
+ choices=["Absolute Steps", "Fraction of Steps"],
389
+ value="Absolute Steps")
390
+ with gr.Row():
391
+ global_start_step = gr.Slider(label="Start Step", minimum=0, maximum=150, step=1, value=0, visible=True)
392
+ global_end_step = gr.Slider(label="End Step (0=конец)", minimum=0, maximum=150, step=1, value=0, visible=True)
393
+ with gr.Row():
394
+ global_start_frac = gr.Slider(label="Start (fraction)", minimum=0.0, maximum=1.0, step=0.01, value=0.0, visible=False)
395
+ global_end_frac = gr.Slider(label="End (fraction)", minimum=0.0, maximum=1.0, step=0.01, value=1.0, visible=False)
396
+
397
+ with gr.Group(visible=False) as individual_group:
398
+ gr.HTML("<div style='margin: 0.5em 0; font-weight: bold;'>Индивидуальные настройки</div>")
399
+ with gr.Accordion("Skew - Step Settings", open=False):
400
+ skew_step_enabled = gr.Checkbox(label="Включить для Skew", value=True)
401
+ skew_step_mode = gr.Radio(label="Режим", choices=["Absolute Steps", "Fraction of Steps"], value="Absolute Steps")
402
+ with gr.Row():
403
+ skew_start_step = gr.Slider(label="Start Step", minimum=0, maximum=150, step=1, value=0, visible=True)
404
+ skew_end_step = gr.Slider(label="End Step", minimum=0, maximum=150, step=1, value=0, visible=True)
405
+ with gr.Row():
406
+ skew_start_frac = gr.Slider(label="Start (fraction)", minimum=0.0, maximum=1.0, step=0.01, value=0.0, visible=False)
407
+ skew_end_frac = gr.Slider(label="End (fraction)", minimum=0.0, maximum=1.0, step=0.01, value=1.0, visible=False)
408
+ with gr.Accordion("Stretch - Step Settings", open=False):
409
+ stretch_step_enabled = gr.Checkbox(label="Включить для Stretch", value=True)
410
+ stretch_step_mode = gr.Radio(label="Режим", choices=["Absolute Steps", "Fraction of Steps"], value="Absolute Steps")
411
+ with gr.Row():
412
+ stretch_start_step = gr.Slider(label="Start Step", minimum=0, maximum=150, step=1, value=0, visible=True)
413
+ stretch_end_step = gr.Slider(label="End Step", minimum=0, maximum=150, step=1, value=0, visible=True)
414
+ with gr.Row():
415
+ stretch_start_frac = gr.Slider(label="Start (fraction)", minimum=0.0, maximum=1.0, step=0.01, value=0.0, visible=False)
416
+ stretch_end_frac = gr.Slider(label="End (fraction)", minimum=0.0, maximum=1.0, step=0.01, value=1.0, visible=False)
417
+ with gr.Accordion("Squash - Step Settings", open=False):
418
+ squash_step_enabled = gr.Checkbox(label="Включить для Squash", value=True)
419
+ squash_step_mode = gr.Radio(label="Режим", choices=["Absolute Steps", "Fraction of Steps"], value="Absolute Steps")
420
+ with gr.Row():
421
+ squash_start_step = gr.Slider(label="Start Step", minimum=0, maximum=150, step=1, value=0, visible=True)
422
+ squash_end_step = gr.Slider(label="End Step", minimum=0, maximum=150, step=1, value=0, visible=True)
423
+ with gr.Row():
424
+ squash_start_frac = gr.Slider(label="Start (fraction)", minimum=0.0, maximum=1.0, step=0.01, value=0.0, visible=False)
425
+ squash_end_frac = gr.Slider(label="End (fraction)", minimum=0.0, maximum=1.0, step=0.01, value=1.0, visible=False)
426
+
427
+ def update_mode_visibility(mode):
428
+ return {
429
+ global_group: gr.update(visible=(mode == "Global")),
430
+ individual_group: gr.update(visible=(mode == "Individual"))
431
+ }
432
+
433
+ step_control_mode.change(
434
+ fn=update_mode_visibility,
435
+ inputs=[step_control_mode],
436
+ outputs=[global_group, individual_group])
437
+
438
+ def _toggle_abs_frac(mode):
439
+ a = mode == "Absolute Steps"
440
+ return gr.update(visible=a), gr.update(visible=a), gr.update(visible=not a), gr.update(visible=not a)
441
+
442
+ global_step_mode.change(fn=_toggle_abs_frac, inputs=[global_step_mode],
443
+ outputs=[global_start_step, global_end_step, global_start_frac, global_end_frac])
444
+ skew_step_mode.change(fn=_toggle_abs_frac, inputs=[skew_step_mode],
445
+ outputs=[skew_start_step, skew_end_step, skew_start_frac, skew_end_frac])
446
+ stretch_step_mode.change(fn=_toggle_abs_frac, inputs=[stretch_step_mode],
447
+ outputs=[stretch_start_step, stretch_end_step, stretch_start_frac, stretch_end_frac])
448
+ squash_step_mode.change(fn=_toggle_abs_frac, inputs=[squash_step_mode],
449
+ outputs=[squash_start_step, squash_end_step, squash_start_frac, squash_end_frac])
450
+
451
+ return [
452
+ enabled, skew, stretch, squash,
453
+ antipodal_blend, antipodal_steps_half,
454
+ step_control_enabled, step_control_mode,
455
+ global_step_mode, global_start_step, global_end_step, global_start_frac, global_end_frac,
456
+ skew_step_enabled, skew_step_mode, skew_start_step, skew_end_step, skew_start_frac, skew_end_frac,
457
+ stretch_step_enabled, stretch_step_mode, stretch_start_step, stretch_end_step, stretch_start_frac, stretch_end_frac,
458
+ squash_step_enabled, squash_step_mode, squash_start_step, squash_end_step, squash_start_frac, squash_end_frac,
459
+ ]
460
+
461
+ def process(self, p,
462
+ enabled, skew, stretch, squash,
463
+ antipodal_blend, antipodal_steps_half,
464
+ step_control_enabled, step_control_mode,
465
+ global_step_mode, global_start_step, global_end_step, global_start_frac, global_end_frac,
466
+ skew_step_enabled, skew_step_mode, skew_start_step, skew_end_step, skew_start_frac, skew_end_frac,
467
+ stretch_step_enabled, stretch_step_mode, stretch_start_step, stretch_end_step, stretch_start_frac, stretch_end_frac,
468
+ squash_step_enabled, squash_step_mode, squash_start_step, squash_end_step, squash_start_frac, squash_end_frac):
469
+
470
+ p._nrs_enabled = enabled
471
+ p._nrs_params = (skew, stretch, squash)
472
+ p._nrs_antipodal_blend = antipodal_blend
473
+ p._nrs_antipodal_steps_half = antipodal_steps_half
474
+ p._nrs_step_control_enabled = step_control_enabled
475
+ p._nrs_step_control_mode = step_control_mode
476
+
477
+ p._nrs_global_step_settings = {
478
+ 'step_mode': global_step_mode,
479
+ 'start_step': global_start_step,
480
+ 'end_step': global_end_step,
481
+ 'start_frac': global_start_frac,
482
+ 'end_frac': global_end_frac
483
+ }
484
+
485
+ p._nrs_individual_step_settings = {
486
+ 'skew': {
487
+ 'enabled': skew_step_enabled, 'step_mode': skew_step_mode,
488
+ 'start_step': skew_start_step, 'end_step': skew_end_step,
489
+ 'start_frac': skew_start_frac, 'end_frac': skew_end_frac
490
+ },
491
+ 'stretch': {
492
+ 'enabled': stretch_step_enabled, 'step_mode': stretch_step_mode,
493
+ 'start_step': stretch_start_step, 'end_step': stretch_end_step,
494
+ 'start_frac': stretch_start_frac, 'end_frac': stretch_end_frac
495
+ },
496
+ 'squash': {
497
+ 'enabled': squash_step_enabled, 'step_mode': squash_step_mode,
498
+ 'start_step': squash_start_step, 'end_step': squash_end_step,
499
+ 'start_frac': squash_start_frac, 'end_frac': squash_end_frac
500
+ }
501
+ }
502
+
503
+ if enabled:
504
+ sd_samplers_cfg_denoiser.CFGDenoiser.combine_denoised = hijacked_combine_denoised
505
+ p._nrs_current_step = 0
506
+
507
+ def postprocess(self, p, processed, *args):
508
+ pass