dikdimon commited on
Commit
b3519ad
·
verified ·
1 Parent(s): 30fc68c

Upload sd-webui-hires-i2i using SD-Hub

Browse files
.gitattributes CHANGED
@@ -142,3 +142,5 @@ DWPose/resources/generation.jpg filter=lfs diff=lfs merge=lfs -text
142
  DWPose/resources/iron.gif filter=lfs diff=lfs merge=lfs -text
143
  DWPose/resources/jay_pose.jpg filter=lfs diff=lfs merge=lfs -text
144
  DWPose/resources/lalaland.gif filter=lfs diff=lfs merge=lfs -text
 
 
 
142
  DWPose/resources/iron.gif filter=lfs diff=lfs merge=lfs -text
143
  DWPose/resources/jay_pose.jpg filter=lfs diff=lfs merge=lfs -text
144
  DWPose/resources/lalaland.gif filter=lfs diff=lfs merge=lfs -text
145
+ sd-webui-hires-i2i/img/off.jpg filter=lfs diff=lfs merge=lfs -text
146
+ sd-webui-hires-i2i/img/on.jpg filter=lfs diff=lfs merge=lfs -text
sd-webui-hires-i2i/.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ __pycache__
sd-webui-hires-i2i/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Haoming
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
sd-webui-hires-i2i/README.md ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SD Webui Hires. Fix img2img
2
+ This is an Extension for the [Automatic1111 Webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui), which allows you to upscale your input image with an Upscaler before the **img2img** pipeline.
3
+
4
+ > Compatible with [Forge](https://github.com/lllyasviel/stable-diffusion-webui-forge)
5
+
6
+ By default, the Webui uses the `LANCZOS` algorithm to resize when the generation dimension is different from the size of your input image. You can also set the `Upscaler for img2img` option in the Settings to use an Upscaler instead. Alternatively, you can also send the image to the **Extras** tab and upscale first, then send the result back to the **img2img** tab.
7
+
8
+ **TL;DR:** This Extension just simplifies the processes
9
+
10
+ <table>
11
+ <thead align="center">
12
+ <tr>
13
+ <td><b>Extension</b></td>
14
+ <td>Off</td>
15
+ <td>On</td>
16
+ </tr>
17
+ </thead>
18
+ <tbody align="center">
19
+ <tr>
20
+ <td><b>Result</b></td>
21
+ <td><img src="img/off.jpg"></td>
22
+ <td><img src="img/on.jpg"></td>
23
+ </tr>
24
+ </tbody>
25
+ </table>
26
+
27
+ <details>
28
+ <summary>Infotext</summary>
29
+
30
+ 1. Generate `a photo of a dog` in `512x512`
31
+ 2. Resize to `256x256` via **Paint 3D**
32
+ 3. Upscale to `1024x1024` via **img2img**, with the Extension disabled and enabled respectively
33
+ - **Upscaler:** `4xLSDIRplusC`
34
+
35
+ </details>
sd-webui-hires-i2i/img/off.jpg ADDED

Git LFS Details

  • SHA256: bc3febdf4407bbbf1111257679cc804fbe1cb698df765492bc058535bcbbe906
  • Pointer size: 131 Bytes
  • Size of remote file: 302 kB
sd-webui-hires-i2i/img/on.jpg ADDED

Git LFS Details

  • SHA256: 02135ce6ba6078e488340e2c7b4d3a51c636c56aecaba687b9cde5b52cce67ef
  • Pointer size: 131 Bytes
  • Size of remote file: 422 kB
sd-webui-hires-i2i/scripts/__pycache__/hires_fix_i2i.cpython-310.pyc ADDED
Binary file (6.92 kB). View file
 
sd-webui-hires-i2i/scripts/hires_fix_i2i.py ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Hires. Fix (txt2img + img2img) — единый скрипт с кнопкой и выбором режима.
4
+ - img2img: апскейл исходного init_image ДО генерации (режим Before).
5
+ - txt2img: апскейл результата ПОСЛЕ генерации (режим After).
6
+ Не падает в txt2img (там нет p.init_images).
7
+
8
+ Требования/среда:
9
+ - Python 3.10+
10
+ - AUTOMATIC1111 SD WebUI (scripts API: ui, setup, process, postprocess)
11
+ - Gradio 3.x (как в A1111)
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import re
17
+ from typing import Optional, Tuple
18
+
19
+ from PIL import Image
20
+ import gradio as gr
21
+
22
+ from modules import scripts, shared, images
23
+ from modules.ui_components import InputAccordion
24
+
25
+
26
+ class HiresUnified(scripts.Script):
27
+ """
28
+ Единый скрипт, совместимый с txt2img и img2img.
29
+
30
+ Жизненный цикл A1111:
31
+ - show(is_img2img) → когда показывать панель
32
+ - ui(is_img2img) → построение UI; возвращаем список компонентов
33
+ - setup(p, *args) → до запуска генерации; можно менять p
34
+ - process(p, *args) → хук до генерации (без processed)
35
+ - postprocess(p, processed, *args)
36
+ → хук после генерации; тут можно менять processed.images
37
+ """
38
+
39
+ def __init__(self):
40
+ super().__init__()
41
+ # Кэш апскейла init-изображения (режим Before) — чтобы не апскейлить повторно
42
+ self._before_cache_key: Optional[Tuple[int, str, float]] = None
43
+ self._before_cache_img: Optional[Image.Image] = None
44
+
45
+ # Отложенный апскейл (режим After / txt2img) — параметры сохраняем в setup()
46
+ self._defer_after: bool = False
47
+ self._defer_upscaler_name: str = "None"
48
+ self._defer_ratio: float = 1.0
49
+
50
+ # ──────────────────────────────────────────────────────────────────────
51
+ # Метаданные/показ панели
52
+ # ──────────────────────────────────────────────────────────────────────
53
+ def title(self):
54
+ return "Hires. Fix (txt2img + img2img)"
55
+
56
+ def show(self, is_img2img):
57
+ # Делаем доступным и в txt2img, и в img2img
58
+ return scripts.AlwaysVisible
59
+
60
+ # ──────────────────────────────────────────────────────────────────────
61
+ # UI (Gradio)
62
+ # ──────────────────────────────────────────────────────────────────────
63
+ def ui(self, is_img2img):
64
+ """
65
+ Порядок возвращаемых компонентов должен соответствовать порядку
66
+ параметров, которые будет получать setup()/process()/postprocess().
67
+ """
68
+ default_upscaler = shared.sd_upscalers[0].name if shared.sd_upscalers else "None"
69
+ default_ratio = 2.0
70
+
71
+ # Удобный дефолт: в img2img работаем "Before", в txt2img — "After"
72
+ default_after = not is_img2img
73
+
74
+ with InputAccordion(value=False, label=self.title()) as enable:
75
+ with gr.Row():
76
+ upscaler = gr.Dropdown(
77
+ label="Upscaler",
78
+ choices=[x.name for x in shared.sd_upscalers] or ["None"],
79
+ value=default_upscaler,
80
+ scale=3,
81
+ )
82
+ ratio = gr.Slider(
83
+ minimum=1.0, maximum=8.0, step=0.5,
84
+ label="Resize by (scale factor)",
85
+ value=default_ratio,
86
+ scale=1,
87
+ )
88
+
89
+ with gr.Row():
90
+ process_after = gr.Checkbox(
91
+ label="Process after generation (txt2img-friendly)",
92
+ value=default_after,
93
+ info="ON: апскейл результата (подходит для txt2img). OFF: апскейл init-изображения (img2img).",
94
+ )
95
+ suggest_btn = gr.Button(
96
+ value="🔍 Suggest scale from upscaler name",
97
+ variant="secondary"
98
+ )
99
+
100
+ info_md = gr.Markdown(value="", visible=True)
101
+
102
+ # Авто-под��тановка масштаба из имени апскейлера (2x/4x и т.п.)
103
+ def _auto_scale_from_name(upscaler_name: str):
104
+ # Ищем '2x', '4X', 'x3' и т.п.
105
+ m = re.search(r"(\d)\s*[xX]|[xX]\s*(\d)", (upscaler_name or "").strip())
106
+ if not m:
107
+ return gr.update(), "No explicit scale found in upscaler name."
108
+ s = int((m.group(1) or m.group(2)))
109
+ return gr.update(value=float(s)), f"Scale set to **{s}x** from upscaler name."
110
+
111
+ upscaler.change(
112
+ fn=lambda name: _auto_scale_from_name(name)[0],
113
+ inputs=[upscaler],
114
+ outputs=[ratio],
115
+ show_progress="hidden",
116
+ )
117
+
118
+ suggest_btn.click(
119
+ fn=_auto_scale_from_name,
120
+ inputs=[upscaler],
121
+ outputs=[ratio, info_md],
122
+ )
123
+
124
+ # Инфотекст для "Copy params"
125
+ self.paste_field_names = []
126
+ self.infotext_fields = [
127
+ (enable, "Hires. Fix Enabled"),
128
+ (upscaler, "Hires Upscaler"),
129
+ (ratio, "Hires Scale"),
130
+ (process_after, "Hires After Mode"),
131
+ ]
132
+ for _, name in self.infotext_fields:
133
+ self.paste_field_names.append(name)
134
+
135
+ # Порядок критичен — именно он попадёт в setup/process/postprocess
136
+ return [enable, upscaler, ratio, process_after]
137
+
138
+ # ──────────────────────────────────────────────────────────────────────
139
+ # Вспомогательные
140
+ # ──────────────────────────────────────────────────────────────────────
141
+ @staticmethod
142
+ def _get_image_bytes_key(img: Image.Image) -> int:
143
+ # Стабильный ключ по содержимому изображения
144
+ try:
145
+ return hash(img.tobytes())
146
+ except Exception:
147
+ return hash(img.convert("RGB").tobytes())
148
+
149
+ @staticmethod
150
+ def _resize_with_upscaler(img: Image.Image, ratio: float, upscaler_name: str) -> Image.Image:
151
+ # Единая точка апскейла через A1111 helper
152
+ if ratio <= 1.0 or upscaler_name == "None":
153
+ return img
154
+ w, h = img.width, img.height
155
+ new_w, new_h = int(w * ratio), int(h * ratio)
156
+ out = images.resize_image( # resize_mode=0 ("just resize"), но с выбранным апскейлером
157
+ 0, img, new_w, new_h, upscaler_name
158
+ )
159
+ return out.convert("RGB")
160
+
161
+ # ──────────────────────────────────────────────────────────────────────
162
+ # setup(): меняем p для img2img (Before) или откладываем на postprocess (After)
163
+ # ──────────────────────────────────────────────────────────────────────
164
+ def setup(
165
+ self,
166
+ p,
167
+ enable: bool,
168
+ upscaler_name: str,
169
+ ratio: float,
170
+ process_after: bool,
171
+ *args,
172
+ **kwargs,
173
+ ):
174
+ # Инфотекст (на стороне p)
175
+ if getattr(p, "extra_generation_params", None) is not None:
176
+ p.extra_generation_params.update({
177
+ "Hires. Fix Enabled": bool(enable),
178
+ "Hires Upscaler": str(upscaler_name),
179
+ "Hires Scale": float(ratio),
180
+ "Hires After Mode": bool(process_after),
181
+ })
182
+
183
+ # Если выключено — ничего не делаем
184
+ if not enable or upscaler_name == "None":
185
+ self._defer_after = False
186
+ return
187
+
188
+ # Если есть init_images И выбран режим "Before" → апскейлим init ДО генерации
189
+ if getattr(p, "init_images", None) and p.init_images and not process_after:
190
+ init_image = p.init_images[0]
191
+ key = (self._get_image_bytes_key(init_image), upscaler_name, float(ratio))
192
+ if key != self._before_cache_key:
193
+ up = self._resize_with_upscaler(init_image, ratio, upscaler_name)
194
+ self._before_cache_key = key
195
+ self._before_cache_img = up
196
+ if self._before_cache_img is not None:
197
+ p.init_images[0] = self._before_cache_img
198
+ # Ничего не откладываем на postprocess
199
+ self._defer_after = False
200
+ else:
201
+ # txt2img (нет init_images) или пользователь принудил "After"
202
+ self._defer_after = True
203
+ self._defer_upscaler_name = upscaler_name
204
+ self._defer_ratio = float(ratio)
205
+
206
+ # ──────────────────────────────────────────────────────────────────────
207
+ # process(): корректная сигнатура (никаких processed). Нам здесь ничего не нужно.
208
+ # ──────────────────────────────────────────────────────────────────────
209
+ def process(
210
+ self,
211
+ p,
212
+ enable: bool,
213
+ upscaler_name: str,
214
+ ratio: float,
215
+ process_after: bool,
216
+ *args,
217
+ **kwargs,
218
+ ):
219
+ # Ничего не делаем в предгенерационном хуке — вся "Before" логика уже в setup().
220
+ return
221
+
222
+ # ──────────────────────────────────────────────────────────────────────
223
+ # postprocess(): делаем отложенный апскейл результата (After / txt2img)
224
+ # ──────────────────────────────────────────────────────────────────────
225
+ def postprocess(
226
+ self,
227
+ p,
228
+ processed,
229
+ enable: bool,
230
+ upscaler_name: str,
231
+ ratio: float,
232
+ process_after: bool,
233
+ *args,
234
+ **kwargs,
235
+ ):
236
+ if not enable:
237
+ return
238
+
239
+ # Выполняем отложенный апскейл результата
240
+ if self._defer_after:
241
+ name = self._defer_upscaler_name
242
+ r = self._defer_ratio
243
+ new_images = []
244
+ for img in getattr(processed, "images", []) or []:
245
+ new_images.append(self._resize_with_upscaler(img, r, name))
246
+ if new_images:
247
+ processed.images = new_images
248
+ if getattr(processed, "extra_generation_params", None) is not None:
249
+ processed.extra_generation_params.update({
250
+ "Hires Applied After": True
251
+ })