This commit is contained in:
Haoming 2026-05-26 23:06:43 +08:00
parent ce8855c5e6
commit b1f55afdf1
7 changed files with 47 additions and 32 deletions

View File

@ -471,7 +471,7 @@ function currentImg2imgSourceResolution(w, h, r) {
if (!img) return [0, 0, r];
const width = img.naturalWidth || img.width;
const height = img.naturalHeight || img.height;
return [Math.round(width / 64.0) * 64, Math.round(height / 64.0) * 64, r];
return [Math.round(width / opts.res_step) * opts.res_step, Math.round(height / opts.res_step) * opts.res_step, r];
}
function updateImg2imgResizeToTextAfterChangingImage() {

View File

@ -20,7 +20,7 @@ from modules.processing import (
)
from modules.sd_models import get_closet_checkpoint_match
from modules.shared import opts, state
from modules.ui import plaintext_to_html
from modules.ui import _STEP, plaintext_to_html, sRound
from modules_forge import main_thread
@ -73,12 +73,12 @@ def process_batch(p, input, output_dir, inpaint_mask_dir, args, to_scale=False,
img = ImageOps.exif_transpose(img)
if to_scale:
p.width = round(img.width * scale_by / 64) * 64
p.height = round(img.height * scale_by / 64) * 64
p.width = sRound(img.width * scale_by)
p.height = sRound(img.height * scale_by)
_w, _h = img.size
if not (_w % 64 == 0 and _h % 64 == 0):
img = images.resize_image(1, img, round(_w / 64) * 64, round(_h / 64) * 64)
if not (_w % _STEP == 0 and _h % _STEP == 0):
img = images.resize_image(1, img, sRound(_w), sRound(_h))
p.init_images = [img] * p.batch_size
@ -209,8 +209,8 @@ def img2img_function(id_task: str, request: gr.Request, mode: int, prompt: str,
if selected_scale_tab == 1 and not is_batch:
assert image, "Can't scale by because no image is selected"
width = round(image.width * scale_by / 64) * 64
height = round(image.height * scale_by / 64) * 64
width = sRound(image.width * scale_by)
height = sRound(image.height * scale_by)
assert 0.0 <= denoising_strength <= 1.0, "can only work with strength in [0.0, 1.0]"

View File

@ -254,9 +254,11 @@ def send_image_and_dimensions(x) -> tuple[Image.Image, int, int]:
if img.mode != "RGB":
img = img.convert("RGB")
from modules.ui import sRound
if shared.opts.send_size and isinstance(img, Image.Image):
w = round(img.width / 64.0) * 64
h = round(img.height / 64.0) * 64
w = sRound(img.width)
h = sRound(img.height)
else:
w = gr.skip()
h = gr.skip()

View File

@ -32,6 +32,7 @@ from modules.sd_models import apply_token_merging, forge_model_reload
from modules.sd_samplers_common import approximation_indexes, decode_first_stage, images_tensor_to_samples
from modules.shared import cmd_opts, opts, state
from modules.sysinfo import set_config
from modules.ui import sRound
from modules_forge import main_entry
from modules_forge.utils import apply_circular_forge
@ -1185,8 +1186,8 @@ def old_hires_fix_first_pass_dimensions(width: int, height: int) -> tuple[int, i
desired_pixel_count = 512 * 512
actual_pixel_count = width * height
scale = math.sqrt(desired_pixel_count / actual_pixel_count)
width = round(scale * width / 64.0) * 64
height = round(scale * height / 64.0) * 64
width = sRound(scale * width)
height = sRound(scale * height)
return width, height
@ -1254,8 +1255,8 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
if self.hr_resize_x == 0 and self.hr_resize_y == 0:
self.extra_generation_params["Hires upscale"] = self.hr_scale
self.hr_upscale_to_x = round(self.width * self.hr_scale / 64.0) * 64
self.hr_upscale_to_y = round(self.height * self.hr_scale / 64.0) * 64
self.hr_upscale_to_x = sRound(self.width * self.hr_scale)
self.hr_upscale_to_y = sRound(self.height * self.hr_scale)
else:
if self.hr_resize_y == 0:
self.hr_upscale_to_x = self.hr_resize_x
@ -1267,8 +1268,8 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
self.hr_upscale_to_x = self.hr_resize_x
self.hr_upscale_to_y = self.hr_resize_y
self.hr_upscale_to_x = round(self.hr_upscale_to_x / 64.0) * 64
self.hr_upscale_to_y = round(self.hr_upscale_to_y / 64.0) * 64
self.hr_upscale_to_x = sRound(self.hr_upscale_to_x)
self.hr_upscale_to_y = sRound(self.hr_upscale_to_y)
self.extra_generation_params["Hires resize"] = f"{self.hr_upscale_to_x}x{self.hr_upscale_to_y}"

View File

@ -163,6 +163,7 @@ options_templates.update(
("system", "System", "system"),
{
"setting_allocated_vram": OptionInfo(1.0, "GPU Weights", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.05}).info("amount of VRAM that Forge can access").info("in % of total vram"),
"res_step": OptionInfo(64, "Resolution Step", gr.Radio, {"choices": (8, 16, 32, 64, 128, 256)}).info('"64" is recommended to prevent compatibility issues').needs_restart(),
"auto_launch_browser": OptionInfo("Local", "Launch the webui in browser on startup", gr.Radio, {"choices": ("Disable", "Local", "Remote")}).info("Remote = always automatically start; Local = only when not sharing the server, such as <b>--share</b>"),
"enable_console_prompts": OptionInfo(False, "Print the generation prompts to console"),
"samples_log_stdout": OptionInfo(False, "Print the generation infotxt to console"),

View File

@ -15,7 +15,7 @@ import modules.processing_scripts.comments as comments
import modules.shared as shared
from modules import extra_networks, gradio_extensions, launch_utils, paths_internal, processing, progress, prompt_parser, script_callbacks, scripts, sd_models, sd_samplers, sd_schedulers, shared_items, sysinfo, timer, ui_checkpoint_merger, ui_common, ui_extensions, ui_extra_networks, ui_loadsave, ui_postprocessing, ui_settings, ui_toprow # noqa: F401
from modules.call_queue import wrap_gradio_call, wrap_gradio_call_no_job, wrap_gradio_gpu_call, wrap_queued_call # noqa: F401
from modules.infotext_utils import PasteField, image_from_url_text
from modules.infotext_utils import PasteField
from modules.paths import script_path
from modules.shared import cmd_opts, opts
from modules.ui_common import create_refresh_button # noqa: F401
@ -80,8 +80,11 @@ detect_image_size_symbol = "\U0001f4d0" # 📐
plaintext_to_html = ui_common.plaintext_to_html
def _round(v: float) -> int:
return round(v / 64.0) * 64
_STEP = int(opts.res_step)
def sRound(val: int) -> int:
return round(val / _STEP) * _STEP
def calc_resolution_hires(enable, width, height, hr_scale, hr_resize_x, hr_resize_y):
@ -91,15 +94,15 @@ def calc_resolution_hires(enable, width, height, hr_scale, hr_resize_x, hr_resiz
p = processing.StableDiffusionProcessingTxt2Img(width=width, height=height, enable_hr=True, hr_scale=hr_scale, hr_resize_x=hr_resize_x, hr_resize_y=hr_resize_y)
p.calculate_target_resolution()
new_width = _round(p.hr_resize_x or p.hr_upscale_to_x)
new_height = _round(p.hr_resize_y or p.hr_upscale_to_y)
new_width = sRound(p.hr_resize_x or p.hr_upscale_to_x)
new_height = sRound(p.hr_resize_y or p.hr_upscale_to_y)
return f"from <span class='resolution'>{p.width}x{p.height}</span> to <span class='resolution'>{new_width}x{new_height}</span>"
def resize_from_to_html(width, height, scale_by):
target_width = _round(int(width) * scale_by)
target_height = _round(int(height) * scale_by)
target_width = sRound(int(width) * scale_by)
target_height = sRound(int(height) * scale_by)
if not target_width or not target_height:
return "no image selected"
@ -211,8 +214,8 @@ def create_ui():
elif category == "dimensions":
with FormRow():
with gr.Column(elem_id="txt2img_column_size", scale=4):
width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width", value=1024, elem_id="txt2img_width")
height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height", value=1024, elem_id="txt2img_height")
width = gr.Slider(minimum=64, maximum=2048, step=_STEP, label="Width", value=1024, elem_id="txt2img_width")
height = gr.Slider(minimum=64, maximum=2048, step=_STEP, label="Height", value=1024, elem_id="txt2img_height")
with gr.Column(elem_id="txt2img_dimensions_row", scale=1, elem_classes="dimensions-tools"):
res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="txt2img_res_switch_btn", tooltip="Switch width/height")
@ -242,8 +245,8 @@ def create_ui():
with FormRow(elem_id="txt2img_hires_fix_row2", variant="compact"):
hr_scale = gr.Slider(minimum=1.0, maximum=4.0, step=0.05, label="Upscale by", value=2.0, elem_id="txt2img_hr_scale")
hr_resize_x = gr.Slider(minimum=0, maximum=4096, step=64, label="Resize width to", value=0, elem_id="txt2img_hr_resize_x")
hr_resize_y = gr.Slider(minimum=0, maximum=4096, step=64, label="Resize height to", value=0, elem_id="txt2img_hr_resize_y")
hr_resize_x = gr.Slider(minimum=0, maximum=4096, step=_STEP, label="Resize width to", value=0, elem_id="txt2img_hr_resize_x")
hr_resize_y = gr.Slider(minimum=0, maximum=4096, step=_STEP, label="Resize height to", value=0, elem_id="txt2img_hr_resize_y")
with FormRow(elem_id="txt2img_hires_fix_row_cfg", variant="compact"):
hr_distilled_cfg = gr.Slider(minimum=1.0, maximum=24.0, step=0.5, label="Hires Distilled CFG Scale", value=3.0, elem_id="txt2img_hr_distilled_cfg")
@ -575,8 +578,8 @@ def create_ui():
with gr.Tab(label="Resize to", id="to", elem_id="img2img_tab_resize_to") as tab_scale_to:
with FormRow():
with gr.Column(elem_id="img2img_column_size", scale=4):
width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width", value=1024, elem_id="img2img_width")
height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height", value=1024, elem_id="img2img_height")
width = gr.Slider(minimum=64, maximum=2048, step=_STEP, label="Width", value=1024, elem_id="img2img_width")
height = gr.Slider(minimum=64, maximum=2048, step=_STEP, label="Height", value=1024, elem_id="img2img_height")
with gr.Column(elem_id="img2img_dimensions_row", scale=1, elem_classes="dimensions-tools"):
res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="img2img_res_switch_btn", tooltip="Switch width/height")
detect_image_size_btn = ToolButton(value=detect_image_size_symbol, elem_id="img2img_detect_image_size_btn", tooltip="Auto detect size from img2img")
@ -602,7 +605,7 @@ def create_ui():
def updateWH(img):
if img and shared.opts.img2img_autosize is True:
return _round(img.size[0]), _round(img.size[1])
return sRound(img.size[0]), sRound(img.size[1])
else:
return gr.skip(), gr.skip()

View File

@ -1,4 +1,5 @@
import json
import math
import os
import gradio as gr
@ -47,8 +48,9 @@ class UiLoadsave:
if getattr(obj, "do_not_save_to_config", False):
return
if field == "value" and getattr(obj, "_internal_preset_param", False):
return
if getattr(obj, "_internal_preset_param", False):
if field in ("value", "step"):
return
saved_value = self.ui_settings.get(key, None)
@ -73,6 +75,12 @@ class UiLoadsave:
except ValueError:
return
if getattr(obj, "elem_id", None) in ("txt2img_width", "txt2img_height", "img2img_width", "img2img_height"):
if field == "minimum":
from modules.ui import _STEP
saved_value = math.ceil(saved_value / _STEP) * _STEP
setattr(obj, field, saved_value)
if init_field is not None:
init_field(saved_value)