dikdimon commited on
Commit
eadb5a4
·
verified ·
1 Parent(s): 2194a40

Upload asymmetric-tiling-sd-webui-2.0 using SD-Hub

Browse files
.gitattributes CHANGED
@@ -179,3 +179,5 @@ sd-webui-llul/images/sample1.jpg filter=lfs diff=lfs merge=lfs -text
179
  sd-webui-llul/images/sample2.jpg filter=lfs diff=lfs merge=lfs -text
180
  sd-webui-llul/images/sample3.jpg filter=lfs diff=lfs merge=lfs -text
181
  sd-webui-llul/images/sample4.jpg filter=lfs diff=lfs merge=lfs -text
 
 
 
179
  sd-webui-llul/images/sample2.jpg filter=lfs diff=lfs merge=lfs -text
180
  sd-webui-llul/images/sample3.jpg filter=lfs diff=lfs merge=lfs -text
181
  sd-webui-llul/images/sample4.jpg filter=lfs diff=lfs merge=lfs -text
182
+ asymmetric-tiling-sd-webui-2.0/Руководство[[:space:]]Advanced[[:space:]]Tiling[[:space:]]v3.0[[:space:]]-[[:space:]]Новые[[:space:]]функции.pdf filter=lfs diff=lfs merge=lfs -text
183
+ asymmetric-tiling-sd-webui-2.0/Руководство[[:space:]]по[[:space:]]новым[[:space:]]функциям.pdf filter=lfs diff=lfs merge=lfs -text
asymmetric-tiling-sd-webui-2.0/LICENSE ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Creative Commons Legal Code
2
+
3
+ CC0 1.0 Universal
4
+
5
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12
+ HEREUNDER.
13
+
14
+ Statement of Purpose
15
+
16
+ The laws of most jurisdictions throughout the world automatically confer
17
+ exclusive Copyright and Related Rights (defined below) upon the creator
18
+ and subsequent owner(s) (each and all, an "owner") of an original work of
19
+ authorship and/or a database (each, a "Work").
20
+
21
+ Certain owners wish to permanently relinquish those rights to a Work for
22
+ the purpose of contributing to a commons of creative, cultural and
23
+ scientific works ("Commons") that the public can reliably and without fear
24
+ of later claims of infringement build upon, modify, incorporate in other
25
+ works, reuse and redistribute as freely as possible in any form whatsoever
26
+ and for any purposes, including without limitation commercial purposes.
27
+ These owners may contribute to the Commons to promote the ideal of a free
28
+ culture and the further production of creative, cultural and scientific
29
+ works, or to gain reputation or greater distribution for their Work in
30
+ part through the use and efforts of others.
31
+
32
+ For these and/or other purposes and motivations, and without any
33
+ expectation of additional consideration or compensation, the person
34
+ associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35
+ is an owner of Copyright and Related Rights in the Work, voluntarily
36
+ elects to apply CC0 to the Work and publicly distribute the Work under its
37
+ terms, with knowledge of his or her Copyright and Related Rights in the
38
+ Work and the meaning and intended legal effect of CC0 on those rights.
39
+
40
+ 1. Copyright and Related Rights. A Work made available under CC0 may be
41
+ protected by copyright and related or neighboring rights ("Copyright and
42
+ Related Rights"). Copyright and Related Rights include, but are not
43
+ limited to, the following:
44
+
45
+ i. the right to reproduce, adapt, distribute, perform, display,
46
+ communicate, and translate a Work;
47
+ ii. moral rights retained by the original author(s) and/or performer(s);
48
+ iii. publicity and privacy rights pertaining to a person's image or
49
+ likeness depicted in a Work;
50
+ iv. rights protecting against unfair competition in regards to a Work,
51
+ subject to the limitations in paragraph 4(a), below;
52
+ v. rights protecting the extraction, dissemination, use and reuse of data
53
+ in a Work;
54
+ vi. database rights (such as those arising under Directive 96/9/EC of the
55
+ European Parliament and of the Council of 11 March 1996 on the legal
56
+ protection of databases, and under any national implementation
57
+ thereof, including any amended or successor version of such
58
+ directive); and
59
+ vii. other similar, equivalent or corresponding rights throughout the
60
+ world based on applicable law or treaty, and any national
61
+ implementations thereof.
62
+
63
+ 2. Waiver. To the greatest extent permitted by, but not in contravention
64
+ of, applicable law, Affirmer hereby overtly, fully, permanently,
65
+ irrevocably and unconditionally waives, abandons, and surrenders all of
66
+ Affirmer's Copyright and Related Rights and associated claims and causes
67
+ of action, whether now known or unknown (including existing as well as
68
+ future claims and causes of action), in the Work (i) in all territories
69
+ worldwide, (ii) for the maximum duration provided by applicable law or
70
+ treaty (including future time extensions), (iii) in any current or future
71
+ medium and for any number of copies, and (iv) for any purpose whatsoever,
72
+ including without limitation commercial, advertising or promotional
73
+ purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74
+ member of the public at large and to the detriment of Affirmer's heirs and
75
+ successors, fully intending that such Waiver shall not be subject to
76
+ revocation, rescission, cancellation, termination, or any other legal or
77
+ equitable action to disrupt the quiet enjoyment of the Work by the public
78
+ as contemplated by Affirmer's express Statement of Purpose.
79
+
80
+ 3. Public License Fallback. Should any part of the Waiver for any reason
81
+ be judged legally invalid or ineffective under applicable law, then the
82
+ Waiver shall be preserved to the maximum extent permitted taking into
83
+ account Affirmer's express Statement of Purpose. In addition, to the
84
+ extent the Waiver is so judged Affirmer hereby grants to each affected
85
+ person a royalty-free, non transferable, non sublicensable, non exclusive,
86
+ irrevocable and unconditional license to exercise Affirmer's Copyright and
87
+ Related Rights in the Work (i) in all territories worldwide, (ii) for the
88
+ maximum duration provided by applicable law or treaty (including future
89
+ time extensions), (iii) in any current or future medium and for any number
90
+ of copies, and (iv) for any purpose whatsoever, including without
91
+ limitation commercial, advertising or promotional purposes (the
92
+ "License"). The License shall be deemed effective as of the date CC0 was
93
+ applied by Affirmer to the Work. Should any part of the License for any
94
+ reason be judged legally invalid or ineffective under applicable law, such
95
+ partial invalidity or ineffectiveness shall not invalidate the remainder
96
+ of the License, and in such case Affirmer hereby affirms that he or she
97
+ will not (i) exercise any of his or her remaining Copyright and Related
98
+ Rights in the Work or (ii) assert any associated claims and causes of
99
+ action with respect to the Work, in either case contrary to Affirmer's
100
+ express Statement of Purpose.
101
+
102
+ 4. Limitations and Disclaimers.
103
+
104
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
105
+ surrendered, licensed or otherwise affected by this document.
106
+ b. Affirmer offers the Work as-is and makes no representations or
107
+ warranties of any kind concerning the Work, express, implied,
108
+ statutory or otherwise, including without limitation warranties of
109
+ title, merchantability, fitness for a particular purpose, non
110
+ infringement, or the absence of latent or other defects, accuracy, or
111
+ the present or absence of errors, whether or not discoverable, all to
112
+ the greatest extent permissible under applicable law.
113
+ c. Affirmer disclaims responsibility for clearing rights of other persons
114
+ that may apply to the Work or any use thereof, including without
115
+ limitation any person's Copyright and Related Rights in the Work.
116
+ Further, Affirmer disclaims responsibility for obtaining any necessary
117
+ consents, permissions or other rights required for any use of the
118
+ Work.
119
+ d. Affirmer understands and acknowledges that Creative Commons is not a
120
+ party to this document and has no duty or obligation with respect to
121
+ this CC0 or use of the Work.
asymmetric-tiling-sd-webui-2.0/README.md ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Asymmetric Tiling for stable-diffusion-webui
2
+
3
+ An always visible script extension for [stable-diffusion-webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui/) to configure seamless image tiling independently for the X and Y axes.
4
+
5
+ To use, install this repository from the "Extensions" tab in stable-diffusion-webui, restart your server, open the `Asymmetric tiling` foldout on txt2img or img2img, and make it active, and check `Tile X` or `Tile Y` as desired. While this script is active, the `Tiling` checkbox in the main UI will be ignored.
6
+
7
+ Like existing tiling options this won't guarantee seamless tiling 100% of the time, but it should manage it for most prompts. You can check that images tile seamlessly using online tools like [Seamless Texture Check](https://www.pycheung.com/checker/).
8
+
9
+ For the old, non-extension version of this script, use the "classic_script" branch.
10
+
11
+ ## X axis tiling examples
12
+ ![00817-3274117678-midnight cityscape, stunning environment, wide-angle, massive scale, landscape, panoramic, lush vegetation, idyllic](https://user-images.githubusercontent.com/19196175/195132862-8c050327-92f3-44a4-9c02-0f11cce0b609.png)
13
+ ![01064-1316547214-(((domino run))), domino toppling, line of standing dominoes, domino cascade, domino effect, black dominos](https://user-images.githubusercontent.com/19196175/195137782-e72fc69a-14f1-4ae7-bac2-219734509aea.png)
14
+
15
+ ## Y Axis tiling examples
16
+ ![00840-2320166501-man climbing ladder, safety diagram](https://user-images.githubusercontent.com/19196175/195132867-1b36848e-135d-4103-8e10-1d760b3a0a4e.png)![01095-949590403-tree, thick branches, photograph, 80mm Sigma f1 4, studio quality, nature photography](https://user-images.githubusercontent.com/19196175/195140638-49b0a4be-fbca-45bc-8e52-6c985202ce29.png)
asymmetric-tiling-sd-webui-2.0/scripts/__pycache__/asymmetric_tiling.cpython-310.pyc ADDED
Binary file (20.9 kB). View file
 
asymmetric-tiling-sd-webui-2.0/scripts/asymmetric_tiling.py ADDED
@@ -0,0 +1,732 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+ import gradio as gr
5
+ from modules import scripts, shared, sd_samplers, sd_samplers_common, sd_samplers_kdiffusion
6
+ import k_diffusion.sampling
7
+ from k_diffusion.sampling import to_d, default_noise_sampler, get_ancestral_step
8
+ from tqdm.auto import trange
9
+ import math
10
+ import numpy as np
11
+
12
+ # ========================================================================
13
+ # КОНСТАНТЫ И КОНФИГУРАЦИЯ
14
+ # ========================================================================
15
+ MODE_OFF = "Default (Off)"
16
+ MODE_CIRCULAR = "Circular"
17
+ MODE_MIRROR = "Mirror (Reflect)"
18
+ MODE_HEXAGONAL = "Hexagonal (Staggered)"
19
+ MODE_PANORAMA = "Panorama 360°"
20
+ MODE_CUBEMAP = "Cubemap (3D)"
21
+ MODE_BLEND = "Soft Blend Edges"
22
+ MODE_ANISOTROPIC = "Anisotropic (Directional)"
23
+ MODE_POLAR = "Polar (Sphere Correct)"
24
+
25
+ # Глобальное хранилище
26
+ _ORIGINAL_METHODS_CACHE = {}
27
+ _MASK_CACHE = {}
28
+ _SAMPLER_REGISTERED = False
29
+
30
+ # ========================================================================
31
+ # KOHAKU LONYU YOG SAMPLER IMPLEMENTATION
32
+ # ========================================================================
33
+
34
+ @torch.no_grad()
35
+ def sample_kohaku_lonyu_yog(model, x, sigmas, extra_args=None, callback=None,
36
+ disable=None, s_churn=0., s_tmin=0., s_tmax=float('inf'),
37
+ s_noise=1., noise_sampler=None, eta=1.):
38
+ """
39
+ Kohaku_LoNyu_Yog Sampler - Geometric Second-Order Method
40
+
41
+ Принцип работы:
42
+ 1. Использует геометрические эвристики в 3D подпространстве
43
+ 2. Находит антипод (-x) для определения направления
44
+ 3. Вычисляет градиенты d, d2 в разных точках
45
+ 4. Усредняет (d+d2)/2 для нахождения "вектора вниз" к визуальному многообразию
46
+ 5. Корректирует позицию и вычисляет d3 для финальной траектории
47
+ 6. Применяется только на первой половине шагов (выпуклая область)
48
+
49
+ Теория: Поскольку 3D пространство - подпространство многомерного,
50
+ все операции в 3D осуществимы в высокоразмерном пространстве латентов.
51
+ """
52
+ extra_args = {} if extra_args is None else extra_args
53
+ s_in = x.new_ones([x.shape[0]])
54
+ noise_sampler = default_noise_sampler(x) if noise_sampler is None else noise_sampler
55
+
56
+ steps_total = len(sigmas) - 1
57
+ halfway_point = steps_total // 2
58
+
59
+ for i in trange(steps_total, disable=disable, desc="Kohaku Sampling"):
60
+ # Ancestral noise injection (опционально)
61
+ gamma = min(s_churn / steps_total, 2 ** 0.5 - 1) if s_tmin <= sigmas[i] <= s_tmax else 0.
62
+ sigma_hat = sigmas[i] * (gamma + 1)
63
+
64
+ if gamma > 0:
65
+ eps = torch.randn_like(x) * s_noise
66
+ x = x + eps * (sigma_hat ** 2 - sigmas[i] ** 2) ** 0.5
67
+
68
+ # Первый вызов модели - базовый градиент
69
+ denoised = model(x, sigma_hat * s_in, **extra_args)
70
+ d = to_d(x, sigma_hat, denoised)
71
+
72
+ # Вычисление шага
73
+ sigma_down, sigma_up = get_ancestral_step(sigmas[i], sigmas[i + 1], eta=eta)
74
+ dt = sigma_down - sigmas[i]
75
+
76
+ # ====== ГЕОМЕТРИЧЕСКИЙ МЕТОД (Первая половина шагов) ======
77
+ if i <= halfway_point:
78
+ # Шаг 1: Находим антипод (-x)
79
+ # Это ключевая идея: рассматриваем противоположную точку
80
+ x_antipode = -x
81
+
82
+ # Шаг 2: Вычисляем градиент d2 в антиподе
83
+ denoised2 = model(x_antipode, sigma_hat * s_in, **extra_args)
84
+ d2 = to_d(x_antipode, sigma_hat, denoised2)
85
+
86
+ # Шаг 3: Геометрическая дедукция
87
+ # Вектор (d+d2)/2 указывает "вниз" к визуальному многообразию
88
+ v_down = (d + d2) / 2
89
+
90
+ # Шаг 4: Коррекция позиции (Lookahead)
91
+ # Двигаемся к точке, которая ближе к целевой области A
92
+ x_closer = x + v_down * dt
93
+
94
+ # Шаг 5: Вычисляем скорость d3 в уточненной точке
95
+ denoised3 = model(x_closer, sigma_hat * s_in, **extra_args)
96
+ d3 = to_d(x_closer, sigma_hat, denoised3)
97
+
98
+ # Шаг 6: Финальная траектория
99
+ # (d+d3)/2 дает более точное направление к истинной цели
100
+ real_d = (d + d3) / 2
101
+ x = x + real_d * dt
102
+
103
+ # Добавляем ancestral шум
104
+ if sigma_up > 0:
105
+ x = x + noise_sampler(sigmas[i], sigmas[i + 1]) * s_noise * sigma_up
106
+
107
+ # ====== СТАНДАРТНЫЙ EULER (Вторая половина) ======
108
+ else:
109
+ # На поздних шагах геометрический метод может отклоняться
110
+ # Причина: траектория становится невыпуклой из-за деталей
111
+ x = x + d * dt
112
+
113
+ if sigma_up > 0:
114
+ x = x + noise_sampler(sigmas[i], sigmas[i + 1]) * s_noise * sigma_up
115
+
116
+ # Callback для мониторинга
117
+ if callback is not None:
118
+ callback({
119
+ 'x': x,
120
+ 'i': i,
121
+ 'sigma': sigmas[i],
122
+ 'sigma_hat': sigma_hat,
123
+ 'denoised': denoised
124
+ })
125
+
126
+ return x
127
+
128
+ # ========================================================================
129
+ # РАСШИРЕННЫЕ ФУНКЦИИ ПАДДИНГА
130
+ # ========================================================================
131
+
132
+ def get_or_create_mask(h, w, device):
133
+ """Кэширование масок для оптимизации"""
134
+ key = (h, w, str(device))
135
+ if key not in _MASK_CACHE:
136
+ row_indices = torch.arange(h, device=device).view(1, 1, h, 1)
137
+ _MASK_CACHE[key] = (row_indices % 2 == 1)
138
+ return _MASK_CACHE[key]
139
+
140
+ def compute_anisotropic_padding(input_tensor, pad_h, pad_w, angle_deg=45):
141
+ """
142
+ Анизотропный паддинг - разное поведение по диагоналям.
143
+ Эмулирует направленные материалы (дерево, металл, волокна).
144
+
145
+ Args:
146
+ angle_deg: Угол направления волокон/текстуры (0-360)
147
+ """
148
+ b, c, h, w = input_tensor.shape
149
+
150
+ # Преобразуем угол в радианы
151
+ angle_rad = math.radians(angle_deg)
152
+
153
+ # Создаем направленную маску весов
154
+ # Вычисляем "силу" паддинга в зависимости от направления
155
+ y_coords = torch.linspace(-1, 1, h, device=input_tensor.device).view(1, 1, h, 1)
156
+ x_coords = torch.linspace(-1, 1, w, device=input_tensor.device).view(1, 1, 1, w)
157
+
158
+ # Проекция на направление волокон
159
+ directional_strength = torch.abs(
160
+ x_coords * math.cos(angle_rad) + y_coords * math.sin(angle_rad)
161
+ )
162
+
163
+ # Вдоль волокон - circular, поперек - reflect
164
+ # Сначала применяем базовый circular паддинг
165
+ padded = F.pad(input_tensor, (pad_w, pad_w, pad_h, pad_h), mode='circular')
166
+
167
+ # Создаем альтернативный reflect паддинг
168
+ padded_reflect = F.pad(input_tensor, (pad_w, pad_w, pad_h, pad_h), mode='reflect')
169
+
170
+ # Смешиваем на основе направления
171
+ directional_strength_expanded = directional_strength.expand(b, c, h + 2*pad_h, w + 2*pad_w)
172
+
173
+ # Альфа-блендинг: вдоль направления больше circular, поперек больше reflect
174
+ alpha = directional_strength_expanded.clamp(0, 1)
175
+ result = padded * alpha + padded_reflect * (1 - alpha)
176
+
177
+ return result
178
+
179
+ def compute_polar_padding(input_tensor, pad_h, pad_w):
180
+ """
181
+ Полярный паддинг для сферических проекций.
182
+ Корректирует переход через полюса с учетом топологии сферы.
183
+
184
+ При переходе через Северный полюс:
185
+ - Сдвиг на W/2 (противоположная сторона сферы)
186
+ - Отражение (инверсия широты)
187
+ """
188
+ b, c, h, w = input_tensor.shape
189
+
190
+ # X-axis: стандартный circular (долгота замыкается)
191
+ x = F.pad(input_tensor, (pad_w, pad_w, 0, 0), mode='circular')
192
+
193
+ # Y-axis: полярная коррекция (широта через полюса)
194
+ shift = w // 2
195
+
196
+ # Верхний паддинг (Северный полюс)
197
+ top_strip = x[:, :, :pad_h, :] # Берем верхние строки
198
+ top_pad = torch.roll(top_strip, shifts=shift, dims=3) # Сдвиг на 180°
199
+ top_pad = torch.flip(top_pad, dims=[2]) # Отражение (инверсия)
200
+
201
+ # Нижний паддинг (Южный полюс)
202
+ bot_strip = x[:, :, -pad_h:, :]
203
+ bot_pad = torch.roll(bot_strip, shifts=shift, dims=3)
204
+ bot_pad = torch.flip(bot_pad, dims=[2])
205
+
206
+ # К��нкатенация
207
+ result = torch.cat([top_pad, x, bot_pad], dim=2)
208
+
209
+ return result
210
+
211
+ def compute_stereoscopic_padding(input_tensor, pad_h, pad_w, eye='left',
212
+ convergence=0.05, separation=0.065):
213
+ """
214
+ Стереоскопический паддинг для 3D изображений.
215
+
216
+ Args:
217
+ eye: 'left' или 'right' - какой глаз
218
+ convergence: Точка схождения (0-1, где 0.5 - центр)
219
+ separation: Межзрачковое расстояние в долях от ширины
220
+ """
221
+ b, c, h, w = input_tensor.shape
222
+
223
+ # Вычисляем сдвиг в зависимости от глаза
224
+ # Левый глаз видит правую часть объектов, правый - левую
225
+ shift_amount = int(w * separation)
226
+
227
+ if eye == 'left':
228
+ # Левый глаз: сдвиг вправо
229
+ shifted = torch.roll(input_tensor, shifts=shift_amount, dims=3)
230
+ else:
231
+ # Правый глаз: сдвиг влево
232
+ shifted = torch.roll(input_tensor, shifts=-shift_amount, dims=3)
233
+
234
+ # Создаем карту глубины на основе положения пикселя
235
+ # Центр имеет меньший сдвиг (ближе), края больший (дальше)
236
+ x_coords = torch.linspace(0, 1, w, device=input_tensor.device).view(1, 1, 1, w)
237
+ depth_map = torch.abs(x_coords - convergence).expand(b, c, h, w)
238
+
239
+ # Смешиваем оригинал и сдвинутый на основе глубины
240
+ # Ближние объекты - больше сдвиг, дальние - меньше
241
+ alpha = depth_map.clamp(0, 1)
242
+ stereo_adjusted = input_tensor * (1 - alpha) + shifted * alpha
243
+
244
+ # Применяем стандартный circular паддинг
245
+ padded = F.pad(stereo_adjusted, (pad_w, pad_w, pad_h, pad_h), mode='circular')
246
+
247
+ return padded
248
+
249
+ def compute_hex_padding_x(input_tensor, pad_l, pad_r):
250
+ """Гексагональный паддинг (из v2.0)"""
251
+ b, c, h, w = input_tensor.shape
252
+ odd_mask = get_or_create_mask(h, w, input_tensor.device).expand(b, c, h, w)
253
+
254
+ shift = w // 2
255
+ input_shifted = torch.roll(input_tensor, shifts=shift, dims=3)
256
+ source = torch.where(odd_mask, input_shifted, input_tensor)
257
+
258
+ left_pad = source[:, :, :, -pad_l:]
259
+ right_pad = source[:, :, :, :pad_r]
260
+
261
+ return torch.cat([left_pad, input_tensor, right_pad], dim=3)
262
+
263
+ def compute_blend_padding(input_tensor, pad_h, pad_w, blend_strength=0.1):
264
+ """Мягкое смешивание краев (из v2.0)"""
265
+ b, c, h, w = input_tensor.shape
266
+ padded = F.pad(input_tensor, (pad_w, pad_w, pad_h, pad_h), mode='circular')
267
+
268
+ blend_w = int(w * blend_strength)
269
+ blend_h = int(h * blend_strength)
270
+
271
+ if blend_h > 0:
272
+ # Вертикальное затухание
273
+ top_mask = torch.linspace(0, 1, blend_h, device=input_tensor.device)
274
+ top_mask = top_mask.view(1, 1, blend_h, 1).expand(b, c, blend_h, w + 2*pad_w)
275
+ padded[:, :, pad_h:pad_h+blend_h, :] *= top_mask
276
+
277
+ bottom_mask = torch.linspace(1, 0, blend_h, device=input_tensor.device)
278
+ bottom_mask = bottom_mask.view(1, 1, blend_h, 1).expand(b, c, blend_h, w + 2*pad_w)
279
+ padded[:, :, pad_h+h-blend_h:pad_h+h, :] *= bottom_mask
280
+
281
+ if blend_w > 0:
282
+ # Горизонтальное затухание
283
+ left_mask = torch.linspace(0, 1, blend_w, device=input_tensor.device)
284
+ left_mask = left_mask.view(1, 1, 1, blend_w).expand(b, c, h + 2*pad_h, blend_w)
285
+ padded[:, :, :, pad_w:pad_w+blend_w] *= left_mask
286
+
287
+ right_mask = torch.linspace(1, 0, blend_w, device=input_tensor.device)
288
+ right_mask = right_mask.view(1, 1, 1, blend_w).expand(b, c, h + 2*pad_h, blend_w)
289
+ padded[:, :, :, pad_w+w-blend_w:pad_w+w] *= right_mask
290
+
291
+ return padded
292
+
293
+ # ========================================================================
294
+ # ГЛАВНАЯ ФУНКЦИЯ ПАДДИНГА
295
+ # ========================================================================
296
+
297
+ def custom_padding_forward(input_tensor, weight, bias, stride, padding, dilation, groups, params):
298
+ """
299
+ Универсальная функция паддинга с поддержкой всех режимов v3.0
300
+ """
301
+ try:
302
+ # Проверка шагов
303
+ current_step = getattr(shared.state, 'sampling_step', 0)
304
+ if not (params['start_step'] <= current_step <= params['end_step']):
305
+ return F.conv2d(input_tensor, weight, bias, stride, padding, dilation, groups)
306
+
307
+ # Вычисление размеров паддинга
308
+ k_h, k_w = weight.shape[2], weight.shape[3]
309
+ d_h, d_w = (dilation, dilation) if isinstance(dilation, int) else dilation
310
+
311
+ if isinstance(padding, str):
312
+ if padding == 'same':
313
+ req_pad_h = ((k_h - 1) * d_h) // 2
314
+ req_pad_w = ((k_w - 1) * d_w) // 2
315
+ elif padding == 'valid':
316
+ req_pad_h = req_pad_w = 0
317
+ else:
318
+ req_pad_h = req_pad_w = 1
319
+ elif isinstance(padding, int):
320
+ req_pad_h = req_pad_w = padding
321
+ elif isinstance(padding, (tuple, list)):
322
+ req_pad_h, req_pad_w = padding[0], padding[1]
323
+ else:
324
+ req_pad_h = req_pad_w = 0
325
+
326
+ x = input_tensor
327
+ mode_x = params['mode_x']
328
+ mode_y = params['mode_y']
329
+
330
+ # ====== СПЕЦИАЛЬНЫЕ РЕЖИМЫ ======
331
+
332
+ # Стереоскопия
333
+ if params.get('stereo_enabled', False):
334
+ eye = params.get('stereo_eye', 'left')
335
+ convergence = params.get('stereo_convergence', 0.5)
336
+ separation = params.get('stereo_separation', 0.065)
337
+ x = compute_stereoscopic_padding(x, req_pad_h, req_pad_w, eye, convergence, separation)
338
+
339
+ # Анизотропный
340
+ elif mode_x == MODE_ANISOTROPIC or mode_y == MODE_ANISOTROPIC:
341
+ angle = params.get('anisotropic_angle', 45)
342
+ x = compute_anisotropic_padding(x, req_pad_h, req_pad_w, angle)
343
+
344
+ # Полярный (сферический)
345
+ elif mode_x == MODE_POLAR or mode_y == MODE_POLAR:
346
+ x = compute_polar_padding(x, req_pad_h, req_pad_w)
347
+
348
+ # Панорама
349
+ elif mode_x == MODE_PANORAMA or mode_y == MODE_PANORAMA:
350
+ x = F.pad(x, (req_pad_w, req_pad_w, 0, 0), mode='circular')
351
+ x = F.pad(x, (0, 0, req_pad_h, req_pad_h), mode='circular')
352
+
353
+ # Blend
354
+ elif mode_x == MODE_BLEND or mode_y == MODE_BLEND:
355
+ blend_str = params.get('blend_strength', 0.1)
356
+ x = compute_blend_padding(x, req_pad_h, req_pad_w, blend_str)
357
+
358
+ # Стандартные режимы (раздельно по осям)
359
+ else:
360
+ # Ось Y
361
+ if mode_y == MODE_CIRCULAR:
362
+ x = F.pad(x, (0, 0, req_pad_h, req_pad_h), mode='circular')
363
+ elif mode_y == MODE_MIRROR:
364
+ x = F.pad(x, (0, 0, req_pad_h, req_pad_h), mode='reflect')
365
+ elif mode_y == MODE_HEXAGONAL:
366
+ x = F.pad(x, (0, 0, req_pad_h, req_pad_h), mode='circular')
367
+ else:
368
+ x = F.pad(x, (0, 0, req_pad_h, req_pad_h), mode='constant', value=0)
369
+
370
+ # Ось X
371
+ if mode_x == MODE_CIRCULAR:
372
+ x = F.pad(x, (req_pad_w, req_pad_w, 0, 0), mode='circular')
373
+ elif mode_x == MODE_MIRROR:
374
+ x = F.pad(x, (req_pad_w, req_pad_w, 0, 0), mode='reflect')
375
+ elif mode_x == MODE_HEXAGONAL:
376
+ x = compute_hex_padding_x(x, req_pad_w, req_pad_w)
377
+ else:
378
+ x = F.pad(x, (req_pad_w, req_pad_w, 0, 0), mode='constant', value=0)
379
+
380
+ # Мультиразрешение (адаптивная интенсивность)
381
+ if params.get('multires_enabled', False):
382
+ step_ratio = current_step / max(params['end_step'], 1)
383
+ if step_ratio < 0.3:
384
+ x_default = F.pad(input_tensor, (req_pad_w, req_pad_w, req_pad_h, req_pad_h),
385
+ mode='constant', value=0)
386
+ alpha = step_ratio * 3
387
+ x = x * alpha + x_default * (1 - alpha)
388
+
389
+ # Выполнение свертки
390
+ return F.conv2d(x, weight, bias, stride, 0, dilation, groups)
391
+
392
+ except Exception as e:
393
+ print(f"Advanced Tiling v3 Error: {e}")
394
+ return F.conv2d(input_tensor, weight, bias, stride, padding, dilation, groups)
395
+
396
+ # ========================================================================
397
+ # РЕГИСТРАЦИЯ KOHAKU SAMPLER
398
+ # ========================================================================
399
+
400
+ def register_kohaku_sampler():
401
+ """Регистрирует Kohaku_LoNyu_Yog сэмплер в WebUI"""
402
+ global _SAMPLER_REGISTERED
403
+
404
+ if _SAMPLER_REGISTERED:
405
+ return
406
+
407
+ # Проверка на дубликаты
408
+ if any(s.name == 'Kohaku_LoNyu_Yog' for s in sd_samplers.all_samplers):
409
+ _SAMPLER_REGISTERED = True
410
+ return
411
+
412
+ # Добавляем функцию в k_diffusion.sampling
413
+ if not hasattr(k_diffusion.sampling, 'sample_kohaku_lonyu_yog'):
414
+ setattr(k_diffusion.sampling, 'sample_kohaku_lonyu_yog', sample_kohaku_lonyu_yog)
415
+
416
+ # Создаем SamplerData
417
+ sampler_data = sd_samplers_common.SamplerData(
418
+ name='Kohaku_LoNyu_Yog',
419
+ constructor=lambda model: sd_samplers_kdiffusion.KDiffusionSampler('sample_kohaku_lonyu_yog', model),
420
+ aliases=['kohaku', 'lonyu'],
421
+ options={'second_order': True}
422
+ )
423
+
424
+ # Регистрируем
425
+ sd_samplers.all_samplers.append(sampler_data)
426
+ sd_samplers.all_samplers_map = {x.name: x for x in sd_samplers.all_samplers}
427
+
428
+ _SAMPLER_REGISTERED = True
429
+ print("✓ Kohaku_LoNyu_Yog sampler registered successfully!")
430
+
431
+ # ========================================================================
432
+ # КЛАСС СКРИПТА
433
+ # ========================================================================
434
+
435
+ class AdvancedTilingScriptV3(scripts.Script):
436
+ def title(self):
437
+ return "Advanced Tiling v3.0 PRO (Kohaku + Stereo + Aniso)"
438
+
439
+ def show(self, is_img2img):
440
+ return scripts.AlwaysVisible
441
+
442
+ def ui(self, is_img2img):
443
+ with gr.Accordion("🚀 Advanced Tiling v3.0 PRO Edition", open=False):
444
+ with gr.Row():
445
+ enabled = gr.Checkbox(label="Enable Tiling", value=False)
446
+ use_kohaku = gr.Checkbox(label="Use Kohaku_LoNyu_Yog Sampler", value=False,
447
+ info="Geometric second-order method for better seamless quality")
448
+
449
+ with gr.Tabs():
450
+ with gr.Tab("🎨 Basic Modes"):
451
+ with gr.Row():
452
+ mode_x = gr.Dropdown(
453
+ label="Mode X",
454
+ choices=[MODE_OFF, MODE_CIRCULAR, MODE_MIRROR, MODE_HEXAGONAL],
455
+ value=MODE_CIRCULAR
456
+ )
457
+ mode_y = gr.Dropdown(
458
+ label="Mode Y",
459
+ choices=[MODE_OFF, MODE_CIRCULAR, MODE_MIRROR, MODE_HEXAGONAL],
460
+ value=MODE_CIRCULAR
461
+ )
462
+
463
+ with gr.Row():
464
+ multires = gr.Checkbox(label="Multi-Resolution Mode", value=False)
465
+ use_blend = gr.Checkbox(label="Soft Blend Edges", value=False)
466
+ blend_strength = gr.Slider(0.0, 0.5, step=0.05, label="Blend Strength", value=0.1)
467
+
468
+ with gr.Tab("🌐 Advanced Modes"):
469
+ gr.Markdown("**Специальные режимы для сложных топологий**")
470
+
471
+ with gr.Row():
472
+ use_panorama = gr.Checkbox(label="Panorama 360°", value=False)
473
+ use_polar = gr.Checkbox(label="Polar (Sphere Correct)", value=False,
474
+ info="Correct pole transitions for equirectangular")
475
+
476
+ with gr.Row():
477
+ use_anisotropic = gr.Checkbox(label="Anisotropic (Directional)", value=False,
478
+ info="Different behavior along diagonals")
479
+ aniso_angle = gr.Slider(0, 360, step=15, label="Anisotropic Angle", value=45,
480
+ info="Direction of fibers/texture (degrees)")
481
+
482
+ with gr.Tab("🎭 Stereoscopic 3D"):
483
+ gr.Markdown("**Генерация стереопар для 3D контента**")
484
+
485
+ with gr.Row():
486
+ stereo_enabled = gr.Checkbox(label="Enable Stereoscopic Mode", value=False)
487
+ stereo_eye = gr.Radio(
488
+ label="Eye",
489
+ choices=["left", "right", "both"],
490
+ value="left",
491
+ info="Which eye view to generate"
492
+ )
493
+
494
+ with gr.Row():
495
+ stereo_separation = gr.Slider(0.0, 0.15, step=0.005,
496
+ label="IPD (Inter-Pupillary Distance)",
497
+ value=0.065,
498
+ info="Eye separation as fraction of width")
499
+ stereo_convergence = gr.Slider(0.0, 1.0, step=0.05,
500
+ label="Convergence Point",
501
+ value=0.5,
502
+ info="Depth at which eyes converge")
503
+
504
+ gr.Markdown("""
505
+ **💡 Совет для стерео:**
506
+ - Генерируйте сначала левый глаз
507
+ - Затем правый с теми же параметрами
508
+ - Используйте Side-by-Side или Anaglyph компоновку
509
+ """)
510
+
511
+ with gr.Row():
512
+ start_step = gr.Slider(0, 150, step=1, label="Start Step", value=0)
513
+ end_step = gr.Slider(0, 150, step=1, label="End Step", value=150)
514
+
515
+ with gr.Row():
516
+ patch_vae = gr.Checkbox(label="Patch VAE Decoder", value=True,
517
+ info="Fix seams in final pixel decode")
518
+
519
+ # Preview Tool
520
+ with gr.Accordion("🔍 Preview & Info", open=False):
521
+ with gr.Tabs():
522
+ with gr.Tab("Visual Preview"):
523
+ preview_btn = gr.Button("Generate Preview", variant="primary")
524
+ preview_img = gr.Image(label="Preview Grid (2x2)")
525
+
526
+ preview_btn.click(
527
+ fn=self.generate_preview,
528
+ inputs=[mode_x, mode_y, use_anisotropic, aniso_angle,
529
+ stereo_enabled, stereo_eye],
530
+ outputs=preview_img
531
+ )
532
+
533
+ with gr.Tab("Kohaku Info"):
534
+ gr.Markdown("""
535
+ ## 🔬 Kohaku_LoNyu_Yog Принцип работы
536
+
537
+ **Геометрическая интерпретация:**
538
+ 1. Трёхмерное пространство - подпространство многомерного латентного
539
+ 2. Находим антипод `-x` (противоположная точка)
540
+ 3. Вычисляем градиенты `d` и `d2`
541
+ 4. `(d+d2)/2` - вектор к визуальному многообразию
542
+ 5. Корректируем позицию: `x_new = x + (d+d2)/2 * dt`
543
+ 6. Финальный градиент `d3` в новой точке
544
+ 7. Итоговый шаг: `(d+d3)/2`
545
+
546
+ **Применение только на первой половине шагов** - на поздних стадиях
547
+ траектория становится невыпуклой из-за деталей, метод отклоняется.
548
+
549
+ **NFE (Function Evaluations):** ~3-4 на шаг (дороже стандартного Euler)
550
+ **Рекомендуемые шаги:** 8-12 (вместо 20-30)
551
+ """)
552
+
553
+ return [enabled, mode_x, mode_y, start_step, end_step, multires,
554
+ use_panorama, use_polar, use_blend, blend_strength,
555
+ use_anisotropic, aniso_angle,
556
+ stereo_enabled, stereo_eye, stereo_separation, stereo_convergence,
557
+ patch_vae, use_kohaku]
558
+
559
+ def generate_preview(self, mx, my, use_aniso, aniso_angle, stereo, eye):
560
+ """Генерация preview с учетом новых режимов"""
561
+ h, w = 256, 256
562
+ img = np.zeros((h, w, 3), dtype=np.uint8)
563
+
564
+ # Базовый градиент
565
+ for i in range(h):
566
+ for j in range(w):
567
+ col = (i + j) % 255
568
+ if i < 5 or i > h-5 or j < 5 or j > w-5:
569
+ img[i, j] = [255, 50, 50]
570
+ else:
571
+ img[i, j] = [col, col//2, 255-col]
572
+
573
+ def get_tile(r, c):
574
+ tile = img.copy()
575
+
576
+ if use_aniso:
577
+ # Визуализация анизотропии
578
+ angle_rad = np.radians(aniso_angle)
579
+ for i in range(h):
580
+ for j in range(w):
581
+ # Создаем направленный паттерн
582
+ dist = abs((j-w/2)*np.cos(angle_rad) + (i-h/2)*np.sin(angle_rad))
583
+ tile[i, j] = tile[i, j] * (0.5 + 0.5 * np.sin(dist * 0.1))
584
+
585
+ elif stereo:
586
+ # Эмуляция сдвига стерео
587
+ shift = 10 if eye == 'left' else -10
588
+ tile = np.roll(tile, shift, axis=1)
589
+
590
+ else:
591
+ if mx == MODE_MIRROR and c % 2 != 0:
592
+ tile = np.fliplr(tile)
593
+ if my == MODE_MIRROR and r % 2 != 0:
594
+ tile = np.flipud(tile)
595
+ if mx == MODE_HEXAGONAL and r % 2 != 0:
596
+ tile = np.roll(tile, w // 2, axis=1)
597
+
598
+ return tile.astype(np.uint8)
599
+
600
+ # Сборка 2x2
601
+ canvas = np.zeros((h*2, w*2, 3), dtype=np.uint8)
602
+ for r in range(2):
603
+ for c in range(2):
604
+ canvas[r*h:(r+1)*h, c*w:(c+1)*w] = get_tile(r, c)
605
+
606
+ return canvas
607
+
608
+ def process(self, p, enabled, mode_x, mode_y, start_step, end_step, multires,
609
+ use_panorama, use_polar, use_blend, blend_strength,
610
+ use_anisotropic, aniso_angle,
611
+ stereo_enabled, stereo_eye, stereo_separation, stereo_convergence,
612
+ patch_vae, use_kohaku):
613
+
614
+ if not enabled:
615
+ return
616
+
617
+ # Валидация
618
+ if start_step > end_step:
619
+ start_step, end_step = end_step, start_step
620
+
621
+ # Регистрация Kohaku сэмплера
622
+ if use_kohaku:
623
+ register_kohaku_sampler()
624
+ # Автоматически переключаем на Kohaku если выбран другой
625
+ if p.sampler_name != 'Kohaku_LoNyu_Yog':
626
+ print(f"Switching sampler from {p.sampler_name} to Kohaku_LoNyu_Yog")
627
+ p.sampler_name = 'Kohaku_LoNyu_Yog'
628
+
629
+ # Применение специальных режимов
630
+ if use_panorama:
631
+ mode_x = mode_y = MODE_PANORAMA
632
+ if use_polar:
633
+ mode_x = mode_y = MODE_POLAR
634
+ if use_blend:
635
+ mode_x = mode_y = MODE_BLEND
636
+ if use_anisotropic:
637
+ mode_x = mode_y = MODE_ANISOTROPIC
638
+
639
+ # Параметры
640
+ params = {
641
+ 'mode_x': mode_x,
642
+ 'mode_y': mode_y,
643
+ 'start_step': start_step,
644
+ 'end_step': end_step,
645
+ 'multires_enabled': multires,
646
+ 'blend_strength': blend_strength,
647
+ 'anisotropic_angle': aniso_angle,
648
+ 'stereo_enabled': stereo_enabled,
649
+ 'stereo_eye': stereo_eye,
650
+ 'stereo_separation': stereo_separation,
651
+ 'stereo_convergence': stereo_convergence
652
+ }
653
+
654
+ # Патчинг UNet
655
+ unet = self.get_unet(p)
656
+ if unet:
657
+ self.restore_original(unet)
658
+ count_unet = self.patch_conv_layers(unet, params)
659
+ print(f"✓ Patched {count_unet} UNet Conv2d layers")
660
+
661
+ # Патчинг VAE
662
+ if patch_vae and hasattr(p.sd_model, 'first_stage_model'):
663
+ vae = p.sd_model.first_stage_model
664
+ count_vae = self.patch_conv_layers(vae, params)
665
+ print(f"✓ Patched {count_vae} VAE Conv2d layers")
666
+
667
+ mode_str = f"{mode_x}/{mode_y}"
668
+ if stereo_enabled:
669
+ mode_str += f" + Stereo({stereo_eye})"
670
+ if use_kohaku:
671
+ mode_str += " + Kohaku"
672
+
673
+ print(f"✓ Advanced Tiling v3.0: Mode={mode_str}, Steps={start_step}-{end_step}")
674
+
675
+ def patch_conv_layers(self, module, params):
676
+ """Патчинг с исправленным замыканием"""
677
+ count = 0
678
+ for layer in module.modules():
679
+ if isinstance(layer, torch.nn.Conv2d):
680
+ if layer.kernel_size == (1, 1) or layer.kernel_size == 1:
681
+ continue
682
+
683
+ if layer not in _ORIGINAL_METHODS_CACHE:
684
+ _ORIGINAL_METHODS_CACHE[layer] = layer._conv_forward
685
+
686
+ def make_patched(mod):
687
+ def patched(input, weight, bias):
688
+ return custom_padding_forward(
689
+ input, weight, bias,
690
+ mod.stride, mod.padding, mod.dilation, mod.groups,
691
+ params
692
+ )
693
+ return patched
694
+
695
+ layer._conv_forward = make_patched(layer)
696
+ count += 1
697
+
698
+ return count
699
+
700
+ def get_unet(self, p):
701
+ """Универсальная детекция UNet"""
702
+ if hasattr(p.sd_model, 'forge_objects') and hasattr(p.sd_model.forge_objects, 'unet'):
703
+ return p.sd_model.forge_objects.unet
704
+ elif hasattr(p.sd_model, 'model') and hasattr(p.sd_model.model, 'diffusion_model'):
705
+ return p.sd_model.model.diffusion_model
706
+ return p.sd_model
707
+
708
+ def postprocess(self, p, processed, *args):
709
+ """Восстановление"""
710
+ unet = self.get_unet(p)
711
+ if unet:
712
+ self.restore_original(unet)
713
+
714
+ if hasattr(p.sd_model, 'first_stage_model'):
715
+ self.restore_original(p.sd_model.first_stage_model)
716
+
717
+ def restore_original(self, module):
718
+ """Восстановление оригинальных методов"""
719
+ if not _ORIGINAL_METHODS_CACHE:
720
+ return
721
+
722
+ restored = 0
723
+ for layer in list(_ORIGINAL_METHODS_CACHE.keys()):
724
+ if hasattr(layer, '_conv_forward'):
725
+ layer._conv_forward = _ORIGINAL_METHODS_CACHE[layer]
726
+ restored += 1
727
+
728
+ _ORIGINAL_METHODS_CACHE.clear()
729
+ _MASK_CACHE.clear()
730
+
731
+ if restored > 0:
732
+ print(f"✓ Restored {restored} layers to original state")
asymmetric-tiling-sd-webui-2.0/Руководство Advanced Tiling v3.0 - Новые функции.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:53b070cef1e7c4538d178cc577397b5571dfd041707a607ae0a0738545bf633b
3
+ size 1214274
asymmetric-tiling-sd-webui-2.0/Руководство по новым функциям.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ddaf444239f26df5f49f65ff0bb28ae3fade21ca765e6de9b8c990940f3e21eb
3
+ size 985644