├── sd-webui-ar_xhox-UI.png ├── .gitignore ├── javascript ├── button_titles.js └── sd-webui-ar.js ├── style.css ├── README.md └── scripts └── sd-webui-ar.py /sd-webui-ar_xhox-UI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xhoxye/sd-webui-ar_xhox/HEAD/sd-webui-ar_xhox-UI.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | 3 | **/*.swp 4 | **/.mypy_cache 5 | 6 | *.py[cod] 7 | aspect_ratios.txt 8 | resolutions.txt 9 | -------------------------------------------------------------------------------- /javascript/button_titles.js: -------------------------------------------------------------------------------- 1 | // Do not put custom titles here. This file is overwritten each time the WebUI is started. 2 | ar_button_titles = {} 3 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | #txt2img_container_aspect_ratio, 2 | #img2img_container_aspect_ratio { 3 | gap: 5px !important; 4 | margin-top: 3px; 5 | margin-bottom: 3px; 6 | } 7 | 8 | #txt2img_row_aspect_ratio, 9 | #txt2img_row_resolutions, 10 | #img2img_row_aspect_ratio, 11 | #img2img_row_resolutions { 12 | gap: 8px; 13 | } 14 | 15 | #txt2img_row_aspect_ratio button, 16 | #txt2img_row_resolutions button, 17 | #img2img_row_aspect_ratio button, 18 | #img2img_row_resolutions button { 19 | gap: 10px !important; 20 | max-width: unset !important; 21 | min-width: var(150px); /* 请替换 为您想要的最小宽度值 */ 22 | font-size: var(0.8em); /* 请替换 为您想要的字体大小 */ 23 | white-space: nowrap; /* 禁止换行 */ 24 | overflow: hidden; /* 隐藏溢出内容 */ 25 | text-overflow: ellipsis; /* 超出部分用省略号显示 */ 26 | } 27 | 28 | button#arc_show_calculator_button, 29 | button#arc_hide_calculator_button, 30 | #arc_empty_space { 31 | max-width: 45px !important; 32 | min-width: unset !important; 33 | padding: var(--size-0-5) var(--size-2) !important; 34 | } 35 | -------------------------------------------------------------------------------- /javascript/sd-webui-ar.js: -------------------------------------------------------------------------------- 1 | if ("undefined" === typeof ar_button_titles) { 2 | ar_button_titles = {}; 3 | } 4 | 5 | ar_button_titles["Calc"] = "Show or hide the aspect ratio calculator"; 6 | ar_button_titles["\u{21c5}"] = "Swap width and height values"; 7 | ar_button_titles["\u2B07\ufe0f"] = "Get dimensions from txt2img/img2img sliders"; 8 | ar_button_titles["\u{1f5bc}"] = "Get dimensions from image on current img2img tab"; 9 | ar_button_titles["Calculate Height"] = "Calculate new height based on source aspect ratio"; 10 | ar_button_titles["Calculate Width"] = "Calculate new width based on source aspect ratio"; 11 | ar_button_titles["Apply"] = "Apply calculated width and height to txt2img/img2img sliders"; 12 | 13 | onUiUpdate(function(){ 14 | gradioApp().querySelectorAll('#txt2img_container_aspect_ratio button, #img2img_container_aspect_ratio button').forEach(function(elem){ 15 | tooltip = ar_button_titles[elem.textContent]; 16 | if(tooltip){ 17 | elem.title = tooltip; 18 | } 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stable Diffusion WebUI 分辨率选择器-xhox 2 | # Stable Diffusion WebUI Aspect Ratio Resolutions selector 3 | 4 | 为 [lllyasviel/stable-diffusion-webui-forge](https://github.com/lllyasviel/stable-diffusion-webui-forge.git)、[AUTOMATIC1111/stable-diffusion-webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui.git) 添加图片分辨率选择按钮。 5 | 6 | Extension for [lllyasviel/stable-diffusion-webui-forge](https://github.com/lllyasviel/stable-diffusion-webui-forge.git), [AUTOMATIC1111/stable-diffusion-webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui.git) adding image aspect ratio resolutions selector buttons. 7 | 8 | ## 更新 Updates 9 | 10 | - 11/02/2024 :添加常用的 SDXL 官方、SD1.5 和自定义分辨率,隐藏删除比例按钮。 11 | - 11/02/2024 :Add common resolutions, hide Delete aspect ratio button. 12 | - 20/02/2023 :warning: this update will remove your local config files (`aspect_ratios.txt` and `resolutions.txt`) and it will create new default ones. These can be then modified freely and preserved in the future. For more info read [here](https://github.com/alemelis/sd-webui-ar/issues/9). 13 | 14 | ## 安装 Install 15 | 16 | Browse to the `Extensions` tab -> go to `Install from URL` -> paste in `https://github.com/xhoxye/sd-webui-ar_xhox` -> click `Install` 17 | 18 | 19 | 安装之后的UI界面效果如下: 20 | Here's how the UI looks like after installing this extension 21 | 22 | sd-webui-ar_xhox-UI 2024 02 11 23 | 24 | ## 使用 Usage 25 | 26 | - 点击你想要设置的分辨率按钮 27 | - Click on the resolutions button you want to set 28 | 29 | ### 自定义 Configuration 30 | 31 | Aspect ratios can be defined in the `/sd-webui-ar_xhox/Custom_resolutions.txt` file. For example, 32 | 33 | ``` 34 | #1024*1024 # 1:1 SDXL square. This line is an example of the format. 这一行是格式示例。 35 | 640*480 36 | 480*640 37 | 1280*720 38 | 720*1280 39 | 1920*1080 40 | 1080*1920 41 | ``` 42 | 43 | Note the `#` marking the line as a comment, i.e. the extension is not reading that line. To use a custom value, un-comment the relative line by removing the starting `#`. 44 | 45 | The format to be used is `width*height # optional comment`. As before, lines starting with `#` will be ignored. 46 | 47 | ## 分辨率比例缩放计算器面板 Calculator Panel 48 | Use the calculator to determine new width or height values based on the aspect ratio of source dimensions. 49 | - Click `Calc` to show or hide the aspect ratio calculator 50 | - Set the source dimensions: 51 | - Enter manually, or 52 | - Click ⬇️ to get source dimentions from txt2img/img2img sliders, or 53 | - Click 🖼️ to get source dimensions from input image component on the current tab 54 | - Click ⇅ to swap the width and height, if desired 55 | - Set the desired width or height, then click either `Calculate Height` or `Calculate Width` to calculate the missing value 56 | - Click `Apply` to send the values to the txt2txt/img2img sliders 57 | --- 58 | Basic usage of aspect ratio calculator 59 | -------------------------------------------------------------------------------- /scripts/sd-webui-ar.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | from pathlib import Path 3 | 4 | import gradio as gr 5 | 6 | import modules.scripts as scripts 7 | from modules.ui_components import ToolButton # 导入ToolButton 系统组件样式 8 | 9 | from math import gcd 10 | 11 | aspect_ratios_dir = scripts.basedir() 12 | 13 | calculator_symbol = "\U0001F5A9" # 计算器符号 14 | switch_values_symbol = "\U000021C5" # 交换符号 15 | get_dimensions_symbol = "\u2B07\ufe0f" # 获取尺寸符号 16 | get_image_dimensions_symbol = "\U0001F5BC" # 获取图片尺寸符号 17 | 18 | 19 | class ResButton(ToolButton): 20 | def __init__(self, res=(512, 512), **kwargs): 21 | super().__init__(**kwargs) 22 | 23 | self.w, self.h = res 24 | 25 | def reset(self): 26 | return [self.w, self.h] 27 | 28 | 29 | class ARButton(ToolButton): 30 | def __init__(self, ar=1.0, **kwargs): 31 | super().__init__(**kwargs) 32 | 33 | self.ar = ar 34 | 35 | def apply(self, w, h): # 是一个类方法,接受两个参数w和h,表示宽度和高度。根据self.ar的值,函数会根据不同的条件对宽度和高度进行调整。 36 | if self.ar > 1.0: # fix height, change width 固定高度,改变宽度。函数会根据self.ar计算出新的宽度w,并计算出新的高度h。 37 | h = 512 38 | w = self.ar * h 39 | elif self.ar < 1.0: # fix width, change height 固定宽度,改变高度。函数会根据self.ar计算出新的高度h,并计算出新的宽度w。 40 | w = 512 41 | h = w / self.ar 42 | else: # set minimum dimension to both 设置两个值的最小尺寸。函数会找到w和h中的最小值,然后将w和h都设置为该最小值。 43 | min_dim = min([w, h]) 44 | w, h = min_dim, min_dim 45 | 46 | return list(map(round, [w, h])) # 函数会将调整后的宽度和高度都四舍五入到最接近的整数,并以列表的形式返回。 47 | 48 | def reset(self, w, h): # 用于更新UI界面上的宽高数值滑块。是一个类方法,接受两个参数w和h。它返回一个列表,列表中包含两个元素,这两个元素都是self.res的值。 49 | return [self.res, self.res] 50 | 51 | 52 | def parse_aspect_ratios_file(filename): # 该函数用于解析预设的宽高比例文件。 53 | labels, values, comments = [], [], [] # 定义三个空列表:labels、values和comments。这些列表将用于存储标签、值和注释。 54 | file = Path(aspect_ratios_dir, filename) 55 | 56 | # 首先,函数会检查文件是否存在,如果不存在则返回三个空列表。 57 | if not file.exists(): 58 | return labels, values, comments # 返回三个空列表:显示的标签,值,注解 59 | 60 | # 如果文件存在,函数会打开文件并读取所有行。 61 | with open(file, "r", encoding="utf-8") as f: 62 | lines = f.readlines() 63 | 64 | if not lines: 65 | return labels, values, comments # 返回三个空列表:显示的标签、值和注解。 66 | 67 | # 排除以 "#" 开头的注释行和不包含逗号的行。 68 | for line in lines: 69 | if line.startswith("#"): # 如果行以#开头,则使用continue语句跳过该行; 70 | continue 71 | 72 | if ',' not in line: # 如果行不包含逗号,则同样使用continue语句跳过该行。 73 | continue 74 | 75 | # 对于每行,函数会尝试将其拆分为标签和值,并将注解保存在变量 comment 中。 76 | try: 77 | label, value = line.strip().split(",") 78 | comment = "" 79 | if "#" in value: # 如果值中包含 "#" 符号,则将值和注释拆分为两个部分。 80 | value, comment = value.split("#") 81 | # 如果拆分失败,则跳过该行并打印错误信息。该函数是一个异常处理语句,用于处理在读取宽高比例文件时可能出现的ValueError异常。 82 | except ValueError: 83 | print(f"skipping badly formatted line in aspect ratios file: {line}") # 跳过格式错误的宽高比例文件行,错误信息中会包含出现异常的行号和内容。 84 | continue 85 | # 将标签、值和注解添加到相应的列表中。 86 | labels.append(label) 87 | values.append(eval(value)) 88 | comments.append(comment) 89 | 90 | return labels, values, comments # 返回三个列表:显示的标签、值和注解。 91 | 92 | 93 | def parse_resolutions_file(filename, resolution_type): 94 | # 该函数的功能是解析一个分辨率文件,将文件中的每一行解析为一个分辨率,并将解析结果存储在三个列表中:labels(标签)、values(分辨率)和comments(注释)。函数的输入参数是一个文件名,函数返回一个包含解析结果的元组。 95 | labels, values, comments = [], [], [] # 定义三个空列表:labels、values和comments。这些列表将用于存储标签、值和注释。 96 | #file = Path(aspect_ratios_dir, filename) 97 | file = Path(aspect_ratios_dir, resolution_type + "_resolutions.txt") 98 | 99 | if not file.exists(): 100 | return labels, values, comments # 函数首先检查文件是否存在,如果不存在则返回空的labels、values和comments列表。 101 | 102 | with open(file, "r", encoding="utf-8") as f: # 然后,函数打开文件并逐行读取文件内容。 103 | lines = f.readlines() 104 | 105 | if not lines: 106 | return labels, values, comments # 如果文件为空,则返回空的labels、values和comments列表。 107 | 108 | for line in lines: 109 | if line.startswith("#"): # 对于每一行,函数首先检查是否以"#"开头,如果是则跳过该行。 110 | continue 111 | 112 | if '*' not in line: # 然后,函数检查该行是否包含*号,如果不包含则跳过该行。 113 | continue 114 | 115 | try: 116 | comment = "" # 在这里定义comment为空字符串 117 | width, height = line.strip().split("*") # 尝试将该行按照井号分割为宽高两个部分:width, height。 118 | if "#" in width: # 如果#在前半部分,跳过 119 | print(f"skipping line with '#' in the first part in resolutions file: {line}") 120 | continue 121 | if "#" in height: # 如果#在后半部分,继续分割 122 | height, comment = height.split("#") 123 | # label, comment = line.strip().split("#") # 尝试将该行按照井号分割为两个部分:label和comment。 124 | # width, height = label.strip().split("*") # 然后,将label按照星号分割为两个部分:width和height。 125 | 126 | except ValueError: 127 | print(f"skipping badly formatted line in resolutions file: {line}") # 如果分割失败,则打印错误信息并跳过该行。跳过格式错误的分辨率文件行 128 | continue 129 | 130 | resolution = (width, height) # 如果分割成功,则将label添加到labels列表中,将resolution添加到values列表中,将comment添加到comments列表中。 131 | 132 | # label = f"{width}x{height}" 133 | label = add_ratio(width, height) 134 | 135 | labels.append(label) 136 | values.append(resolution) 137 | comments.append(comment) 138 | 139 | return labels, values, comments # 最后,函数返回包含解析结果的元组。 140 | 141 | 142 | # TODO: write a generic function handling both cases 待办事项:编写一个通用的函数,处理分别写入多个文件的情况 143 | 144 | # 这个函数暂时没用到,是以前的功能遗留代码 145 | def write_aspect_ratios_file(filename): # 该函数的功能是将多个宽高比信息的列表写入到指定的文件中。函数接受一个参数filename,表示要写入的文件名。 146 | # 定义了一个列表aspect_ratios,其中包含了常用SD1.5宽高比例的信息。 147 | aspect_ratios = [ 148 | "1:1, 1.0 # 1:1 ratio based on minimum dimension\n", 149 | "2:3, 2/3 # \n", 150 | "3:2, 3/2 # Set width based on 3:2 ratio to height\n", 151 | "3:4, 3/4 # \n", 152 | "4:3, 4/3 # Set width based on 4:3 ratio to height\n", 153 | "16:9, 16/9 # Set width based on 16:9 ratio to height", 154 | ] 155 | with open(filename, "w", encoding="utf-8") as f: # 使用with open语句打开指定的文件,并以utf-8编码写入aspect_ratios列表中的内容。 156 | f.writelines(aspect_ratios) # 最后,使用f.writelines(aspect_ratios)将列表中的内容写入文件中。 157 | 158 | 159 | def write_resolutions_file(filename, resolution_type): # 该函数的功能是将一个包含多个分辨率的列表写入到指定的文件中。 160 | # 定义一个名为resolutions的列表,其中包含了多个字符串元素。每个字符串代表一种图像分辨率配置, 161 | if resolution_type == "SD15": 162 | resolutions = [ 163 | # ... SD1.5 分辨率的列表 ... 164 | "#1024*1024 # 1:1 square SDXL方形。这一行是格式示例,以下列表不建议修改,推荐修改使用自定义分辨率的文件", 165 | "512*512", 166 | "512*768", 167 | "768*512", 168 | "768*1024", 169 | "1024*768", 170 | "1024*1024", 171 | ] 172 | elif resolution_type == "Custom": 173 | resolutions = [ 174 | # ... 自定义分辨率的列表 ... 175 | "#1024*1024 # 1:1 square SDXL方形。这一行是格式示例,推荐修改使用", 176 | "640*480", 177 | "480*640", 178 | "1280*720", 179 | "720*1280", 180 | "1920*1080", 181 | "1080*1920", 182 | ] 183 | elif resolution_type == "SDXL": 184 | resolutions = [ 185 | # ... SDXL分辨率的列表 ... 186 | "#1024*1024 # 1:1 square SDXL方形。这一行是格式示例,以下列表不建议修改,推荐修改使用自定义分辨率的文件", 187 | "704*1408", 188 | "704*1344", 189 | "768*1344", 190 | "768*1280", 191 | "832*1216", 192 | "832*1152", 193 | "896*1152", 194 | "896*1088", 195 | "960*1088", 196 | "960*1024", 197 | "1024*960", 198 | "1088*960", 199 | "1088*896", 200 | "1152*896", 201 | "1152*832", 202 | "1216*832", 203 | "1280*768", 204 | "1344*768", 205 | "1344*704", 206 | "1408*704", 207 | "1472*704", 208 | "1536*640", 209 | "1600*640", 210 | "1664*576", 211 | ] 212 | else: 213 | raise ValueError("Unsupported resolution type") 214 | 215 | filename = Path(aspect_ratios_dir, f"{resolution_type}_resolutions.txt") # 根据 resolution_type 生成对应的文件名 216 | with open(filename, "w", encoding="utf-8") as f: 217 | for res in resolutions: 218 | f.write("%s\n" % res) 219 | #with open(filename, "w", encoding="utf-8") as f: # 使用with open语句打开指定的文件,并以utf-8编码写入resolutions列表中的内容。 220 | #f.writelines(resolutions) #调用文件对象f的writelines()方法,将resolutions列表中的所有字符串依次写入到已打开的文件中。 221 | 222 | 223 | def write_js_titles_file(button_titles): 224 | filename = Path(aspect_ratios_dir, "javascript", "button_titles.js") 225 | content = [ 226 | "// Do not put custom titles here. This file is overwritten each time the WebUI is started.\n" # 不要在这里添加自定义标题。每次启动WebUI时,都会覆盖此文件 227 | ] 228 | content.append("ar_button_titles = {\n") 229 | counter = 0 230 | while counter < len(button_titles[0]): 231 | content.append( 232 | f' "{button_titles[0][counter]}" : "{button_titles[1][counter]}",\n' 233 | ) 234 | counter = counter + 1 235 | content.append("}") 236 | 237 | with open(filename, "w", encoding="utf-8") as f: 238 | f.writelines(content) 239 | 240 | def add_ratio(width, height): 241 | a, b = int(width), int(height) 242 | g = gcd(a, b) 243 | c, d = 1, 1 244 | if g < 8: 245 | if (a, b) == (768, 1366): 246 | c, d = 9, 16 247 | elif (a, b) == (915, 1144): 248 | c, d = 4, 5 249 | elif (a, b) == (1182, 886): 250 | c, d = 4, 3 251 | elif (a, b) == (1366, 768): 252 | c, d = 16, 9 253 | elif (a, b) == (1564, 670): 254 | c, d = 21, 9 255 | else: 256 | c, d = a // g, b // g 257 | return f'{a}x{b} | {c}:{d}' 258 | 259 | def get_reduced_ratio(n, d): # 该函数的功能是根据给定的两个整数n和d,计算并返回一个缩放比例。用在尺寸计算器显示比例 260 | n, d = list(map(int, (n, d))) # 首先,将n和d转换为整数类型。 261 | 262 | if n == d: 263 | return "1:1" 264 | 265 | if n < d: 266 | div = gcd(d, n) # 计算n和d的最大公约数,并将其赋值给变量div。 267 | else: 268 | div = gcd(n, d) # 如果n大于等于d,则计算d和n的最大公约数,并将其赋值给变量div。 269 | 270 | w = int(n) // div # w是n除以div的整数商 271 | h = int(d) // div # h是d除以div的整数商。 272 | 273 | if w == 8 and h == 5: # 如果w等于8且h等于5,则将w的值修改为16,h的值修改为10。 274 | w = 16 275 | h = 10 276 | 277 | return f"{w}:{h}" # 最后,将w和h的值以字符串的形式返回,格式为"w:h"。 278 | 279 | 280 | def solve_aspect_ratio(w, h, n, d): # 用于计算缩放后的宽高比数值 281 | # 根据输入的参数,如果宽度不为0且不为None,则返回宽度除以(n / d)的值,四舍五入到最接近的整数。 282 | if w != 0 and w: 283 | return round(w / (n / d)) 284 | # 如果高度不为0且不为None,则返回高度乘以(n / d)的值,四舍五入到最接近的整数。 285 | elif h != 0 and h: 286 | return round(h * (n / d)) 287 | # 如果宽度和高度都为0或None,则返回0。 288 | else: 289 | return 0 290 | 291 | 292 | class AspectRatioScript(scripts.Script): # 定义这个插件脚本的类 293 | def read_aspect_ratios(self): 294 | ar_file = Path(aspect_ratios_dir, "aspect_ratios.txt") # 读取一个名为"aspect_ratios.txt"的比例文件 295 | 296 | if not ar_file.exists(): 297 | write_aspect_ratios_file(ar_file) # 如果文件不存在,则会调用write_aspect_ratios_file函数创建该文件。 298 | 299 | ( 300 | self.aspect_ratio_labels, 301 | aspect_ratios, 302 | self.aspect_ratio_comments, 303 | ) = parse_aspect_ratios_file("aspect_ratios.txt") # 调用 parse_aspect_ratios_file 函数将其解析为三个变量:self.aspect_ratio_labels、aspect_ratios和self.aspect_ratio_comments。 304 | 305 | self.aspect_ratios = list(map(float, aspect_ratios)) # 将aspect_ratios列表中的每个元素转换为浮点数 306 | 307 | #待办事项: 308 | 309 | # TODO: check for duplicates 检查重复值 310 | 311 | # TODO: check for invalid values 检查无效值 312 | 313 | # TODO: use comments as tooltips 鼠标悬浮提示 314 | 315 | # see https://github.com/alemelis/sd-webui-ar/issues/5 316 | 317 | def read_resolutions(self, resolution_type): 318 | res_file = Path(aspect_ratios_dir, f"{resolution_type}_resolutions.txt") # # 根据 resolution_type 读取对应的分辨率文件,并将其解析为三个变量:res_labels、res和res_comments 319 | 320 | if not res_file.exists(): 321 | write_resolutions_file(res_file, resolution_type) # 如果文件不存在,则会调用 write_resolutions_file 函数创建该文件。 322 | 323 | self.res_labels, res, self.res_comments = parse_resolutions_file( # 调用 parse_resolutions_file 函数将其解析为三个变量:res_labels、res和res_comments 324 | f"{resolution_type}_resolutions.txt", resolution_type 325 | ) 326 | self.res = [list(map(int, r)) for r in res] # 将 res 列表中的每个元素转换为整数列表。 327 | 328 | def title(self): 329 | return "Aspect Ratio picker-xhox" 330 | 331 | def show(self, is_img2img): 332 | return scripts.AlwaysVisible 333 | 334 | def ui(self, is_img2img): 335 | with gr.Accordion(open=True, label=self.title()): #可折叠面板,用于显示插件脚本的标题,折叠默认打开 336 | 337 | with gr.Column( 338 | elem_id=f'{"img" if is_img2img else "txt"}2img_container_aspect_ratio' 339 | ): 340 | 341 | with gr.Accordion(open=False, label='常用SD1.5宽高比例', visible=False): # 常用SD1.5宽高比例 标题,可折叠面板,准备删除 342 | self.read_aspect_ratios() #读取宽高比例文件 343 | 344 | with gr.Row( 345 | elem_id=f'{"img" if is_img2img else "txt"}2img_row_aspect_ratio' 346 | ): 347 | # Aspect Ratio buttons 宽高比例按钮。创建了一个ARButton的列表,其中每个按钮代表一个不同的宽高比例。 348 | btns = [ 349 | ARButton(ar=ar, value=label) 350 | for ar, label in zip( 351 | self.aspect_ratios, 352 | self.aspect_ratio_labels, 353 | ) 354 | ] 355 | 356 | with contextlib.suppress(AttributeError): # 使用contextlib.suppress来捕获可能发生的AttributeError异常 357 | # 遍历ARButton列表,根据is_img2img的值设置不同的分辨率,并为每个按钮添加点击事件,当按钮被点击时,会调用apply函数,并传递输入和输出的分辨率。 358 | for b in btns: 359 | if is_img2img: 360 | resolution = [self.i2i_w, self.i2i_h] 361 | else: 362 | resolution = [self.t2i_w, self.t2i_h] 363 | 364 | b.click( 365 | b.apply, 366 | inputs=resolution, 367 | outputs=resolution, 368 | ) 369 | 370 | arc_title_heading = gr.Markdown(value="SD1.5 resolutions") # SD1.5 常用分辨率 标题 371 | self.read_resolutions(resolution_type="SD15") # 读取SD1.5分辨率文件 372 | for i in range(0, len(self.res), 6): 373 | with gr.Row(elem_id=f'{"img" if is_img2img else "txt"}2img_row_resolutions'): 374 | 375 | # Resolution buttons 分辨率按钮 376 | btns = [ 377 | ResButton(res=res, value=label) 378 | for res, label in zip(self.res[i:i+6], self.res_labels[i:i+6]) 379 | ] 380 | with contextlib.suppress(AttributeError): # 使用contextlib.suppress来抑制可能发生的AttributeError异常,并且遍历按钮列表。 381 | # 对于每个按钮,根据条件设置分辨率(resolution),然后调用按钮的click方法,传入一个回调函数(b.reset)和输出分辨率(outputs)。 382 | for b in btns: 383 | if is_img2img: 384 | resolution = [self.i2i_w, self.i2i_h] 385 | else: 386 | resolution = [self.t2i_w, self.t2i_h] 387 | 388 | b.click( 389 | b.reset, 390 | outputs=resolution, 391 | ) 392 | 393 | 394 | arc_title_heading = gr.Markdown(value="SDXL base resolutions") # SDXL 官方分辨率 标题 395 | self.read_resolutions(resolution_type="SDXL") #读取SDXL分辨率文件 396 | 397 | # 该函数是一个for循环,用于遍历一个列表(self.res)中的元素,并且每次遍历6个元素。在每次遍历中,使用gr.Row创建一个行元素,并且根据条件设置行元素的id。然后,根据条件创建一组按钮(btns),每个按钮都包含一个分辨率(res)和一个标签(label)。 398 | for i in range(0, len(self.res), 6): 399 | with gr.Row(elem_id=f'{"img" if is_img2img else "txt"}2img_row_resolutions'): 400 | 401 | # Resolution buttons 分辨率按钮 402 | btns = [ 403 | ResButton(res=res, value=label) 404 | for res, label in zip(self.res[i:i+6], self.res_labels[i:i+6]) 405 | ] 406 | with contextlib.suppress(AttributeError): # 使用contextlib.suppress来抑制可能发生的AttributeError异常,并且遍历按钮列表。 407 | # 对于每个按钮,根据条件设置分辨率(resolution),然后调用按钮的click方法,传入一个回调函数(b.reset)和输出分辨率(outputs)。 408 | for b in btns: 409 | if is_img2img: 410 | resolution = [self.i2i_w, self.i2i_h] 411 | else: 412 | resolution = [self.t2i_w, self.t2i_h] 413 | 414 | b.click( 415 | b.reset, 416 | outputs=resolution, 417 | ) 418 | 419 | with gr.Accordion(open=False, label='Custom resolutions'): # 自定义分辨率 标题,可折叠面板,默认折叠 420 | self.read_resolutions(resolution_type="Custom") # 读取自定义分辨率文件 421 | for i in range(0, len(self.res), 6): 422 | with gr.Row(elem_id=f'{"img" if is_img2img else "txt"}2img_row_resolutions'): 423 | 424 | # Resolution buttons 分辨率按钮 425 | btns = [ 426 | ResButton(res=res, value=label) 427 | for res, label in zip(self.res[i:i+6], self.res_labels[i:i+6]) 428 | ] 429 | with contextlib.suppress(AttributeError): # 使用contextlib.suppress来抑制可能发生的AttributeError异常,并且遍历按钮列表。 430 | # 对于每个按钮,根据条件设置分辨率(resolution),然后调用按钮的click方法,传入一个回调函数(b.reset)和输出分辨率(outputs)。 431 | for b in btns: 432 | if is_img2img: 433 | resolution = [self.i2i_w, self.i2i_h] 434 | else: 435 | resolution = [self.t2i_w, self.t2i_h] 436 | 437 | b.click( 438 | b.reset, 439 | outputs=resolution, 440 | ) 441 | 442 | # 移除按钮的定义 443 | # Toggle calculator display button 切换显示计算器的按钮 444 | # arc_hide_calculator = gr.Button( 445 | # value="Calc", 446 | # visible=True, 447 | # variant="primary", 448 | # elem_id="arc_hide_calculator_button", 449 | # ) 450 | # arc_show_calculator = gr.Button( 451 | # value="Calc", 452 | # visible=False, 453 | # variant="secondary", 454 | # elem_id="arc_show_calculator_button", 455 | # ) 456 | 457 | # Write button_titles.js with labels and comments read from aspect ratios and resolutions files 编写一个名为button_titles.js的文件,其中的标签和注释是从宽高比和分辨率文件中读取的 458 | button_titles = [self.aspect_ratio_labels + self.res_labels] 459 | button_titles.append(self.aspect_ratio_comments + self.res_comments) 460 | write_js_titles_file(button_titles) 461 | 462 | # dummy components needed for JS function JS函数所需的虚拟组件 463 | dummy_text1 = gr.Text(visible=False) 464 | dummy_text2 = gr.Text(visible=False) 465 | dummy_text3 = gr.Text(visible=False) 466 | dummy_text4 = gr.Text(visible=False) 467 | 468 | # Aspect Ratio Calculator 宽高比计算器 469 | with gr.Column( 470 | visible=True, variant="panel", elem_id="arc_panel" 471 | ) as arc_panel: 472 | arc_title_heading = gr.Markdown(value=" Aspect Ratio Calculator") # 宽高比计算器标题 473 | with gr.Row(): 474 | with gr.Column(min_width=150): 475 | arc_width1 = gr.Number(label="Width 1") 476 | arc_height1 = gr.Number(label="Height 1") 477 | 478 | with gr.Column(min_width=150): 479 | arc_desired_width = gr.Number(label="Width 2") 480 | arc_desired_height = gr.Number(label="Height 2") 481 | 482 | with gr.Column(min_width=150): 483 | arc_ar_display = gr.Markdown(value="Aspect Ratio:") 484 | with gr.Row( 485 | elem_id=f'{"img" if is_img2img else "txt"}2img_arc_tool_buttons' 486 | ): 487 | # Switch resolution values button 交换分辨率宽与高值按钮 488 | arc_swap = ToolButton(value=switch_values_symbol) 489 | arc_swap.click( 490 | lambda w, h, w2, h2: (h, w, h2, w2), 491 | inputs=[ 492 | arc_width1, 493 | arc_height1, 494 | arc_desired_width, 495 | arc_desired_height, 496 | ], 497 | outputs=[ 498 | arc_width1, 499 | arc_height1, 500 | arc_desired_width, 501 | arc_desired_height, 502 | ], 503 | ) 504 | 505 | with contextlib.suppress(AttributeError): 506 | # For img2img tab 图生图标签页生效 507 | if is_img2img: 508 | # Get slider dimensions button 获取滑块尺寸按钮 509 | resolution = [self.i2i_w, self.i2i_h] 510 | arc_get_img2img_dim = ToolButton( 511 | value=get_dimensions_symbol 512 | ) 513 | arc_get_img2img_dim.click( 514 | lambda w, h: (w, h), 515 | inputs=resolution, 516 | outputs=[arc_width1, arc_height1], 517 | ) 518 | 519 | # Javascript function to select image element from current img2img tab JavaScript函数,用于从当前img2img选项卡中选择图像元素 520 | current_tab_image = """ 521 | function current_tab_image(...args) { 522 | const tab_index = get_img2img_tab_index(); 523 | // Get current tab's image (on Batch tab, use image from img2img tab) 524 | if (tab_index == 5) { 525 | image = args[0]; 526 | } else { 527 | image = args[tab_index]; 528 | } 529 | // On Inpaint tab, select just the image and drop the mask 530 | if (tab_index == 2 && image !== null) { 531 | image = image["image"]; 532 | } 533 | return [image, null, null, null, null]; 534 | } 535 | 536 | """ 537 | 538 | # Get image dimensions 获取图像尺寸 539 | def get_dims( 540 | img: list, 541 | dummy_text1, 542 | dummy_text2, 543 | dummy_text3, 544 | dummy_text4, 545 | ): 546 | if img: 547 | width = img.size[0] 548 | height = img.size[1] 549 | return width, height 550 | else: 551 | return 0, 0 552 | 553 | # Get image dimensions button 获取图像尺寸按钮 554 | arc_get_image_dim = ToolButton( 555 | value=get_image_dimensions_symbol 556 | ) 557 | arc_get_image_dim.click( 558 | fn=get_dims, 559 | inputs=self.image, 560 | outputs=[arc_width1, arc_height1], 561 | _js=current_tab_image, 562 | ) 563 | 564 | else: 565 | # For txt2img tab 文生图标签页生效 566 | # Get slider dimensions button 获取滑块尺寸按钮 567 | resolution = [self.t2i_w, self.t2i_h] 568 | arc_get_txt2img_dim = ToolButton( 569 | value=get_dimensions_symbol 570 | ) 571 | arc_get_txt2img_dim.click( 572 | lambda w, h: (w, h), 573 | inputs=resolution, 574 | outputs=[arc_width1, arc_height1], 575 | ) 576 | 577 | # Update aspect ratio display on change 使用 change事件 更新比例显示 578 | arc_width1.change( 579 | lambda w, h: (f"Aspect Ratio: **{get_reduced_ratio(w,h)}**"), 580 | inputs=[arc_width1, arc_height1], 581 | outputs=[arc_ar_display], 582 | ) 583 | arc_height1.change( 584 | lambda w, h: (f"Aspect Ratio: **{get_reduced_ratio(w,h)}**"), 585 | inputs=[arc_width1, arc_height1], 586 | outputs=[arc_ar_display], 587 | ) 588 | 589 | with gr.Row(): 590 | # Calculate and Apply buttons 计算和应用按钮 591 | arc_calc_height = gr.Button(value="Calculate Height",scale=1) 592 | arc_calc_height.click( 593 | lambda w2, w1, h1: (solve_aspect_ratio(w2, 0, w1, h1)), 594 | inputs=[arc_desired_width, arc_width1, arc_height1], 595 | outputs=[arc_desired_height], 596 | ) 597 | arc_calc_width = gr.Button(value="Calculate Width", scale=1) 598 | arc_calc_width.click( 599 | lambda h2, w1, h1: (solve_aspect_ratio(0, h2, w1, h1)), 600 | inputs=[arc_desired_height, arc_width1, arc_height1], 601 | outputs=[arc_desired_width], 602 | ) 603 | arc_apply_params = gr.Button(value="Apply") 604 | with contextlib.suppress(AttributeError): 605 | if is_img2img: 606 | resolution = [self.i2i_w, self.i2i_h] 607 | else: 608 | resolution = [self.t2i_w, self.t2i_h] 609 | 610 | arc_apply_params.click( 611 | lambda w2, h2: (w2, h2), 612 | inputs=[arc_desired_width, arc_desired_height], 613 | outputs=resolution, 614 | ) 615 | 616 | # 移除按钮的定义 617 | # Show calculator pane (and reset number input values) 显示计算器面板(以及重置数字输入值),点击事件 618 | # arc_show_calculator.click( 619 | # lambda: [ 620 | # gr.update(visible=True), 621 | # gr.update(visible=False), 622 | # gr.update(visible=True), 623 | # gr.update(value=512), 624 | # gr.update(value=512), 625 | # gr.update(value=0), 626 | # gr.update(value=0), 627 | # gr.update(value="Aspect Ratio: **1:1**"), 628 | # ], 629 | # None, 630 | # [ 631 | # arc_panel, 632 | # arc_show_calculator, 633 | # arc_hide_calculator, 634 | # arc_width1, 635 | # arc_height1, 636 | # arc_desired_width, 637 | # arc_desired_height, 638 | # arc_ar_display, 639 | # ], 640 | # ) 641 | # Hide calculator pane 隐藏计算器面板 642 | # arc_hide_calculator.click( 643 | # lambda: [ 644 | # gr.update(visible=False), 645 | # gr.update(visible=True), 646 | # gr.update(visible=False), 647 | # ], 648 | # None, 649 | # [arc_panel, arc_show_calculator, arc_hide_calculator], 650 | # ) 651 | 652 | # https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/7456#issuecomment-1414465888 653 | def after_component(self, component, **kwargs): 654 | if kwargs.get("elem_id") == "txt2img_width": 655 | self.t2i_w = component 656 | if kwargs.get("elem_id") == "txt2img_height": 657 | self.t2i_h = component 658 | 659 | if kwargs.get("elem_id") == "img2img_width": 660 | self.i2i_w = component 661 | if kwargs.get("elem_id") == "img2img_height": 662 | self.i2i_h = component 663 | 664 | if kwargs.get("elem_id") == "img2img_image": 665 | self.image = [component] 666 | if kwargs.get("elem_id") == "img2img_sketch": 667 | self.image.append(component) 668 | if kwargs.get("elem_id") == "img2maskimg": 669 | self.image.append(component) 670 | if kwargs.get("elem_id") == "inpaint_sketch": 671 | self.image.append(component) 672 | if kwargs.get("elem_id") == "img_inpaint_base": 673 | self.image.append(component) 674 | --------------------------------------------------------------------------------