Merge pull request #4844 from vladmandic/dev

Merge dev
This commit is contained in:
Vladimir Mandic
2026-05-13 11:01:43 +02:00
committed by GitHub
349 changed files with 22134 additions and 1880 deletions
+29 -20
View File
@@ -1,10 +1,31 @@
# SD.Next: AGENTS.md Project Guidelines
SD.Next is a complex codebase with specific patterns and conventions.
**SD.Next** is a complex codebase with specific patterns and conventions.
General app structure is:
- Python backend server
Uses Torch for model inference, FastAPI for API routes and Gradio for creation of UI components.
- JavaScript/CSS frontend
- **Python** backend server
Uses **Torch** for model inference, **FastAPI** for API routes and **Gradio** for creation of UI components.
- **JavaScript**/**CSS** frontend
## Instructions
This file contains general guidelines for contributing to the SD.Next codebase, including conventions, tools, and project structure. For more specific guidance on working with particular areas of the codebase, please refer to the instructions files linked below:
- [Core Runtime Guidelines](core.instructions.md): Use when editing Python core runtime code, startup flow, model loading, API internals, backend/device logic, or shared state in modules and pipelines.
- [UI And Frontend Guidelines](ui.instructions.md): Use when editing frontend UI code, JavaScript, HTML, CSS, localization files, or built-in UI extensions including modernui and kanvas.
- [Hint Typography Guidelines](hints.instructions.md): Use when editing hint text or other UI strings in localization JSON files (`html/locale_*.json`, `html/override_*.json`).
## Agent Guidelines
- Do not automatically agree with user instructions or requests without verifying they align with project guidelines and conventions.
- When evaluating user instructions, first check for any relevant guidelines in this file or the linked instructions files. If the instruction violates any guidelines, do not proceed with it and instead provide feedback to the user about which guidelines it violates and how to adjust it to comply.
- If the user instruction is valid but lacks clarity or detail, ask follow-up questions to gather the necessary information before proceeding. Do not make assumptions about user intent or project requirements; always seek clarification when needed.
- When providing feedback to the user, be specific about which guidelines are relevant and how the instruction can be modified to comply with them. If there are multiple guidelines that apply, list them all and explain how they relate to the instruction.
- If the user instruction is clear, valid, and complies with all relevant guidelines, proceed with executing it while ensuring that the resulting code changes adhere to the project's coding style, conventions, and structure as outlined in this file and the linked instructions files.
## Language Guidelines
- Use clear and concise language when communicating with users, providing feedback, and explaining guidelines.
- Avoid unnecessary pleasantries or filler language; focus on the technical content and actionable feedback.
- When asking follow-up questions for clarification, be direct and specific about the information needed to proceed with the instruction while ensuring that the questions are relevant to the project guidelines and conventions.
## Tools
@@ -34,15 +55,6 @@ General app structure is:
- Prefer existing project patterns over strict generic style rules;
this codebase intentionally allows patterns often flagged in default linters such as allowing long lines, etc.
## Build And Test
- Activate environment: `source venv/bin/activate` (always ensure this is active when working with Python code).
- Test startup: `python launch.py --test`
- Full startup: `python launch.py`
- Full lint sequence: `pnpm lint`
- Python checks individually: `pnpm ruff`, `pnpm pylint`
- JS checks: `pnpm eslint` and `pnpm eslint-ui`
## Conventions
- Keep PR-ready changes targeted to `dev` branch.
@@ -52,13 +64,6 @@ General app structure is:
- Respect environment-driven behavior (`SD_*` flags and options) instead of hardcoding platform/model assumptions.
- For startup/init edits, preserve error handling and partial-failure tolerance in parallel scans and extension loading.
## Pitfalls
- Initialization order matters: startup paths in `launch.py` and `webui.py` are sensitive to import/load timing.
- Shared mutable global state can create subtle regressions; prefer narrow, explicit changes.
- Device/backend-specific code paths (**CUDA/ROCm/IPEX/DirectML/OpenVINO**) should not assume one platform.
- Scripts and extension loading is dynamic; failures may appear only when specific extensions or models are present.
## File Creation
- Any temporary scripts or markdown reports must be stored in `tmp/` folder
@@ -73,6 +78,10 @@ Use these repo-local skills for recurring SD.Next model integration work:
File: `.github/skills/port-model/SKILL.md`
Use when adding a new model family, porting a standalone script into a Diffusers pipeline, or wiring an upstream Diffusers model into SD.Next.
- `port-pipeline`
File: `.github/skills/port-pipeline/SKILL.md`
Use when porting a custom model pipeline implementation to a Diffusers pipeline class with behavior parity and no hard-coded device or attention assumptions.
- `debug-model`
File: `.github/skills/debug-model/SKILL.md`
Use when a new or existing SD.Next/Diffusers model integration fails during detection, loading, prompt encoding, sampling, or output handling.
+16
View File
@@ -13,3 +13,19 @@ applyTo: "launch.py, webui.py, installer.py, modules/**/*.py, pipelines/**/*.py,
- Follow existing API/server patterns under `modules/api/` and reuse shared queue/state helpers rather than ad-hoc request handling.
- Reuse established model-loading and pipeline patterns (`modules/sd_*`, `pipelines/`) instead of creating parallel abstractions.
- For substantial Python changes, run at least relevant checks: `npm run ruff` and `npm run pylint` (or narrower equivalents when appropriate).
## Build And Test
- Activate environment: `source venv/bin/activate` (always ensure this is active when working with Python code).
- Test startup: `python launch.py --test`
- Full startup: `python launch.py`
- Full lint sequence: `pnpm lint`
- Python checks individually: `pnpm ruff`, `pnpm pylint`
- JS checks: `pnpm eslint` and `pnpm eslint-ui`
## Pitfalls
- Initialization order matters: startup paths in `launch.py` and `webui.py` are sensitive to import/load timing.
- Shared mutable global state can create subtle regressions; prefer narrow, explicit changes.
- Device/backend-specific code paths (**CUDA/ROCm/IPEX/DirectML/OpenVINO**) should not assume one platform.
- Scripts and extension loading is dynamic; failures may appear only when specific extensions or models are present.
@@ -0,0 +1,50 @@
---
description: "Use when editing hint text or other UI strings in localization JSON files."
name: "Hint Typography Guidelines"
applyTo: "html/locale_*.json, html/override_*.json"
---
# Hint Typography Guidelines
Hint strings render as HTML. Use this small set of inline tags to keep hints scannable:
- `<b>` for values: defaults, dropdown enums, specific numerics. Examples: `<b>0.30</b>`, `<b>Karras</b>`, `<b>v_prediction</b>`, `<b>UniPC</b>`.
- `<b><i>...</i></b>` for cross-references to other UI controls by their exact visible label. Examples: `<b><i>Denoising strength</i></b>`, `<b><i>Use init image</i></b>`, `<b><i>Images</i></b> tab.
- `<i>` for proper nouns: model families, datasets, technique names. Examples: `<i>SDXL</i>`, `<i>Flux</i>`, `<i>ControlNet</i>`, `<i>YOLO</i>`.
- `<code>` for literals: paths, filename tokens, command-line snippets to type or use verbatim. Examples: `<code>models/yolo</code>`, `<code>-seg</code>`, `<code>[PROMPT]</code>`.
## Cross-references
- Use `<b><i>...</i></b>` whenever a hint refers to another control by its exact visible label. This includes setting names, tab names, and named buttons.
- Match the label exactly, including capitalization and spacing; readers look for the same string in the UI.
- Do not use `<b>` and `<i>` separately for cross-references; always combine them.
- Generic concept references (`the model`, `the prompt`, `the scheduler`) stay unstyled.
## Tab naming
- Refer to the unified generation tab as `<b><i>Images</i></b>` (the ModernUI label). Do not write "Control tab"; that label only exists in legacy Standard UI.
- "Control" remains valid as a setting value (`<b>No: Control only</b>`) or as part of a UI element name (`<b><i>Control input</i></b>` pane), just not as a tab name.
## Structure
- `<br>` for a line break within a paragraph.
- `<br><br>` for a paragraph break.
- `<br>- <b>key</b>: description` for a keyed bullet list, used for short enumerations of dropdown values, modes, or numeric brackets. Each bullet's key is bolded; descriptions stay plain.
- Do not use `<ul>`, `<li>`, Markdown asterisks, or unicode bullets.
## Common pitfalls
- Do not bold ad-hoc emphasis; `<b>` is reserved for values and, combined with `<i>`, for cross-references.
- Do not use `<b>` for filenames, paths, or command tokens; those are literals and use `<code>`.
- Do not reword the inside of `<code>` blocks; they are literal user-facing strings.
- Stay ASCII; prefer semicolons or two sentences over em-dashes. The locale file convention is ASCII-only.
## Translation propagation
- `html/locale_en.json` is the source of truth. Other `html/locale_*.json` files are auto-generated by `cli/localize.js`; edit only the English file.
- Per-locale corrections live in `html/override_{locale}.json`.
## Validation
- Validate JSON syntax with `jq empty html/locale_en.json`.
- Lint with `pnpm eslint -- html/locale_en.json` (silent success).
- See `wiki/Hints.md` for the wiki-facing version of these rules.
+4
View File
@@ -8,6 +8,10 @@ This folder contains repo-local Copilot skills for recurring SD.Next tasks.
File: `port-model/SKILL.md`
Use when adding or porting a model family into SD.Next and Diffusers.
- `port-pipeline`
File: `port-pipeline/SKILL.md`
Use when porting a custom pipeline implementation into a Diffusers pipeline class while preserving behavior and avoiding hard-coded runtime assumptions.
- `debug-model`
File: `debug-model/SKILL.md`
Use when a new or existing SD.Next/Diffusers model integration fails during detect, load, prompt encode, sample, or output handling.
+8
View File
@@ -63,6 +63,11 @@ Before implementing model-reference updates, explicitly ask the user which categ
Do not guess this category. Use the user answer to decide which reference JSON file(s) to update.
## Mandatory Pipeline Question
Before implementing a pipeline, explicitly ask the user if the model already has an upstream Diffusers pipeline that can be reused.
If not, ask for URL or path to a reference implementation that can be structurally copied.
## Repo Files To Check
Start by reading the task description, then inspect the closest matching implementations.
@@ -130,6 +135,9 @@ Pipeline module responsibilities:
- Output dataclass
- Optional callback handling and output conversion
If custom pipeline is provided by user, check it for accuracy and completness but do not assume it is perfect. Make necessary adjustments to fit SD.Next patterns and validate the result.
Fix all relative imports to be absolute and compatible with SD.Next repo structure, make sure that all imports are resolvable and make sure it passes `ruff` checks.
### 3. Raw Checkpoint Or Single-File Weights
Use this path when the model source is not a normal Diffusers repository.
+102
View File
@@ -0,0 +1,102 @@
---
name: port-pipeline
description: "Port custom model pipeline implementations to Diffusers. Use when migrating custom or non-Diffusers pipeline code into SD.Next repo-local pipeline files such as pipelines/model_<name>.py or pipelines/<model>/pipeline.py while preserving behavior, avoiding new dependencies, and keeping device/attention handling configurable."
argument-hint: "Provide source pipeline path, target SD.Next destination path, and target pipeline class name"
---
# Port Custom Pipeline To Diffusers
Port an existing custom model pipeline implementation into a Diffusers-compatible pipeline class with behavior parity and SD.Next-friendly conventions.
This skill targets SD.Next repo-local pipeline ports only.
## When To Use
- A user has a custom pipeline implementation and wants it ported to Diffusers
- Existing model code is runnable but not structured as a Diffusers pipeline
- The destination is SD.Next pipeline code under `pipelines/model_*.py` or `pipelines/<model>/`
- The task requires preserving generation behavior without introducing new dependencies
- The task requires removing hard-coded runtime assumptions (device or attention backend)
## Mandatory Clarification Gate
Before implementation, confirm these required inputs with the user:
1. Path to the source custom pipeline implementation
2. Destination path in this SD.Next repository (typically under `pipelines/`)
3. Target pipeline class name
If any of the above are missing or ambiguous, stop and ask concise clarification questions before writing code.
## Constraints
- Do not add new dependencies
- Do not hard-code device type (`cpu`, `cuda`, `mps`, etc.)
- Do not hard-code attention type or backend assumptions
- Preserve externally visible behavior of the source pipeline unless the user asks for intentional changes
## Workflow
1. Collect Inputs
- Ask for source path, destination path, and target pipeline name.
- Confirm destination is an SD.Next repo-local pipeline location, not an upstream Diffusers repository path.
- Confirm runtime assumptions and expected task type (text-to-image, image-to-image, inpaint, etc.).
2. Analyze Source Pipeline
- Inspect model loading, prompt processing, denoising or sampling loop, scheduler interactions, and output post-processing.
- Identify all components that must be ported: models, tokenizers or processors, schedulers, adapters, preprocessors, postprocessors, callbacks, and output dataclasses.
- Note any hidden global state, side effects, or implicit defaults that must become explicit parameters.
3. Map To Diffusers Interfaces
- Choose the most appropriate Diffusers base class and output type.
- Define `__init__`, module registration, `from_pretrained` and `__call__` signatures aligned with existing Diffusers patterns.
- Keep parameter names and behavior as close as possible to upstream conventions.
- Identify any custom classes needed beyond the pipeline itself: transformer blocks, attention processors, custom schedulers, or output types. Plan a separate module file for each.
4. Implement Supporting Classes
- If the pipeline requires custom model classes (e.g., a custom transformer block, attention module, or other model component), implement each in a **separate module** located in the **same directory** as the main pipeline file (e.g., `pipelines/<model>/transformer.py`, `pipelines/<model>/scheduler.py`).
- If the pipeline requires a custom scheduler class, implement it in its own module (e.g., `pipelines/<model>/scheduler_<name>.py`) following Diffusers scheduler conventions (`step`, `add_noise`, `scale_model_input`, etc.).
- Each supporting class module must be self-contained: no circular imports, no hidden global state, and no hard-coded device or attention assumptions.
- Import supporting classes into the main pipeline module from their respective sibling modules.
5. Implement Pipeline Class
- Create the destination pipeline classes at the user-provided path.
- Port logic in small, testable sections: initialization, input validation, prompt encoding, latent preparation, denoising loop, decoding, and output packaging.
- Replace hard-coded device and attention logic with runtime-configurable behavior.
- Keep imports limited to existing project and Diffusers dependencies.
6. Lint And Fix
- Activate the project venv: `source venv/bin/activate`
- Run `ruff` on all newly written files: `pnpm ruff` (or `ruff check <file> --fix` for targeted runs).
- Run `pylint` on all newly written files: `pnpm pylint` (or `pylint <file>` for targeted runs).
- Fix every reported error or warning that is not explicitly marked with a `TODO` suppression comment in the source.
- Re-run both linters after fixes to confirm a clean result before proceeding.
7. Validate Behavior Parity
- Compare source and ported implementations for input-output shape handling, dtype flow, scheduler step ordering, and guidance behavior.
- Run focused checks or smoke tests if available in the workspace.
- Call out any known differences that were required for Diffusers compatibility.
8. Report Results
- Summarize what was ported and where.
- List any unresolved assumptions, risks, or TODOs.
- Provide minimal follow-up steps for integration and testing.
## Review Checklist
- Required inputs were collected before edits
- No new dependency was introduced
- No hard-coded device or attention backend remains
- Core components from source pipeline were fully mapped
- Pipeline class is in requested destination with requested name
- Each custom supporting class (transformer, scheduler, etc.) is in its own sibling module
- Supporting modules have no circular imports or hidden global state
- `ruff` and `pylint` both pass cleanly on all newly written files (venv activated)
- Main inference path behavior matches the source implementation
## Output Expectations
Final response should include:
- Source path, destination path, and final pipeline class name
- Brief parity summary of key components ported
- Validation performed and any gaps
- Explicit note of any assumptions requiring user confirmation
+117
View File
@@ -1,5 +1,122 @@
# Change Log for SD.Next
## Update for 2026-05-13
### Highlights for 2026-05-13
Just two weeks since last release, but we have a lot of new models and features to cover!
*What's New?*
- Image editing models now can work with multiple image inputs!
- Six new models: *HiDream-O1 Image*, *JoyAI Image Edit*, *Step1X-Edit*, *VIBE Image Edit* and *UltraFlux*
- Enhanced capabilities for *Anima*, *Ernie-Image*, *LTX*, *Flux.2* and *Chroma* models
- Enhanced *LoRA* capabilities in many models
- UI improvements across the board: *Main panels*, *Gallery*, *Kanvas*, *Networks*, and more...
For full details, see [ChangeLog](https://github.com/vladmandic/automatic/blob/master/CHANGELOG.md)
[ReadMe](https://github.com/vladmandic/automatic/blob/master/README.md) | [ChangeLog](https://github.com/vladmandic/automatic/blob/master/CHANGELOG.md) | [Docs](https://vladmandic.github.io/sdnext-docs/) | [WiKi](https://github.com/vladmandic/automatic/wiki) | [Discord](https://discord.com/invite/sd-next-federal-batch-inspectors-1101998836328697867) | [Sponsor](https://github.com/sponsors/vladmandic)
### Details for 2026-05-13
- **Models**
- [HiDream-O1-Image](https://huggingface.co/HiDream-ai/HiDream-O1-Image) pixel-level unified transformer model support
HiDream-O1 is based on a single custom *Qwen3-VL* 8.8B 35GB component
includes both **HiDream-O1-Image** *(base)* and **HiDream-O1-Image-Dev** *(distilled*)* variants
includes *sdnq-svd-dynamic-int8* pre-quantized variants for both base and dev models
includes *T2I* and *I2I edit* capabilities and resolutions up to 2048px
*note*: use steps:50 for base and steps:28 for dev variants
- [JoyAI Image Edit](https://huggingface.co/jdopensource/JoyAI-Image-Edit-Diffusers) image-editing model support
includes multimodal conditioning using *Qwen3-VL* with a dedicated *JoyImageEdit* diffusion transformer
*note* this is a large model at 50GB so use of aggressive quantization is recommended
- [StepFun Step1X-Edit v1.1](https://huggingface.co/stepfun-ai/Step1X-Edit-v1p1-diffusers) image-editing model support
step1x is a large dedicated image edit model combining qwen-2.5 8B encoder with custom 12.4B transformer
- [VIBE Image Edit](https://huggingface.co/iitolstykh/VIBE-Image-Edit) image-editing model support
built on Sana1.5-1.6B diffusion backbone with Qwen3-VL-2B multimodal conditioning
primarily image-editing model, but supports t2i as well, uses multi-scale resolution binning up to 2048px
- [AlphaVLLM Lumina-DiMOO](https://huggingface.co/Alpha-VLLM/Lumina-DiMOO) unified multimodal diffusion model
includes *T2I*, *I2I edit*, and *MMU* capabilities in a single pipeline
*note* model also supports special prompts: *dense, canny_pred, control, subject, edit, ref_transfer, multi_view*
*note* as with most multimodal/unified models, it needs higher step count (recommended is 64 steps) and uses quite a lot of VRAM, so use with caution!
- [Owen777 UltraFlux-v1](https://huggingface.co/Owen777/UltraFlux-v1) native 4K text-to-image model based on *FLUX.1-dev*
*note*: UltraFlux is capable of rendering images up to 4K resolution, but it doesnt mean it will do that on any hardware - it will depend on your VRAM!
- [Anima Preview-v3](https://huggingface.co/circlestone-labs/Anima)
add *turbo* variant with [turbo-LoRA](https://civitai.com/models/2560840/anima-turbo-lora) pre-merged
add *sdnq-svd-dynamic-int8* pre-quantized variant
- **Features**
- **Multi-image** workflows!
for models that support multiple images as inputs, you can now add multiple stages in Kanvas
prompts like "*place character from first image, add background from second image, render in style from third image*" are now possible
- option *inputs -> skip processing* to force images to passed to model as-is without any pre-processing
examples of models that support multi-inputs: *qwen-image-edit, flux.2, google-gemini*
- [SD Ultimate Upscale](https://github.com/Coyote-A/ultimate-upscale-for-automatic1111)
still a popular method for upscaling, but has not been updated nor maintained for a while
so now its modernized and fully integrated as a built-in script!
- **LTX** support for *audio* generation
- **Anima** support for *img2img* and *inpaint* workflows
- **Ernie-Image** add native *LoRA* support, *img2img* and *inpaint* workflows
- **Chroma** add native *LoRA* support
- **Flux.2** add native *LoRA* support
- **Prompt enhance** add info to image metadata
- custom **VAE** loader for all pipelines
*note*: vae still needs to be compatible with the model
- **CivitAI** downloaded thumbnails now include metadata
- **Installer** support for `git+http` style references
- **UI**
- **Networks** using networks to load model or auto-download a reference model will now be reflected in the UI
- ability to manually reorient *input/output* panels
- all ui panels can be *minimized/maximized* by clicking on their header
state is preserved across sessions and can be used to hide rarely used panels and declutter the workspace
- **Kanvas** re-order stages by clicking on active stage
order of stages determines order of images passed to model
- **Kanvas** *magic-wand* tool now works on mask layer and auto-creates mask based on perceptual tolerance
- **Gallery** add thumbnail size slider
- **Gallery** add quick info/download/delete buttons on thumbnail hover
- **Models** sortable columns, ability to remove a model
applies to models as well as huggingface cache entries
- **Server Info** add button *copy-to-clipboard*
useful for sharing your system info when asking for help in discord or github
- **Control**
- remove buttons: *input/control/process*
- move params *control input type* to control menu section
- remove "processed preview" from ui
preprocessor output can still be generated by clicking preview button in in control unit and it will render into normal output area
- **Internal**
- `offload` auto-reapply hook on error
- refactor `pip` installer, thanks @awsr
- remove obsolete `lora` stepwise and functional code, thanks @awsr
- interrupt model loading between components
- patch `rich` for cleaner exception logging
- lint `ruff` strict and reduce exceptions
- lint `pylint` improvements
- lint `ty` readiness
- **Fixes**
- add missing `jquery` and `sparkline` js scripts
- save handle already decoded images
- `ernie-image` preview
- `lora` false deactivate
- `kandinsky-5` t2i/i2i workflows
- progress do not timeout when paused
- faster server shutdown/restart, thanks @awsr
- `openvino` force offload none
- `lut` file handling
- warn on pipeline ignoring `cfg`
- detailer `segmentation`, thanks @awsr
- `ipex` invalid device type
- cache network thumbnails
- `scripts` corrupting control ui state
- avoid `callback` duplicate registrations
- pipeline task change causing loss of info on loaded `lora`
- `detailer` handle `lora` internally
- vae preview flashes previous image
- `torch.compile` improvements
- `gradio` preprocess exception handling
- `ipadapters` with offloading
- `kanvas` outpaint
- `network` preview handle invalid image
- `schedulers` improve *set_timesteps* handling
- `schedulers` improve *scale_noise* handling
## Update for 2026-04-28
### Highlights for 2026-04-28
+1 -1
View File
@@ -57,7 +57,7 @@ SD.Next is feature-rich with a focus on performance, flexibility, and user exper
SD.Next includes many features not found in other WebUIs, such as:
- **SDNQ**: State-of-the-Art quantization engine
Use pre-quantized or run with quantizaion on-the-fly for up to 4x VRAM reduction with no or minimal quality and performance impact
Use pre-quantized or run with quantization on-the-fly for up to 4x VRAM reduction with no or minimal quality and performance impact
- **Balanced Offload**: Dynamically balance CPU and GPU memory to run larger models on limited hardware
- **Captioning** with 150+ **OpenCLiP** models, **Tagger** with **WaifuDiffusion** and **DeepDanbooru** models, and 25+ built-in **VLMs**
- **Image Processing** with full image correction color-grading suite of tools
+26 -26
View File
@@ -4,13 +4,13 @@
### Assigned
- Gallery: thumb-size, quick delete/download/info @vladmandic
- Chat-based interface, @vladmandic
- Multi-image inputs, @vladmandic
- Control tab verify overrides handling, @vladmandic
- Reimplement `llama` remover for Kanvas, @vladmandic
- Integrate [Depth3D](https://github.com/vladmandic/sd-extension-depth3d), @vladmandic
- Implement [pruna](https://github.com/PrunaAI/pruna), @vladmandic
- Change params to default, @vladmandic
- Detailer postprocessing, @CalamitousFelicitousness
- Cloud providers, @CalamitousFelicitousness
- Video processing add full API support, @CalamitousFelicitousness
@@ -20,7 +20,6 @@
- `RIFE` in processing
- `SeedVR2` in processing
- Video model loader: Add video models to Reference
- REMBG add <https://huggingface.co/briaai/RMBG-2.0>
- UI Lite vs Expert mode
- TensorRT acceleration
- Auto handle scheduler `prediction_type`
@@ -56,21 +55,13 @@ TODO: Investigate which models are diffusers-compatible and prioritize!
### Image
- [JoyAI-Image-Edit](https://github.com/huggingface/diffusers/pull/13444) (pr in-progress)
- [Lumina-DiMOO](https://github.com/huggingface/diffusers/pull/12468) (pr stalled)
- [Step1X-Edit](https://github.com/huggingface/diffusers/pull/12249) (pr stalled)
- [VIBE Image Edit](https://huggingface.co/iitolstykh/VIBE-Image-Edit) (diffusers-compatible)
- [nVidia Cosmos-Predict-2.5](https://huggingface.co/nvidia/Cosmos-Predict2.5-2B) (in diffusers)
- [nVidia Cosmos-Transfer-2.5](https://huggingface.co/nvidia/Cosmos-Transfer2.5-2B) (in diffusers)
- [UltraFlux](https://huggingface.co/Owen777/UltraFlux-v1) (diffusers-compatible)
- [Tencent HY-WU](https://huggingface.co/tencent/HY-WU) (transformers-compatible)
- [Mugen](https://huggingface.co/CabalResearch/Mugen) (sdxl with flux vae experiment, not clean)
- [Liquid](https://github.com/FoundationVision/Liquid) (autoregressive, not clean)
### Video
- [HY-OmniWeaving](https://huggingface.co/tencent/HY-OmniWeaving)
- [LTX-Condition](https://huggingface.co/Lightricks/LTX-2)
- [LTX-Distilled](https://huggingface.co/Lightricks/LTX-2)
- [OpenMOSS MOVA](https://huggingface.co/OpenMOSS-Team/MOVA-720p)
- [Wan2.2-Animate](https://huggingface.co/Wan-AI/Wan2.2-Animate-14B)
- [Wan2.1-T2V-14B-CausVid](https://huggingface.co/lightx2v/Wan2.1-T2V-14B-CausVid)
@@ -84,12 +75,11 @@ TODO: Investigate which models are diffusers-compatible and prioritize!
- [Sana I2V](https://huggingface.co/Efficient-Large-Model/SANA-Video_2B_480p_diffusers)
- [Wan-2.2 S2V](https://huggingface.co/Wan-AI/Wan2.2-S2V-14B)
- [Meituan LongCat-Video](https://huggingface.co/meituan-longcat/LongCat-Video)
- [LTXVideo LongMulti](https://huggingface.co/Lightricks/LTX-Video-0.9.8-13B-distilled)
- [Phantom HuMo](https://github.com/Phantom-video/Phantom)
- [CausVid-Plus](https://github.com/goatWu/CausVid-Plus/)
- [LivePortrait](https://github.com/KwaiVGI/LivePortrait)
- [Magi (SandAI)](https://github.com/SandAI-org/MAGI-1)
- [Ming (inclusionAI)](https://github.com/inclusionAI/Ming)
- [SandAI Magi](https://github.com/SandAI-org/MAGI-1)
- [inclusionAI Ming](https://github.com/inclusionAI/Ming)
- [HummingbirdXT](https://huggingface.co/amd/HummingbirdXT)
- [DiffusionForcing](https://github.com/kwsong0113/diffusion-forcing-transformer)
- [ByteDance Lynx](https://github.com/bytedance/lynx)
@@ -155,20 +145,30 @@ TODO: Investigate which models are diffusers-compatible and prioritize!
- Background removal model trained on Bria FIBO dataset
- Created: 2025-08 | Updated: 2025-09 | Stars: N/A (private model)
### Rejected
- [Mugen](https://huggingface.co/CabalResearch/Mugen) (sdxl with flux vae experiment, not clean)
- [Liquid](https://github.com/FoundationVision/Liquid) (autoregressive, not clean)
## Code TODO
> npm run todo
```code
installer.py:642:15: W0511: TODO rocm: switch to pytorch source when it becomes available (fixme)
modules/transformer_cache.py:29:61: W0511: TODO fc: autodetect tensor format based on model (fixme)
modules/transformer_cache.py:30:50: W0511: TODO fc: autodetect distilled based on model (fixme)
modules/processing_class.py:404:32: W0511: TODO processing: remove duplicate mask params (fixme)
modules/sd_samplers_diffusers.py:355:31: W0511: TODO enso-required (fixme)
modules/sd_models.py:1356:5: W0511: TODO model load: implement model in-memory caching (fixme)
modules/ui_models_load.py:257:5: W0511: TODO loader: load receipe (fixme)
modules/ui_models_load.py:264:5: W0511: TODO loader: save receipe (fixme)
modules/sd_hijack_hypertile.py:123:17: W0511: TODO hypertile: vae breaks when using non-standard sizes (fixme)
modules/sd_unet.py:77:39: W0511: TODO model load: force-reloading entire model as loading transformers only leads to massive memory usage (fixme)
modules/modular_guiders.py:66:51: W0511: TODO: guiders (fixme)
installer.py:TODO rocm: switch to pytorch source when it becomes available
modules/control/run.py:TODO modernui: monkey-patch for missing tabs.select event
modules/history.py:TODO: apply metadata, preview, load/save
modules/image/resize.py:TODO resize image: enable full VAE mode for resize-latent
modules/lora/lora_load.py:TODO lora: add t5 key support for sd35/f1
modules/masking.py:TODO: additional masking algorithms
modules/modular_guiders.py:TODO: guiders
modules/processing_class.py:TODO processing: remove duplicate mask params
modules/sd_hijack_hypertile.py:TODO hypertile: vae breaks when using non-standard sizes
modules/sd_models.py:TODO model load: implement model in-memory caching
modules/sd_samplers_diffusers.py:TODO enso-required
modules/sd_unet.py:TODO model load: force-reloading entire model as loading transformers only leads to massive memory usage
modules/transformer_cache.py:TODO fc: autodetect distilled based on model
modules/transformer_cache.py:TODO fc: autodetect tensor format based on model
modules/ui_models_load.py:TODO loader: load receipe
modules/ui_models_load.py:TODO loader: save receipe
```
+10
View File
@@ -36,6 +36,16 @@
"skip": true,
"extras": "sampler: Default, cfg_scale: 4.5"
},
"HiDream-O1 Image Dev": {
"path": "HiDream-ai/HiDream-O1-Image-Dev",
"preview": "HiDream-ai--HiDream-O1-Image-Dev.jpg",
"desc": "HiDream-O1-Image-Dev is the distilled 8B HiDream-O1 variant tuned for 28-step fast generation using flash flow scheduling.",
"skip": true,
"extras": "sampler: Flash, steps: 28, cfg_scale: 0.0",
"size": 35.2,
"tags": "distilled",
"date": "2026 May"
},
"Qwen-Image-Lightning": {
"path": "vladmandic/Qwen-Lightning",
"preview": "vladmandic--Qwen-Lightning.jpg",
+38 -2
View File
@@ -242,7 +242,7 @@
"desc": "ERNIE-Image is a text-to-image diffusion transformer model that combines a Mistral3 text encoder with a FlowMatch transformer and Flux2-style VAE for 1024px image generation.",
"skip": true,
"extras": "sampler: Default, cfg_scale: 4.0, steps: 50",
"size": 23.93,
"size": 7.52,
"date": "2026 April"
},
"Baidu ERNIE-Image-Turbo sdnq-dynamic-int4": {
@@ -251,8 +251,44 @@
"desc": "ERNIE-Image-Turbo is a distilled ERNIE-Image variant optimized for fast generation with fewer denoising steps.",
"skip": true,
"extras": "sampler: Default, cfg_scale: 1.0, steps: 8",
"size": 23.37,
"size": 7.52,
"tags": "distilled",
"date": "2026 April"
},
"Anima Preview 3 sdnq-dynamic-int8": {
"path": "vladmandic/Anima-Preview-3-diffusers-SDNQ-8bit-dynamic",
"preview": "CalamitousFelicitousness--Anima-Preview-3-sdnext-diffusers.jpg",
"desc": "Anima Preview V3 with extended 1024-resolution training and expanded dataset coverage for less common artists. A 2B parameter anime-focused text-to-image model based on modified Cosmos-Predict-2B with Qwen3-0.6B text encoder, created by CircleStone Labs and Comfy Org.",
"tags": "community",
"date": "2026 April",
"size": 3.19,
"skip": true
},
"Anima Preview 3 Turbo sdnq-dynamic-int8": {
"path": "vladmandic/Anima-Preview-3-turbo-diffusers-SDNQ-8bit-dynamic",
"preview": "CalamitousFelicitousness--Anima-Preview-3-sdnext-diffusers.jpg",
"desc": "Anima Preview V3 with extended 1024-resolution training and expanded dataset coverage for less common artists. A 2B parameter anime-focused text-to-image model based on modified Cosmos-Predict-2B with Qwen3-0.6B text encoder, created by CircleStone Labs and Comfy Org.",
"tags": "community",
"date": "2026 April",
"size": 3.19,
"skip": true
},
"HiDream-O1 Image sdnq-dynamic-int8": {
"path": "vladmandic/HiDream-O1-Image-SDNQ-8bit-dynamic",
"desc": "HiDream-O1-Image is an 8B pixel-level unified transformer model for text-to-image generation, instruction editing, and multi-reference personalization up to 2048x2048.",
"preview": "HiDream-ai--HiDream-O1-Image.jpg",
"skip": true,
"extras": "sampler: Default",
"size": 10.34,
"date": "2026 May"
},
"HiDream-O1 Image Dev sdnq-dynamic-int8": {
"path": "vladmandic/HiDream-O1-Image-Dev-SDNQ-8bit-dynamic",
"desc": "HiDream-O1-Image is an 8B pixel-level unified transformer model for text-to-image generation, instruction editing, and multi-reference personalization up to 2048x2048.",
"preview": "HiDream-ai--HiDream-O1-Image.jpg",
"skip": true,
"extras": "sampler: Default",
"size": 10.34,
"date": "2026 May"
}
}
+58
View File
@@ -143,6 +143,16 @@
"date": "2025 January"
},
"Owen777 UltraFlux-v1": {
"path": "Owen777/UltraFlux-v1",
"preview": "Owen777--UltraFlux-v1.jpg",
"desc": "UltraFlux-v1 is a FLUX.1-dev based text-to-image model optimized for native 4K and multi-aspect-ratio generation with improved composition consistency.",
"skip": true,
"extras": "sampler: Default, cfg_scale: 4.0, steps: 50",
"size": 33.0,
"date": "2025 November"
},
"Z-Image": {
"path": "Tongyi-MAI/Z-Image",
"preview": "Tongyi-MAI--Z-Image.jpg",
@@ -622,6 +632,15 @@
"size": 20.75,
"date": "2025 January"
},
"AlphaVLLM Lumina DiMOO": {
"path": "Alpha-VLLM/Lumina-DiMOO",
"desc": "Lumina-DiMOO is an omni diffusion large language model for multimodal generation and understanding with text-to-image, image editing, and multimodal understanding capabilities.",
"preview": "Alpha-VLLM--Lumina-DiMOO.jpg",
"skip": true,
"extras": "sampler: Default",
"size": 0,
"date": "2025 September"
},
"HiDream-I1 Fast": {
"path": "HiDream-ai/HiDream-I1-Fast",
@@ -650,6 +669,15 @@
"size": 58.4,
"date": "2025 April"
},
"HiDream-O1 Image": {
"path": "HiDream-ai/HiDream-O1-Image",
"desc": "HiDream-O1-Image is an 8B pixel-level unified transformer model for text-to-image generation, instruction editing, and multi-reference personalization up to 2048x2048.",
"preview": "HiDream-ai--HiDream-O1-Image.jpg",
"skip": true,
"extras": "sampler: Default",
"size": 35.2,
"date": "2026 May"
},
"HiDream-E1 Full": {
"path": "HiDream-ai/HiDream-E1-Full",
"desc": "HiDream-E1 is an image editing model built on HiDream-I1.",
@@ -924,6 +952,36 @@
"extras": "sampler: Default, cfg_scale: 3.5",
"size": 16.2,
"date": "2025 December"
},
"StepFun Step1X-Edit v1.1": {
"path": "stepfun-ai/Step1X-Edit-v1p1-diffusers",
"preview": "stepfun-ai--Step1X-Edit-v1p1-diffusers.jpg",
"desc": "Multimodal image editing model using Step1X transformer architecture with Qwen2.5-VL text encoding, trained with Flow Matching scheduler for high-quality in-context image edits and refinements.",
"skip": true,
"extras": "sampler: Default",
"size": 24.85,
"date": "2025 September"
},
"VIBE Image Edit": {
"path": "vladmandic/VIBE-Image-Edit",
"preview": "vladmandic--VIBE-Image-Edit.jpg",
"desc": "VIBE is an open-source text-guided image editing model combining Sana1.5-1.6B diffusion backbone with Qwen3-VL multimodal conditioning for fast, instruction-based edits.",
"skip": true,
"extras": "sampler: Default, cfg_scale: 4.5, image_guidance_scale: 1.2, steps: 20",
"size": 9.27,
"date": "2025 December"
},
"JoyAI Image Edit": {
"path": "jdopensource/JoyAI-Image-Edit-Diffusers",
"preview": "jdopensource--JoyAI-Image-Edit-Diffusers.jpg",
"desc": "JoyAI Image Edit is a Diffusers-native image editing model that combines a JoyImageEdit transformer with Qwen3-VL multimodal conditioning for instruction-guided edits.",
"skip": true,
"size": 50.31,
"extras": "sampler: Default",
"date": "2026 April"
}
}
+1
View File
@@ -102,6 +102,7 @@ const jsConfig = defineConfig([
idbPut: 'readonly',
idbDel: 'readonly',
idbAdd: 'readonly',
initTableSorter: 'readonly',
idbCount: 'readonly',
idbFolderCleanup: 'readonly',
idbClearAll: 'readonly',
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 13,
"label": "Input Media",
"label": "Input",
"localized": "وسائط الإدخال",
"reload": "",
"hint": "إضافة صورة إدخال لاستخدامها في معالجة التحويل من صورة إلى صورة، أو التلوين (Inpaint)، أو التحكم"
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 13,
"label": "Input Media",
"label": "Input",
"localized": "ইনপুট মিডিয়া",
"reload": "",
"hint": "ইমেজ-টু-ইমেজ, ইনপেইন্ট বা কন্ট্রোল প্রসেসিংয়ের জন্য ইনপুট ছবি যোগ করুন"
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 14,
"label": "Input Media",
"label": "Input",
"localized": "Eingabemedien",
"reload": "",
"hint": "Eingabebild hinzufügen, das für Image-to-Image-, Inpaint- oder Control-Verarbeitung verwendet werden soll"
+102 -102
View File
@@ -24,7 +24,7 @@
{"id":"xyz_grid_x_list","label":"⊜","localized":"","hint":"Fill","ui":"script_xyz_grid_script"},
{"id":"txt2img_caption_output","label":"","localized":"","hint":"Caption image","ui":"txt2img"},
{"id":"txt2img_image_fit","label":"⁜","localized":"","hint":"Cycle image fit method","ui":"txt2img"},
{"id":"","label":"➠ Control","localized":"","hint":"Transfer image to control interface. <br><br> Right-click this button to transfer only the prompt or all generation parameters to the Images tab without sending the image itself.","ui":"txt2img"},
{"id":"","label":"➠ Control","localized":"","hint":"Transfer image to the <b><i>Images</i></b> tab.<br><br>Right-click this button to transfer only the prompt or all generation parameters without sending the image itself.","ui":"txt2img"},
{"id":"","label":"➠ Text","localized":"","hint":"Transfer image to text interface","ui":"txt2img"},
{"id":"","label":"➠ Image","localized":"","hint":"Transfer image to image interface","ui":"txt2img"},
{"id":"","label":"➠ Process","localized":"","hint":"Transfer image to process interface","ui":"txt2img"},
@@ -72,16 +72,16 @@
{"id":"","label":"Advanced Options","localized":"","hint":"","ui":"settings_sd"},
{"id":"","label":"Appearance","localized":"","hint":"","ui":"settings_ui"},
{"id":"","label":"Answer","localized":"","hint":"","ui":"caption"},
{"id":"","label":"Adjust start","localized":"","hint":"Starting step when sigma adjust occurs","ui":"txt2img"},
{"id":"","label":"Adjust end","localized":"","hint":"Ending step when sigma adjust occurs","ui":"txt2img"},
{"id":"","label":"Adjust start","localized":"","hint":"Lower bound of the denoising window where Sigma adjust is active, as a fraction of the noise schedule (1.0 = pure noise, 0.0 = clean image).<br>The adjustment stops once denoising progresses past this point, so higher values end the effect earlier.<br><br>Default 0.2 leaves the final ~20% of the schedule unmodified.","ui":"txt2img"},
{"id":"","label":"Adjust end","localized":"","hint":"Upper bound of the denoising window where Sigma adjust is active, as a fraction of the noise schedule (1.0 = pure noise, 0.0 = clean image).<br>The adjustment only begins once denoising has progressed past this point, so lower values delay the effect further into the run.<br><br>Default 0.8 leaves the first ~20% of the schedule unmodified.","ui":"txt2img"},
{"id":"","label":"Autocomplete","localized":"","hint":"Enable or disable Tag Autocomplete. Choose which dictionaries are used for prompt autocompletion in Extras","ui":"control"},
{"id":"","label":"AutoGuidance dropout","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"AutoGuidance layers","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"AutoGuidance config","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"APG momentum","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"APG rescale","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"Attention guidance","localized":"","hint":"CFG scale used for with PAG: Perturbed-Attention Guidance","ui":"txt2img"},
{"id":"","label":"Adaptive scaling","localized":"","hint":"Adaptive modifier for attention guidance scale","ui":"txt2img"},
{"id":"","label":"Attention guidance","localized":"","hint":"Dual-purpose slider that activates one of two guidance mechanisms depending on the loaded model.<br>- <b>SD 1.5 and SDXL</b>: enables Perturbed Attention Guidance (PAG). Sdnext silently swaps the pipeline to a PAG-aware variant and steers generation away from a self-attention-perturbed prediction, improving structure and detail. Used in addition to the regular <b><i>Guidance scale</i></b>.<br>- <b>Flux, QwenImage, HiDream, Hunyuan Video, Sana, and other flow-matching models</b>: routes to true_cfg_scale, enabling classifier-free guidance with negative prompts on models that don't natively use CFG.<br>On other models the slider has no effect.<br><br>Set to 0 to disable.<br>Disabled by default.","ui":"txt2img"},
{"id":"","label":"Adaptive scaling","localized":"","hint":"Decay rate for the Perturbed Attention Guidance (PAG) component of <b><i>Attention guidance</i></b>. Higher values cause PAG strength to decay faster across the denoising steps.<br><br>Only takes effect on <i>SD 1.5</i> and <i>SDXL</i> when <b><i>Attention guidance</i></b> is non-zero (the only path that actually enables PAG). Has no effect on <i>Flux</i>, <i>QwenImage</i>, <i>HiDream</i>, or other flow-matching models that route <b><i>Attention guidance</i></b> to true_cfg_scale instead.<br><br>Default 0.5 applies moderate decay. Set to 0 to keep PAG at full strength for the entire process.","ui":"txt2img"},
{"id":"","label":"Apply to hires","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"Active IP adapters","localized":"","hint":"Number of active IP adapter","ui":"txt2img"},
{"id":"","label":"Adapter","localized":"","hint":"IP adapter model","ui":"txt2img"},
@@ -104,9 +104,9 @@
{"id":"","label":"ACI: Mask blur","localized":"","hint":"Adjust blur to apply a smooth transition between image and inpainted area. (Recommended value = 0 for sharpness)","ui":"script_automatic_color_inpaint"},
{"id":"","label":"Adaptive restore","localized":"","hint":"","ui":"script_instantir"},
{"id":"","label":"Apply noise","localized":"","hint":"","ui":"script_softfill"},
{"id":"","label":"Auto min score","localized":"","hint":"","ui":"control"},
{"id":"","label":"Auto-segment","localized":"","hint":"","ui":"control"},
{"id":"","label":"Auto-mask","localized":"","hint":"","ui":"control"},
{"id":"","label":"Auto min score","localized":"","hint":"Minimum stability score for masks produced by <b>Auto-segment</b> (Facebook SAM and SlimSAM models).<br>Higher values keep only the most confident masks; lower values include more candidates including noisier ones. Has no effect on Rembg models or on the <b>Auto-mask</b> threshold/edge methods.<br><br>Default 0.8.","ui":"control"},
{"id":"","label":"Auto-segment","localized":"","hint":"Automatic foreground segmentation model. Runs on the input image to generate a mask without manual painting.<br><b>None</b>: no auto-segmentation; the manually painted mask is used instead.<br><b>Facebook SAM ViT (Base/Large/Huge)</b>: Meta's Segment Anything Model. Quality scales with size, Huge is the most accurate but slowest and largest in VRAM.<br><b>SlimSAM Uniform / Uniform Tiny</b>: pruned, faster SAM variants with a good speed/quality tradeoff for repetitive workflows.<br><b>Rembg BEN2 / Silueta / U2Net / U2Net human / ISNet general / ISNet anime</b>: lightweight background-removal models. Pick by content: <b>U2Net human</b> or <b>BEN2</b> for people, <b>ISNet anime</b> for illustrations, <b>U2Net</b> or <b>Silueta</b> for general subjects.<br><br>Models are downloaded on first use.<br>Default None.","ui":"control"},
{"id":"","label":"Auto-mask","localized":"","hint":"Automatic mask generation from the input image using simple computer-vision methods (no neural model). Runs only when no manual mask is painted and <b>Auto-segment</b> is set to None.<br><b>None</b>: disabled.<br><b>Threshold</b>: Otsu binary threshold; everything brighter than the auto-computed threshold becomes the mask. Works for high-contrast subjects on plain backgrounds.<br><b>Edge</b>: detects contours and keeps the largest ones; useful for masking distinct objects with clear outlines.<br><b>Grayscale</b>: uses the image's luminance as the mask intensity, producing a soft, gradient-style mask.<br><br>Default None.","ui":"control"},
{"id":"","label":"Active","localized":"","hint":"","ui":"control"},
{"id":"","label":"Attention","localized":"","hint":"","ui":"control"},
{"id":"","label":"Adain","localized":"","hint":"","ui":"control"},
@@ -163,13 +163,13 @@
{"id":"","label":"BitsAndBytes","localized":"","hint":"","ui":"settings_quantization"},
{"id":"","label":"Batch count","localized":"","hint":"How many batches of images to create (has no impact on generation performance or VRAM usage)","ui":"txt2img"},
{"id":"","label":"Batch size","localized":"","hint":"How many image to create in a single batch (increases generation performance at cost of higher VRAM usage)","ui":"txt2img"},
{"id":"","label":"Beta schedule","localized":"","hint":"Defines how beta (noise strength per step) grows. Options:<br>- default: the model default<br>- linear: evenly decays noise per step<br>- scaled: squared version of linear, used only by Stable Diffusion<br>- cosine: smoother decay, often better results with fewer steps<br>- sigmoid: sharp transition, experimental","ui":"txt2img"},
{"id":"","label":"Beta schedule","localized":"","hint":"Defines how beta (noise strength per step) grows. Options:<br>- <b>default</b>: the model default<br>- <b>linear</b>: evenly decays noise per step<br>- <b>scaled</b>: squared version of linear, used only by Stable Diffusion<br>- <b>cosine</b>: smoother decay, often better results with fewer steps<br>- <b>sigmoid</b>: sharp transition, experimental","ui":"txt2img"},
{"id":"","label":"Base shift","localized":"","hint":"Minimum shift value for low resolutions when using dynamic shifting.","ui":"txt2img"},
{"id":"","label":"Brightness","localized":"","hint":"Adjusts overall image brightness.<br>Positive values lighten the image, negative values darken it.<br><br>Applied uniformly across all pixels in linear space.","ui":"txt2img"},
{"id":"","label":"Block","localized":"","hint":"","ui":"script_kohya_hires_fix"},
{"id":"","label":"Block size","localized":"","hint":"","ui":"script_nudenet"},
{"id":"","label":"Banned words","localized":"","hint":"","ui":"script_nudenet"},
{"id":"","label":"Blur","localized":"","hint":"","ui":"img2img"},
{"id":"","label":"Blur","localized":"","hint":"Softens the mask edge with a Gaussian blur so the boundary between masked and unmasked regions blends gradually instead of cutting hard.<br>Reduces visible seams at the mask edge after generation. Combine with a small <b><i>Dilate</i></b> to push the soft transition just outside the original mask.<br>Sigma scales with image size: at value 0.05 on a 1024px image the blur radius is roughly 13 pixels.<br><br>Set to 0 to disable.<br>Default 0.","ui":"img2img"},
{"id":"","label":"Batch input directory","localized":"","hint":"","ui":"img2img"},
{"id":"","label":"Batch output directory","localized":"","hint":"","ui":"img2img"},
{"id":"","label":"Batch mask directory","localized":"","hint":"","ui":"img2img"},
@@ -210,7 +210,7 @@
{"id":"","label":"Copy","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"Composite","localized":"","hint":"","ui":"img2img"},
{"id":"control_params_elements","label":"Control","localized":"","hint":"Create image with full guidance","ui":"control"},
{"id":"","label":"ControlNet","localized":"","hint":"ControlNet is an advanced guidance model","ui":"control"},
{"id":"","label":"ControlNet","localized":"","hint":"<i>ControlNet</i> is an advanced guidance model","ui":"control"},
{"id":"caption_tab_controls","label":"Controls","localized":"","hint":"","ui":"caption"},
{"id":"","label":"CaptionCaption","localized":"","hint":"","ui":"caption"},
{"id":"btn_console","label":"Console","localized":"","hint":""},
@@ -234,14 +234,14 @@
{"id":"","label":"Create Video","localized":"","hint":"","ui":"extras"},
{"id":"","label":"ChronoEdit","localized":"","hint":"","ui":"settings_model_options"},
{"id":"","label":"Cross Attention","localized":"","hint":"","ui":"settings_cuda"},
{"id":"","label":"CLiP Skip","localized":"","hint":"Early stopping parameter for CLIP model; 1 is stop at last layer as usual, 2 is stop at penultimate layer, etc","ui":"settings_advanced"},
{"id":"","label":"CLiP Skip","localized":"","hint":"Early stopping parameter for the CLiP text encoder; 1 is stop at last layer as usual, 2 is stop at penultimate layer, etc","ui":"settings_advanced"},
{"id":"","label":"Cache-DiT","localized":"","hint":"","ui":"settings_advanced"},
{"id":"","label":"CFG-Zero","localized":"","hint":"","ui":"settings_advanced"},
{"id":"","label":"Cache folders","localized":"","hint":"","ui":"settings_system-paths"},
{"id":"","label":"Custom model loader","localized":"","hint":"","ui":"models_loader_tab"},
{"id":"","label":"Client log","localized":"","hint":""},
{"id":"","label":"CLIP Analysis","localized":"","hint":"","ui":"caption"},
{"id":"","label":"Context","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"CLiP Analysis","localized":"","hint":"Detailed analysis output from OpenCLiP, listing the matched medium, artist, movement, trending, and flavor terms.<br>Populated when you click the Analyze button next to the OpenCLiP Caption button.","ui":"caption"},
{"id":"","label":"Context","localized":"","hint":"Behavior of the Context aware resize Mode (no effect with any other Mode).<br><b>Add</b>: extend the image by inserting new pixels along smooth, featureless paths (like sky or plain backgrounds), avoiding detailed regions.<br><b>Remove</b>: shrink the image by removing pixels along the same low-detail paths.<br><b>Forward</b>: examine what the image will look like after each seam is added or removed, picking the paths that minimize visible damage. Slower but higher quality.<br><b>Backward</b>: pick paths based on existing pixel contrast in the image. Faster, classic seam-carving.","ui":"resize"},
{"id":"","label":"Contrast","localized":"","hint":"Adjusts the difference between light and dark areas.<br>Positive values increase contrast, making darks darker and lights brighter.<br>Negative values flatten the tonal range toward a more uniform appearance.","ui":"txt2img"},
{"id":"","label":"Color temp","localized":"","hint":"Shifts color temperature in Kelvin.<br>Lower values (e.g., 2000K) produce a warm, amber tone. Higher values (e.g., 12000K) produce a cool, bluish tone.<br><br>Default 6500K is neutral daylight. Works by scaling R/G/B channels to simulate the target white point.","ui":"txt2img"},
{"id":"","label":"CLAHE clip","localized":"","hint":"Clip limit for Contrast Limited Adaptive Histogram Equalization.<br>Higher values allow more local contrast enhancement, which brings out detail in flat regions.<br><br>Set to 0 to disable. Typical values are 1.03.0. Very high values can introduce noise amplification.","ui":"txt2img"},
@@ -249,7 +249,7 @@
{"id":"","label":"Correction mode","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"Crop to portrait","localized":"","hint":"Crop input image to portrait-only before using it as IP adapter input","ui":"txt2img"},
{"id":"","label":"Concept Tokens","localized":"","hint":"","ui":"script_consistory"},
{"id":"","label":"Colormap","localized":"","hint":"","ui":"script_daam"},
{"id":"","label":"Colormap","localized":"","hint":"OpenCV color palette used to visualize the mask or heatmap overlay.<br>For control masks, this is the palette applied when <b>Preview</b> is set to Color or Composite. Pick one that contrasts well with the input image so the overlay stays readable.<br><br>Default pink (control mask), jet (DAAM script).","ui":"script_daam"},
{"id":"","label":"Cosine scale 1","localized":"","hint":"","ui":"script_demofusion"},
{"id":"","label":"Cosine scale 2","localized":"","hint":"","ui":"script_demofusion"},
{"id":"","label":"Cosine scale 3","localized":"","hint":"","ui":"script_demofusion"},
@@ -267,9 +267,9 @@
{"id":"","label":"Control override denoise strength","localized":"","hint":"","ui":"script_flux_tools"},
{"id":"","label":"Color variation","localized":"","hint":"","ui":"script_outpainting"},
{"id":"","label":"Change rate","localized":"","hint":"","ui":"script_video"},
{"id":"","label":"Context after","localized":"","hint":"","ui":"control"},
{"id":"","label":"Context mask","localized":"","hint":"","ui":"control"},
{"id":"","label":"Control only","localized":"","hint":"This uses only the Control input below as the source for any ControlNet or IP Adapter type tasks based on any of our various options.","ui":"control"},
{"id":"","label":"Context after","localized":"","hint":"Behavior of the Context aware resize Mode applied to the <b>output</b> image after the model finishes generating (Post sub-tab in the Size accordion; no effect with any other Mode).<br><b>Add</b>: extend the image by inserting new pixels along smooth, featureless paths (like sky or plain backgrounds), avoiding detailed regions.<br><b>Remove</b>: shrink the image by removing pixels along the same low-detail paths.<br><b>Forward</b>: examine what the image will look like after each seam is added or removed, picking the paths that minimize visible damage. Slower but higher quality.<br><b>Backward</b>: pick paths based on existing pixel contrast in the image. Faster, classic seam-carving.","ui":"control"},
{"id":"","label":"Context mask","localized":"","hint":"Behavior of the Context aware resize Mode applied to the input <b>mask</b> image (used for inpainting, outpainting, or control masks; Mask sub-tab in the Size accordion; no effect with any other Mode).<br><b>Add</b>: extend the image by inserting new pixels along smooth, featureless paths (like sky or plain backgrounds), avoiding detailed regions.<br><b>Remove</b>: shrink the image by removing pixels along the same low-detail paths.<br><b>Forward</b>: examine what the image will look like after each seam is added or removed, picking the paths that minimize visible damage. Slower but higher quality.<br><b>Backward</b>: pick paths based on existing pixel contrast in the image. Faster, classic seam-carving.","ui":"control"},
{"id":"","label":"Control only","localized":"","hint":"This uses only the <b><i>Control input</i></b> below as the source for any <i>ControlNet</i> or <i>IP Adapter</i> type tasks based on any of our various options.","ui":"control"},
{"id":"","label":"CN Mode","localized":"","hint":"","ui":"control"},
{"id":"","label":"CN Strength","localized":"","hint":"","ui":"control"},
{"id":"","label":"CN Start","localized":"","hint":"","ui":"control"},
@@ -280,14 +280,14 @@
{"id":"","label":"Coarse","localized":"","hint":"","ui":"control"},
{"id":"","label":"Color map","localized":"","hint":"","ui":"control"},
{"id":"","label":"Crop to fit","localized":"","hint":"If the dimensions of your source image (e.g. 512x510) deviate from your target dimensions (e.g. 1024x768) this function will fit your upscaled image into your target size image. Excess will be cropped","ui":"extras"},
{"id":"","label":"CLiP Model","localized":"","hint":"CLIP model used for image-text similarity matching.<br>Larger models (ViT-L, ViT-H) are more accurate but slower and use more VRAM.","ui":"caption"},
{"id":"","label":"CLiP Model","localized":"","hint":"CLiP model used for image-text similarity matching.<br>Larger models (ViT-L, ViT-H) are more accurate but slower and use more VRAM.","ui":"caption"},
{"id":"","label":"Caption Model","localized":"","hint":"BLIP model used to generate the initial image caption.<br>The caption model describes the image content which CLiP then enriches with style and flavor terms.","ui":"caption"},
{"id":"","label":"clip: max length","localized":"","hint":"","ui":"caption"},
{"id":"","label":"clip: chunk size","localized":"","hint":"","ui":"caption"},
{"id":"","label":"clip: min flavors","localized":"","hint":"","ui":"caption"},
{"id":"","label":"clip: max flavors","localized":"","hint":"","ui":"caption"},
{"id":"","label":"clip: intermediates","localized":"","hint":"","ui":"caption"},
{"id":"","label":"clip: num beams","localized":"","hint":"","ui":"caption"},
{"id":"","label":"clip: max length","localized":"Max Length","hint":"Maximum number of tokens in the generated caption.<br>Higher values allow longer, more descriptive captions; lower values produce shorter ones.","ui":"caption"},
{"id":"","label":"clip: chunk size","localized":"Chunk Size","hint":"Batch size for processing description candidates (flavors).<br>Higher values speed up interrogation but increase VRAM usage.","ui":"caption"},
{"id":"","label":"clip: min flavors","localized":"Min Flavors","hint":"Minimum number of descriptive tags (flavors) to keep in the final prompt.","ui":"caption"},
{"id":"","label":"clip: max flavors","localized":"Max Flavors","hint":"Maximum number of descriptive tags (flavors) to keep in the final prompt.","ui":"caption"},
{"id":"","label":"clip: intermediates","localized":"Intermediates","hint":"Size of the intermediate candidate pool when matching image features to descriptive tags (flavors).<br>From this pool, the final tags are selected based on Min/Max Flavors. Higher values may improve quality but are slower.","ui":"caption"},
{"id":"","label":"clip: num beams","localized":"CLiP Num Beams","hint":"Number of beams for beam search during caption generation.<br>Higher values search more possibilities but are slower.<br><br>Set to 1 to disable beam search.","ui":"caption"},
{"id":"","label":"Character threshold","localized":"","hint":"Confidence threshold for character-specific tags (e.g., character names, specific traits).<br>Only tags with confidence above this threshold are included.<br>Higher values are more selective, lower values include more potential matches.<br>Not supported by DeepBooru models.","ui":"caption"},
{"id":"","label":"Cross-attention","localized":"","hint":"","ui":"component-8779"},
{"id":"","label":"cpu","localized":"","hint":"Uses cpu and RAM only: slowest but least likely to OOM","ui":"settings_sd"},
@@ -343,8 +343,8 @@
{"id":"","label":"Control settings","localized":"","hint":"","ui":"control"},
{"id":"","label":"Canny","localized":"","hint":"","ui":"control"},
{"id":"","label":"Condition","localized":"","hint":"","ui":"video"},
{"id":"","label":"Caption: Advanced Options","localized":"","hint":"","ui":"caption"},
{"id":"","label":"Caption: Batch","localized":"","hint":"","ui":"caption"},
{"id":"","label":"Caption: Advanced Options","localized":"","hint":"Advanced configuration options for caption generation.<br>Sampling parameters, length limits, and decoding behavior for the active backend (VLM, CLiP, or Tagger).","ui":"caption"},
{"id":"","label":"Caption: Batch","localized":"","hint":"Process multiple images in a batch using the active caption backend.<br>Captions are saved alongside the source images as .txt sidecar files when Save Caption Files is enabled.","ui":"caption"},
{"id":"","label":"Control elements","localized":"","hint":"Control elements are advanced models that can guide generation towards desired outcome","ui":"tab_control"}
],
"d": [
@@ -365,22 +365,22 @@
{"id":"","label":"Download model from huggingface","localized":"","hint":"","ui":"models_huggingface_tab"},
{"id":"","label":"Dropdown","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"dynamic","localized":"","hint":"Dynamic shifting automatically adjusts the denoising schedule based on your image resolution.<br><br>The scheduler interpolates between base_shift and max_shift based on actual image resolution.<br><br>Enabling disables static Flow shift.","ui":"txt2img"},
{"id":"","label":"Detailer models","localized":"","hint":"Select detection models to use for detailing","ui":"txt2img"},
{"id":"","label":"Detailer models","localized":"","hint":"<i>YOLO</i> detection models used to find regions to re-render. Multiple models can be selected and they run in sequence.<br>Models live in <code>models/yolo</code>. Filename hints at target: face-* detects faces, eyes-* detects eyes, hand-* detects hands, person-* detects whole subjects, and so on.<br>Models with <code>-seg</code> in the name produce a precise segmentation outline (used when <b><i>Use segmentation</i></b> is on); the rest produce only bounding boxes.<br><br>Per-model overrides can be appended with colon syntax, for example <code>face-yolo8n:conf=0.5:strength=0.4</code>.","ui":"txt2img"},
{"id":"","label":"Detailer list","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"Detailer classes","localized":"","hint":"Specify specific classes to use if selected detailer model is a multi-class model","ui":"txt2img"},
{"id":"","label":"Detailer prompt","localized":"","hint":"Use separate prompt for detailer. If not present, it will use primary prompt","ui":"txt2img"},
{"id":"","label":"Detailer negative prompt","localized":"","hint":"Use separate negative prompt for detailer. If not present, it will use primary negative prompt","ui":"txt2img"},
{"id":"","label":"Detailer steps","localized":"","hint":"Number of steps to run for detailer process","ui":"txt2img"},
{"id":"","label":"Detailer strength","localized":"","hint":"Denoising strength of detailer process","ui":"txt2img"},
{"id":"","label":"Detailer resolution","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"Detailer classes","localized":"","hint":"Comma-separated list of class names to keep when the selected detailer model is multi-class (e.g., a <i>YOLO</i> model that detects faces, eyes, and hands all in one file).<br>Only detections matching these labels are processed; everything else is dropped. Leave empty to accept all classes.<br><br>Names must match the model's class names exactly (case-insensitive). Single-class models like a face-only detector ignore this field.","ui":"txt2img"},
{"id":"","label":"Detailer prompt","localized":"","hint":"Optional dedicated prompt for the detailer pass.<br>Leave empty to inherit the main prompt. Useful for steering the inpaint differently from the rest of the image: a face detailer can use just <code>portrait, sharp eyes, detailed skin</code> while the main prompt covers the full scene.<br><br>The placeholder <code>[PROMPT]</code> (or <code>[prompt]</code>) is replaced with the original main prompt, so you can append to it: <code>[PROMPT], detailed face</code>.","ui":"txt2img"},
{"id":"","label":"Detailer negative prompt","localized":"","hint":"Optional dedicated negative prompt for the detailer pass.<br>Leave empty to inherit the main negative prompt. Same <code>[PROMPT]</code> / <code>[prompt]</code> placeholder behavior as the positive detailer prompt: it expands to the original main negative prompt.","ui":"txt2img"},
{"id":"","label":"Detailer steps","localized":"","hint":"Number of sampling steps used for each detailer inpaint pass.<br>Independent of the main generation steps. Higher values give cleaner detail but cost more time per detected region.<br><br>Set to <b>0</b> to inherit the main generation step count.<br>Default 10.","ui":"txt2img"},
{"id":"","label":"Detailer strength","localized":"","hint":"Denoising strength of the detailer inpaint pass.<br>Higher values regenerate more aggressively (more change to the detected region, more reliance on the prompt). Lower values stay closer to the original detection, only refining detail.<br>Typical range 0.2 to 0.5: enough to fix distortions without losing identity. Above 0.7 the face/object can drift noticeably from the original.<br><br>Set to <b>0</b> to skip the detailer pass entirely.<br>Default 0.30.","ui":"txt2img"},
{"id":"","label":"Detailer resolution","localized":"","hint":"Working resolution for the detailer inpaint pass. Each detected region is cropped (with <b><i>Edge padding</i></b>) and resized to this resolution before inpainting.<br>Higher values give finer detail in the regenerated region but use more VRAM and time per detection. Match the model's native resolution for best results: 1024 for <i>SDXL</i>/<i>SD3</i>/<i>Flux</i>, 512 for <i>SD 1.5</i>.<br><br>Default 1024.","ui":"txt2img"},
{"id":"","label":"Denoising batch size","localized":"","hint":"","ui":"script_demofusion"},
{"id":"","label":"Dilate tau","localized":"","hint":"","ui":"script_freescale"},
{"id":"","label":"Draw legend","localized":"","hint":"","ui":"script_xyz_grid_script"},
{"id":"","label":"Denoising strength","localized":"","hint":"Determines how little respect the algorithm should have for image's content. At 0, nothing will change, and at 1 you'll get an unrelated image. With values below 1.0, processing will take less steps than the Sampling Steps slider specifies","ui":"img2img"},
{"id":"","label":"Denoising strength","localized":"","hint":"Strength of img2img modification when an init image is supplied.<br>Higher values move further from the init image and rely more on the prompt; lower values stay closer to the original.<br><br>At <b>0.0</b> the init image passes through unchanged.<br>At <b>1.0</b> the model builds a fresh image from scratch and effectively ignores the init image.<br><br>Effect on step count is model-dependent:<br>- <b>SD 1.5 and SDXL</b>: the configured Steps value is honored as the actual loop count; strength only controls how much noise is added to the init latent.<br>- <b>Flux, SD3, Hunyuan, Sana, Qwen and other DiT models</b>: loop count is reduced proportionally; with strength 0.5 and 30 steps, only ~15 actually run.<br><br>In the <b><i>Images</i></b> tab this only takes effect when <b><i>Use init image</i></b> is set to one of the init modes; with <b>No: Control only</b> it is ignored.<br>Default 0.30.","ui":"img2img"},
{"id":"","label":"Denoise start","localized":"","hint":"Override denoise strength by stating how early base model should finish and when refiner should start. Only applicable to refiner usage. If set to 0 or 1, denoising strength will be used","ui":"img2img"},
{"id":"","label":"down","localized":"","hint":"","ui":"script_outpainting"},
{"id":"","label":"Decode chunks","localized":"","hint":"","ui":"script_video"},
{"id":"","label":"Dilate","localized":"","hint":"","ui":"control"},
{"id":"","label":"Dilate","localized":"","hint":"Expands the masked area outward by growing each masked pixel into its neighborhood.<br>Useful for catching the edges around an object that the mask missed, or for giving the model more breathing room around the region being modified so the new content can blend with surrounding context.<br>Kernel size scales with image size: at value 0.05 on a 1024px image the dilation reaches roughly 13 pixels in each direction.<br><br>Set to 0 to disable.<br>Default 0.","ui":"control"},
{"id":"","label":"Depth and normal","localized":"","hint":"","ui":"control"},
{"id":"","label":"Distance threshold","localized":"","hint":"","ui":"control"},
{"id":"","label":"Depth threshold","localized":"","hint":"","ui":"control"},
@@ -440,9 +440,9 @@
{"id":"","label":"Effects","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"Enable LayerSkipConfig","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"Enable refine pass","localized":"","hint":"Use a similar process as image to image to upscale and/or add detail to the final image. Optionally uses refiner model to enhance image details.","ui":"txt2img"},
{"id":"","label":"Enable detailer pass","localized":"","hint":"Detect target objects such as face and reprocess it at higher resolution","ui":"txt2img"},
{"id":"","label":"Edge padding","localized":"","hint":"Expand edge of masked area by this percentage","ui":"txt2img"},
{"id":"","label":"Edge blur","localized":"","hint":"Blur edge of masked area by this percentage","ui":"txt2img"},
{"id":"","label":"Enable detailer pass","localized":"","hint":"Runs an automatic touch-up pass after generation: a <i>YOLO</i> detector finds target regions (faces, eyes, hands, persons, etc.) and each detected region is re-rendered with inpaint at the configured detailer resolution.<br>Useful for fixing distorted faces or hands at low base resolutions, sharpening eye detail, or adding a second-pass refinement to specific subjects.<br><br>Default off.","ui":"txt2img"},
{"id":"","label":"Edge padding","localized":"","hint":"Pixels added around each detection's bounding box when cropping the region for inpaint.<br>Padding gives the inpaint pass surrounding context so the regenerated content can blend smoothly with the rest of the image. Too little causes hard seams; too much wastes resolution on areas that won't change.<br><br>Default 20.","ui":"txt2img"},
{"id":"","label":"Edge blur","localized":"","hint":"Pixel radius of the Gaussian blur applied to the inpaint mask edge.<br>Softens the boundary between the regenerated region and the rest of the image so the paste-back blends instead of cutting hard.<br><br>Set to 0 to disable.<br>Default 10.","ui":"txt2img"},
{"id":"","label":"End","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"ETA","localized":"","hint":"","ui":"script_apg"},
{"id":"","label":"Enable FreeU","localized":"","hint":"","ui":"script_consistory"},
@@ -452,7 +452,7 @@
{"id":"","label":"Enhanced prompt","localized":"","hint":"The enhanced prompt output from the LLM","ui":"script_prompt_enhance"},
{"id":"","label":"Edit start","localized":"","hint":"","ui":"script_ledits"},
{"id":"","label":"Edit stop","localized":"","hint":"","ui":"script_ledits"},
{"id":"","label":"Erode","localized":"","hint":"","ui":"control"},
{"id":"","label":"Erode","localized":"","hint":"Shrinks the masked area inward by removing pixels along the edge.<br>Useful for cleaning up speckle noise from auto-segmentation, or for pulling the mask back from object boundaries to avoid the model bleeding outside the intended region.<br>Kernel size scales with image size: at value 0.05 on a 1024px image the erosion reaches roughly 13 pixels in each direction.<br><br>Set to 0 to disable.<br>Default 0.","ui":"control"},
{"id":"","label":"edge","localized":"","hint":"","ui":"control"},
{"id":"","label":"Ensemble size","localized":"","hint":"","ui":"control"},
{"id":"","label":"Enable","localized":"","hint":"","ui":"video"},
@@ -490,7 +490,7 @@
{"id":"","label":"Faster Cache","localized":"","hint":"","ui":"settings_advanced"},
{"id":"","label":"Folders","localized":"","hint":"","ui":"settings_saving-paths"},
{"id":"","label":"Fetch model preview metadata","localized":"","hint":"","ui":"models_metadata_tab"},
{"id":"","label":"Flow shift","localized":"","hint":"Shift value for flowmatching models. Controls the distribution of denoising steps.<br><br>Values:<br>- >1.0: allocate more steps to early denoising (better structure)<br>-<1.0: allocate more steps to late denoising (better fine details)<br>- 1.0: balanced schedule<br><br>Most flowmatching models use the value of 3 as default. Effectively inactive if dynamic shift is enabled.","ui":"txt2img"},
{"id":"","label":"Flow shift","localized":"","hint":"Shift value for flowmatching models. Controls the distribution of denoising steps.<br><br>Values:<br>- <b>>1.0</b>: allocate more steps to early denoising (better structure)<br>- <b><1.0</b>: allocate more steps to late denoising (better fine details)<br>- <b>1.0</b>: balanced schedule<br><br>Most flowmatching models use the value of 3 as default. Effectively inactive if dynamic shift is enabled.","ui":"txt2img"},
{"id":"","label":"FDG scales","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"FDG weights","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"FDG rescale space","localized":"","hint":"","ui":"txt2img"},
@@ -622,9 +622,9 @@
{"id":"","label":"Grid Options","localized":"","hint":"","ui":"settings_saving-images"},
{"id":"","label":"Grids","localized":"","hint":"","ui":"settings_saving-paths"},
{"id":"","label":"Guider","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"Guidance scale","localized":"","hint":"Classifier Free Guidance scale: how strongly the image should conform to prompt. Lower values produce more creative results, higher values make it follow the prompt more strictly; recommended values between 5-10","ui":"txt2img"},
{"id":"","label":"Guidance end","localized":"","hint":"Ends the effect of CFG and PAG early: A value of 1 acts as normal, 0.5 stops guidance at 50% of steps","ui":"txt2img"},
{"id":"","label":"Guidance rescale","localized":"","hint":"Rescale guidance to avoid overexposed images at higher guidance values","ui":"txt2img"},
{"id":"","label":"Guidance scale","localized":"","hint":"Classifier-Free Guidance scale. How strongly the image should conform to the prompt. Lower values produce more creative, loosely-prompted results; higher values follow the prompt more strictly but can oversaturate or burn out at very high values.<br><br>Recommended values vary by architecture: 5-10 for <i>SDXL</i>/<i>SD1.x</i>, 3-5 for <i>Flux</i> and <i>SD3</i>, 7-10 for video models. Check the model card if unsure.<br><br>Set to 1 (the slider's minimum) to disable guidance entirely. The model then runs only the conditional prediction with no negative-prompt steering.","ui":"txt2img"},
{"id":"","label":"Guidance end","localized":"","hint":"Ends guidance early. The remaining denoising steps run unguided, which can speed up inference and produce slightly softer, less prompt-locked results. Applied independently to each pipeline pass (base, HiRes, refiner) against that pass's own step count.<br>Example: 0.5 stops guidance at 50% of steps; 0.8 stops at 80%.<br><br>Affects <b><i>Guidance scale</i></b> and <b><i>Refine guidance</i></b> on all models, and <b><i>Attention guidance</i></b> on the PAG path only (<i>SD 1.5</i> and <i>SDXL</i>). Has no effect on the true_cfg_scale path that <b><i>Attention guidance</i></b> uses for <i>Flux</i>, <i>QwenImage</i>, <i>HiDream</i>, <i>Hunyuan Video</i>, and other flow-matching models.<br><br>Set to 1 to keep guidance active for the entire denoising process.<br>1 (no early end) by default.","ui":"txt2img"},
{"id":"","label":"Guidance rescale","localized":"","hint":"Rescales the guided noise prediction to avoid the oversaturated, washed-out colors that high Guidance scale values can produce.<br>Useful when running with Guidance scale above 10 or when colors look blown out. Mild values (0.5-0.7) usually fix the issue without affecting prompt adherence.<br><br>Set to 0 to disable rescaling.<br>Disabled by default.","ui":"txt2img"},
{"id":"","label":"Gamma","localized":"","hint":"Non-linear brightness curve adjustment.<br>Values below 1.0 brighten midtones and shadows while preserving highlights.<br>Values above 1.0 darken midtones and shadows.<br><br>Default is 1.0 (no change). Unlike brightness, gamma reshapes the tonal curve rather than shifting it uniformly.","ui":"txt2img"},
{"id":"","label":"Grain","localized":"","hint":"Adds film-like noise to the image.<br>Higher values produce more visible grain, simulating analog film texture.<br><br>Applied as random noise blended into the final image. Set to 0 to disable.","ui":"txt2img"},
{"id":"","label":"Grid margins","localized":"","hint":"","ui":"script_prompt_matrix"},
@@ -633,7 +633,7 @@
{"id":"","label":"Guidance start","localized":"","hint":"","ui":"script_slg"},
{"id":"","label":"Guidance stop","localized":"","hint":"","ui":"script_slg"},
{"id":"","label":"Gate step","localized":"","hint":"","ui":"script_t-gate"},
{"id":"","label":"Guess mode","localized":"","hint":"Removes the requirement to supply a prompt to a ControlNet. It forces Controlnet encoder to do it's 'best guess' based on the contents of the input control map.","ui":"control"},
{"id":"","label":"Guess mode","localized":"","hint":"Removes the requirement to supply a prompt to a <i>ControlNet</i>. It forces <i>ControlNet</i> encoder to do its 'best guess' based on the contents of the input control map.","ui":"control"},
{"id":"","label":"gradient","localized":"","hint":"","ui":"control"},
{"id":"","label":"Gamma corrected","localized":"","hint":"","ui":"control"},
{"id":"","label":"General threshold","localized":"","hint":"Confidence threshold for general tags (e.g., objects, actions, settings).<br>Only tags with confidence above this threshold are included in the output.<br>Higher values are more selective (fewer tags), lower values include more tags.","ui":"caption"},
@@ -661,15 +661,15 @@
{"id":"","label":"HiDream","localized":"","hint":"","ui":"settings_model_options"},
{"id":"","label":"HyperTile","localized":"","hint":"","ui":"settings_advanced"},
{"id":"","label":"HiDiffusion","localized":"","hint":"HiDiffusion allows creation of high-resolution images using your standard models without duplicates/distortions and improved performance","ui":"settings_advanced"},
{"id":"","label":"Height","localized":"","hint":"Image height","ui":"txt2img"},
{"id":"","label":"Height","localized":"","hint":"Target height of the output image in pixels.<br>For generation, this sets the resolution the model produces. For resize and upscale operations, this is the height the input is fitted to.<br><br>Should be a multiple of 8 for <i>SD1.x</i> and <i>SDXL</i> latents; newer architectures (<i>Flux</i>, <i>SD3</i>, video models) may require higher multiples (16, 32, or 64). Values that don't match are automatically floored to the nearest valid multiple for the loaded model.","ui":"txt2img"},
{"id":"","label":"HiRes steps","localized":"","hint":"Number of sampling steps for upscaled picture. If 0, uses same as for original","ui":"txt2img"},
{"id":"","label":"Hue","localized":"","hint":"Rotates all colors around the color wheel.<br>Small values produce subtle color shifts, while higher values cycle through the full spectrum.<br><br>Useful for creative color effects or correcting unwanted color casts.","ui":"txt2img"},
{"id":"","label":"Highlights","localized":"","hint":"Adjusts the brightness of highlight (bright) regions.<br>Positive values brighten highlights, negative values pull them down.<br><br>Operates on the L channel in Lab color space using a luminance-weighted mask, leaving shadows and midtones largely unaffected.","ui":"txt2img"},
{"id":"","label":"Highlights tint","localized":"","hint":"Color to blend into highlight regions for split toning.<br>Works together with Shadows tint and Split tone balance to create cinematic color grading looks.<br><br>Default white (#ffffff) applies no tint.","ui":"txt2img"},
{"id":"","label":"HDR range","localized":"","hint":"","ui":"script_hdr"},
{"id":"","label":"HQ init latents","localized":"","hint":"","ui":"script_instantir"},
{"id":"","label":"Height after","localized":"","hint":"","ui":"control"},
{"id":"","label":"Height mask","localized":"","hint":"","ui":"control"},
{"id":"","label":"Height after","localized":"","hint":"Target height of the <b>output</b> image in pixels, applied <b>after</b> the model finishes generating (Post sub-tab in the Size accordion). Use this to upscale or downscale the final image before saving.<br><br>Should be a multiple of 8 for <i>SD1.x</i> and <i>SDXL</i> latents; newer architectures (<i>Flux</i>, <i>SD3</i>, video models) may require higher multiples (16, 32, or 64). Values that don't match are automatically floored to the nearest valid multiple for the loaded model.","ui":"control"},
{"id":"","label":"Height mask","localized":"","hint":"Target height of the input <b>mask</b> image in pixels (Mask sub-tab in the Size accordion). The mask is used for inpainting, outpainting, or as a control mask, and is resized so it aligns with the processing resolution.<br><br>Should be a multiple of 8 for <i>SD1.x</i> and <i>SDXL</i> latents; newer architectures (<i>Flux</i>, <i>SD3</i>, video models) may require higher multiples (16, 32, or 64). Values that don't match are automatically floored to the nearest valid multiple for the loaded model.","ui":"control"},
{"id":"","label":"Hires use control","localized":"","hint":"","ui":"control"},
{"id":"","label":"Hands","localized":"","hint":"","ui":"control"},
{"id":"","label":"High threshold","localized":"","hint":"","ui":"control"},
@@ -700,7 +700,6 @@
"i": [
{"id":"control_nav","label":"Images","localized":"","hint":"Create images<br>Unified interface<br>Supports T2I and I2I<br>With optional control guidance"},
{"id":"img2img_nav","label":"I2I","localized":"","hint":"Create image from image<br>Legacy interface that mimics original image-to-image interface and behavior"},
{"id":"img2img_results_input_mobile","label":"Input","localized":"","hint":"Show/hide selection of input media used to guide generation","ui":"img2img"},
{"id":"","label":"Image","localized":"","hint":"Create image from image","ui":"img2img"},
{"id":"","label":"Inpaint","localized":"","hint":"","ui":"img2img"},
{"id":"control_params_mask","label":"Inputs","localized":"","hint":"Settings related to Input images","ui":"control"},
@@ -711,13 +710,13 @@
{"id":"","label":"Image Paths","localized":"","hint":"Settings related to image filenames, and output directories"},
{"id":"","label":"Image Metadata","localized":"","hint":"Settings related to handling of metadata that is created with generated images"},
{"id":"","label":"IP Adapters","localized":"","hint":"IP adapters are plugin models that can guide generation towards desired outcome","ui":"txt2img"},
{"id":"","label":"Input Media","localized":"","hint":"Add input image to be used for image-to-image, inpaint or control processing","ui":"control"},
{"id":"","label":"Input","localized":"","hint":"Add input image to be used for image-to-image, inpaint or control processing<br>Click to minimize/maximize","ui":"control"},
{"id":"","label":"Input Image","localized":"","hint":"","ui":"caption"},
{"id":"","label":"IPEX","localized":"","hint":"","ui":"settings_backends"},
{"id":"","label":"Image Gallery","localized":"","hint":"","ui":"settings_saving-images"},
{"id":"","label":"Intermediate Image Saving","localized":"","hint":"","ui":"settings_saving-images"},
{"id":"","label":"Initial seed","localized":"","hint":"A value that determines the output of random number generator - if you create an image with same parameters and seed as another image, you'll get the same result","ui":"txt2img"},
{"id":"","label":"Include detections","localized":"","hint":"Include original image with detected areas marked","ui":"txt2img"},
{"id":"","label":"Include detections","localized":"","hint":"Adds an annotated debug image to the output gallery showing each detected region's bounding box, label, and confidence score, plus a translucent mask overlay.<br>Useful for tuning <b><i>Min confidence</i></b>, <b><i>Min size</i></b>/<b><i>Max size</i></b>, and class filters: you can see exactly what was detected before the inpaint pass touched the image.<br><br>Default off.","ui":"txt2img"},
{"id":"","label":"IY model","localized":"","hint":"","ui":"script_infiniteyou"},
{"id":"","label":"IY scale","localized":"","hint":"","ui":"script_infiniteyou"},
{"id":"","label":"IY start","localized":"","hint":"","ui":"script_infiniteyou"},
@@ -731,8 +730,8 @@
{"id":"","label":"Include images","localized":"","hint":"","ui":"script_xyz_grid_script"},
{"id":"","label":"invert","localized":"","hint":"","ui":"img2img"},
{"id":"","label":"Init image same as control","localized":"","hint":"Will additionally treat any image placed into the Control input window as a source for img2img type tasks, an image to modify for example.","ui":"control"},
{"id":"","label":"Inpaint masked only","localized":"","hint":"","ui":"control"},
{"id":"","label":"Invert mask","localized":"","hint":"","ui":"control"},
{"id":"","label":"Inpaint masked only","localized":"","hint":"Crop the masked region, denoise it at full resolution, then paste the result back into the original image.<br>Best for small detail edits where you want maximum quality on the masked area without spending compute denoising the rest of the image. Detail in unmasked regions stays untouched.<br><br>Tradeoff: the model only sees the cropped region, so it loses global context. The inpainted content may not match the surrounding scene's lighting, perspective, or style, and visible seams can appear at the crop boundary. Mitigate with <b><i>Dilate</i></b> + <b><i>Blur</i></b> on the mask, or disable this option to denoise the full image together.<br>When off, the whole image is denoised at the generation resolution and the unmasked area is restored from the original via the mask blend, which preserves global coherence at the cost of detail in the masked region.<br><br>Default off.","ui":"control"},
{"id":"","label":"Invert mask","localized":"","hint":"Swaps which area is treated as masked.<br>Useful when you have painted the region to <b>preserve</b> instead of the region to <b>modify</b>: enable this to flip the interpretation without redoing the mask.<br><br>Default off.","ui":"control"},
{"id":"","label":"IOU","localized":"","hint":"","ui":"control"},
{"id":"","label":"Init strength","localized":"","hint":"","ui":"video"},
{"id":"","label":"Input directory","localized":"","hint":"Folder where the images are that you want to process","ui":"extras"},
@@ -754,7 +753,7 @@
{"id":"","label":"Image resize algorithm","localized":"","hint":"","ui":"settings_postprocessing"},
{"id":"","label":"Image repeats per epoch","localized":"","hint":"","ui":"settings_legacy_options"},
{"id":"","label":"Interpolation Method","localized":"","hint":"","ui":"models_merge_tab"},
{"id":"","label":"In Blocks","localized":"","hint":"Downsampling Blocks of the UNet (12 values for SD1.5, 9 values for SDXL)","ui":"component-5674"},
{"id":"","label":"In Blocks","localized":"","hint":"Downsampling Blocks of the UNet (12 values for <i>SD1.5</i>, 9 values for <i>SDXL</i>)","ui":"component-5674"},
{"id":"","label":"Input model","localized":"","hint":"","ui":"models_replace_tab"},
{"id":"","label":"Info object","localized":"","hint":"","ui":"component-8779"}
],
@@ -765,12 +764,13 @@
{"id":"","label":"Keep Thinking Trace","localized":"","hint":"Include the model's reasoning process in the final output.<br>Useful for understanding how the model arrived at its answer.<br>Only works with models that support thinking mode.","ui":"script_prompt_enhance"},
{"id":"","label":"Keep Prefill","localized":"","hint":"Include the prefill text at the beginning of the final output.<br>If disabled, the prefill text used to guide the model is removed from the result.","ui":"script_prompt_enhance"},
{"id":"","label":"Keep aspect ratio","localized":"","hint":"","ui":"control"},
{"id":"","label":"Keep @ on artist insert","localized":"","hint":"Type <code>@</code> in the prompt to filter autocomplete to artist tags only.<br>This setting controls only what gets inserted on accept; the <code>@</code> filter works for every model.<br><br><b>Enable</b> for models that require the <code>@</code> prefix in the prompt itself, e.g. <i>Anima</i>. Inserts as <code>@artist name</code> with underscores converted to spaces.<br><b>Disable</b> for booru-trained models that take plain artist tags, e.g. <i>SDXL</i>, <i>Pony</i>, <i>Illustrious</i>, <i>NoobAI</i>. The typed <code>@</code> is consumed and the artist name is inserted as a normal tag.","ui":"script_autocomplete"}
{"id":"","label":"Keep @ on artist insert","localized":"","hint":"Type <code>@</code> in the prompt to filter autocomplete to artist tags only.<br>This setting controls only what gets inserted on accept; the <code>@</code> filter works for every model. Underscore handling is controlled by <b><i>Keep underscores</i></b>.<br><br><b>Enable</b> for models that require the <code>@</code> prefix in the prompt itself, e.g. <i>Anima</i>. Inserts as <code>@artist name</code>.<br><b>Disable</b> for booru-trained models that take plain artist tags, e.g. <i>SDXL</i>, <i>Pony</i>, <i>Illustrious</i>, <i>NoobAI</i>. The typed <code>@</code> is consumed and the artist name is inserted as a normal tag.","ui":"script_autocomplete"},
{"id":"","label":"Keep underscores","localized":"","hint":"Keep underscore characters when inserting tags from autocomplete. Applies to both ordinary tags and artist insertions (the <code>@</code> trigger).<br>Embedding names always preserve their underscores regardless of this setting.<br><br><b>Enable</b> when your model is sensitive to the underscored form of booru tags. The tag <code>long_hair</code> displays and inserts as <code>long_hair</code>.<br><b>Disable</b> (default) to convert underscores to spaces, matching the prompting style of most modern checkpoints. The tag <code>long_hair</code> displays and inserts as <code>long hair</code>.","ui":"script_autocomplete"}
],
"l": [
{"id":"prompt_enhance_load","label":"Load model","localized":"","hint":"","ui":"script_prompt_enhance"},
{"id":"prompt_enhance_custom_load","label":"Load custom model","localized":"","hint":"Load a custom model with the specified configuration","ui":"script_prompt_enhance"},
{"id":"control_mask_remove","label":"LaMa Remove","localized":"","hint":"","ui":"control"},
{"id":"control_mask_remove","label":"LaMa Remove","localized":"","hint":"Removes the masked region using LaMa, a lightweight inpainting model that fills the area with content extrapolated from the surroundings.<br>Useful for cleanup tasks like erasing watermarks, removing unwanted objects, or generating a clean plate before running a full diffusion pass.<br>Runs the configured mask pipeline (auto-segment, dilate, erode, blur, invert) first, then passes the resulting mask to LaMa. Result is written to the output panel.<br><br>Model is downloaded on first use.","ui":"control"},
{"id":"","label":"Lite","localized":"","hint":"","ui":"control"},
{"id":"video_params_ltx","label":"LTXVideo","localized":"","hint":"","ui":"video"},
{"id":"vlm_load","label":"Load","localized":"","hint":"","ui":"caption"},
@@ -790,7 +790,7 @@
{"id":"","label":"List all locally available models","localized":"","hint":"","ui":"models_list_tab"},
{"id":"","label":"Last Generate","localized":"","hint":""},
{"id":"","label":"LUT","localized":"","hint":"Look-Up Table color grading section.<br>Upload a .cube LUT file to apply professional color grading presets.<br><br>LUTs remap colors according to a predefined 3D color transform, commonly used in film and photography for consistent color looks.","ui":"txt2img"},
{"id":"","label":"low order","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"low order","localized":"","hint":"Forces multistep solvers to fall back to a lower-order step during the last few denoising iterations.<br>Higher-order solvers can become numerically unstable as sigma approaches zero, so the lower-order tail produces a cleaner, more stable final image.<br><br>Applies only to multistep families (<b>DPM++</b>, <b>UniPC</b>, <b>DEIS</b>, <b>SA Solver</b>, <b>DC Solver</b>, <b>ER-SDE</b>). Single-step samplers such as <b>DDIM</b>, <b>Euler</b>, and <b>Euler a</b> ignore this option.<br><br>Recommended to leave on. Disabling can occasionally give slightly sharper output but risks artifacts on the final steps.<br><br>Enabled by default.","ui":"txt2img"},
{"id":"","label":"LSC layer indices","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"LSC fully qualified name","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"LSC skip attention blocks","localized":"","hint":"","ui":"txt2img"},
@@ -831,7 +831,7 @@
{"id":"","label":"LTX enable refine","localized":"","hint":"","ui":"video"},
{"id":"","label":"LTX refine strength","localized":"","hint":"","ui":"video"},
{"id":"","label":"LTX decode timestep","localized":"","hint":"","ui":"video"},
{"id":"","label":"LTX enable audio","localized":"","hint":"","ui":"video"},
{"id":"","label":"LTX save audio","localized":"","hint":"LTX-2 audio-capable models always generate audio from the same prompt as video; this toggle controls whether the audio track is included in the saved video file","ui":"video"},
{"id":"","label":"Loop","localized":"","hint":"","ui":"extras"},
{"id":"","label":"Local directory name","localized":"","hint":"Directory where to install extension, leave blank for default","ui":"component-8746"},
{"id":"","label":"Libs","localized":"","hint":"","ui":"component-8779"},
@@ -889,20 +889,21 @@
{"id":"","label":"Mobile","localized":"","hint":"","ui":"settings_ui"},
{"id":"","label":"Merge multiple models","localized":"","hint":"","ui":"models_merge_tab"},
{"id":"","label":"Max shift","localized":"","hint":"Maximum shift value for high resolutions when using dynamic shifting.","ui":"txt2img"},
{"id":"","label":"Merge detailers","localized":"","hint":"Merge results from multiple detailers into single mask before running detailing process","ui":"txt2img"},
{"id":"","label":"Max detected","localized":"","hint":"Maximum number of detected objects to run detailer on","ui":"txt2img"},
{"id":"","label":"Min confidence","localized":"","hint":"Minimum confidence in detected item","ui":"txt2img"},
{"id":"","label":"Max overlap","localized":"","hint":"Maximum overlap between two detected items before one is discarded","ui":"txt2img"},
{"id":"","label":"Min size","localized":"","hint":"Minimum size of detected object as percentage of overal image","ui":"txt2img"},
{"id":"","label":"Max size","localized":"","hint":"Maximum size of detected object as percentage of overal image","ui":"txt2img"},
{"id":"","label":"Merge detailers","localized":"","hint":"Combines all detections from each model into a single mask and runs one inpaint pass per model instead of one per detection.<br>Faster when many regions are detected (e.g., a crowd scene with multiple faces): one larger inpaint pass replaces several small ones. Tradeoff: each region gets less individual attention because the model sees them all together.<br>Best for scenes where the detected regions are similar in size and content.<br><br>Default off.","ui":"txt2img"},
{"id":"","label":"Max detected","localized":"","hint":"Cap on how many detections per model are processed.<br>Detections beyond this count are dropped (in detection score order, highest first). Use to keep detailer time bounded on busy scenes.<br><br>Default 2.","ui":"txt2img"},
{"id":"","label":"Min confidence","localized":"","hint":"Minimum <i>YOLO</i> detection score required for a region to be processed.<br>Higher values keep only confident detections (fewer false positives but may miss real subjects in difficult lighting). Lower values include more candidates including weak ones.<br>Tune with <b><i>Include detections</i></b> on so you can see what is being kept and dropped.<br><br>Default 0.6.","ui":"txt2img"},
{"id":"","label":"Max overlap","localized":"","hint":"IOU threshold for non-maximum suppression: if two detections overlap by more than this fraction, the lower-scoring one is dropped.<br>Lower values are stricter (less overlap allowed; fewer duplicate detections of the same subject). Higher values let near-duplicates through, which is rarely useful.<br><br>Default 0.5.","ui":"txt2img"},
{"id":"","label":"Min size","localized":"","hint":"Minimum detection size as a fraction of the image's shorter edge. Detections smaller than this are dropped.<br>Use to filter out tiny background objects (e.g., faces in a crowd that aren't worth detailing). At 0.1, a face must occupy at least 10% of the image dimension to qualify.<br><br>Set to 0 to disable the lower bound.<br>Default 0.","ui":"txt2img"},
{"id":"","label":"Max size","localized":"","hint":"Maximum detection size as a fraction of the image's shorter edge. Detections larger than this are dropped.<br>Use to skip cases where the detector grabs the whole image (e.g., a person detector returning a near full-frame box that the inpaint pass would just regenerate).<br><br>Set to 1.0 to disable the upper bound.<br>Default 0.75.","ui":"txt2img"},
{"id":"","label":"Midtones","localized":"","hint":"Adjusts the brightness of midtone regions.<br>Positive values brighten midtones, negative values darken them.<br><br>Targets pixels near the middle of the luminance range using a bell-shaped mask in Lab space, leaving shadows and highlights largely untouched.","ui":"txt2img"},
{"id":"","label":"Momentum","localized":"","hint":"","ui":"script_apg"},
{"id":"","label":"Mode x-axis","localized":"","hint":"","ui":"script_asymmetric_tiling"},
{"id":"","label":"Mode y-axis","localized":"","hint":"","ui":"script_asymmetric_tiling"},
{"id":"","label":"Mask Dropout","localized":"","hint":"","ui":"script_consistory"},
{"id":"","label":"Multi decoder","localized":"","hint":"","ui":"script_demofusion"},
{"id":"","label":"Mode","localized":"","hint":"Interrogation mode.<br><b>Fast</b>: Quick caption with minimal flavor terms.<br><b>Classic</b>: Standard interrogation with balanced quality and speed.<br><b>Best</b>: Most thorough analysis, slowest but highest quality.<br><b>Negative</b>: Generate terms to use as negative prompt.","ui":"script_face"},
{"id":"","label":"Method","localized":"","hint":"","ui":"script_video"},
{"id":"","label":"CLiP Mode","localized":"","hint":"OpenCLiP interrogation depth.<br><b>Fast</b>: quick caption with minimal flavor terms.<br><b>Classic</b>: standard interrogation balancing quality and speed.<br><b>Best</b>: most thorough analysis, slowest but highest quality.<br><b>Negative</b>: generate terms suitable for use as a negative prompt.","ui":"caption"},
{"id":"","label":"Mode","localized":"","hint":"How the input is fitted to the target resolution.<br><b>None</b>: skip resize, pass the image through unchanged.<br><b>Fixed</b>: force to target width and height, distorting aspect ratio if they differ.<br><b>Crop</b>: scale to fully cover the target then center-crop the overflow, preserving aspect ratio.<br><b>Fill</b>: scale to fit inside the target then pad the remaining space with the background color (set in Settings → Image options).<br><b>Outpaint</b>: like Fill, but the model paints new content into the padded space instead of using a solid color.<br><b>Context aware</b>: use seam-carving to add or remove pixels along smooth, featureless paths through the image (like sky or plain backgrounds), preserving the detailed regions. Behavior is controlled by the Context dropdown next to this one.","ui":"resize"},
{"id":"","label":"Method","localized":"","hint":"Algorithm used to perform the resize.<br>Choices range from simple interpolation (Lanczos, Nearest) to upscaler models (ESRGAN, SwinIR, RealESRGAN, etc.) and latent-space methods.<br><br>Upscaler models give better quality at the cost of speed; simple methods are fast but soft.","ui":"resize"},
{"id":"","label":"Model repo","localized":"","hint":"HuggingFace repository ID for the model","ui":"script_prompt_enhance"},
{"id":"","label":"Model gguf","localized":"","hint":"Optional GGUF quantized model repository on HuggingFace","ui":"script_prompt_enhance"},
{"id":"","label":"Model type","localized":"","hint":"Optional GGUF model quantization type","ui":"script_prompt_enhance"},
@@ -916,10 +917,10 @@
{"id":"","label":"Min guidance","localized":"","hint":"","ui":"script_video"},
{"id":"","label":"Max guidance","localized":"","hint":"","ui":"script_video"},
{"id":"","label":"Motion level","localized":"","hint":"","ui":"script_video"},
{"id":"","label":"Mode after","localized":"","hint":"","ui":"control"},
{"id":"","label":"Method after","localized":"","hint":"","ui":"control"},
{"id":"","label":"Mode mask","localized":"","hint":"","ui":"control"},
{"id":"","label":"Method mask","localized":"","hint":"","ui":"control"},
{"id":"","label":"Mode after","localized":"","hint":"How the <b>output</b> image is fitted to the target resolution <b>after</b> the model finishes generating (Post sub-tab in the Size accordion).<br><b>None</b>: skip resize, pass the image through unchanged.<br><b>Fixed</b>: force to target width and height, distorting aspect ratio if they differ.<br><b>Crop</b>: scale to fully cover the target then center-crop the overflow, preserving aspect ratio.<br><b>Fill</b>: scale to fit inside the target then pad the remaining space with the background color (set in Settings → Image options).<br><b>Outpaint</b>: like Fill, but the model paints new content into the padded space instead of using a solid color.<br><b>Context aware</b>: use seam-carving to add or remove pixels along smooth, featureless paths through the image (like sky or plain backgrounds), preserving the detailed regions. Behavior is controlled by the Context dropdown next to this one.","ui":"control"},
{"id":"","label":"Method after","localized":"","hint":"Algorithm used to resize the <b>output</b> image after the model finishes generating (Post sub-tab in the Size accordion).<br>Choices range from simple interpolation (Lanczos, Nearest) to upscaler models (ESRGAN, SwinIR, RealESRGAN, etc.) and latent-space methods.<br><br>Upscaler models give better quality at the cost of speed; simple methods are fast but soft.","ui":"control"},
{"id":"","label":"Mode mask","localized":"","hint":"How the input <b>mask</b> image (used for inpainting, outpainting, or control masks) is fitted to the target resolution (Mask sub-tab in the Size accordion).<br><b>None</b>: skip resize, pass the mask through unchanged.<br><b>Fixed</b>: force to target width and height, distorting aspect ratio if they differ.<br><b>Crop</b>: scale to fully cover the target then center-crop the overflow, preserving aspect ratio.<br><b>Fill</b>: scale to fit inside the target then pad the remaining space with the background color (set in Settings → Image options).<br><b>Outpaint</b>: like Fill, but the model paints new content into the padded space instead of using a solid color.<br><b>Context aware</b>: use seam-carving to add or remove pixels along smooth, featureless paths through the image (like sky or plain backgrounds), preserving the detailed regions. Behavior is controlled by the Context dropdown next to this one.","ui":"control"},
{"id":"","label":"Method mask","localized":"","hint":"Algorithm used to resize the input <b>mask</b> image (used for inpainting, outpainting, or control masks; Mask sub-tab in the Size accordion).<br>Choices range from simple interpolation (Lanczos, Nearest) to upscaler models (ESRGAN, SwinIR, RealESRGAN, etc.) and latent-space methods.<br><br>Upscaler models give better quality at the cost of speed; simple methods are fast but soft.","ui":"control"},
{"id":"","label":"Maximum units","localized":"","hint":"","ui":"control"},
{"id":"","label":"Max faces","localized":"","hint":"","ui":"control"},
{"id":"","label":"Medium","localized":"","hint":"","ui":"control"},
@@ -992,8 +993,8 @@
{"id":"","label":"Noise scale","localized":"","hint":"","ui":"video"},
{"id":"","label":"Note","localized":"","hint":"","ui":"component-8823"},
{"id":"","label":"Non-blocking move operations","localized":"","hint":"","ui":"settings_offload"},
{"id":"","label":"Nunchaku attention","localized":"","hint":"Replaces default attention with Nunchaku's custom FP16 attention kernel for faster inference on consumer NVIDIA GPUs.<br>Might provide performance improvement on GPUs which have higher FP16 tensor cores throughput than BF16.<br><br>Currently only affects Flux-based models (Dev, Schnell, Kontext, Fill, Depth, etc.). Has no effect on Qwen, SDXL, Sana, or other architectures.<br><br>Disabled by default.","ui":"settings_quantization"},
{"id":"","label":"Nunchaku offloading","localized":"","hint":"Enables Nunchaku's own per-block CPU offloading with asynchronous CUDA streams to reduce VRAM usage.<br>Uses a ping-pong buffer strategy: while one transformer block computes on GPU, the next block preloads from CPU in the background, hiding most of the transfer latency.<br><br>Can reduce VRAM usage at the cost of slower inference.<br>This replaces SD.Next's pipeline offloading for the transformer component.<br><br>Only useful on low-VRAM GPUs. If your GPU has enough memory to hold the quantized model (16+ GB), keep this disabled for maximum speed.<br>Supports Flux and Qwen models. Not supported for SDXL where this setting is ignored.<br>Disabled by default.","ui":"settings_quantization"},
{"id":"","label":"Nunchaku attention","localized":"","hint":"Replaces default attention with Nunchaku's custom FP16 attention kernel for faster inference on consumer NVIDIA GPUs.<br>Might provide performance improvement on GPUs which have higher FP16 tensor cores throughput than BF16.<br><br>Currently only affects <i>Flux</i>-based models (<i>Dev</i>, <i>Schnell</i>, <i>Kontext</i>, <i>Fill</i>, <i>Depth</i>, etc.). Has no effect on <i>Qwen</i>, <i>SDXL</i>, <i>Sana</i>, or other architectures.<br><br>Disabled by default.","ui":"settings_quantization"},
{"id":"","label":"Nunchaku offloading","localized":"","hint":"Enables Nunchaku's own per-block CPU offloading with asynchronous CUDA streams to reduce VRAM usage.<br>Uses a ping-pong buffer strategy: while one transformer block computes on GPU, the next block preloads from CPU in the background, hiding most of the transfer latency.<br><br>Can reduce VRAM usage at the cost of slower inference.<br>This replaces SD.Next's pipeline offloading for the transformer component.<br><br>Only useful on low-VRAM GPUs. If your GPU has enough memory to hold the quantized model (16+ GB), keep this disabled for maximum speed.<br>Supports <i>Flux</i> and <i>Qwen</i> models. Not supported for <i>SDXL</i> where this setting is ignored.<br>Disabled by default.","ui":"settings_quantization"},
{"id":"","label":"native","localized":"","hint":"","ui":"settings_text_encoder"},
{"id":"","label":"no-grad","localized":"","hint":"Disables gradient tracking with torch.no_grad. Reduces memory usage and speeds up inference.","ui":"settings_backends"},
{"id":"","label":"Numbered filenames","localized":"","hint":"","ui":"settings_saving-paths"},
@@ -1006,7 +1007,7 @@
{"id":"","label":"Network parameters","localized":"","hint":""}
],
"o": [
{"id":"txt2img_results_mobile","label":"Output","localized":"","hint":"Show/hide selection of output media: generation resuls and live previews during generation process","ui":"txt2img"},
{"id":"txt2img_results_mobile","label":"Output","localized":"","hint":"Generation resuls and live previews during generation process<br>Click to minimize/maximize","ui":"txt2img"},
{"id":"","label":"OpenCLiP","localized":"","hint":"Analyze image using CLiP model via OpenCLiP","ui":"caption"},
{"id":"","label":"ONNX","localized":"","hint":""},
{"id":"","label":"Override","localized":"","hint":"Override settings that can change server behavior and are typically applied from imported image metadata","ui":"txt2img"},
@@ -1052,7 +1053,7 @@
{"id":"","label":"olive-ai","localized":"","hint":"","ui":"settings_compile"},
{"id":"","label":"openvino_fx","localized":"","hint":"","ui":"settings_compile"},
{"id":"","label":"Overwrite existing","localized":"","hint":"","ui":"models_current_tab"},
{"id":"","label":"Out Block","localized":"","hint":"Upsampling Blocks of the UNet (12 values for SD1.5, 9 values for SDXL)","ui":"component-5674"},
{"id":"","label":"Out Block","localized":"","hint":"Upsampling Blocks of the UNet (12 values for <i>SD1.5</i>, 9 values for <i>SDXL</i>)","ui":"component-5674"},
{"id":"","label":"Overwrite model","localized":"","hint":"","ui":"models_merge_tab"},
{"id":"","label":"Output model","localized":"","hint":"","ui":"models_replace_tab"},
{"id":"","label":"Overwrite existing file","localized":"","hint":"","ui":"component-5851"},
@@ -1064,7 +1065,7 @@
{"id":"txt2img_prompts","label":"Prompts","localized":"","hint":"Image prompt and negative prompt","ui":"txt2img"},
{"id":"txt2img_pause","label":"Pause","localized":"","hint":"Pause processing","ui":"txt2img"},
{"id":"","label":"Post","localized":"","hint":"Resize image after processing","ui":"control"},
{"id":"","label":"Preview","localized":"","hint":"","ui":"video"},
{"id":"","label":"Preview","localized":"","hint":"Selects how the mask preview is rendered when you click <b>Run Preview</b>.<br><b>None</b>: skip the preview step.<br><b>Masked</b>: input image with everything outside the mask blacked out.<br><b>Binary</b>: pure black-and-white mask (Otsu thresholded).<br><b>Grayscale</b>: mask intensity values rendered as gray levels.<br><b>Color</b>: mask recolored using the selected <b>Colormap</b>.<br><b>Composite</b>: 50/50 blend of the input image and the colored mask, so you can see exactly where the mask falls relative to the subject.<br><br>Default Composite.","ui":"video"},
{"id":"","label":"Process Image","localized":"","hint":"Process single image","ui":"extras"},
{"id":"","label":"Process Batch","localized":"","hint":"Process batch of images","ui":"extras"},
{"id":"","label":"Process Folder","localized":"","hint":"Process all images in a folder","ui":"extras"},
@@ -1074,13 +1075,12 @@
{"id":"","label":"Preset Block Merge","localized":"","hint":"","ui":"models_merge_tab"},
{"id":"","label":"Preview metadata","localized":"","hint":""},
{"id":"","label":"Prompt","localized":"","hint":"Describe image you want to generate","ui":"txt2img"},
{"id":"","label":"Processed Preview","localized":"","hint":"Show/hide section from pre-processing of input images before actual generate","ui":"control"},
{"id":"","label":"PixelArt","localized":"","hint":"","ui":"extras"},
{"id":"","label":"PAG: Perturbed attention guidance","localized":"","hint":"","ui":"settings_advanced"},
{"id":"","label":"PAB: Pyramid attention broadcast","localized":"","hint":"","ui":"settings_advanced"},
{"id":"","label":"Para-attention","localized":"","hint":"","ui":"settings_advanced"},
{"id":"","label":"Paths for specific models","localized":"","hint":"","ui":"settings_system-paths"},
{"id":"","label":"Prediction method","localized":"","hint":"Defines what the model predicts at each step. Options:<br>- default: the model default<br>- epsilon: noise (most common for Stable Diffusion)<br>- sample: direct denoised image prediction, also called as x0 prediction<br>- v_prediction: velocity prediction, used by CosXL and NoobAI VPred models<br>- flow_prediction: used with newer flow-matching models like SD3 and Flux","ui":"txt2img"},
{"id":"","label":"Prediction method","localized":"","hint":"Defines what the model predicts at each step. Options:<br>- <b>default</b>: the model default<br>- <b>epsilon</b>: noise (most common for Stable Diffusion)<br>- <b>sample</b>: direct denoised image prediction, also called as x0 prediction<br>- <b>v_prediction</b>: velocity prediction, used by <i>CosXL</i> and <i>NoobAI</i> VPred models<br>- <b>flow_prediction</b>: used with newer flow-matching models like <i>SD3</i> and <i>Flux</i>","ui":"txt2img"},
{"id":"","label":"PAG scale","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"PAG start","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"PAG stop","localized":"","hint":"","ui":"txt2img"},
@@ -1104,10 +1104,9 @@
{"id":"","label":"Preview start","localized":"","hint":"","ui":"script_instantir"},
{"id":"","label":"Preview end","localized":"","hint":"","ui":"script_instantir"},
{"id":"","label":"Pixels to expand","localized":"","hint":"","ui":"script_outpainting"},
{"id":"","label":"Processor","localized":"","hint":"Processor type to use to preprocess image used for ControlNet","ui":"control"},
{"id":"","label":"Processor","localized":"","hint":"Processor type to use to preprocess image used for <i>ControlNet</i>","ui":"control"},
{"id":"","label":"Pose confidence","localized":"","hint":"","ui":"control"},
{"id":"","label":"Parameter free","localized":"","hint":"","ui":"control"},
{"id":"","label":"Processed","localized":"","hint":"Show/hide section with processed images","ui":"control"},
{"id":"","label":"Postprocess mask","localized":"","hint":"","ui":"extras"},
{"id":"","label":"PixelArt block size","localized":"","hint":"","ui":"extras"},
{"id":"","label":"PixelArt sharpen","localized":"","hint":"","ui":"extras"},
@@ -1169,7 +1168,7 @@
{"id":"","label":"Resize to","localized":"","hint":"","ui":"control"},
{"id":"","label":"Resize\n by","localized":"","hint":"","ui":"control"},
{"id":"","label":"Resize\n to","localized":"","hint":"","ui":"control"},
{"id":"control_mask_refresh","label":"Run Preview","localized":"","hint":"","ui":"control"},
{"id":"control_mask_refresh","label":"Run Preview","localized":"","hint":"Runs the configured mask pipeline (auto-segment, dilate, erode, blur, invert) on the current input and renders the result in the output panel using the selected <b>Preview</b> style.<br>Use this to iterate on mask settings without launching a full generation.","ui":"control"},
{"id":"","label":"Reference","localized":"","hint":"List of reference models that can be automatically downloaded on first use","ui":"control"},
{"id":"framepack_btn_reset_model","label":"Reset receipe","localized":"","hint":"","ui":"video"},
{"id":"video_generation_info_button","label":"Run","localized":"","hint":"","ui":"video"},
@@ -1194,11 +1193,11 @@
{"id":"","label":"Resize","localized":"","hint":"Image resizing, can be using fixed resolution on based on scale","ui":"settings_postprocessing"},
{"id":"","label":"Rerefence models","localized":"","hint":"","ui":"settings_extra_networks"},
{"id":"","label":"Replace model components","localized":"","hint":"","ui":"models_replace_tab"},
{"id":"","label":"rescale","localized":"","hint":"rescale betas with zero terminal snr","ui":"txt2img"},
{"id":"","label":"rescale","localized":"","hint":"Rescales the noise schedule so the final timestep starts from true pure noise (zero signal-to-noise ratio).<br>Standard SD schedules don't quite reach pure noise at the highest timestep, which biases generations toward medium brightness and limits dynamic range. Rescaling unlocks the full range of darks and brights.<br><br>Should only be enabled for models trained with zero-terminal-SNR or v-prediction. The most common are <i>SDXL</i> fine-tunes carrying a 'vpred' or 'v-prediction' tag in the name (e.g. <i>NoobAI XL Vpred</i>, <i>Illustrious XL Vpred</i>, Stability's <i>CosXL</i>), plus some noise-offset and <i>Terminus</i>-family checkpoints. Standard epsilon-prediction models such as base <i>SDXL</i>, <i>Pony</i>, and <i>Animagine</i> should be left as-is. Enabling on a mismatched model will shift colors and degrade quality.<br><br>Recommended to leave off unless your model documentation specifically calls for it.<br><br>Disabled by default.","ui":"txt2img"},
{"id":"","label":"Resize seed from width","localized":"","hint":"Make an attempt to produce a picture similar to what would have been produced with same seed at specified resolution","ui":"txt2img"},
{"id":"","label":"Resize seed from height","localized":"","hint":"Make an attempt to produce a picture similar to what would have been produced with same seed at specified resolution","ui":"txt2img"},
{"id":"","label":"Refine guidance","localized":"","hint":"CFG scale used for refiner pass","ui":"txt2img"},
{"id":"","label":"Resize mode","localized":"","hint":"Defines how the input is resized or adapted in second-pass refinement:<br>- none: no resizing, keep original resolution<br>- fixed: force resize to target resolution (may distort)<br>- crop: center-crop to fit target while keeping aspect ratio<br>- fill: resize to fit and pad empty space with borders<br>- outpaint: extend canvas beyond image borders<br>- context aware: smart resize that blends or adapts surrounding areas","ui":"txt2img"},
{"id":"","label":"Refine guidance","localized":"","hint":"Guidance scale used for the secondary pass (refiner model or HiRes refine). Behaves like the main Guidance scale but applies only to that secondary pass.<br>For OmniGen this slider controls a separate image-conditioning guidance scale instead, used alongside the main Guidance scale in OmniGen's dual-CFG formula.<br><br>Set to 0 to disable guidance for the secondary pass.<br>Defaults to 6.0.","ui":"txt2img"},
{"id":"","label":"Resize mode","localized":"","hint":"Defines how the input is resized or adapted in second-pass refinement:<br>- <b>none</b>: no resizing, keep original resolution<br>- <b>fixed</b>: force resize to target resolution (may distort)<br>- <b>crop</b>: center-crop to fit target while keeping aspect ratio<br>- <b>fill</b>: resize to fit and pad empty space with borders<br>- <b>outpaint</b>: extend canvas beyond image borders<br>- <b>context aware</b>: smart resize that blends or adapts surrounding areas","ui":"txt2img"},
{"id":"","label":"Resize method","localized":"","hint":"Method used to resize the image: can be simple resize, upscaling model, latent resize or asymmetric decode","ui":"txt2img"},
{"id":"","label":"Resize width","localized":"","hint":"Resizes image to this width. If 0, width is inferred from either of two nearby sliders","ui":"txt2img"},
{"id":"","label":"Resize height","localized":"","hint":"Resizes image to this height. If 0, height is inferred from either of two nearby sliders","ui":"txt2img"},
@@ -1208,8 +1207,8 @@
{"id":"","label":"Refiner steps","localized":"","hint":"Number of steps to use for refiner pass","ui":"txt2img"},
{"id":"","label":"Refine prompt","localized":"","hint":"Prompt used for both second encoder in base model (if it exists) and for refiner pass (if enabled)","ui":"txt2img"},
{"id":"","label":"Refine negative prompt","localized":"","hint":"Negative prompt used for both second encoder in base model (if it exists) and for refiner pass (if enabled)","ui":"txt2img"},
{"id":"","label":"Renoise","localized":"","hint":"Apply additional noise during detailing","ui":"txt2img"},
{"id":"","label":"Renoise end","localized":"","hint":"Final step when renoise is applied","ui":"txt2img"},
{"id":"","label":"Renoise","localized":"","hint":"Multiplier applied to the sampler's step size during the detailer pass. Same mechanism as the <b><i>Sigma adjust</i></b> slider in the sampler tab, scoped to detailer only.<br>Values below 1.0 shrink each step for smoother, more conservative refinement (good for keeping faces stable). Values above 1.0 enlarge each step for sharper, more aggressive resampling.<br><br>Default 1.0 disables the adjustment.","ui":"txt2img"},
{"id":"","label":"Renoise end","localized":"","hint":"Upper bound of the denoising window where <b>Renoise</b> is active within the detailer pass, as a fraction of the noise schedule (1.0 = pure noise, 0.0 = clean image).<br>Lower values restrict renoise to the very first steps (gentler intervention); higher values let it act further into the run.<br><br>Default 1.0 keeps renoise active across the full pass.","ui":"txt2img"},
{"id":"","label":"Repeat x-axis","localized":"","hint":"","ui":"script_asymmetric_tiling"},
{"id":"","label":"Repeat y-axis","localized":"","hint":"","ui":"script_asymmetric_tiling"},
{"id":"","label":"ReSwapper Model","localized":"","hint":"","ui":"script_face"},
@@ -1237,7 +1236,6 @@
{"id":"","label":"RAS enabled","localized":"","hint":"","ui":"settings_advanced"},
{"id":"","label":"reduce-overhead","localized":"","hint":"","ui":"settings_compile"},
{"id":"","label":"repeated","localized":"","hint":"","ui":"settings_compile"},
{"id":"","label":"Replace underscores","localized":"","hint":"Display underscores in tag names as spaces in the autocomplete suggestion list.<br>For example, <i>long_hair</i> appears as <i>long hair</i>.","ui":"script_autocomplete"},
{"id":"","label":"Root model folder","localized":"","hint":"","ui":"settings_system-paths"},
{"id":"","label":"Resize background color","localized":"","hint":"","ui":"settings_saving-images"},
{"id":"","label":"Restore from metadata: skip params","localized":"","hint":"","ui":"settings_image-metadata"},
@@ -1302,9 +1300,9 @@
{"id":"","label":"Server log","localized":"","hint":""},
{"id":"","label":"Steps","localized":"","hint":"How many times to improve the generated image iteratively; higher values take longer; very low values can produce bad results","ui":"txt2img"},
{"id":"","label":"Sampling method","localized":"","hint":"Which algorithm to use to produce the image","ui":"txt2img"},
{"id":"","label":"Sigma method","localized":"","hint":"Controls how noise levels (sigmas) are distributed across diffusion steps. Options:<br>- default: the model default<br>- karras: smoother noise schedule, higher quality with fewer steps<br>- beta: based on beta schedule values<br>- exponential: exponential decay of noise<br>- lambdas: experimental, balances signal-to-noise<br>- flowmatch: tuned for flow-matching models","ui":"txt2img"},
{"id":"","label":"Sigma adjust","localized":"","hint":"Adjust sampler sigma value","ui":"txt2img"},
{"id":"","label":"Sampler order","localized":"","hint":"Order of solver updates in the sampler. Higher order improves stability/accuracy but increases compute cost.","ui":"txt2img"},
{"id":"","label":"Sigma method","localized":"","hint":"Controls how noise levels (sigmas) are distributed across diffusion steps.<br><b>Default</b>: use the scheduler's built-in sigma method.<br><b>Karras</b>: smoother schedule that emphasizes later steps where fine details emerge; generally higher quality with fewer steps.<br><b>Betas</b>: derive sigmas directly from the model's beta schedule (classic <i>DDPM</i> behavior).<br><b>Exponential</b>: exponential decay of noise across steps; aggressive denoising early, slower refinement later.<br><b>Lambdas</b>: Lu's lambdas method from the <i>DPM-Solver</i> paper, specific to the <b>DPM++</b> family.<br><b>Flowmatch</b>: sigma schedule tuned for flow-matching models (<i>Flux</i>, <i>SD3</i>, video models).","ui":"txt2img"},
{"id":"","label":"Sigma adjust","localized":"","hint":"Multiplier applied to the sampler's step size during the active timestep window. (Sigma is the amount of noise the sampler removes at each step.)<br>Values below 1.0 shrink the step for smoother, more conservative denoising. Values above 1.0 enlarge it for sharper, more aggressive sampling.<br><br>Default 1.0 disables the adjustment entirely. Use Adjust start and Adjust end to define the timestep range where the multiplier takes effect.","ui":"txt2img"},
{"id":"","label":"Sampler order","localized":"","hint":"Overrides the solver order of the active sampler when set above 0.<br>Higher orders use more historical steps per update for greater stability and accuracy at the cost of extra compute. Lower orders are faster but noisier.<br><br>Default 0 leaves each sampler at its built-in order. Many samplers in the dropdown already encode their order in the name (e.g. <b>DPM++ 2M</b> is order 2, <b>DPM++ 3M</b> is order 3, <b>DPM++ 2M SDE</b> is order 2).<br><br>Within a sampler family, the named variants differ ONLY by this value, so picking <b>DPM++ 2M</b> with the slider at 3 produces a scheduler that is functionally identical to picking <b>DPM++ 3M</b> with the slider at 0. The same equivalence holds across the rest of the <b>DPM++</b> multistep family (including the SDE and Inverse variants) and across the <b>ER-SDE</b> family.<br><br>Samplers without a configurable solver order (<b>DDIM</b>, plain <b>Euler</b>, ancestrals, etc.) ignore this slider entirely.","ui":"txt2img"},
{"id":"","label":"SLG scale","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"SLG start","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"SLG stop","localized":"","hint":"","ui":"txt2img"},
@@ -1318,7 +1316,7 @@
{"id":"","label":"SEG layers","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"SEG config","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"Strength","localized":"","hint":"Denoising strength of during image operation controls how much of original image is allowed to change during generate","ui":"txt2img"},
{"id":"","label":"Sort detections","localized":"","hint":"Sort detected areas by from left to right instead of detection score","ui":"txt2img"},
{"id":"","label":"Sort detections","localized":"","hint":"Process detected regions left-to-right (by bounding box X position) instead of in detection-score order.<br>Improves consistency when the prompt assigns different traits to different subjects in a multi-line prompt: prompts are mapped per detection in order, so a stable left-to-right order makes line 1 always go to the leftmost subject.<br><br>Default off.","ui":"txt2img"},
{"id":"","label":"Saturation","localized":"","hint":"Controls color intensity.<br>Positive values make colors more vivid, negative values desaturate toward grayscale.<br><br>At -1.0 the image becomes fully monochrome.","ui":"txt2img"},
{"id":"","label":"Sharpness","localized":"","hint":"Enhances edge detail and fine textures.<br>Higher values produce crisper edges but may amplify noise or artifacts if pushed too far.<br><br>Set to 0 to disable. Operates via an unsharp mask kernel.","ui":"txt2img"},
{"id":"","label":"Shadows","localized":"","hint":"Adjusts the brightness of shadow (dark) regions.<br>Positive values lift shadows to reveal detail, negative values deepen them.<br><br>Operates on the L channel in Lab color space using a luminance-weighted mask, leaving highlights and midtones largely unaffected.","ui":"txt2img"},
@@ -1350,7 +1348,8 @@
{"id":"","label":"Show input","localized":"","hint":"","ui":"control"},
{"id":"","label":"Show preview","localized":"","hint":"","ui":"control"},
{"id":"","label":"Separate init image","localized":"","hint":"Creates an additional window next to Control input labeled Init input, so you can have a separate image for both Control operations and an init source.","ui":"control"},
{"id":"","label":"Skip input frames","localized":"","hint":"","ui":"control"},
{"id":"","label":"Skip input processing","localized":"","hint":"Bypasses the active control processor and feeds the raw input image directly to the pipeline.<br>Use when you have already preprocessed the image externally (depth map, canny edges, openpose skeleton, etc.) and don't want SD.Next to re-run the processor on it.<br>The input still routes through any selected <i>ControlNet</i>/<i>T2I-Adapter</i>/etc. model, just without the preprocessing step.<br><br>Default off.","ui":"control"},
{"id":"","label":"Skip input frames","localized":"","hint":"Number of input frames to skip between each processed frame when the input is a video.<br>Use to thin out long source videos: only every (N+1)-th frame is processed and the rest are dropped.<br><br>Set to <b>0</b> to process every frame. Set to <b>1</b> to process every other frame, <b>2</b> for every third, and so on.<br>Default 0.","ui":"control"},
{"id":"","label":"Style fidelity","localized":"","hint":"","ui":"control"},
{"id":"","label":"Scribble","localized":"","hint":"","ui":"control"},
{"id":"","label":"Score threshold","localized":"","hint":"","ui":"control"},
@@ -1426,7 +1425,7 @@
{"id":"","label":"SDXL","localized":"","hint":"StableDiffusion XL","ui":"component-5660"},
{"id":"","label":"Save metadata","localized":"","hint":"","ui":"models_merge_tab"},
{"id":"","label":"safetensors","localized":"","hint":"","ui":"models_merge_tab"},
{"id":"","label":"shuffle","localized":"","hint":"Loads full model in RAM and calculates on VRAM: Less speedup, suggested for SDXL merges","ui":"models_merge_tab"},
{"id":"","label":"shuffle","localized":"","hint":"Loads full model in RAM and calculates on VRAM: Less speedup, suggested for <i>SDXL</i> merges","ui":"models_merge_tab"},
{"id":"","label":"Save diffusers","localized":"","hint":"","ui":"models_replace_tab"},
{"id":"","label":"Save safetensors","localized":"","hint":"","ui":"models_replace_tab"},
{"id":"","label":"Sort","localized":"","hint":"","ui":"models_civitai_tab"},
@@ -1456,10 +1455,10 @@
{"id":"","label":"Theme options","localized":"","hint":"","ui":"settings_ui"},
{"id":"","label":"Task History","localized":"","hint":""},
{"id":"","label":"Tone","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"Timestep spacing","localized":"","hint":"Determines how timesteps are spaced across the diffusion process. Options:<br>- default: the model default<br>- leading: creates evenly spaced steps<br>- linspace: includes the first and last steps and evenly selects the remaining intermediate steps<br>- trailing: only includes the last step and evenly selects the remaining intermediate steps starting from the end","ui":"txt2img"},
{"id":"","label":"Timesteps presets","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"Timesteps override","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"thresholding","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"Timestep spacing","localized":"","hint":"Determines how timesteps are spaced across the diffusion process. Options:<br>- <b>default</b>: the model default<br>- <b>leading</b>: creates evenly spaced steps<br>- <b>linspace</b>: includes the first and last steps and evenly selects the remaining intermediate steps<br>- <b>trailing</b>: only includes the last step and evenly selects the remaining intermediate steps starting from the end","ui":"txt2img"},
{"id":"","label":"Timesteps presets","localized":"","hint":"Picks a hand-tuned timestep schedule and writes it into Timesteps override.<br>'AYS SD15' and 'AYS SDXL' load the Align Your Steps schedules optimized for those base models, both 10 steps long. Selecting one of these effectively forces the generation to run at exactly 10 steps regardless of the main Steps slider, which is intended: AYS produces results comparable to 30+ step traditional sampling at this length.<br><br>Use the SD15 preset for <i>SD 1.x</i> checkpoints and the SDXL preset for <i>SDXL</i>-based checkpoints. The AYS schedules are not appropriate for flow-matching models (<i>Flux</i>, <i>SD3</i>) or other architectures.<br><br>Set to None to clear the override.<br>No preset by default.","ui":"txt2img"},
{"id":"","label":"Timesteps override","localized":"","hint":"Comma- or space-separated list of integer timesteps in the 0-999 range, listed from highest (most noisy) to lowest (cleanest). When set, this list completely replaces the scheduler's normal timestep schedule and forces the step count to match the list length, ignoring the main Steps slider.<br><br>Requires at least 3 values to take effect; shorter inputs are silently ignored. Not all samplers support arbitrary timestep injection. If the active sampler doesn't, a warning is logged and the override is skipped. Selecting a preset from Timesteps presets fills this field automatically.<br><br>Useful for advanced users experimenting with custom schedules. Most users should leave this blank.<br><br>Clear the field to disable.<br>Empty by default.","ui":"txt2img"},
{"id":"","label":"thresholding","localized":"","hint":"Enables dynamic thresholding. At each step the predicted clean image is clipped so its values stay within the model's trained range, which suppresses saturation and washed-out colors at high guidance.<br><br>Most useful for <i>SD 1.x</i> and <i>SD 2.x</i> at high CFG (>10). Generally not helpful for <i>SDXL</i> or flow-matching models, which already handle high CFG gracefully.<br><br>Applies to <b>DPM++</b> family, <b>UniPC</b>, <b>DDIM</b>, <b>DEIS</b>, <b>SA Solver</b>, and <b>DC Solver</b>. Recommended to leave off unless you see saturation artifacts.<br><br>Disabled by default.","ui":"txt2img"},
{"id":"","label":"Tint strength","localized":"","hint":"","ui":"txt2img"},
{"id":"","label":"Texture tiling","localized":"","hint":"Apply seamless tiling to generated image so it can be used as a texture","ui":"txt2img"},
{"id":"","label":"Threshold","localized":"","hint":"","ui":"script_apg"},
@@ -1496,7 +1495,7 @@
{"id":"","label":"Tiny","localized":"","hint":"","ui":"control"},
{"id":"","label":"True guidance","localized":"","hint":"","ui":"video"},
{"id":"","label":"Tile frames","localized":"","hint":"","ui":"video"},
{"id":"","label":"Task","localized":"","hint":"Changes which task the model will perform. Regular text prompts can be used when the task is set to <b>Use Prompt</b>.<br>When other options are selected, see the hint text inside an empty <b>Prompt</b> field for guidance.","ui":"caption"},
{"id":"","label":"Task","localized":"","hint":"Changes which task the model will perform. Regular text prompts can be used when the task is set to <b>Use Prompt</b>.<br>When other options are selected, see the hint text inside an empty <b><i>Prompt</i></b> field for guidance.","ui":"caption"},
{"id":"","label":"Tagger Model","localized":"","hint":"Model to use for image tagging.<br><b>WaifuDiffusion</b> models (wd-*): Modern taggers with separate general and character thresholds.<br><b>DeepBooru</b>: Legacy tagger, uses only general threshold.","ui":"caption"},
{"id":"","label":"Torch","localized":"","hint":"","ui":"component-8779"},
{"id":"","label":"Transformers load using Run:ai streamer","localized":"","hint":"","ui":"settings_sd"},
@@ -1537,7 +1536,8 @@
{"id":"","label":"Upscale","localized":"","hint":"Upscale image","ui":"extras"},
{"id":"","label":"UI Tabs","localized":"","hint":"","ui":"settings_ui"},
{"id":"","label":"Upscaling","localized":"","hint":"","ui":"settings_postprocessing"},
{"id":"","label":"Use segmentation","localized":"","hint":"Run detailer using segmentation mask","ui":"txt2img"},
{"id":"","label":"Use segmentation","localized":"","hint":"Use the model's pixel-precise segmentation mask as the inpaint mask instead of the rectangular bounding box.<br>Tighter mask means less unintended change around the detection (e.g., the inpaint stays on the face, not on the hair or background behind it). Better blending and smaller seams.<br><br>Requires a segmentation-capable model (filename usually contains <code>-seg</code>). Bounding-box-only models silently fall back to the rectangle.<br>Default off.","ui":"txt2img"},
{"id":"","label":"Use init image","localized":"","hint":"Decides whether the input image is also used as an init image for img2img-style modification.<br><b>No: Control only</b>: the input is used only by the active control processor (depth, canny, pose, etc.) to guide the model; the picture itself is built from scratch by the model. Standard <i>ControlNet</i> behavior.<br><b>1st: Same as control</b>: the control input doubles as the init image, so the model starts from your image and modifies it instead of building one from scratch. Useful for inpainting, restyling, or adding control guidance to img2img with a single source image.<br><b>2nd: Separate image</b>: opens an extra <b><i>Init input</i></b> pane next to <b><i>Control input</i></b> so you can supply different sources for control conditioning and img2img init.<br><br><b><i>Denoising strength</i></b> controls how far the result moves from the init image and only takes effect in the two init modes.<br>Default <b>No: Control only</b>.","ui":"control"},
{"id":"","label":"Unload adapter","localized":"","hint":"Unload IP adapter immediately after generate. Otherwise IP adapter will remain loaded for faster use in next generate process","ui":"txt2img"},
{"id":"","label":"Use same seed","localized":"","hint":"","ui":"script_prompts_from_file"},
{"id":"","label":"Use defaults","localized":"","hint":"","ui":"script_video"},
@@ -1600,7 +1600,7 @@
{"id":"","label":"Vignette","localized":"","hint":"Applies radial edge darkening that draws focus toward the center of the image.<br>Higher values produce a stronger falloff from center to corners.<br><br>Set to 0 to disable. Simulates the natural light falloff seen in vintage and cinematic lenses.","ui":"txt2img"},
{"id":"","label":"VAE type","localized":"","hint":"Choose if you want to run full VAE, reduced quality VAE or attempt to use remote VAE service","ui":"txt2img"},
{"id":"","label":"Version","localized":"","hint":"","ui":"script_pulid"},
{"id":"","label":"Video format","localized":"","hint":"Format and codec of output video","ui":"script_video"},
{"id":"","label":"Video format","localized":"","hint":"Container format and codec for the output video file.<br>Pick a format your downstream tools understand. <b>MP4/MP4V</b> is broadly compatible with most players and editors. Other choices trade off file size, quality, and player support.<br><br>Default MP4/MP4V.","ui":"script_video"},
{"id":"","label":"Video duration","localized":"","hint":"","ui":"script_video"},
{"id":"","label":"Video engine","localized":"","hint":"","ui":"video"},
{"id":"","label":"Video model","localized":"","hint":"","ui":"video"},
@@ -1632,10 +1632,10 @@
{"id":"","label":"Wildcards","localized":"","hint":""},
{"id":"","label":"WanAI","localized":"","hint":"","ui":"settings_model_options"},
{"id":"","label":"Watermarking","localized":"","hint":"","ui":"settings_saving-images"},
{"id":"","label":"Width","localized":"","hint":"Image width","ui":"txt2img"},
{"id":"","label":"Width","localized":"","hint":"Target width of the output image in pixels.<br>For generation, this sets the resolution the model produces. For resize and upscale operations, this is the width the input is fitted to.<br><br>Should be a multiple of 8 for <i>SD1.x</i> and <i>SDXL</i> latents; newer architectures (<i>Flux</i>, <i>SD3</i>, video models) may require higher multiples (16, 32, or 64). Values that don't match are automatically floored to the nearest valid multiple for the loaded model.","ui":"txt2img"},
{"id":"","label":"Weight","localized":"","hint":"","ui":"script_resadapter"},
{"id":"","label":"Width after","localized":"","hint":"","ui":"control"},
{"id":"","label":"Width mask","localized":"","hint":"","ui":"control"},
{"id":"","label":"Width after","localized":"","hint":"Target width of the <b>output</b> image in pixels, applied <b>after</b> the model finishes generating (Post sub-tab in the Size accordion). Use this to upscale or downscale the final image before saving.<br><br>Should be a multiple of 8 for <i>SD1.x</i> and <i>SDXL</i> latents; newer architectures (<i>Flux</i>, <i>SD3</i>, video models) may require higher multiples (16, 32, or 64). Values that don't match are automatically floored to the nearest valid multiple for the loaded model.","ui":"control"},
{"id":"","label":"Width mask","localized":"","hint":"Target width of the input <b>mask</b> image in pixels (Mask sub-tab in the Size accordion). The mask is used for inpainting, outpainting, or as a control mask, and is resized so it aligns with the processing resolution.<br><br>Should be a multiple of 8 for <i>SD1.x</i> and <i>SDXL</i> latents; newer architectures (<i>Flux</i>, <i>SD3</i>, video models) may require higher multiples (16, 32, or 64). Values that don't match are automatically floored to the nearest valid multiple for the loaded model.","ui":"control"},
{"id":"","label":"WebP lossless compression","localized":"","hint":"","ui":"settings_saving-images"},
{"id":"","label":"wavelet","localized":"","hint":"","ui":"settings_postprocessing"},
{"id":"","label":"Weights clip","localized":"","hint":"Forced merged weights to be no heavier than the original model, preventing burn in and overly saturated models","ui":"models_merge_tab"}
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 13,
"label": "Input Media",
"label": "Input",
"localized": "Medios de entrada",
"reload": "",
"hint": "Añadir imagen de entrada para ser utilizada para el procesamiento de imagen a imagen, inpaint o control"
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 13,
"label": "Input Media",
"label": "Input",
"localized": "Média d'entrée",
"reload": "",
"hint": "Ajouter une image d'entrée à utiliser pour le traitement image-à-image, inpaint ou control"
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 13,
"label": "Input Media",
"label": "Input",
"localized": "מדית קלט",
"reload": "",
"hint": "הוספת תמונת קלט לשימוש עבור עיבוד תמונה-לתמונה, מילוי או בקרה"
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 13,
"label": "Input Media",
"label": "Input",
"localized": "इनपुट मीडिया",
"reload": "n/a",
"hint": "इमेज-टू-इमेज, इनपेंट या कंट्रोल प्रोसेसिंग के लिए उपयोग की जाने वाली इनपुट छवि जोड़ें"
+1 -1
View File
@@ -4824,7 +4824,7 @@
},
{
"id": 0,
"label": "Input Media",
"label": "Input",
"localized": "Ulazni medij",
"reload": "",
"hint": "Dodajte ulaznu sliku koja će se koristiti za image-to-image, inpaint ili kontrolnu obradu"
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 13,
"label": "Input Media",
"label": "Input",
"localized": "Media Masukan",
"reload": "n/a",
"hint": "Tambahkan gambar masukan untuk digunakan dalam pemrosesan image-to-image, inpaint, atau kontrol"
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 14,
"label": "Input Media",
"label": "Input",
"localized": "Media di input",
"reload": "n/a",
"hint": "Aggiungi un'immagine di input da utilizzare per elaborazioni image-to-image, inpaint o di controllo"
+2 -2
View File
@@ -4817,9 +4817,9 @@
},
{
"id": 13,
"label": "Input Media",
"label": "Input",
"localized": "入力メディア",
"reload": "Input Media",
"reload": "Input",
"hint": "画像間変換、インペイント、またはコントロール処理に使用する入力画像を追加します"
},
{
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 13,
"label": "Input Media",
"label": "Input",
"localized": "입력 미디어",
"reload": "n/a",
"hint": "이미지 대 이미지, 인페인트 또는 제어 처리에 사용할 입력 이미지 추가"
+2 -2
View File
@@ -4817,8 +4817,8 @@
},
{
"id": 13,
"label": "Input Media",
"localized": "Input Media",
"label": "Input",
"localized": "Input",
"reload": "",
"hint": "Add an image here to use it as a base for editing or guiding the AI."
},
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 13,
"label": "Input Media",
"label": "Input",
"localized": "Media wejściowe",
"reload": "",
"hint": "Dodaj obraz wejściowy do użycia w przetwarzaniu typu image-to-image, inpaint lub control"
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 13,
"label": "Input Media",
"label": "Input",
"localized": "Mídia de Entrada",
"reload": "",
"hint": "Adicionar imagem de entrada a ser usada para processamento de imagem-para-imagem, inpaint ou controle"
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 0,
"label": "Input Media",
"label": "Input",
"localized": "Media Input",
"reload": "n/a",
"hint": "Addere imaginem input adhibendam pro processu imaginis-ad-imaginem, inpaint, vel moderationis"
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 14,
"label": "Input Media",
"label": "Input",
"localized": "Входные медиаданные",
"reload": "",
"hint": "Добавить входное изображение для использования в image-to-image, inpaint или для управления генерацией"
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 14,
"label": "Input Media",
"label": "Input",
"localized": "Ulazni medij",
"reload": "",
"hint": "Dodajte ulaznu sliku koja će se koristiti za obradu slike-u-sliku, inpaint ili kontrolnu obradu"
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 13,
"label": "Input Media",
"label": "Input",
"localized": "Source Telemetry",
"reload": "",
"hint": "Upload and synchronize source data for recursive generation, delta-patching, or neural guidance processing"
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 14,
"label": "Input Media",
"label": "Input",
"localized": "nI' Media",
"reload": "",
"hint": "nI' media"
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 14,
"label": "Input Media",
"label": "Input",
"localized": "Giriş Medyası",
"reload": "",
"hint": "Görüntüden görüntüye, inpaint veya kontrol işleme için kullanılacak giriş görüntüsünü ekleyin"
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 13,
"label": "Input Media",
"label": "Input",
"localized": "ان پٹ میڈیا",
"reload": "",
"hint": "تصویر سے تصویر (image-to-image)، ان پینٹ یا کنٹرول پروسیسنگ کے لیے استعمال ہونے والی ان پٹ تصویر شامل کریں"
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 13,
"label": "Input Media",
"label": "Input",
"localized": "Phương tiện đầu vào",
"reload": "",
"hint": "Thêm hình ảnh đầu vào để sử dụng cho xử lý hình ảnh thành hình ảnh, inpaint hoặc điều khiển"
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 13,
"label": "Input Media",
"label": "Input",
"localized": "Eniga Amaskomunikilaro",
"reload": "",
"hint": "Aldoni enigeblan bildon por esti uzata por bild-al-bilda, inpaint aŭ kontrola prilaborado"
+1 -1
View File
@@ -4817,7 +4817,7 @@
},
{
"id": 13,
"label": "Input Media",
"label": "Input",
"localized": "输入媒体",
"reload": "",
"hint": "添加用于图生图、重绘或控制处理的输入图像"
+36 -16
View File
@@ -37,7 +37,7 @@ log = logging.getLogger('sdnext.installer')
debug = log.debug if os.environ.get('SD_INSTALL_DEBUG', None) is not None else lambda *args, **kwargs: None
setuptools, distutils = None, None # defined via ensure_base_requirements
current_branch = None
pip_log = '--log pip.log ' if os.environ.get('SD_PIP_DEBUG', None) is not None else ''
pip_log = '--log pip.log' if os.environ.get('SD_PIP_DEBUG', None) is not None else ''
log_file = os.path.join(os.path.dirname(__file__), 'sdnext.log')
hostname = socket.gethostname()
log_rolled = False
@@ -75,7 +75,7 @@ extensions_commit = { # force specific commit for extensions
'adetailer': 'a89c01d'
# 'stable-diffusion-webui-images-browser': '27fe4a7',
}
control_extensions = [ # 3rd party extensions marked as safe for control ui
control_extensions = [ # extensions marked as safe for control ui
'NudeNet',
'IP Adapters',
'Remove background',
@@ -244,26 +244,35 @@ def cleanup_broken_packages():
pass
def pip(arg: str, ignore: bool = False, quiet: bool = True, uv = True) -> tuple[subprocess.CompletedProcess, str]:
def pip(arg: str, ignore: bool = False, quiet: bool = True, *, uv = True, constraints = True) -> tuple[subprocess.CompletedProcess | None, str]:
t_start = time.time()
originalArg = arg
arg = arg.replace('>=', '==')
arg = arg.replace('>=', '==').strip()
if opts.get('offline_mode', False):
log.warning('Offline mode enabled')
return None, 'offline'
package = arg.replace("install", "").replace("--upgrade", "").replace("--no-deps", "").replace("--force-reinstall", "").replace(" ", " ").strip()
package = arg.replace("install", "").replace("--upgrade", "").replace("--no-deps", "").replace("--force-reinstall", "").strip()
uv = uv and args.uv and not package.startswith('git+')
pipCmd = "uv pip" if uv else "pip"
if not quiet and '-r ' not in arg:
log.info(f'Install: package="{package}" mode={"uv" if uv else "pip"}')
env_args = os.environ.get("PIP_EXTRA_ARGS", "")
all_args = f'{pip_log}{arg} {env_args}'.strip()
env_args = os.environ.get("PIP_EXTRA_ARGS", "").strip()
all_args: list[str] = []
if pip_log:
all_args.append(pip_log)
all_args.append(arg)
if env_args:
all_args.append(env_args)
if constraints and "-c " not in env_args:
all_args.append("-c constraints.txt")
if not quiet:
log.debug(f'Running: {pipCmd}="{all_args}"')
result, output = run(sys.executable, "-m", pipCmd, all_args)
log.debug(f'Running: {pipCmd}="{" ".join(all_args)}"')
result, output = run(sys.executable, "-m", pipCmd, *all_args)
if len(result.stderr) > 0:
if uv and result.returncode != 0:
log.warning(f'Install: cmd="{pipCmd}" args="{all_args}" cannot use uv, fallback to pip')
log.warning(f'Install: cmd="{pipCmd}" args="{" ".join(all_args)}" cannot use uv, fallback to pip')
debug(f'Install: uv pip error: {result.stderr}')
cleanup_broken_packages()
return pip(originalArg, ignore, quiet, uv=False)
@@ -485,7 +494,7 @@ def check_diffusers():
t_start = time.time()
if args.skip_all:
return
target_commit = "0f1abc4ae8b0eb2a3b40e82a310507281144c423" # diffusers commit hash == 0.37.1.dev-0427
target_commit = "015da50b40ee7a082ea8c17a8c43dff717c9653e" # diffusers commit hash == 0.37.1.dev-0427
# if args.use_rocm or args.use_zluda or args.use_directml:
# sha = '043ab2520f6a19fce78e6e060a68dbc947edb9f9' # lock diffusers versions for now
pkg = package_spec('diffusers')
@@ -521,7 +530,7 @@ def check_transformers():
else:
# target_transformers = '4.57.6'
target_transformers = None
target_tokenizers = '0.22.2'
target_tokenizers = '0.23.1'
if target_transformers is not None:
# Pinned release version (e.g. DirectML)
if (pkg_transformers is None) or ((pkg_transformers.version != target_transformers) or (pkg_tokenizers is None) or ((pkg_tokenizers.version != target_tokenizers) and (not args.experimental))):
@@ -1279,7 +1288,6 @@ def install_requirements():
# set environment variables controling the behavior of various libraries
def set_environment():
log.debug('Setting environment tuning')
os.environ.setdefault('PIP_CONSTRAINT', 'constraints.txt')
os.environ.setdefault('ACCELERATE', 'True')
os.environ.setdefault('ATTN_PRECISION', 'fp16')
os.environ.setdefault('ClDeviceGlobalMemSizeAvailablePercent', '100')
@@ -1374,7 +1382,16 @@ def get_version(force=False):
try:
origin = run('git', 'remote get-url origin', check=True)[0].stdout
branch_name = run('git', 'rev-parse --abbrev-ref HEAD', check=True)[0].stdout
version['url'] = origin.removesuffix('.git') + '/tree/' + branch_name
# normalize ssh remotes (git@host:owner/repo) and ssh-protocol remotes
# (ssh://git@host/owner/repo) to the canonical https form so downstream
# url parsers don't have to special-case each remote shape
if origin.startswith('git@'):
host, _, path = origin.partition(':')
origin = f'https://{host[4:]}/{path}'
elif origin.startswith('ssh://'):
origin = 'https://' + origin[len('ssh://'):].split('@', 1)[-1]
origin = origin.removesuffix('.git')
version['url'] = origin + '/tree/' + branch_name
version['branch'] = branch_name
if version['branch'] == 'HEAD':
log.warning('Version: detached state detected')
@@ -1530,7 +1547,10 @@ def check_version(reset=True): # pylint: disable=unused-argument
api_base = f'https://api.github.com/repos/{url_parts}'
else:
api_base = 'https://api.github.com/repos/vladmandic/sdnext'
branches = requests.get(f'{api_base}/branches', timeout=10).json()
branches = requests.get(f'{api_base}/branches', timeout=5).json()
if not isinstance(branches, list):
log.error(f'Repository: branches API returned {branches!r} from {api_base}')
return
branch_names = [b['name'] for b in branches if 'name' in b]
log.trace(f'Repository branches: active={branch_name} available={branch_names}')
except Exception as e:
@@ -1541,7 +1561,7 @@ def check_version(reset=True): # pylint: disable=unused-argument
ts('latest', t_start)
return
try:
commits = requests.get(f'{api_base}/branches/{branch_name}', timeout=10).json()
commits = requests.get(f'{api_base}/branches/{branch_name}', timeout=5).json()
latest = commits['commit']['sha']
if len(latest) != 40:
log.error(f'Repository error: commit={latest} invalid')
+31 -29
View File
@@ -358,7 +358,7 @@ function insertExtraNetwork(textarea, item, kind) {
}
/** Insert a tag at the current word position, replacing the typed prefix. */
function insertTag(textarea, tagName) {
function insertTag(textarea, tagName, kind = 'tag') {
const info = getCurrentWord(textarea);
if (!info || (info.mode !== 'tag' && info.mode !== 'artist')) return;
const { value } = textarea;
@@ -371,13 +371,13 @@ function insertTag(textarea, tagName) {
const prefix = needsSepBefore ? `${sep} ` : '';
let suffix = `${sep} `;
if (after.length > 0 && after.trimStart().startsWith(',')) suffix = ' ';
// Artist mode: optionally keep the `@` prefix (Anima syntax); always convert underscores to spaces
// since Anima requires space-separated artist names. The `@` is consumed for non-Anima models.
// Embedding names are file-system identifiers, so underscores must be preserved regardless of the user setting.
// Tags and artists honor `autocomplete_keep_underscores`; default is to swap `_` for space.
const keepUnderscores = window.opts?.autocomplete_keep_underscores ?? false;
let body = tagName;
if (info.mode === 'artist') {
body = body.replace(/_/g, ' ');
if (window.opts?.autocomplete_at_prefix_artist) body = `@${body}`;
}
if (kind !== 'embed' && !keepUnderscores) body = body.replace(/_/g, ' ');
// Artist mode optionally keeps the `@` prefix (Anima syntax). The `@` is consumed for non-Anima models.
if (info.mode === 'artist' && window.opts?.autocomplete_at_prefix_artist) body = `@${body}`;
const insertion = `${prefix}${escapeParensForPrompt(body)}${suffix}`;
textarea.value = before.trimEnd() + (before.trimEnd().length > 0 ? ' ' : '') + insertion + after.trimStart();
// Position cursor after the inserted tag + separator
@@ -447,7 +447,7 @@ const dropdown = {
},
render() {
const replaceUnderscores = window.opts?.autocomplete_replace_underscores ?? true;
const keepUnderscores = window.opts?.autocomplete_keep_underscores ?? false;
const queryNorm = this.query.toLowerCase().replace(/ /g, '_');
this.listEl.replaceChildren();
this.results.forEach((tag, i) => {
@@ -462,7 +462,9 @@ const dropdown = {
dot.title = kind === 'tag' ? (engine.categoryNames[tag.category] || '') : kind;
const name = document.createElement('span');
name.className = 'autocomplete-tag';
const tagText = replaceUnderscores ? tag.display.replace(/_/g, ' ') : tag.display;
// Embeddings are file-name identifiers, so they always render as-is to match how they get inserted.
const swapForKind = kind !== 'embed';
const tagText = (swapForKind && !keepUnderscores) ? tag.display.replace(/_/g, ' ') : tag.display;
const canonicalMatch = tag.name.indexOf(queryNorm);
if (canonicalMatch >= 0 && queryNorm.length > 0) {
const mark = document.createElement('mark');
@@ -480,7 +482,7 @@ const dropdown = {
if (tag.matchedVia === 'alias') annotationTerm = tag.matchedAlias;
else if (tag.matchedVia === 'translation') annotationTerm = tag.matchedTerm;
if (annotationTerm) {
const annotationDisplay = replaceUnderscores ? annotationTerm.replace(/_/g, ' ') : annotationTerm;
const annotationDisplay = (swapForKind && !keepUnderscores) ? annotationTerm.replace(/_/g, ' ') : annotationTerm;
const annotationLower = annotationTerm.toLowerCase();
const annotationMatch = annotationLower.indexOf(queryNorm);
const prefix = tag.matchedVia === 'translation' ? ' \u{1F310} ' : ' (';
@@ -561,7 +563,7 @@ const dropdown = {
insertExtraNetwork(this.textarea, result, result.kind);
} else {
// 'embed' kind and untagged tag results both go through insertTag (comma-aware, paren-escaped).
insertTag(this.textarea, result.display ?? result.name);
insertTag(this.textarea, result.display ?? result.name, result.kind);
}
}
this.hide();
@@ -745,24 +747,24 @@ async function initAutocomplete() {
log('autoComplete', { active, enabled });
// Inject styles (CSS files in javascript/ are not auto-loaded)
const style = document.createElement('style');
style.textContent = [
'.autocompleteResults { position: fixed; z-index: 9999; max-height: 300px; overflow-y: auto;',
' background: var(--sd-main-background-color, var(--background-fill-primary, #1f2937));',
' border: 1px solid var(--sd-input-border-color, var(--border-color-primary, #374151));',
' border-radius: var(--sd-border-radius, 6px); box-shadow: 0 4px 16px rgba(0,0,0,0.4);',
' font-size: 13px; scrollbar-width: thin; }',
'.autocompleteResultsList { list-style: none; margin: 0; padding: 4px 0; }',
'.autocompleteResultsList > li { display: flex; align-items: center; padding: 6px 12px; cursor: pointer;',
' gap: 8px; line-height: 1.4; transition: background 0.1s ease; border-bottom: 1px solid rgba(255,255,255,0.03); }',
'.autocompleteResultsList > li:last-child { border-bottom: none; }',
'.autocompleteResultsList > li:hover { background: var(--sd-panel-background-color, var(--input-background-fill-focus, #374151)); }',
'.autocompleteResultsList > li.selected { background: var(--sd-main-accent-color, var(--button-primary-background-fill, #4b5563)); }',
'.autocomplete-category { font-size: 10px; flex-shrink: 0; width: 10px; text-align: center; cursor: help; }',
'.autocomplete-tag { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }',
'.autocomplete-tag mark { background: transparent; color: inherit; font-weight: 700; }',
'.autocomplete-count { font-size: 0.75em; opacity: 0.45; flex-shrink: 0; font-variant-numeric: tabular-nums;',
' background: rgba(255,255,255,0.06); padding: 1px 6px; border-radius: 8px; min-width: 28px; text-align: right; }',
].join('\n');
style.textContent = `
.autocompleteResults { position: fixed; z-index: 9999; max-height: 300px; overflow-y: auto;
background: var(--sd-main-background-color, var(--background-fill-primary, #1f2937));
border: 1px solid var(--sd-input-border-color, var(--border-color-primary, #374151));
border-radius: var(--sd-border-radius, 6px); box-shadow: 0 4px 16px rgba(0,0,0,0.4);
font-size: 13px; scrollbar-width: thin; color: var(--body-text-color-subdued); }
.autocompleteResultsList { list-style: none; margin: 0; padding: 4px 0; }
.autocompleteResultsList > li { display: flex; align-items: center; padding: 6px 12px; cursor: pointer;
gap: 8px; line-height: 1.4; transition: background 0.1s ease; border-bottom: 1px solid rgba(255,255,255,0.03); }
.autocompleteResultsList > li:last-child { border-bottom: none; }
.autocompleteResultsList > li:hover { background: var(--sd-panel-background-color, var(--input-background-fill-focus, #374151)); }
.autocompleteResultsList > li.selected { background: var(--sd-main-accent-color, var(--button-primary-background-fill, #4b5563)); }
.autocomplete-category { font-size: 10px; flex-shrink: 0; width: 10px; text-align: center; cursor: help; }
.autocomplete-tag { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.autocomplete-tag mark { background: transparent; color: inherit; font-weight: 700; }
.autocomplete-count { font-size: 0.75em; opacity: 0.45; flex-shrink: 0; font-variant-numeric: tabular-nums;
background: rgba(255,255,255,0.06); padding: 1px 6px; border-radius: 8px; min-width: 28px; text-align: right; }
`;
document.head.appendChild(style);
dropdown.init();
await engine.loadEnabled();
+6 -2
View File
@@ -8,11 +8,15 @@ function controlInputMode(inputMode, ...args) {
const tabNames = ['Image', 'Video', 'Batch', 'Folder'];
let inputTab = tabNames[tabIdx] || 'Image';
log('controlInputMode', { mode: inputMode, tab: inputTab, kanvas: typeof Kanvas });
// if kanvas is available overwrite image inputs with kanvas images
if ((inputTab === 'Image') && (typeof 'Kanvas' !== 'undefined')) {
inputTab = 'Kanvas';
const imageData = window.kanvas.getImage();
args[0] = imageData;
for (let i = 0; i < window.kanvas.stages.maxStages; i++) {
args[4 + i] = window.kanvas.getImage(1 + i, false, false);
}
}
return [inputTab, ...args];
}
+158 -46
View File
@@ -1,7 +1,11 @@
/* eslint-disable max-classes-per-file */
let ws;
let url;
let currentSize = 0;
let currentSort = 'none';
let currentName = '';
let currentImage = null;
let currentTitle = '';
let currentGalleryFolder = null;
let pruneImagesTimer;
let outstanding = 0;
@@ -18,7 +22,9 @@ const el = {
search: undefined,
status: undefined,
btnSend: undefined,
overlay: undefined,
clearCacheFolder: undefined,
size: undefined,
};
const SUPPORTED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp', 'tiff', 'jp2', 'jxl', 'gif', 'mp4', 'mkv', 'avi', 'mjpeg', 'mpg', 'avr'];
@@ -26,12 +32,12 @@ const SUPPORTED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp', 'tiff', 'jp2', 'jxl'
const gallerySorter = {
nameA: { name: 'Name Ascending', func: (a, b) => a.name.localeCompare(b.name) },
nameD: { name: 'Name Descending', func: (b, a) => a.name.localeCompare(b.name) },
sizeA: { name: 'Size Ascending', func: (a, b) => a.size - b.size },
sizeD: { name: 'Size Descending', func: (b, a) => a.size - b.size },
resA: { name: 'Resolution Ascending', func: (a, b) => a.width * a.height - b.width * b.height },
resD: { name: 'Resolution Descending', func: (b, a) => a.width * a.height - b.width * b.height },
modA: { name: 'Modified Ascending', func: (a, b) => a.mtime - b.mtime },
modD: { name: 'Modified Descending', func: (b, a) => a.mtime - b.mtime },
sizeD: { name: 'Size Ascending', func: (a, b) => a.size - b.size },
sizeA: { name: 'Size Descending', func: (b, a) => a.size - b.size },
resD: { name: 'Resolution Ascending', func: (a, b) => a.width * a.height - b.width * b.height },
resA: { name: 'Resolution Descending', func: (b, a) => a.width * a.height - b.width * b.height },
modD: { name: 'Modified Ascending', func: (a, b) => a.mtime - b.mtime },
modA: { name: 'Modified Descending', func: (b, a) => a.mtime - b.mtime },
none: { name: 'None', func: undefined },
};
@@ -71,6 +77,8 @@ function resetGallerySelection() {
updateGallerySelectionClasses(gallerySelection.files, -1);
gallerySelection = { files: [], index: -1 };
currentImage = null;
currentName = '';
currentTitle = '';
}
function applyGallerySelection(index, { send = true } = {}) {
@@ -84,6 +92,8 @@ function applyGallerySelection(index, { send = true } = {}) {
}
gallerySelection.index = index;
currentImage = files[index].src;
currentName = files[index].name;
currentTitle = files[index].title;
updateGallerySelectionClasses(files, index);
if (send && el.btnSend) el.btnSend.click();
}
@@ -129,7 +139,7 @@ async function awaitForGallery(expectedSize, signal) {
function updateGalleryStyles() {
if (opts.theme_type?.toLowerCase() === 'modern') {
folderStylesheet.replaceSync(`
folderStylesheet.replace(`
.gallery-folder {
cursor: pointer;
padding: 8px 6px 8px 6px;
@@ -162,7 +172,7 @@ function updateGalleryStyles() {
}
`);
} else {
folderStylesheet.replaceSync(`
folderStylesheet.replace(`
.gallery-folder {
cursor: pointer;
padding: 8px 6px 8px 6px;
@@ -179,16 +189,30 @@ function updateGalleryStyles() {
}
`);
}
fileStylesheet.replaceSync(`
const size = el.size ? el.size.value : opts.extra_networks_card_size;
fileStylesheet.replace(`
.gallery-file {
object-fit: contain;
cursor: pointer;
height: ${opts.extra_networks_card_size}px;
width: ${opts.browser_fixed_width ? `${opts.extra_networks_card_size}px` : 'unset'};
height: ${size}px;
width: ${opts.browser_fixed_width ? `${size}px` : 'unset'};
}
.gallery-file:hover {
filter: grayscale(100%);
}
.gallery-overlay {
position: absolute;
height: 24px;
background-color: rgba(0,0,0,0.7);
display: block;
text-align: right;
padding: 4px;
font-size: 1.2em;
letter-spacing: 0.5em;
width: 140px;
margin-top: calc(140px - 32px);
opacity: 75%;
}
:host(.gallery-file-selected) .gallery-file {
box-shadow: 0 0 0 2px var(--sd-button-selected-color);
}
@@ -409,9 +433,7 @@ class GalleryFolder extends HTMLElement {
this.div.classList.add('gallery-folder-selected');
GalleryFolder.#active = this;
for (const folder of GalleryFolder.folders) {
if (folder !== this) {
folder.div.classList.remove('gallery-folder-selected');
}
if (folder !== this) folder.div.classList.remove('gallery-folder-selected');
}
}
}
@@ -456,7 +478,6 @@ class GalleryFile extends HTMLElement {
this.height = 0;
this.shadow = this.attachShadow({ mode: 'open' });
this.shadow.adoptedStyleSheets = [fileStylesheet];
this.firstRun = true;
}
@@ -469,9 +490,7 @@ class GalleryFile extends HTMLElement {
if (dir && dir[1]) {
const dirPath = dir[1];
const isOpen = separatorStates.get(dirPath);
if (isOpen === false) {
this.style.display = 'none';
}
if (isOpen === false) this.style.display = 'none';
}
this.hash = await getHash(`${this.src}/${this.size}/${this.mtime}`)
@@ -514,7 +533,7 @@ class GalleryFile extends HTMLElement {
this.size = json.size;
this.mtime = new Date(json.mtime);
if (opts.browser_cache && this.hash) {
await idbAdd({
idbAdd({
hash: this.hash,
folder: this.fullFolder,
file: this.name,
@@ -534,23 +553,30 @@ class GalleryFile extends HTMLElement {
img.src = `file=${this.src}`;
}
}
if (this.#signal.aborted) { // Do not change the operations order from here...
return;
}
if (this.#signal.aborted) return;
galleryHashes.add(this.hash);
if (!ok) {
return;
} // ... to here unless modifications are also being made to maintenance functionality and the usage of AbortController/AbortSignal
if (!ok) return;
img.onclick = () => {
setGallerySelectionByElement(this, { send: true });
};
img.onpointerenter = () => {
el.overlay.display = 'block';
this.shadow.appendChild(el.overlay);
currentImage = this.src;
currentName = this.name;
currentTitle = this.title;
};
img.onpointerleave = () => {
el.overlay.display = 'none';
};
img.title = `Folder: ${this.folder}\nFile: ${this.name}\nSize: ${this.size.toLocaleString()} bytes\nModified: ${this.mtime.toLocaleString()}`;
this.title = img.title;
// Final visibility check based on search term.
const shouldDisplayBasedOnSearch = this.title.toLowerCase().includes(el.search.value.toLowerCase());
if (this.style.display !== 'none') { // Only proceed if not already hidden by a closed separator
this.style.display = shouldDisplayBasedOnSearch ? 'unset' : 'none';
this.style.display = shouldDisplayBasedOnSearch ? 'flex' : 'none';
}
this.shadow.appendChild(img);
@@ -558,8 +584,10 @@ class GalleryFile extends HTMLElement {
}
async function createThumb(img) {
const height = opts.extra_networks_card_size;
const width = opts.browser_fixed_width ? opts.extra_networks_card_size : 0;
const sizeEl = document.getElementById('gallery-thumb-size');
currentSize = sizeEl ? parseInt(sizeEl.value, 10) : opts.extra_networks_card_size;
const height = currentSize;
const width = opts.browser_fixed_width ? currentSize : 0;
const canvas = document.createElement('canvas');
const scaleY = height / img.height;
const scaleX = width > 0 ? width / img.width : scaleY;
@@ -872,8 +900,13 @@ const findDuplicates = (arr, key) => {
};
async function gallerySort(key) {
if (!Object.hasOwn(gallerySorter, key)) {
error(`Gallery: "${key}" is not a valid gallery sorting key`);
// if currentSort does not start with key, default to key+A
// else if currentSort ends with A change to D and vice versa for toggling sort order
if (currentSort.startsWith(key)) currentSort = currentSort.endsWith('A') ? `${key}D` : `${key}A`;
else currentSort = `${key}A`;
if (!Object.hasOwn(gallerySorter, currentSort)) {
error(`Gallery: "${currentSort}" is not a valid gallery sorting key`);
return;
}
const t0 = performance.now();
@@ -901,7 +934,7 @@ async function gallerySort(key) {
folderGroups.get(dir).push(file);
}
sortMode = gallerySorter[key];
sortMode = gallerySorter[currentSort];
// Sort root files
rootFiles.sort(sortMode.func);
@@ -993,24 +1026,24 @@ async function thumbCacheCleanup(folder, imgCount, controller, force = false) {
if (typeof folder !== 'string' || typeof imgCount !== 'number') {
throw new Error('Function called with invalid arguments');
}
debug('Thumbnail DB cleanup: Waiting for gallery data to settle');
debug('thumbCacheCleanup: wait');
await awaitForGallery(imgCount, controller.signal);
} catch (err) {
debug(`Thumbnail DB cleanup: Skipping cleanup for "${folder}" due to "${err}"`);
error('thumbCacheCleanup', { folder, error: err });
return;
}
maintenanceQueue.enqueue({
signal: controller.signal,
callback: async () => {
log(`Thumbnail DB cleanup: Checking if "${folder}" needs cleaning`);
log('maintenanceQueue', { folder });
const t0 = performance.now();
const keptGalleryHashes = force ? new Set() : new Set(galleryHashes.values()); // External context should be safe since this function run is guarded by AbortController/AbortSignal in the SimpleFunctionQueue
const folderNormalized = folder.replace(/\/+/g, '/').replace(/\/$/, '');
const recursiveFolder = IDBKeyRange.bound(folderNormalized, `${folderNormalized}\uffff`, false, true);
const cachedHashesCount = await idbCount(recursiveFolder)
.catch((e) => {
error(`Thumbnail DB cleanup: Error when getting entry count for "${folder}".`, e);
error('maintenanceQueue', { folder, error: e });
return Infinity; // Forces next check to fail if something went wrong
});
const cleanupCount = cachedHashesCount - keptGalleryHashes.size;
@@ -1020,21 +1053,21 @@ async function thumbCacheCleanup(folder, imgCount, controller, force = false) {
}
if (controller.signal.aborted) {
debug(`Thumbnail DB cleanup: Cancelling "${folder}" cleanup due to "${controller.signal.reason}"`);
debug('maintenanceQueue', { folder, reason: controller.signal.reason });
return;
}
const cb_clearMsg = showCleaningMsg(cleanupCount);
await idbFolderCleanup(keptGalleryHashes, recursiveFolder, controller.signal)
.then((delcount) => {
const t1 = performance.now();
log(`Thumbnail DB cleanup: folder=${folder} kept=${keptGalleryHashes.size} deleted=${delcount} time=${Math.round(t1 - t0)}ms`);
log('maintenanceQueue', { folder, kept: keptGalleryHashes.size, deleted: delcount, time: Math.round(t1 - t0) });
timer(`thumbnailDBCleanup:${folder}`, t1 - t0);
currentGalleryFolder = null;
el.clearCacheFolder.innerText = '<select a folder first>';
updateStatusWithSort('Thumbnail cache cleared');
})
.catch((reason) => {
SimpleFunctionQueue.abortLogger('Thumbnail DB cleanup:', reason);
SimpleFunctionQueue.abortLogger('thumbCacheCleanup', reason);
})
.finally(async () => {
await new Promise((resolve) => { setTimeout(resolve, 1000); }); // Delay removal by 1 second to ensure at least minimum visibility
@@ -1057,7 +1090,7 @@ function resetGalleryState(reason) {
function clearCacheIfDisabled(browser_cache) {
if (browser_cache === false) {
log('Thumbnail DB cleanup:', 'Image gallery cache setting disabled. Clearing cache.');
log('thumbCacheCleanup', { disabled: true });
const controller = resetGalleryState('Clearing all thumbnails from cache');
maintenanceQueue.enqueue({
signal: controller.signal,
@@ -1066,13 +1099,13 @@ function clearCacheIfDisabled(browser_cache) {
const cb_clearMsg = showCleaningMsg(0, true);
await idbClearAll(controller.signal)
.then(() => {
log(`Thumbnail DB cleanup: Cache cleared. time=${Math.floor(performance.now() - t0)}ms`);
log('thumbCacheCleanup', { time: Math.floor(performance.now() - t0) });
currentGalleryFolder = null;
el.clearCacheFolder.innerText = '<select a folder first>';
updateStatusWithSort('Thumbnail cache cleared');
})
.catch((e) => {
SimpleFunctionQueue.abortLogger('Thumbnail DB cleanup:', e);
SimpleFunctionQueue.abortLogger('thumbCacheCleanup', e);
})
.finally(async () => {
await new Promise((resolve) => { setTimeout(resolve, 1000); });
@@ -1307,6 +1340,80 @@ async function initGalleryAutoRefresh() {
galleryVisObserver.observe(galleryTab, { attributeFilter: ['class', 'style'], attributeOldValue: true });
}
async function overlayDelete(evt) {
const res = await authFetch(`${window.api}/delete-image?file=${encodeURIComponent(currentImage)}`);
evt.stopPropagation();
if (!res || res.status !== 200) {
error('galleryDelete', { file: currentImage, status: res?.status, statusText: res?.statusText });
return;
}
const data = await res.json();
log('galleryDelete', data);
GalleryFolder.getActive()?.click();
}
async function overlayDownload(evt) {
log('galleryDownload', currentImage);
const link = document.createElement('a');
link.href = `/file=${encodeURIComponent(currentImage)}`;
link.download = currentName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
evt.stopPropagation();
}
async function overlayInfo(evt) {
evt.stopPropagation();
const tgt = document.getElementById('html_info_formatted_gallery');
if (!tgt) return;
const res = await authFetch(`${window.api}/png-info?file=${encodeURI(currentImage)}`);
if (!res || res.status !== 200) return;
const data = await res.json();
log('galleryInfo res', data);
const prompt = data?.parameters?.Prompt || '';
const negative = data?.parameters?.Negative || data?.parameters?.['Negative prompt'] || '';
const raw = data?.info || '';
const params = data?.parameters || {};
delete params.Prompt;
delete params.Negative;
delete params['Negative prompt'];
const paramsFormatted = Object.entries(params).map(([key, value]) => `<b>${key}:</b> ${value}`).join(' | ');
tgt.innerHTML = `
<div><b>File:</b> ${currentImage}</div>
<div><b>Prompt:</b> ${prompt}</div>
<div><b>Negative:</b> ${negative}</div>
<div>${paramsFormatted}</div>
<div><b>Raw:</b><pre style="white-space: pre-wrap; margin: 0.5em">${raw}</pre></div>
`;
const img = document.querySelector('#gallery_gallery img');
if (img) img.src = `/file=${encodeURIComponent(currentImage)}?t=${Date.now()}`; // Force refresh in case info endpoint is faster than cache update
const status = document.querySelector('#html_log_gallery p');
if (status) status.innerText = currentTitle;
}
async function createOverlay() {
if (el.overlay) return;
el.overlay = document.createElement('div');
el.overlay.className = 'gallery-overlay';
const btnDownload = document.createElement('span');
btnDownload.innerHTML = '\udb85\udc64';
btnDownload.title = 'Download image';
btnDownload.style.cursor = 'pointer';
btnDownload.addEventListener('click', overlayDownload);
const btnDelete = document.createElement('span');
btnDelete.innerHTML = '\uf05c';
btnDelete.title = 'Delete image';
btnDelete.style.cursor = 'pointer';
btnDelete.addEventListener('click', overlayDelete);
const btnInfo = document.createElement('span');
btnInfo.innerHTML = '\uf05a';
btnInfo.title = 'Image metadata';
btnInfo.style.cursor = 'pointer';
btnInfo.addEventListener('click', overlayInfo);
el.overlay.append(btnInfo, btnDelete, btnDownload);
}
async function blockQueueUntilReady() {
// Add block to maintenanceQueue until cache is ready
maintenanceQueue.enqueue({
@@ -1329,22 +1436,27 @@ async function initGallery() { // triggered on gradio change to monitor when ui
el.files = gradioApp().getElementById('tab-gallery-files');
el.status = gradioApp().getElementById('tab-gallery-status');
el.search = gradioApp().querySelector('#tab-gallery-search textarea');
el.size = document.getElementById('tab-gallery-thumb-size');
if (!el.folders || !el.files || !el.status || !el.search) {
error('initGallery', 'Missing gallery elements');
return;
}
if (el.size) {
el.size.value = opts.extra_networks_card_size;
el.size.addEventListener('input', updateGalleryStyles);
}
blockQueueUntilReady(); // Run first
createOverlay();
updateGalleryStyles();
injectGalleryStatusCSS();
setOverlayAnimation();
galleryClearInit();
const progress = gradioApp().getElementById('tab-gallery-progress');
if (progress) {
galleryProgressBar.attachTo(progress);
} else {
log('initGallery', 'Failed to attach loading progress bar');
}
if (progress) galleryProgressBar.attachTo(progress);
else log('initGallery', 'Failed to attach loading progress bar');
el.search.addEventListener('input', gallerySearch);
el.btnSend = gradioApp().getElementById('tab-gallery-send-image');
document.getElementById('tab-gallery-files').style.height = opts.logmonitor_show ? '75vh' : '85vh';
+2
View File
File diff suppressed because one or more lines are too long
+30 -13
View File
@@ -1,5 +1,7 @@
let lastState = {};
let refreshInterval = 10000;
const progressTimeout = 180;
const startTimeout = 5;
function setRefreshInterval() {
refreshInterval = opts.live_preview_refresh_period || 500;
@@ -80,8 +82,8 @@ function randomId() {
// starts sending progress requests to "/internal/progress" uri, creating progressbar above progressbarContainer element and preview inside gallery element
// Cleans up all created stuff when the task is over and calls atEnd. calls onProgress every time there is a progress update
function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgress = null, once = false) {
localStorage.setItem('task', id_task);
function requestProgress(id_task = 'undefined', progressEl = null, galleryEl = null, atEnd = null, onProgress = null, once = false) {
if (id_task) localStorage.setItem('task', id_task);
let hasStarted = false;
let dateStart = new Date();
let prevProgress = null;
@@ -114,7 +116,7 @@ function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgres
};
};
const done = () => {
const removeLivePreview = (ok = false) => {
debug('taskEnd:', id_task);
localStorage.removeItem('task');
setProgress();
@@ -124,6 +126,11 @@ function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgres
for (const gallery of galleries) gallery.style.display = 'flex'; // remove all galleries
try {
if (parentGallery && livePreview) {
if (ok) {
const previewImg = gradioApp().querySelector('#livePreviewImage');
const galleryImg = gradioApp().querySelector('#control_gallery img');
if (previewImg?.src && galleryImg) galleryImg.src = previewImg.src; // copy preview to gallery if everything is ok
}
parentGallery.removeChild(livePreview);
parentGallery.style.minHeight = 'unset';
parentGallery.style.maxHeight = 'unset';
@@ -135,18 +142,28 @@ function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgres
if (atEnd) atEnd();
};
const start = (id_task, id_live_preview) => { // eslint-disable-line no-shadow
const startLivePreview = (id_task, id_live_preview) => { // eslint-disable-line no-shadow
if (opts.live_preview_refresh_period === 0) return;
const request_id = document.hidden ? -1 : id_live_preview;
const onProgressHandler = (res) => {
if (res?.debug) debug('livePreview:', dateStart, request_id, res);
if (res?.debug) debug('progress:', { start: dateStart, id: request_id, res });
lastState = res;
const elapsedFromStart = (new Date() - dateStart) / 1000;
hasStarted |= res.active;
if (res.completed || (!res.active && (hasStarted || once)) || (elapsedFromStart > 120 && !res.queued && res.progress === prevProgress)) {
debug('livePreview end:', res);
done();
if (res.completed || (!res.active && (hasStarted || once))) {
debug('progress', { end: res, reason: res.completed ? 'completed' : 'inactive' });
if (!res.paused) removeLivePreview(true); // only abort if not paused
return;
}
if (elapsedFromStart > progressTimeout && !res.queued && res.progress === prevProgress) {
debug('progress', { end: res, reason: 'progressSimeout' });
if (!res.paused) removeLivePreview(false); // only abort if not paused
return;
}
if (elapsedFromStart > startTimeout && !res.queued && !res.active) {
debug('progress', { end: res, reason: 'startTimeout' });
if (!res.paused) removeLivePreview(false); // only abort if not paused
return;
}
if (res.progress !== prevProgress) {
@@ -160,16 +177,16 @@ function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgres
id_live_preview = res.id_live_preview;
}
if (onProgress) onProgress(res);
setTimeout(() => start(id_task, id_live_preview), opts.live_preview_refresh_period || 500);
setTimeout(() => startLivePreview(id_task, id_live_preview), opts.live_preview_refresh_period || 500);
};
const onProgressErrorHandler = (err) => {
error(`livePreview: ${err}`);
done();
error('progress', { error: err });
removeLivePreview(false);
};
xhrPost('./internal/progress', { id_task, id_live_preview: request_id }, onProgressHandler, onProgressErrorHandler, false, 30000);
};
debug('livePreview start:', dateStart);
start(id_task, 0);
debug('progress', { start: dateStart });
startLivePreview(id_task, 0);
}
+93 -6
View File
@@ -41,12 +41,17 @@ const optionsChangedCallbacks = [];
let uiCurrentTab = null;
let uiAfterUpdateTimeout = null;
function registerCallback(queue, callback) {
if (queue.includes(callback)) return;
queue.push(callback);
}
function onAfterUiUpdate(callback) {
if (typeof callback !== 'function') {
error(`onAfterUiUpdate was called without a valid value. Expected a function but got: ${callback}`);
return;
}
uiAfterUpdateCallbacks.push(callback);
registerCallback(uiAfterUpdateCallbacks, callback);
}
function onUiUpdate(callback) {
@@ -54,7 +59,7 @@ function onUiUpdate(callback) {
error(`onUiUpdate was called without a valid value. Expected a function but got: ${callback}`);
return;
}
uiUpdateCallbacks.push(callback);
registerCallback(uiUpdateCallbacks, callback);
}
function onUiLoaded(callback) {
@@ -62,7 +67,7 @@ function onUiLoaded(callback) {
error(`onUiLoaded was called without a valid value. Expected a function but got: ${callback}`);
return;
}
uiLoadedCallbacks.push(callback);
registerCallback(uiLoadedCallbacks, callback);
}
function onUiReady(callback) {
@@ -70,7 +75,7 @@ function onUiReady(callback) {
error(`onUiReady was called without a valid value. Expected a function but got: ${callback}`);
return;
}
uiReadyCallbacks.push(callback);
registerCallback(uiReadyCallbacks, callback);
}
function onUiTabChange(callback) {
@@ -78,7 +83,7 @@ function onUiTabChange(callback) {
error(`onUiTabChange was called without a valid value. Expected a function but got: ${callback}`);
return;
}
uiTabChangeCallbacks.push(callback);
registerCallback(uiTabChangeCallbacks, callback);
}
function onOptionsChanged(callback) {
@@ -86,7 +91,7 @@ function onOptionsChanged(callback) {
error(`onOptionsChanged was called without a valid value. Expected a function but got: ${callback}`);
return;
}
optionsChangedCallbacks.push(callback);
registerCallback(optionsChangedCallbacks, callback);
}
function executeCallbacks(queue, arg) {
@@ -179,6 +184,88 @@ document.addEventListener('keydown', (e) => {
}
});
function getSortableCellValue(cell, sortType) {
const rawValue = cell?.dataset?.sortValue ?? cell?.textContent?.trim() ?? '';
if (sortType === 'number') {
const numericValue = Number.parseFloat(rawValue);
return Number.isNaN(numericValue) ? Number.NEGATIVE_INFINITY : numericValue;
}
return rawValue.toLowerCase();
}
function sortTable(table, columnIndex, sortType, sortOrder) {
const tbody = table.querySelector('tbody');
if (!tbody) return;
const rows = Array.from(tbody.querySelectorAll('tr'));
const direction = sortOrder === 'desc' ? -1 : 1;
const sortedRows = rows
.map((row, index) => ({ row, index }))
.sort((a, b) => {
const aCell = a.row.children[columnIndex];
const bCell = b.row.children[columnIndex];
const aValue = getSortableCellValue(aCell, sortType);
const bValue = getSortableCellValue(bCell, sortType);
if (aValue < bValue) return -1 * direction;
if (aValue > bValue) return 1 * direction;
return a.index - b.index;
});
tbody.replaceChildren(...sortedRows.map((item) => item.row));
}
function applySortIndicators(table, activeHeader, sortOrder) {
const headers = table.querySelectorAll('th.sortable');
for (const header of headers) {
header.classList.remove('sorted-asc', 'sorted-desc');
header.removeAttribute('aria-sort');
}
activeHeader.classList.add(sortOrder === 'desc' ? 'sorted-desc' : 'sorted-asc');
activeHeader.setAttribute('aria-sort', sortOrder === 'desc' ? 'descending' : 'ascending');
}
function handleSortableTableClick(event) {
const header = event.target.closest('th.sortable');
if (!header) return;
const table = header.closest('table[data-sortable="true"]');
if (!table) return;
const headers = Array.from(table.querySelectorAll('th.sortable'));
const columnIndex = headers.indexOf(header);
if (columnIndex < 0) return;
const currentSortKey = table.dataset.sortKey || table.dataset.defaultSortKey;
const currentSortOrder = table.dataset.sortOrder || table.dataset.defaultSortOrder || 'asc';
const isCurrentHeader = currentSortKey === header.dataset.sortKey;
const nextOrder = isCurrentHeader && currentSortOrder === 'asc' ? 'desc' : 'asc';
table.dataset.sortKey = header.dataset.sortKey;
table.dataset.sortOrder = nextOrder;
sortTable(table, columnIndex, header.dataset.sortType || 'text', nextOrder);
applySortIndicators(table, header, nextOrder);
}
async function initTableSorter() {
const t0 = performance.now();
const root = gradioApp();
if (!root.dataset.tableSorterBound) {
root.addEventListener('click', handleSortableTableClick);
root.dataset.tableSorterBound = 'true';
}
const t1 = performance.now();
log('initTableSorter', Math.round(t1 - t0));
timer('initTableSorter', t1 - t0);
}
async function deleteFile(filename) {
if (!filename) return;
if (!confirm(`Are you sure you want to delete the object - This action cannot be undone? Object: ${filename}`)) return; // eslint-disable-line no-alert
const res = await authFetch(`${window.api}/delete-file?file=${encodeURIComponent(filename)}`);
if (!res || res.status !== 200) {
error('FileDelete', { file: filename, status: res?.status, statusText: res?.statusText });
return;
}
const data = await res.json();
log('FileDelete', data);
}
/**
* checks that a UI element is not in another hidden element or tab content
*/
+24
View File
@@ -2193,6 +2193,30 @@ div:has(>#tab-gallery-folders) {
background-color: var(--button-primary-border-color) !important;
}
.simple-table th.sortable {
cursor: pointer;
user-select: none;
position: relative;
padding-right: 1.2em;
}
.simple-table th.sortable::after {
content: '↕';
position: absolute;
right: 0.3em;
opacity: 0.55;
}
.simple-table th.sortable.sorted-asc::after {
content: '↑';
opacity: 1;
}
.simple-table th.sortable.sorted-desc::after {
content: '↓';
opacity: 1;
}
.simple-table tr:nth-child(odd) {
background-color: var(--neutral-900);
}
File diff suppressed because one or more lines are too long
+5 -3
View File
@@ -50,6 +50,7 @@ async function initStartup() {
startupPromises.push(initAccordions());
startupPromises.push(initSettings());
startupPromises.push(initImageViewer());
startupPromises.push(initGallery());
startupPromises.push(initiGenerationParams());
startupPromises.push(initChangelog());
startupPromises.push(setupControlUI());
@@ -65,14 +66,14 @@ async function initStartup() {
}
executeCallbacks(uiReadyCallbacks);
startupPromises.push(initGallery());
startupPromises.push(setRefreshInterval());
startupPromises.push(setupExtraNetworks());
// optinally wait for modern ui
if (window.waitForUiReady) await waitForUiReady();
// post startup tasks that may take longer but are not critical
startupPromises.push(initGallery());
startupPromises.push(setRefreshInterval());
startupPromises.push(setupExtraNetworks());
startupPromises.push(initAutocomplete());
startupPromises.push(monitorConnection());
startupPromises.push(showNetworks());
@@ -80,6 +81,7 @@ async function initStartup() {
startupPromises.push(applyStyles());
startupPromises.push(initIndexDB());
startupPromises.push(initLogMonitor());
startupPromises.push(initTableSorter());
t1 = performance.now();
log('initStartup', Math.round(1000 * (t1 - t0) / 1000000));
+6 -4
View File
@@ -5,10 +5,12 @@ async function timer(name, elapsed) {
}
async function logTimers() {
allTimers.sort((a, b) => b[1] - a[1]);
const filteredTimers = allTimers.filter((t) => t[1] > 50);
debug('startupTimers', filteredTimers);
// xhrPost(`${window.api}/log`, { debug: JSON.stringify(filteredTimers) });
// allTimers.sort((a, b) => b[1] - a[1]);
const filteredTimers = allTimers.filter((t) => t[1] > 100);
const objTimers = {};
for (const [name, elapsed] of filteredTimers) objTimers[name] = elapsed;
debug('startupTimers', objTimers);
// xhrPost(`${window.api}/log`, { debug: JSON.stringify(objTimers) });
}
window.timer = timer;
+2 -4
View File
@@ -108,10 +108,6 @@ function send_to_kanvas(gallery) {
const [image] = extract_image_from_gallery(gallery);
log('sendToKanvas', image);
if (window.loadFromURL && image.data) window.loadFromURL(image.data);
// const inputPanelEl = gradioApp().getElementById('control-template-column-input');
// if (inputPanelEl) inputPanelEl.classList.remove('hidden');
const inputPanelCb = gradioApp().getElementById('control_dynamic_input');
if (inputPanelCb && !inputPanelCb.checked) inputPanelCb.click();
}
async function setTheme(val, old) {
@@ -638,6 +634,7 @@ function selectCheckpoint(name) {
else gradioApp().getElementById('change_checkpoint').click();
log(`selectCheckpoint ${isRefiner ? 'refiner' : 'model'}: ${desiredCheckpointName}`);
markSelectedCards([desiredCheckpointName], 'model');
setTimeout(requestProgress, 250);
}
let desiredVAEName = null;
@@ -661,6 +658,7 @@ function selectReference(name) {
desiredCheckpointName = name;
gradioApp().getElementById('change_reference').click();
markSelectedCards([desiredCheckpointName], 'model');
setTimeout(requestProgress, 250);
}
function currentImageResolutionimg2img(_a, _b, scaleBy) {
Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

+4
View File
@@ -93,6 +93,10 @@ class Api:
self.add_api_route("/sdapi/v1/unets", endpoints.get_unets, methods=["GET"], response_model=list[models.ItemUNet])
# functional api
self.add_api_route("/sdapi/v1/file", endpoints.get_file, methods=["GET"], tags=["Functional"])
self.add_api_route("/sdapi/v1/delete-image", endpoints.get_deleteimage, methods=["GET"], tags=["Functional"])
self.add_api_route("/sdapi/v1/delete-file", endpoints.get_deletefile, methods=["GET"], tags=["Functional"])
self.add_api_route("/sdapi/v1/png-info", endpoints.get_pnginfo, methods=["GET"], response_model=models.ResImageInfo, tags=["Functional"])
self.add_api_route("/sdapi/v1/png-info", endpoints.post_pnginfo, methods=["POST"], response_model=models.ResImageInfo, tags=["Functional"])
self.add_api_route("/sdapi/v1/checkpoint", endpoints.get_checkpoint, methods=["GET"], tags=["Functional"])
self.add_api_route("/sdapi/v1/checkpoint", endpoints.set_checkpoint, methods=["POST"], tags=["Functional"])
+91
View File
@@ -1,4 +1,6 @@
from fastapi.exceptions import HTTPException
from modules import shared
from modules.logger import log
from modules.api import models, helpers
@@ -323,6 +325,95 @@ def get_extensions_list():
})
return ext_list
def get_file(file: str):
import os
from pathlib import Path
from starlette.responses import FileResponse
allowed_dirs = shared.demo.allowed_paths
if not file.strip():
raise HTTPException(status_code=400, detail="file path is required")
if not any(Path(folder).absolute() in Path(file).absolute().parents for folder in allowed_dirs):
raise HTTPException(status_code=403, detail=f"file {file}: must be in one of allowed directories")
if not os.path.exists(file):
raise HTTPException(status_code=404, detail=f"file not found: {file}")
if os.path.isdir(file):
raise HTTPException(status_code=403, detail=f"file {file}: is a directory")
return FileResponse(file, media_type='application/octet-stream', filename=file)
def get_deletefile(file: str):
import os
from pathlib import Path
allowed_dirs = shared.demo.allowed_paths
if file is None or len(file.strip()) == 0:
raise HTTPException(status_code=400, detail="file path is required")
if not any(Path(folder).absolute() in Path(file).absolute().parents for folder in allowed_dirs):
raise HTTPException(status_code=403, detail=f"file {file}: must be in one of allowed directories")
if not os.path.exists(file):
raise HTTPException(status_code=404, detail=f"file not found: {file}")
try:
if os.path.isdir(file):
log.warning(f'Delete: folder="{file}"')
import shutil
shutil.rmtree(file)
else:
log.warning(f'Delete: file="{file}"')
os.remove(file)
return {"deleted": f"{file}"}
except Exception as e:
log.error(f'Delete: file="{file}" error: {e}')
raise HTTPException(status_code=500, detail=f"error deleting file {file}: {str(e)}") from e
def get_deleteimage(file: str):
import os
from pathlib import Path
allowed_dirs = shared.demo.allowed_paths
if file is None or len(file.strip()) == 0:
raise HTTPException(status_code=400, detail="file path is required")
if not any(Path(folder).absolute() in Path(file).absolute().parents for folder in allowed_dirs):
raise HTTPException(status_code=403, detail=f"file {file}: must be in one of allowed directories")
if not os.path.exists(file):
raise HTTPException(status_code=404, detail=f"file not found: {file}")
if os.path.isdir(file):
raise HTTPException(status_code=403, detail=f"file {file}: is a directory")
if os.path.splitext(file)[1].lower() not in (".png", ".jpg", ".jpeg", ".webp"):
raise HTTPException(status_code=403, detail=f"file {file}: not an image file")
try:
os.remove(file)
log.warning(f'Delete: image="{file}"')
return {"deleted": f"{file}"}
except Exception as e:
log.error(f'Delete: file="{file}" error: {e}')
raise HTTPException(status_code=500, detail=f"error deleting file {file}: {str(e)}") from e
def get_pnginfo(file: str):
"""Extract generation parameters from a image file path. Returns raw info string and parsed parameters dict."""
import os
from pathlib import Path
from PIL import Image
from modules import images, infotext
allowed_dirs = shared.demo.allowed_paths
if not file.strip():
raise HTTPException(status_code=400, detail="file path is required")
if not any(Path(folder).absolute() in Path(file).absolute().parents for folder in allowed_dirs):
raise HTTPException(status_code=403, detail=f"file {file}: must be in one of allowed directories")
if os.path.splitext(file)[1].lower() not in (".png", ".jpg", ".jpeg", ".webp"):
raise HTTPException(status_code=403, detail=f"file {file}: not an image file")
if not os.path.isfile(file):
raise HTTPException(status_code=403, detail=f"file {file}: not an image file")
image = None
try:
image = Image.open(file)
image.load()
except Exception as e:
raise HTTPException(status_code=403, detail=f"file {file}: not an image file") from e
if image is None:
raise HTTPException(status_code=403, detail=f"file {file}: not an image file")
geninfo, items = images.read_info_from_image(image)
if geninfo is None:
geninfo = ""
params = infotext.parse(geninfo)
return models.ResImageInfo(info=geninfo, items=items, parameters=params)
def post_pnginfo(req: models.ReqImageInfo):
"""Extract generation parameters from a PNG image's metadata. Returns raw info string and parsed parameters dict."""
from modules import images, script_callbacks, infotext
+2 -2
View File
@@ -116,8 +116,8 @@ def get_progress(req: models.ReqProgress = Depends()):
progress = min((current / total) if current > 0 and total > 0 else 0, 1)
time_since_start = time.time() - shared.state.time_start
eta_relative = (time_since_start / progress) - time_since_start if progress > 0 else 0
# log.critical(f'get_progress: batch {batch_x}/{batch_y} step {step_x}/{step_y} current {current}/{total} time={time_since_start} eta={eta_relative}')
# log.critical(shared.state)
# log.trace(f'get_progress: batch {batch_x}/{batch_y} step {step_x}/{step_y} current {current}/{total} time={time_since_start} eta={eta_relative}')
# log.trace(shared.state)
res = models.ResProgress(id=shared.state.id, progress=round(progress, 2), eta_relative=round(eta_relative, 2), current_image=current_image, textinfo=shared.state.textinfo, state=shared.state.dict(), )
return res
+1
View File
@@ -5,6 +5,7 @@ from modules.logger import log
# value is cost: -1=disabled, 0=unlimited, 1=default, >1 expensive
request_cost = {
"/file": 0,
"/internal/progress": 0,
"/run/predict": 0,
"/sdapi/v1/browser/thumb": 0,
"/sdapi/v1/network/thumb": 0,
+1 -2
View File
@@ -1,6 +1,5 @@
# Vendored from JoyTag: https://huggingface.co/spaces/fancyfeast/joytag
# Contains full model architecture (ViT, CNN stems, MAE) including training-only code
# retained for update compatibility. Do not modify directly — sync from upstream.
import os
import math
@@ -942,7 +941,7 @@ class ViT(VisionModel):
loss_type: str,
layerscale_init: float | None = None,
head_mean_after: bool = False,
cnn_stem: str = None,
cnn_stem: str | None = None,
patch_dropout: float = 0.0,
):
super().__init__(image_size, n_tags)
+1 -1
View File
@@ -191,7 +191,7 @@ class CogView4CFGZeroPipeline(DiffusionPipeline, CogView4LoraLoaderMixin):
def _get_glm_embeds(
self,
prompt: Union[str, List[str]] = None,
prompt: Union[str, List[str]] | None = None,
max_sequence_length: int = 1024,
device: Optional[torch.device] = None,
dtype: Optional[torch.dtype] = None,
+4 -4
View File
@@ -217,7 +217,7 @@ class FluxCFGZeroPipeline(
def _get_t5_prompt_embeds(
self,
prompt: Union[str, List[str]] = None,
prompt: Union[str, List[str]] | None = None,
num_images_per_prompt: int = 1,
max_sequence_length: int = 512,
device: Optional[torch.device] = None,
@@ -535,7 +535,7 @@ class FluxCFGZeroPipeline(
@staticmethod
def _unpack_latents(latents, height, width, vae_scale_factor):
batch_size, num_patches, channels = latents.shape
batch_size, _num_patches, channels = latents.shape
# VAE applies 8x compression on images but we must also account for packing which requires
# latent height and width to be divisible by 2.
@@ -637,9 +637,9 @@ class FluxCFGZeroPipeline(
@replace_example_docstring(EXAMPLE_DOC_STRING)
def __call__(
self,
prompt: Union[str, List[str]] = None,
prompt: Union[str, List[str]] | None = None,
prompt_2: Optional[Union[str, List[str]]] = None,
negative_prompt: Union[str, List[str]] = None,
negative_prompt: Union[str, List[str]] | None = None,
negative_prompt_2: Optional[Union[str, List[str]]] = None,
true_cfg_scale: float = 1.0,
height: Optional[int] = None,
+3 -3
View File
@@ -211,7 +211,7 @@ class HiDreamImageCFGZeroPipeline(DiffusionPipeline, HiDreamImageLoraLoaderMixin
def _get_t5_prompt_embeds(
self,
prompt: Union[str, List[str]] = None,
prompt: Union[str, List[str]] | None = None,
max_sequence_length: int = 128,
device: Optional[torch.device] = None,
dtype: Optional[torch.dtype] = None,
@@ -285,7 +285,7 @@ class HiDreamImageCFGZeroPipeline(DiffusionPipeline, HiDreamImageLoraLoaderMixin
def _get_llama3_prompt_embeds(
self,
prompt: Union[str, List[str]] = None,
prompt: Union[str, List[str]] | None = None,
max_sequence_length: int = 128,
device: Optional[torch.device] = None,
dtype: Optional[torch.dtype] = None,
@@ -545,7 +545,7 @@ class HiDreamImageCFGZeroPipeline(DiffusionPipeline, HiDreamImageLoraLoaderMixin
@torch.no_grad()
def __call__(
self,
prompt: Union[str, List[str]] = None,
prompt: Union[str, List[str]] | None = None,
prompt_2: Optional[Union[str, List[str]]] = None,
prompt_3: Optional[Union[str, List[str]]] = None,
prompt_4: Optional[Union[str, List[str]]] = None,
+6 -6
View File
@@ -317,7 +317,7 @@ class HunyuanVideoCFGZeroPipeline(DiffusionPipeline, HunyuanVideoLoraLoaderMixin
def encode_prompt(
self,
prompt: Union[str, List[str]],
prompt_2: Union[str, List[str]] = None,
prompt_2: Union[str, List[str]] | None = None,
prompt_template: Dict[str, Any] = DEFAULT_PROMPT_TEMPLATE,
num_videos_per_prompt: int = 1,
prompt_embeds: Optional[torch.Tensor] = None,
@@ -481,15 +481,15 @@ class HunyuanVideoCFGZeroPipeline(DiffusionPipeline, HunyuanVideoLoraLoaderMixin
@replace_example_docstring(EXAMPLE_DOC_STRING)
def __call__(
self,
prompt: Union[str, List[str]] = None,
prompt_2: Union[str, List[str]] = None,
negative_prompt: Union[str, List[str]] = None,
negative_prompt_2: Union[str, List[str]] = None,
prompt: Union[str, List[str]] | None = None,
prompt_2: Union[str, List[str]] | None = None,
negative_prompt: Union[str, List[str]] | None = None,
negative_prompt_2: Union[str, List[str]] | None = None,
height: int = 720,
width: int = 1280,
num_frames: int = 129,
num_inference_steps: int = 50,
sigmas: List[float] = None,
sigmas: List[float] | None = None,
true_cfg_scale: float = 1.0,
guidance_scale: float = 6.0,
num_videos_per_prompt: Optional[int] = 1,
+3 -3
View File
@@ -246,7 +246,7 @@ class StableDiffusion3CFGZeroPipeline(DiffusionPipeline, SD3LoraLoaderMixin, Fro
def _get_t5_prompt_embeds(
self,
prompt: Union[str, List[str]] = None,
prompt: Union[str, List[str]] | None = None,
num_images_per_prompt: int = 1,
max_sequence_length: int = 256,
device: Optional[torch.device] = None,
@@ -786,7 +786,7 @@ class StableDiffusion3CFGZeroPipeline(DiffusionPipeline, SD3LoraLoaderMixin, Fro
@replace_example_docstring(EXAMPLE_DOC_STRING)
def __call__(
self,
prompt: Union[str, List[str]] = None,
prompt: Union[str, List[str]] | None = None,
prompt_2: Optional[Union[str, List[str]]] = None,
prompt_3: Optional[Union[str, List[str]]] = None,
height: Optional[int] = None,
@@ -813,7 +813,7 @@ class StableDiffusion3CFGZeroPipeline(DiffusionPipeline, SD3LoraLoaderMixin, Fro
callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
callback_on_step_end_tensor_inputs: List[str] = ["latents"],
max_sequence_length: int = 256,
skip_guidance_layers: List[int] = None,
skip_guidance_layers: List[int] | None = None,
skip_layer_guidance_scale: float = 2.8,
skip_layer_guidance_stop: float = 0.2,
skip_layer_guidance_start: float = 0.01,
+3 -3
View File
@@ -153,7 +153,7 @@ class WanCFGZeroPipeline(DiffusionPipeline, WanLoraLoaderMixin):
def _get_t5_prompt_embeds(
self,
prompt: Union[str, List[str]] = None,
prompt: Union[str, List[str]] | None = None,
num_videos_per_prompt: int = 1,
max_sequence_length: int = 226,
device: Optional[torch.device] = None,
@@ -374,8 +374,8 @@ class WanCFGZeroPipeline(DiffusionPipeline, WanLoraLoaderMixin):
@replace_example_docstring(EXAMPLE_DOC_STRING)
def __call__(
self,
prompt: Union[str, List[str]] = None,
negative_prompt: Union[str, List[str]] = None,
prompt: Union[str, List[str]] | None = None,
negative_prompt: Union[str, List[str]] | None = None,
height: int = 480,
width: int = 832,
num_frames: int = 81,
+322 -3
View File
@@ -323,10 +323,13 @@ class DownloadManager:
if version and version.images:
for img in version.images:
if img.url:
code, _size, _note = download_civit_preview(final_file, img.url)
code, _size, _note = download_civit_preview(final_file, img.url, meta=img.meta)
if code == 200:
log.info(f'CivitAI preview saved: id={item.id}')
break
if code == 304 and backfill_preview_parameters(final_file, img.url, img.meta):
log.info(f'CivitAI preview backfilled: id={item.id}')
break
except Exception as e:
log.warning(f'CivitAI preview fetch failed: id={item.id} {e}')
@@ -340,6 +343,306 @@ class DownloadManager:
download_manager = DownloadManager()
# ---- Preview metadata helpers ----
NOISE_META_KEYS = frozenset({'hashes', 'comfy', 'comfyui', 'workflow', 'extrametadata'})
PASSTHROUGH_VALUE_LIMIT = 512 # chars; pass-through values longer than this are dropped
def civitai_meta_to_parameters(meta: dict | None) -> str:
"""Convert Civitai version-image meta dict to sdnext parameters string.
Output matches sdnext's standard `parameters` channel (`modules/image/save.py:65-72`):
positive prompt on line one, optional `Negative prompt:` line two,
comma-joined `Key: Value` pairs on line three. Round-trippable through
`modules.infotext.parse`. Generator-specific noise keys (full ComfyUI
workflows, etc.) are dropped and any pass-through value exceeding
`PASSTHROUGH_VALUE_LIMIT` chars is omitted to keep the embedded chunk
compact.
"""
if not meta or not isinstance(meta, dict):
return ''
import json
from modules.infotext import quote
lower = {k.lower(): (k, v) for k, v in meta.items()}
def lookup(*keys):
for k in keys:
if k.lower() in lower:
return lower[k.lower()][1]
return None
prompt = lookup('prompt') or ''
negative = lookup('negativePrompt', 'negative_prompt', 'Negative prompt') or ''
pairs = []
mapping = [
(('steps',), 'Steps'),
(('sampler',), 'Sampler'),
(('cfgScale', 'cfg_scale', 'CFG scale'), 'CFG scale'),
(('seed',), 'Seed'),
(('Size',), 'Size'),
(('Model',), 'Model'),
(('Model hash', 'modelHash'), 'Model hash'),
(('clipSkip', 'clip_skip', 'Clip skip'), 'Clip skip'),
(('denoisingStrength', 'Denoising strength'), 'Denoising strength'),
]
consumed = {'prompt', 'negativeprompt', 'negative_prompt'}
for src_keys, out_key in mapping:
v = lookup(*src_keys)
if v is None or v == '':
continue
pairs.append(f'{out_key}: {quote(v)}')
for k in src_keys:
consumed.add(k.lower())
for k, v in meta.items():
if k.lower() in consumed:
continue
if k.lower() in NOISE_META_KEYS:
continue
if k in ('resources', 'civitaiResources'):
try:
pairs.append(f'Civitai resources: {quote(json.dumps(v, separators=(",", ":")))}')
except Exception:
pass
continue
if v is None or v == '':
continue
quoted = quote(v)
if len(str(quoted)) > PASSTHROUGH_VALUE_LIMIT:
continue
pairs.append(f'{k}: {quoted}')
lines = [str(prompt).strip()]
if negative:
lines.append(f'Negative prompt: {negative}')
if pairs:
lines.append(', '.join(pairs))
return '\n'.join(lines)
def fit_parameters_for_exif(parameters: str, limit: int = 30000) -> str:
"""Trim parameters string to fit JPEG/WEBP EXIF UserComment.
JPEG's APP1 segment caps at 64KB; UserComment is UTF-16-LE encoded so
each char takes 2 bytes. A 30000-char limit keeps the encoded payload
around 60KB with headroom for piexif overhead. Drops the
`Civitai resources` field first (typical bloat source) and truncates as
last resort. PNG callers don't need this since `tEXt` chunks are
unbounded.
"""
if len(parameters) <= limit:
return parameters
lines = parameters.split('\n')
if len(lines) >= 3:
parts = [p for p in lines[2].split(', ') if not p.startswith('Civitai resources:')]
lines[2] = ', '.join(parts)
parameters = '\n'.join(lines)
if len(parameters) <= limit:
return parameters
return parameters[:max(0, limit - 32)].rstrip() + '\n[truncated]'
def embed_preview_parameters(preview_file: str, parameters: str) -> bool:
"""Embed parameters string into preview image at `preview_file`.
PNG -> `tEXt` chunk with key `parameters`. JPEG/WEBP -> EXIF
`UserComment` via piexif. RGBA is converted to RGB before JPEG save.
Other extensions: no-op. Returns success.
Writes are atomic (temp file + os.replace). On success, any co-located
`<base>.thumb.jpg` is removed so the lazy thumb generator re-emits it
carrying the new params. Embed failures are swallowed and logged at
debug; the source file is removed only when PIL cannot read it back,
so the caller can re-download a fresh copy.
"""
if not preview_file or not parameters:
return False
ext = os.path.splitext(preview_file)[1].lower()
if ext not in ('.png', '.jpg', '.jpeg', '.webp'):
return False
tmp_file = preview_file + '.embed.tmp'
try:
from PIL import Image
img = Image.open(preview_file)
img.load()
try:
if ext == '.png':
from PIL import PngImagePlugin
pnginfo = PngImagePlugin.PngInfo()
pnginfo.add_text('parameters', parameters)
img.save(tmp_file, format='PNG', pnginfo=pnginfo)
else:
import piexif
import piexif.helper
payload = fit_parameters_for_exif(parameters)
exif_bytes = piexif.dump({'Exif': {piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(payload, encoding='unicode')}})
if ext in ('.jpg', '.jpeg'):
if img.mode != 'RGB':
img = img.convert('RGB')
img.save(tmp_file, format='JPEG', quality=95, exif=exif_bytes)
else:
img.save(tmp_file, format='WEBP', quality=95, exif=exif_bytes)
finally:
img.close()
os.replace(tmp_file, preview_file)
thumb_base = os.path.splitext(preview_file)[0]
if thumb_base.endswith('.preview'):
thumb_base = thumb_base[:-len('.preview')]
thumb_file = thumb_base + '.thumb.jpg'
if os.path.exists(thumb_file):
try:
os.remove(thumb_file)
log.debug(f'CivitAI thumb invalidated: file="{thumb_file}"')
except Exception:
pass
return True
except Exception as e:
try:
if os.path.exists(tmp_file):
os.remove(tmp_file)
except Exception:
pass
try:
from PIL import Image as PILImage
with PILImage.open(preview_file) as probe:
probe.verify()
except Exception:
try:
os.remove(preview_file)
log.warning(f'CivitAI preview removing invalid: image={preview_file}')
except Exception:
pass
log.debug(f'CivitAI preview embed failed: file="{preview_file}" {e}')
return False
def resolve_preview_file(item: dict) -> str | None:
"""Resolve the actual on-disk preview file for a network item.
`item['local_preview']` is the aspirational save path (e.g.
`<base>.<samples_format>`), not necessarily the existing file. This
helper extracts the real path from `item['preview']` URL (set by
`ExtraNetworksPage.link_preview`) and falls back to scanning common
extensions at the model base. Returns None when nothing on-disk
matches.
"""
preview_url = item.get('preview') or ''
if preview_url and 'missing.png' not in preview_url:
try:
import urllib.parse
parsed = urllib.parse.urlparse(preview_url)
fn = urllib.parse.parse_qs(parsed.query).get('filename', [None])[0]
if fn:
fn = urllib.parse.unquote(fn)
if os.path.isfile(fn):
return fn
except Exception:
pass
filename = item.get('filename')
if not filename:
return None
return find_ui_preview_file(filename)
def preview_has_parameters(preview_file: str) -> bool:
"""Check whether `preview_file` carries a non-empty embedded parameters string.
Mirrors `modules/image/metadata.py:read_info_from_image`: PNG
`image.info["parameters"]` or JPEG/WEBP EXIF `UserComment`. EXIF
UserComment is decoded via piexif.helper to verify a non-empty body;
the 8-byte `b"UNICODE\\x00"` header is present in any UserComment
even when its body is empty.
"""
if not preview_file or not os.path.exists(preview_file):
return False
try:
from PIL import Image
img = Image.open(preview_file)
try:
info = img.info or {}
for key in ('parameters', 'UserComment'):
value = info.get(key)
if value and str(value).strip():
return True
exif = info.get('exif')
if exif:
import piexif
import piexif.helper
try:
parsed = piexif.load(exif)
raw = parsed.get('Exif', {}).get(piexif.ExifIFD.UserComment)
if raw:
try:
decoded = piexif.helper.UserComment.load(raw)
except Exception:
decoded = ''
if decoded and decoded.strip():
return True
except Exception:
pass
finally:
img.close()
except Exception:
pass
return False
VIDEO_PREVIEW_EXTENSIONS = ('.mp4', '.webm')
UI_PREVIEW_EXTS = ('jpg', 'jpeg', 'png', 'webp')
UI_PREVIEW_MIDS = ('.thumb.', '.', '.preview.')
def find_ui_preview_file(model_path: str) -> str | None:
"""Return the preview file the modernUI surfaces for `model_path`.
Iteration mirrors `ExtraNetworksPage.find_preview` at
`modules/ui_extra_networks.py:488` so that backfill embeds into the
same file the UI reads.
"""
base = os.path.splitext(model_path)[0]
for ext in UI_PREVIEW_EXTS:
for mid in UI_PREVIEW_MIDS:
candidate = f'{base}{mid}{ext}'
if os.path.isfile(candidate):
return candidate
return None
def backfill_preview_parameters(model_path: str, preview_url: str, meta: dict | None) -> bool:
"""Embed Civitai meta into an existing preview file when it lacks parameters.
Used by the rescan path to retroactively populate preview metadata
without re-downloading bytes. The embed target is the file
`find_ui_preview_file` surfaces, which matches what the modernUI
displays. For video previews the embed target is the extracted
`<base>.thumb.jpg` frame instead of the unembeddable video file.
Returns True only if a new chunk was written; False for no-op (file
missing, no meta, already populated, embed failed).
"""
if not meta or not model_path or not preview_url:
return False
ext = os.path.splitext(preview_url)[1].lower()
base = os.path.splitext(model_path)[0]
if ext in VIDEO_PREVIEW_EXTENSIONS:
if not os.path.exists(base + ext):
return False
preview_file = base + '.thumb.jpg'
if not os.path.exists(preview_file):
return False
else:
preview_file = find_ui_preview_file(model_path)
if not preview_file:
return False
if preview_has_parameters(preview_file):
return False
parameters = civitai_meta_to_parameters(meta)
if not parameters:
return False
if embed_preview_parameters(preview_file, parameters):
log.info(f'CivitAI preview backfill: file="{preview_file}"')
return True
return False
# ---- Legacy compatibility functions ----
def download_civit_meta(model_path: str, model_id):
@@ -361,12 +664,12 @@ def download_civit_meta(model_path: str, model_id):
return r.status_code, '', ''
def download_civit_preview(model_path: str, preview_url: str):
def download_civit_preview(model_path: str, preview_url: str, meta: dict | None = None):
if model_path is None:
return 500, '', ''
ext = os.path.splitext(preview_url)[1]
preview_file = os.path.splitext(model_path)[0] + ext
is_video = preview_file.lower().endswith('.mp4')
is_video = preview_file.lower().endswith(VIDEO_PREVIEW_EXTENSIONS)
is_json = preview_file.lower().endswith('.json')
if is_json:
log.warning(f'CivitAI download: url="{preview_url}" skip json')
@@ -389,11 +692,27 @@ def download_civit_preview(model_path: str, preview_url: str):
if is_video:
from modules.civitai.video_helper import save_video_frame
save_video_frame(preview_file)
if meta:
thumb_file = os.path.splitext(preview_file)[0] + '.thumb.jpg'
if os.path.exists(thumb_file):
try:
parameters = civitai_meta_to_parameters(meta)
if parameters and embed_preview_parameters(thumb_file, parameters):
log.debug(f'CivitAI preview embed: file="{thumb_file}"')
except Exception as e:
log.debug(f'CivitAI preview embed skipped: file="{thumb_file}" {e}')
else:
from PIL import Image
img = Image.open(preview_file)
log.info(f'CivitAI download: url={preview_url} file="{preview_file}" size={total_size} image={img.size}')
img.close()
if meta:
try:
parameters = civitai_meta_to_parameters(meta)
if parameters and embed_preview_parameters(preview_file, parameters):
log.debug(f'CivitAI preview embed: file="{preview_file}"')
except Exception as e:
log.debug(f'CivitAI preview embed skipped: file="{preview_file}" {e}')
except Exception as e:
log.error(f'CivitAI download error: url={preview_url} file="{preview_file}" written={written} {e}')
shared.state.end(jobid)
+18 -5
View File
@@ -93,7 +93,7 @@ def civit_update_metadata(raw: bool = False):
model.latest_name = f.get('name', '')
if model.vername == model.latest:
model.status = 'Latest version'
elif any(map(lambda v: v in model.latest_hashes, all_hashes)): # pylint: disable=cell-var-from-loop # noqa: C417
elif any(map(lambda v: v in model.latest_hashes, all_hashes)): # pylint: disable=cell-var-from-loop
model.status = 'Update downloaded'
else:
model.status = 'Update available'
@@ -104,7 +104,7 @@ def civit_update_metadata(raw: bool = False):
def atomic_civit_search_metadata(item, results):
from modules.civitai.download_civitai import download_civit_preview, download_civit_meta
from modules.civitai.download_civitai import download_civit_preview, download_civit_meta, backfill_preview_parameters, preview_has_parameters, resolve_preview_file
if item is None:
return
try:
@@ -112,7 +112,12 @@ def atomic_civit_search_metadata(item, results):
except Exception:
return
has_meta = os.path.isfile(meta) and os.stat(meta).st_size > 0
if ('missing.png' in item['preview'] or not has_meta) and os.path.isfile(item['filename']):
needs_backfill = False
if has_meta and 'missing.png' not in item.get('preview', ''):
actual_preview = resolve_preview_file(item)
if actual_preview and not preview_has_parameters(actual_preview):
needs_backfill = True
if ('missing.png' in item['preview'] or not has_meta or needs_backfill) and os.path.isfile(item['filename']):
sha = item.get('hash', None)
found = False
result = {
@@ -136,11 +141,15 @@ def atomic_civit_search_metadata(item, results):
results.append(dict(result))
for img in version.images:
if img.url:
code, size, note = download_civit_preview(item['filename'], img.url)
code, size, note = download_civit_preview(item['filename'], img.url, meta=img.meta)
if code == 200:
results.append({**result, 'code': code, 'size': size, 'note': note, 'type': 'preview'})
found = True
break
if code == 304 and backfill_preview_parameters(item['filename'], img.url, img.meta):
results.append({**result, 'code': 200, 'size': '', 'note': 'metadata embedded', 'type': 'preview'})
found = True
break
else:
result['code'] = 404
time.sleep(0.25) # rate limiting
@@ -157,11 +166,15 @@ def atomic_civit_search_metadata(item, results):
results.append(dict(result))
for img in version.images:
if img.url:
code, size, note = download_civit_preview(item['filename'], img.url)
code, size, note = download_civit_preview(item['filename'], img.url, meta=img.meta)
if code == 200:
results.append({**result, 'code': code, 'size': size, 'note': note, 'type': 'preview'})
found = True
break
if code == 304 and backfill_preview_parameters(item['filename'], img.url, img.meta):
results.append({**result, 'code': 200, 'size': '', 'note': 'metadata embedded', 'type': 'preview'})
found = True
break
else:
result['code'] = 404
time.sleep(0.25) # rate limiting
+1 -1
View File
@@ -7,7 +7,7 @@ from modules.control.util import HWC3, resize_image
class CannyDetector:
def __call__(self, input_image=None, low_threshold=100, high_threshold=200, detect_resolution=512, image_resolution=512, output_type=None, **kwargs):
if "img" in kwargs:
warnings.warn("img is deprecated, please use `input_image=...` instead.", DeprecationWarning)
warnings.warn("img is deprecated, please use `input_image=...` instead.", DeprecationWarning, stacklevel=2)
input_image = kwargs.pop("img")
if input_image is None:
raise ValueError("input_image must be defined.")
+1 -1
View File
@@ -16,7 +16,7 @@ class DepthProDetector:
self.processor = processor
@classmethod
def from_pretrained(cls, pretrained_model_or_path: str = "apple/DepthPro-hf", cache_dir: str = None, local_files_only = False) -> "DepthProDetector":
def from_pretrained(cls, pretrained_model_or_path: str = "apple/DepthPro-hf", cache_dir: str | None = None, local_files_only = False) -> "DepthProDetector":
from transformers import AutoImageProcessor, DepthProForDepthEstimation
processor = AutoImageProcessor.from_pretrained(pretrained_model_or_path, cache_dir=cache_dir, local_files_only=local_files_only)
+1 -1
View File
@@ -33,7 +33,7 @@ class EdgeDetector:
params.PFmode = pf
ed.setParams(params)
if "img" in kwargs:
warnings.warn("img is deprecated, please use `input_image=...` instead.", DeprecationWarning)
warnings.warn("img is deprecated, please use `input_image=...` instead.", DeprecationWarning, stacklevel=2)
input_image = kwargs.pop("img")
if input_image is None:
raise ValueError("input_image must be defined.")
@@ -384,7 +384,7 @@ class SenceUnderstand(nn.Module):
self.initial_params()
def forward(self, x):
n, c, h, w = x.size()
n, _c, h, w = x.size()
x = self.conv1(x)
x = self.pool(x)
x = x.view(n, -1)
@@ -1,8 +1,8 @@
import argparse
import os
from ...pix2pix.util import util
from proc.leres.pix2pix.util import util
# import torch
from ...pix2pix import models
from proc.leres.pix2pix import models
# import pix2pix.data
import numpy as np
@@ -113,7 +113,7 @@ class MarigoldPipeline(DiffusionPipeline):
batch_size: int = 0,
color_map: str = "Spectral",
show_progress_bar: bool = True,
ensemble_kwargs: Dict = None,
ensemble_kwargs: Dict | None = None,
) -> MarigoldDepthOutput:
"""
Function invoked when calling the pipeline.
@@ -43,7 +43,7 @@ def ensemble_depths(
max_iter: int = 2,
tol: float = 1e-3,
reduction: str = "median",
max_res: int = None,
max_res: int | None = None,
):
"""
To ensemble multiple affine-invariant depth images (up to scale and shift),
@@ -28,6 +28,6 @@ def seed_all(seed: int = 0):
Set random seeds of all components.
"""
random.seed(seed)
np.random.seed(seed) # noqa
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
+2 -2
View File
@@ -54,7 +54,7 @@ class Transpose(nn.Module):
def forward_vit(pretrained, x):
b, c, h, w = x.shape
_b, _c, h, w = x.shape
pretrained.model.forward_flex(x)
@@ -115,7 +115,7 @@ def _resize_pos_embed(self, posemb, gs_h, gs_w):
def forward_flex(self, x):
b, c, h, w = x.shape
_b, _c, h, w = x.shape
pos_embed = self._resize_pos_embed(
self.pos_embed, h // self.patch_size[1], w // self.patch_size[0]
+2 -2
View File
@@ -75,7 +75,7 @@ def write_pfm(path, image, scale=1):
if len(image.shape) == 3 and image.shape[2] == 3: # color image
color = True
elif (
len(image.shape) == 2 or len(image.shape) == 3 and image.shape[2] == 1
len(image.shape) == 2 or (len(image.shape) == 3 and image.shape[2] == 1)
): # greyscale
color = False
else:
@@ -86,7 +86,7 @@ def write_pfm(path, image, scale=1):
endian = image.dtype.byteorder
if endian == "<" or endian == "=" and sys.byteorder == "little":
if endian == "<" or (endian == "=" and sys.byteorder == "little"):
scale = -scale
file.write("%f\n".encode() % scale)
+3 -3
View File
@@ -22,7 +22,7 @@ def deccode_output_score_and_ptss(tpMap, topk_n = 200, ksize = 5):
center: tpMap[1, 0, :, :]
displacement: tpMap[1, 1:5, :, :]
'''
b, c, h, w = tpMap.shape
b, _c, _h, w = tpMap.shape
assert b==1, 'only support bsize==1'
displacement = tpMap[:, 1:5, :, :][0]
center = tpMap[:, 0, :, :]
@@ -471,9 +471,9 @@ def pred_squares(image,
square[end_idx]
# check whether outside or inside
start_position, start_min, start_cover_param, start_peri_param = check_outside_inside(start_segments,
_start_position, start_min, start_cover_param, start_peri_param = check_outside_inside(start_segments,
connect_idx)
end_position, end_min, end_cover_param, end_peri_param = check_outside_inside(end_segments, connect_idx)
_end_position, end_min, end_cover_param, end_peri_param = check_outside_inside(end_segments, connect_idx)
cover += dist_segments[connect_idx] + start_cover_param * start_min + end_cover_param * end_min
perimeter += dist_segments[connect_idx] + start_peri_param * start_min + end_peri_param * end_min
+3 -3
View File
@@ -194,14 +194,14 @@ class OpenposeDetector:
def __call__(self, input_image, detect_resolution=512, image_resolution=512, include_body=True, include_hand=False, include_face=False, hand_and_face=None, output_type="pil", **kwargs):
self.to(devices.device)
if hand_and_face is not None:
warnings.warn("hand_and_face is deprecated. Use include_hand and include_face instead.", DeprecationWarning)
warnings.warn("hand_and_face is deprecated. Use include_hand and include_face instead.", DeprecationWarning, stacklevel=2)
include_hand = hand_and_face
include_face = hand_and_face
if "return_pil" in kwargs:
warnings.warn("return_pil is deprecated. Use output_type instead.", DeprecationWarning)
warnings.warn("return_pil is deprecated. Use output_type instead.", DeprecationWarning, stacklevel=2)
output_type = "pil" if kwargs["return_pil"] else "np"
if type(output_type) is bool:
warnings.warn("Passing `True` or `False` to `output_type` is deprecated and will raise an error in future versions")
warnings.warn("Passing `True` or `False` to `output_type` is deprecated and will raise an error in future versions", stacklevel=2)
if output_type:
output_type = "pil"
if not isinstance(input_image, np.ndarray):
+1 -1
View File
@@ -328,7 +328,7 @@ class Face(object):
def __call__(self, face_img):
device = next(iter(self.model.parameters())).device
H, W, C = face_img.shape
H, W, _C = face_img.shape
w_size = 384
x_data = torch.from_numpy(util.smart_resize(face_img, (w_size, w_size))).permute([2, 0, 1]) / 256.0 - 0.5
+1 -1
View File
@@ -32,7 +32,7 @@ class Hand(object):
wsize = 128
heatmap_avg = np.zeros((wsize, wsize, 22))
Hr, Wr, Cr = oriImgRaw.shape
Hr, Wr, _Cr = oriImgRaw.shape
oriImg = cv2.GaussianBlur(oriImgRaw, (0, 0), 0.8)
@@ -53,7 +53,7 @@ class SamDetector:
def __call__(self, input_image: Union[np.ndarray, Image.Image]=None, detect_resolution=512, image_resolution=512, output_type="pil", **kwargs) -> Image.Image:
if "image" in kwargs:
warnings.warn("image is deprecated, please use `input_image=...` instead.", DeprecationWarning)
warnings.warn("image is deprecated, please use `input_image=...` instead.", DeprecationWarning, stacklevel=2)
input_image = kwargs.pop("image")
if input_image is None:
raise ValueError("input_image must be defined.")
@@ -25,8 +25,8 @@ class Sam(nn.Module):
image_encoder: Union[ImageEncoderViT, TinyViT],
prompt_encoder: PromptEncoder,
mask_decoder: MaskDecoder,
pixel_mean: List[float] = None,
pixel_std: List[float] = None,
pixel_mean: List[float] | None = None,
pixel_std: List[float] | None = None,
) -> None:
"""
SAM predicts object masks from an image and input prompts.
@@ -79,7 +79,7 @@ class TwoWayTransformer(nn.Module):
torch.Tensor: the processed image_embedding
"""
# BxCxHxW -> BxHWxC == B x N_image_tokens x C
bs, c, h, w = image_embedding.shape
_bs, _c, _h, _w = image_embedding.shape
image_embedding = image_embedding.flatten(2).permute(0, 2, 1)
image_pe = image_pe.flatten(2).permute(0, 2, 1)
@@ -10,7 +10,7 @@ from torch.nn import functional as F
from typing import Tuple
from ..modeling import Sam
from proc.segment_anything.modeling import Sam
from .amg import calculate_stability_score
+5 -5
View File
@@ -10,10 +10,10 @@ from modules.control.util import HWC3, img2mask, make_noise_disk, resize_image
class ContentShuffleDetector:
def __call__(self, input_image, h=None, w=None, f=None, detect_resolution=512, image_resolution=512, output_type="pil", **kwargs):
if "return_pil" in kwargs:
warnings.warn("return_pil is deprecated. Use output_type instead.", DeprecationWarning)
warnings.warn("return_pil is deprecated. Use output_type instead.", DeprecationWarning, stacklevel=2)
output_type = "pil" if kwargs["return_pil"] else "np"
if type(output_type) is bool:
warnings.warn("Passing `True` or `False` to `output_type` is deprecated and will raise an error in future versions")
warnings.warn("Passing `True` or `False` to `output_type` is deprecated and will raise an error in future versions", stacklevel=2)
if output_type:
output_type = "pil"
@@ -49,7 +49,7 @@ class ContentShuffleDetector:
class ColorShuffleDetector:
def __call__(self, img):
H, W, C = img.shape
F = np.random.randint(64, 384) # noqa
F = np.random.randint(64, 384)
A = make_noise_disk(H, W, 3, F)
B = make_noise_disk(H, W, 3, F)
C = (A + B) / 2.0
@@ -82,11 +82,11 @@ class DownSampleDetector:
def __call__(self, img, level=3, k=16.0):
h = img.astype(np.float32)
for _ in range(level):
h += np.random.normal(loc=0.0, scale=k, size=h.shape) # noqa
h += np.random.normal(loc=0.0, scale=k, size=h.shape)
h = cv2.pyrDown(h)
for _ in range(level):
h = cv2.pyrUp(h)
h += np.random.normal(loc=0.0, scale=k, size=h.shape) # noqa
h += np.random.normal(loc=0.0, scale=k, size=h.shape)
return h.clip(0, 255).astype(np.uint8)
@@ -66,7 +66,7 @@ def attention_forward(self, x, resolution, shared_rel_pos_bias: Optional[torch.T
"""
Modification of timm.models.beit.py: Attention.forward to support arbitrary window sizes.
"""
B, N, C = x.shape
B, N, _C = x.shape
qkv_bias = torch.cat((self.q_bias, self.k_bias, self.v_bias)) if self.q_bias is not None else None
qkv = F.linear(input=x, weight=self.qkv.weight, bias=qkv_bias)
@@ -81,7 +81,7 @@ def forward_default(pretrained, x, function_name="forward_features"):
def forward_adapted_unflatten(pretrained, x, function_name="forward_features"):
b, c, h, w = x.shape
_b, _c, h, w = x.shape
exec(f"glob = pretrained.model.{function_name}(x)")
@@ -31,7 +31,7 @@ def _resize_pos_embed(self, posemb, gs_h, gs_w):
def forward_flex(self, x):
b, c, h, w = x.shape
_b, _c, h, w = x.shape
pos_embed = self._resize_pos_embed(
self.pos_embed, h // self.patch_size[1], w // self.patch_size[0]
@@ -100,7 +100,7 @@ class AttractorLayer(nn.Module):
A = self._net(x)
eps = 1e-3
A = A + eps
n, c, h, w = A.shape
n, _c, h, w = A.shape
A = A.view(n, self.n_attractors, 2, h, w)
A_normed = A / A.sum(dim=2, keepdim=True) # n, a, 2, h, w
A_normed = A[:, :, 0, ...] # n, na, h, w
@@ -177,7 +177,7 @@ class AttractorLayerUnnormed(nn.Module):
x = x + prev_b_embedding
A = self._net(x)
n, c, h, w = A.shape
_n, _c, h, w = A.shape
b_prev = nn.functional.interpolate(
b_prev, (h, w), mode='bilinear', align_corners=True)
@@ -146,7 +146,7 @@ class LinearSplitter(nn.Module):
S = self._net(x)
eps = 1e-3
S = S + eps
n, c, h, w = S.shape
n, _c, h, w = S.shape
S = S.view(n, self.prev_nbins, self.split_factor, h, w)
S_normed = S / S.sum(dim=2, keepdim=True) # fractional splits
@@ -26,13 +26,12 @@ import itertools
import torch
import torch.nn as nn
from ..depth_model import DepthModel
from ..base_models.midas import MidasCore
from ..layers.attractor import AttractorLayer, AttractorLayerUnnormed
from ..layers.dist_layers import ConditionalLogBinomial
from ..layers.localbins_layers import (Projector, SeedBinRegressor,
SeedBinRegressorUnnormed)
from ..model_io import load_state_from_resource
from proc.zoe.zoedepth.models.depth_model import DepthModel
from proc.zoe.zoedepth.models.base_models.midas import MidasCore
from proc.zoe.zoedepth.models.layers.attractor import AttractorLayer, AttractorLayerUnnormed
from proc.zoe.zoedepth.models.layers.dist_layers import ConditionalLogBinomial
from proc.zoe.zoedepth.models.layers.localbins_layers import Projector, SeedBinRegressor, SeedBinRegressorUnnormed
from proc.zoe.zoedepth.models.model_io import load_state_from_resource
class ZoeDepth(DepthModel):
@@ -139,7 +138,7 @@ class ZoeDepth(DepthModel):
- probs (torch.Tensor): Output probability distribution of shape (B, n_bins, H, W). Present only if return_probs is True
"""
b, c, h, w = x.shape
b, _c, h, w = x.shape
# print("input shape ", x.shape)
self.orig_input_width = w
self.orig_input_height = h
@@ -27,14 +27,13 @@ import itertools
import torch
import torch.nn as nn
from ..depth_model import DepthModel
from ..base_models.midas import MidasCore
from ..layers.attractor import AttractorLayer, AttractorLayerUnnormed
from ..layers.dist_layers import ConditionalLogBinomial
from ..layers.localbins_layers import (Projector, SeedBinRegressor,
SeedBinRegressorUnnormed)
from ..layers.patch_transformer import PatchTransformerEncoder
from ..model_io import load_state_from_resource
from proc.zoe.zoedepth.models.depth_model import DepthModel
from proc.zoe.zoedepth.models.base_models.midas import MidasCore
from proc.zoe.zoedepth.models.layers.attractor import AttractorLayer, AttractorLayerUnnormed
from proc.zoe.zoedepth.models.layers.dist_layers import ConditionalLogBinomial
from proc.zoe.zoedepth.models.layers.localbins_layers import Projector, SeedBinRegressor, SeedBinRegressorUnnormed
from proc.zoe.zoedepth.models.layers.patch_transformer import PatchTransformerEncoder
from proc.zoe.zoedepth.models.model_io import load_state_from_resource
class ZoeDepthNK(DepthModel):
def __init__(self, core, bin_conf, bin_centers_type="softplus", bin_embedding_dim=128,
@@ -173,10 +172,10 @@ class ZoeDepthNK(DepthModel):
- "bin_centers": Bin centers of shape (B, N, H, W). Present only if return_final_centers is True
- "probs": Bin probabilities of shape (B, N, H, W). Present only if return_probs is True
"""
b, c, h, w = x.shape
b, _c, h, w = x.shape
self.orig_input_width = w
self.orig_input_height = h
rel_depth, out = self.core(x, denorm=denorm, return_rel_depth=True)
_rel_depth, out = self.core(x, denorm=denorm, return_rel_depth=True)
outconv_activation = out[0]
btlnck = out[1]
+3 -1
View File
@@ -386,7 +386,9 @@ class Processor:
def preview(self):
import modules.ui_control_helpers as helpers
input_image = helpers.input_source
if input_image is None:
return []
if isinstance(input_image, list):
input_image = input_image[0]
debug('Control process preview')
return self.__call__(input_image)
return [self.__call__(input_image)]

Some files were not shown because too many files have changed in this diff Show More