mirror of
https://github.com/vladmandic/automatic
synced 2026-09-18 16:54:33 +02:00
+1
-1
@@ -51,7 +51,7 @@ build/
|
||||
dist/
|
||||
|
||||
# dynamically generated
|
||||
/repositories/ip-instruct/
|
||||
/repositories/deepseek-vl2/
|
||||
|
||||
# all dynamic stuff
|
||||
/extensions/**/*
|
||||
|
||||
+2
-2
@@ -23,5 +23,5 @@
|
||||
url = https://github.com/ArtVentureX/sd-webui-agent-scheduler
|
||||
ignore = dirty
|
||||
[submodule "extensions-builtin/sdnext-modernui"]
|
||||
path = extensions-builtin/sdnext-modernui
|
||||
url = https://github.com/BinaryQuantumSoul/sdnext-modernui
|
||||
path = extensions-builtin/sdnext-modernui
|
||||
url = https://github.com/BinaryQuantumSoul/sdnext-modernui
|
||||
|
||||
@@ -23,12 +23,14 @@ ignore-paths=/usr/lib/.*$,
|
||||
modules/k-diffusion,
|
||||
modules/ldsr,
|
||||
modules/meissonic,
|
||||
modules/mod,
|
||||
modules/omnigen,
|
||||
modules/onnx_impl,
|
||||
modules/pag,
|
||||
modules/pixelsmith,
|
||||
modules/prompt_parser_xhinker.py,
|
||||
modules/pulid/eva_clip,
|
||||
modules/ras,
|
||||
modules/rife,
|
||||
modules/schedulers,
|
||||
modules/taesd,
|
||||
|
||||
@@ -17,12 +17,14 @@ exclude = [
|
||||
"modules/k-diffusion",
|
||||
"modules/ldsr",
|
||||
"modules/meissonic",
|
||||
"modules/mod",
|
||||
"modules/omnigen",
|
||||
"modules/pag",
|
||||
"modules/pixelsmith",
|
||||
"modules/postprocess/aurasr_arch.py",
|
||||
"modules/prompt_parser_xhinker.py",
|
||||
"modules/pulid/eva_clip",
|
||||
"modules/ras",
|
||||
"modules/rife",
|
||||
"modules/schedulers",
|
||||
"modules/segmoe",
|
||||
|
||||
+141
@@ -1,5 +1,146 @@
|
||||
# Change Log for SD.Next
|
||||
|
||||
## Update for 2025-02-20
|
||||
|
||||
### Highlight for 2025-02-20
|
||||
|
||||
We're back with another update with nearly 100 commits!
|
||||
- Starting with massive UI update with full [localization](https://vladmandic.github.io/sdnext-docs/Locale/) for 8 languages
|
||||
and 100+ new [hints](https://vladmandic.github.io/sdnext-docs/Hints/)
|
||||
- Big update to [Docker](https://vladmandic.github.io/sdnext-docs/Docker/) containers
|
||||
with support for all major compute platforms
|
||||
- A lot of [outpainting](https://vladmandic.github.io/sdnext-docs/Outpaint/) goodies
|
||||
- Support for new models: [AlphaVLLM Lumina 2](https://github.com/Alpha-VLLM/Lumina-Image-2.0) and [Ostris Flex.1-Alpha](https://huggingface.co/ostris/Flex.1-alpha)
|
||||
- And new **Mixture-of-Diffusers** regional prompting & tiling pipeline
|
||||
- Follow-up to last weeks **interrogate/captioning** rewrite
|
||||
now with redesigned captioning UI, batch support, and much more
|
||||
plus **JoyTag**, **JoyCaption**, **PaliGemma**, **ToriiGate**, **Ovis2** added to list of supported models
|
||||
- Some changes to **prompt parsing** to allow more control as well as
|
||||
more flexibility when mouting SDNext server to custom URL
|
||||
- Of course, cumulative fixes...
|
||||
|
||||
*...and more* - see [changelog](https://github.com/vladmandic/sdnext/blob/dev/CHANGELOG.md) for full details!
|
||||
|
||||
### Details for 2025-02-20
|
||||
|
||||
- **User Interface**
|
||||
- **Hints**
|
||||
- added/updated 100+ ui hints!
|
||||
- [hints](https://vladmandic.github.io/sdnext-docs/Hints/) documentation and contribution guide
|
||||
- **Localization**
|
||||
- full ui localization!
|
||||
*english, croatian, spanish, french, italian, portuguese, chinese, japanese, korean, russian*
|
||||
- set in *settings -> user interface -> language*
|
||||
- [localization](https://vladmandic.github.io/sdnext-docs/Locale/) documentation
|
||||
- **UI**
|
||||
- force browser cache-invalidate on page load
|
||||
- configurable request timeout
|
||||
- modernui improve gallery styling
|
||||
- modernui improve networks styling
|
||||
- modernui support variable card size
|
||||
- **Docs**
|
||||
- New [Outpaint](https://vladmandic.github.io/sdnext-docs/Outpaint/) step-by-step guide
|
||||
- Updated [Docker](https://github.com/vladmandic/sdnext/wiki/Docker) guide
|
||||
includes build and publish and both local and cloud examples
|
||||
- **Models**
|
||||
- [AlphaVLLM Lumina 2](https://github.com/Alpha-VLLM/Lumina-Image-2.0)
|
||||
new foundation model for image generation based o Gemma-2-2B text encoder and a flow-based diffusion transformer
|
||||
fully supports offloading and on-the-fly quantization
|
||||
simply select from *networks -> models -> reference*
|
||||
- [Ostris Flex.1-Alpha](https://huggingface.co/ostris/Flex.1-alpha)
|
||||
originally based on *Flux.1-Schnell*, but retrained and with different architecture
|
||||
result is model smaller than *Flux.1-Dev*, but with similar capabilities
|
||||
fully supports offloading and on-the-fly quantization
|
||||
simply select from *networks -> models -> reference*
|
||||
- **Functions**
|
||||
- [Mixture-of-Diffusers](https://huggingface.co/posts/elismasilva/251775641926329)
|
||||
Regional tiling type of a solution for SDXL models
|
||||
select from *scripts -> mixture of diffusers*
|
||||
- [Automatic Color Inpaint]
|
||||
Automatically creates mask based on selected color and triggers inpaint
|
||||
simply select in *scripts -> automatic color inpaint* when in img2img mode
|
||||
- [RAS: Region-Adaptive Sampling](https://github.com/microsoft/RAS) *experimental*
|
||||
Speeds up SD3.5 models by sampling only regions of interest
|
||||
Enable in *settings -> pipeline modifiers -> ras*
|
||||
- **Interrogate/Captioning**
|
||||
- Redesigned captioning UI
|
||||
split from Process tab into separate tab
|
||||
split `clip` vs `vlm` models processing
|
||||
direct *send-to* buttons on all tabs: txt/img/ctrl->process/caption, process/caption->txt/img/ctrl
|
||||
- Advanced params:
|
||||
VLM: *max-tokens, num-beams, temperature, top-k, top-p, do-sample*
|
||||
CLiP: *min-length, max-length, chunk-size, min-flavors, max-flavors, flavor-count, num-beams*
|
||||
params are auto-saved in `config.json` and used when using quick interrogate
|
||||
params that are set to 0 mean use model defaults
|
||||
- Batch processing: VLM and CLiP
|
||||
for example, can be used to caption your training dataset in one go
|
||||
add option to append to captions file, can be used to run multiple captioning models in sequence
|
||||
add option to run recursively on all subfolders
|
||||
add progress bar
|
||||
- Add additional VLM models:
|
||||
[JoyTag](https://huggingface.co/fancyfeast/joytag)
|
||||
[JoyCaption 2](https://huggingface.co/fancyfeast/llama-joycaption-alpha-two-hf-llava)
|
||||
[Google PaliGemma 2](https://huggingface.co/google/paligemma2-3b-pt-224) 3B
|
||||
[ToriiGate 0.4](https://huggingface.co/Minthy/ToriiGate-v0.4-7B) 7B
|
||||
[AIDC Ovis2](https://huggingface.co/AIDC-AI/Ovis2-1B) 1B/2B/4B
|
||||
- *Note* some models require `flash-attn` to be installed
|
||||
due to binary/build dependencies, it should not be done automatically,
|
||||
see [flash-attn](https://github.com/Dao-AILab/flash-attention) for installation instructions
|
||||
- **Docker**
|
||||
- updated **CUDA** receipe to `torch==2.6.0` with `cuda==12.6` and add prebuilt image
|
||||
- added **ROCm** receipe and prebuilt image
|
||||
- added **IPEX** receipe and add prebuilt image
|
||||
- added **OpenVINO** receipe and prebuilt image
|
||||
- **System**
|
||||
- improve **python==3.12** compatibility
|
||||
- **Torch**
|
||||
- for **zluda** set default to `torch==2.6.0+cu118`
|
||||
- for **openvino** set default to `torch==2.6.0+cpu`
|
||||
- **OpenVINO**
|
||||
- update to `openvino==2025.0.0`
|
||||
- improve upscaler compatibility
|
||||
- enable upscaler compile by default
|
||||
- fix shape mismatch errors on too many resolution changes
|
||||
- **ZLUDA**
|
||||
- update to `zluda==3.8.8`
|
||||
- **Other**
|
||||
- **Asymmetric tiling**
|
||||
allows for configurable image tiling for x/y axis separately
|
||||
enable in *scripts -> asymmetric tiling*
|
||||
*note*: traditional symmetric tiling is achieved by setting circular mode for both x and y
|
||||
- **Styles**
|
||||
ability to save and/or restore prompts before or after parsing of wildcards
|
||||
set in *settings -> networks -> styles*
|
||||
- **Access tokens**
|
||||
persist *models -> hugginface -> token*
|
||||
persist *models -> civitai -> token*
|
||||
- global switch to lancosz method for all interal resize ops and bicubic for interpolation ops
|
||||
- **Text encoder**
|
||||
add advanced per-model options for text encoder
|
||||
set in *settings -> text encoder -> Optional*
|
||||
- **Subpath**
|
||||
allow setting additional mount subpath over which server url will be accessible
|
||||
set in *settings -> user interface*
|
||||
- **Prompt parsing**
|
||||
better handling of prompt parsing when using masking char `\`
|
||||
- **Fixes**
|
||||
- update torch nightly urls
|
||||
- docs/wiki always use relative links
|
||||
- ui use correct timezone for log display
|
||||
- ui improve settings search behavior
|
||||
- ui log scroll to bottom
|
||||
- ui fix send to inpaint/sketch
|
||||
- modernui add control init image toggle
|
||||
- modernui fix sampler advanced options
|
||||
- outpaint fixes
|
||||
- validate output before hires/refine
|
||||
- scheduler fix sigma index out of bounds
|
||||
- force pydantic version reinstall/reload
|
||||
- multi-unit when using controlnet-union
|
||||
- pulid with hidiffusion
|
||||
- api: stricter access control
|
||||
- api: universal handle mount subpaths
|
||||
|
||||
## Update for 2025-02-05
|
||||
|
||||
- refresh dev/master branches
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
## SD.Next Features
|
||||
|
||||
All individual features are not listed here, instead check [ChangeLog](CHANGELOG.md) for full list of changes
|
||||
- Fully localized:
|
||||
▹ **English | Chinese | Russian | Spanish | German | French | Italian | Portuguese | Japanese | Korean**
|
||||
- Multiple UIs!
|
||||
▹ **Standard | Modern**
|
||||
- Multiple [diffusion models](https://vladmandic.github.io/sdnext-docs/Model-Support/)!
|
||||
@@ -34,6 +36,7 @@ All individual features are not listed here, instead check [ChangeLog](CHANGELOG
|
||||
- Optimized processing with latest `torch` developments with built-in support for model compile, quantize and compress
|
||||
Compile backends: *Triton | StableFast | DeepCache | OneDiff | TeaCache | etc.*
|
||||
Quantization and compression methods: *BitsAndBytes | TorchAO | Optimum-Quanto | NNCF*
|
||||
- **Interrogate/Captioning** with 150+ **OpenCLiP** models and 20+ built-in **VLMs**
|
||||
- Built-in queue management
|
||||
- Built in installer with automatic updates and dependency management
|
||||
- Mobile compatible
|
||||
@@ -68,6 +71,8 @@ SD.Next supports broad range of models: [supported models](https://vladmandic.gi
|
||||
- *ONNX/Olive*
|
||||
- *AMD* GPUs on Windows using **ZLUDA** libraries
|
||||
|
||||
Plus Docker container receipes for: [CUDA, ROCm, Intel IPEX and OpenVINO](https://vladmandic.github.io/sdnext-docs/Docker/)
|
||||
|
||||
## Getting started
|
||||
|
||||
- Get started with **SD.Next** by following the [installation instructions](https://vladmandic.github.io/sdnext-docs/Installation/)
|
||||
|
||||
@@ -2,29 +2,26 @@
|
||||
|
||||
Main ToDo list can be found at [GitHub projects](https://github.com/users/vladmandic/projects)
|
||||
|
||||
## Pending
|
||||
|
||||
- LoRA direct with caching
|
||||
- Previewer issues
|
||||
- Redesign postprocessing
|
||||
|
||||
## Future Candidates
|
||||
|
||||
- Flux NF4 loader: <https://github.com/huggingface/diffusers/issues/9996>
|
||||
- IPAdapter negative: <https://github.com/huggingface/diffusers/discussions/7167>
|
||||
- Control API enhance scripts compatibility
|
||||
- PixelSmith: <https://github.com/Thanos-DB/Pixelsmith>
|
||||
- Redesign postprocessing
|
||||
- Flux NF4 loader: <https://github.com/huggingface/diffusers/issues/9996>
|
||||
- IPAdapter negative: <https://github.com/huggingface/diffusers/discussions/7167>
|
||||
- Control API enhance scripts compatibility
|
||||
- CogView4
|
||||
|
||||
## Code TODO
|
||||
|
||||
- TODO install: enable ROCm for windows when available
|
||||
- TODO resize image: enable full VAE mode for resize-latent
|
||||
- TODO processing: remove duplicate mask params
|
||||
- TODO flux: fix loader for civitai nf4 models
|
||||
- TODO model loader: implement model in-memory caching
|
||||
- TODO hypertile: vae breaks when using non-standard sizes
|
||||
- TODO model load: force-reloading entire model as loading transformers only leads to massive memory usage
|
||||
- TODO lora load: direct with bnb
|
||||
- TODO lora make: support quantized flux
|
||||
- TODO control: support scripts via api
|
||||
- TODO modernui: monkey-patch for missing tabs.select event
|
||||
- flux: loader for civitai nf4 models (fixme)
|
||||
- hypertile: vae breaks when using non-standard sizes (fixme)
|
||||
- install: enable ROCm for windows when available (fixme)
|
||||
- lora make support quantized flux (fixme)
|
||||
- lora: add other quantization types (fixme)
|
||||
- model load: force-reloading entire model as loading transformers only leads to massive memory usage (fixme)
|
||||
- model loader: implement model in-memory caching (fixme)
|
||||
- modernui: monkey-patch for missing tabs.select event (fixme)
|
||||
- processing: remove duplicate mask params (fixme)
|
||||
- resize image: enable full VAE mode for resize-latent (fixme)
|
||||
- sana: fails when quantized (fixme)
|
||||
- support scripts via api (fixme)
|
||||
- transformer from-single-file with quant (fixme)
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import torch
|
||||
import diffusers
|
||||
import safetensors
|
||||
import safetensors.torch as sf
|
||||
|
||||
log = logging.getLogger("sd")
|
||||
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s | %(message)s')
|
||||
|
||||
|
||||
def convert(model_id, output_name):
|
||||
if os.path.exists(output_name):
|
||||
log.error(f'Output already exists: {output_name}')
|
||||
return
|
||||
pipe = diffusers.DiffusionPipeline.from_pretrained(model_id)
|
||||
metadata = { 'model_id': model_id }
|
||||
model = {}
|
||||
model['state_dict'] = vars(pipe)['_internal_dict']
|
||||
for k in model['state_dict'].keys():
|
||||
# print(k, getattr(pipe, k))
|
||||
model[k] = getattr(pipe, k)
|
||||
sf.save_model(model, output_name, metadata=metadata)
|
||||
# log.info(f'Saved model: {output_name}')
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.argv.pop(0)
|
||||
if len(sys.argv) < 2:
|
||||
log.info('Usage: hf-convert.py <model_id> <output_name>')
|
||||
sys.exit(1)
|
||||
log.debug(f'Packages: torch={torch.__version__} diffusers={diffusers.__version__} safetensors={safetensors.__version__}')
|
||||
convert(sys.argv[0], sys.argv[1])
|
||||
@@ -29,4 +29,3 @@ if __name__ == "__main__":
|
||||
print('=== BEGIN ===')
|
||||
print(f'{b64}')
|
||||
print('=== END ===')
|
||||
|
||||
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Remove the entries that no longer exist in locale from override.
|
||||
|
||||
import sys
|
||||
import json
|
||||
from rich import print # pylint: disable=redefined-builtin
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.argv.pop(0)
|
||||
if len(sys.argv) == 0:
|
||||
print('Invalid parameters.')
|
||||
sys.exit(1)
|
||||
filename = sys.argv[0]
|
||||
labels = []
|
||||
override = None
|
||||
try:
|
||||
with open('html/locale_en.json', 'r', encoding="utf-8") as f:
|
||||
locale = json.load(f)
|
||||
for v in locale.values():
|
||||
for item in v:
|
||||
labels.append(item['label'])
|
||||
with open(filename, 'r', encoding="utf-8") as f:
|
||||
override = json.load(f)
|
||||
except Exception:
|
||||
print('Invalid file format.')
|
||||
sys.exit(1)
|
||||
with open(filename, 'w', encoding="utf-8") as f:
|
||||
json.dump([item for item in override if item['label'] in labels], f, ensure_ascii=False)
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env node
|
||||
// script used to localize sdnext ui and hints to multiple languages using google gemini ai
|
||||
|
||||
const fs = require('fs');
|
||||
const process = require('process');
|
||||
const { GoogleGenerativeAI } = require('@google/generative-ai');
|
||||
|
||||
const api_key = process.env.GOOGLE_AI_API_KEY;
|
||||
const model = 'gemini-2.0-flash-exp';
|
||||
const prompt = `
|
||||
Translate attached JSON from English to {language} using following rules: fields id and label should be preserved from original, field localized should be a translated version of field label and field hint should be translated in-place.
|
||||
Every JSON entry should have id, label, localized and hint fields. Output should be pure JSON without any additional text. To better match translation, context of the text is related to Stable Diffusion and topic of Generative AI.`;
|
||||
const languages = {
|
||||
hr: 'Croatian',
|
||||
de: 'German',
|
||||
es: 'Spanish',
|
||||
fr: 'French',
|
||||
it: 'Italian',
|
||||
pt: 'Portuguese',
|
||||
zh: 'Chinese',
|
||||
ja: 'Japanese',
|
||||
ko: 'Korean',
|
||||
ru: 'Russian',
|
||||
};
|
||||
const chunkLines = 100;
|
||||
|
||||
async function localize() {
|
||||
if (!api_key || api_key.length < 10) {
|
||||
console.error('localize: set GOOGLE_AI_API_KEY env variable with your API key');
|
||||
process.exit();
|
||||
}
|
||||
const genAI = new GoogleGenerativeAI(api_key);
|
||||
const instance = genAI.getGenerativeModel({ model });
|
||||
const raw = fs.readFileSync('html/locale_en.json');
|
||||
const json = JSON.parse(raw);
|
||||
for (const locale of Object.keys(languages)) {
|
||||
const lang = languages[locale];
|
||||
const target = prompt.replace('{language}', lang).trim();
|
||||
const output = {};
|
||||
const fn = `html/locale_${locale}.json`;
|
||||
for (const section of Object.keys(json)) {
|
||||
const data = json[section];
|
||||
output[section] = [];
|
||||
for (let i = 0; i < data.length; i += chunkLines) {
|
||||
let markdown;
|
||||
try {
|
||||
const chunk = data.slice(i, i + chunkLines);
|
||||
const result = await instance.generateContent([target, JSON.stringify(chunk)]);
|
||||
markdown = result.response.text();
|
||||
const text = markdown.replaceAll('```', '').replace(/^.*\n/, '');
|
||||
const parsed = JSON.parse(text);
|
||||
output[section].push(...parsed);
|
||||
console.log(`localize: locale=${locale} lang=${lang} section=${section} chunk=${chunk.length} output=${output[section].length} fn=${fn}`);
|
||||
} catch (err) {
|
||||
console.error('localize:', err);
|
||||
console.error('localize input:', { target, section, i });
|
||||
console.error('localize output:', { markdown });
|
||||
}
|
||||
}
|
||||
const txt = JSON.stringify(output, null, 2);
|
||||
fs.writeFileSync(fn, txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
localize();
|
||||
@@ -7,7 +7,7 @@ from rich import print # pylint: disable=redefined-builtin
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.argv.pop(0)
|
||||
fn = sys.argv[0] if len(sys.argv) > 0 else 'locale_en.json'
|
||||
fn = sys.argv[0] if len(sys.argv) > 0 else 'html/locale_en.json'
|
||||
if not os.path.isfile(fn):
|
||||
print(f'File not found: {fn}')
|
||||
sys.exit(1)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# docs: <https://github.com/vladmandic/sdnext/wiki/Docker>
|
||||
|
||||
# base image
|
||||
FROM pytorch/pytorch:2.5.1-cuda12.4-cudnn9-runtime
|
||||
FROM pytorch/pytorch:2.6.0-cuda12.6-cudnn9-runtime
|
||||
|
||||
# metadata
|
||||
LABEL org.opencontainers.image.vendor="SD.Next"
|
||||
@@ -13,7 +13,7 @@ LABEL org.opencontainers.image.source="https://github.com/vladmandic/sdnext/"
|
||||
LABEL org.opencontainers.image.licenses="AGPL-3.0"
|
||||
LABEL org.opencontainers.image.title="SD.Next"
|
||||
LABEL org.opencontainers.image.description="SD.Next: Advanced Implementation of Stable Diffusion and other Diffusion-based generative image models"
|
||||
LABEL org.opencontainers.image.base.name="https://hub.docker.com/pytorch/pytorch:2.5.1-cuda12.4-cudnn9-runtime"
|
||||
LABEL org.opencontainers.image.base.name="https://hub.docker.com/pytorch/pytorch:2.6.0-cuda12.6-cudnn9-runtime"
|
||||
LABEL org.opencontainers.image.version="latest"
|
||||
|
||||
# minimum install
|
||||
@@ -29,8 +29,8 @@ WORKDIR /app
|
||||
|
||||
# stop pip and uv from caching
|
||||
ENV PIP_NO_CACHE_DIR=true
|
||||
ENV PIP_ROOT_USER_ACTION=ignore
|
||||
ENV UV_NO_CACHE=true
|
||||
ENV PIP_ROOT_USER_ACTION=ignore
|
||||
# disable model hashing for faster startup
|
||||
ENV SD_NOHASHING=true
|
||||
# set data directories
|
||||
@@ -0,0 +1,98 @@
|
||||
# SD.Next IPEX Dockerfile
|
||||
# docs: <https://github.com/vladmandic/sdnext/wiki/Docker>
|
||||
|
||||
# base image
|
||||
FROM ubuntu:noble
|
||||
|
||||
# metadata
|
||||
LABEL org.opencontainers.image.vendor="SD.Next"
|
||||
LABEL org.opencontainers.image.authors="disty0"
|
||||
LABEL org.opencontainers.image.url="https://github.com/vladmandic/sdnext/"
|
||||
LABEL org.opencontainers.image.documentation="https://github.com/vladmandic/sdnext/wiki/Docker"
|
||||
LABEL org.opencontainers.image.source="https://github.com/vladmandic/sdnext/"
|
||||
LABEL org.opencontainers.image.licenses="AGPL-3.0"
|
||||
LABEL org.opencontainers.image.title="SD.Next IPEX"
|
||||
LABEL org.opencontainers.image.description="SD.Next: Advanced Implementation of Stable Diffusion and other Diffusion-based generative image models"
|
||||
LABEL org.opencontainers.image.base.name="https://hub.docker.com/_/ubuntu:noble"
|
||||
LABEL org.opencontainers.image.version="latest"
|
||||
|
||||
# essentials
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends --fix-missing \
|
||||
software-properties-common \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
wget \
|
||||
gpg \
|
||||
git
|
||||
|
||||
# intel compute runtime
|
||||
RUN wget -qO - https://repositories.intel.com/gpu/intel-graphics.key | gpg --yes --dearmor --output /usr/share/keyrings/intel-graphics.gpg
|
||||
RUN echo "deb [arch=amd64,i386 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/gpu/ubuntu noble client" | tee /etc/apt/sources.list.d/intel-gpu-noble.list
|
||||
RUN apt-get update
|
||||
|
||||
RUN apt-get install -y --no-install-recommends --fix-missing \
|
||||
intel-opencl-icd \
|
||||
libze-intel-gpu1 \
|
||||
libze1
|
||||
|
||||
# required by pytorch / ipex
|
||||
RUN apt-get install -y --no-install-recommends --fix-missing \
|
||||
libgl1 \
|
||||
libglib2.0-0 \
|
||||
libgomp1
|
||||
|
||||
# python3.12
|
||||
RUN apt-get install -y --no-install-recommends --fix-missing \
|
||||
python3 \
|
||||
python3-dev \
|
||||
python3-venv \
|
||||
python3-pip
|
||||
|
||||
# jemalloc is not required but it is highly recommended (also used with optional ipexrun)
|
||||
RUN apt-get install -y --no-install-recommends --fix-missing libjemalloc-dev
|
||||
ENV LD_PRELOAD=libjemalloc.so.2
|
||||
|
||||
# cleanup
|
||||
RUN /usr/sbin/ldconfig
|
||||
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# stop pip and uv from caching
|
||||
ENV PIP_NO_CACHE_DIR=true
|
||||
ENV UV_NO_CACHE=true
|
||||
|
||||
# set paths to use with sdnext
|
||||
ENV SD_DOCKER=true
|
||||
ENV SD_DATADIR="/mnt/data"
|
||||
ENV SD_MODELSDIR="/mnt/models"
|
||||
ENV venv_dir="/mnt/python/venv"
|
||||
|
||||
# paths used by sdnext can be a volume if necessary
|
||||
#VOLUME [ "/app" ]
|
||||
#VOLUME [ "/mnt/data" ]
|
||||
#VOLUME [ "/mnt/models" ]
|
||||
#VOLUME [ "/mnt/python" ]
|
||||
#VOLUME [ "/root/.cache/huggingface" ]
|
||||
|
||||
# intel specific environment variables
|
||||
#ENV IPEX_SDPA_SLICE_TRIGGER_RATE=1
|
||||
#ENV IPEX_ATTENTION_SLICE_RATE=0.5
|
||||
#ENV IPEX_FORCE_ATTENTION_SLICE=-1
|
||||
#ENV IPEXRUN=False
|
||||
|
||||
# git clone and run sdnext
|
||||
RUN echo '#!/bin/bash\ngit status || git clone https://github.com/vladmandic/sdnext.git .\n/app/webui.sh "$@"' | tee /bin/startup.sh
|
||||
RUN chmod 755 /bin/startup.sh
|
||||
|
||||
# actually run sdnext
|
||||
WORKDIR /app
|
||||
ENTRYPOINT [ "startup.sh", "-f", "--use-ipex", "--uv", "--listen", "--debug", "--api-log", "--log", "sdnext.log" ]
|
||||
|
||||
# expose port
|
||||
EXPOSE 7860
|
||||
|
||||
# healthcheck function
|
||||
# HEALTHCHECK --interval=60s --timeout=10s --start-period=60s --retries=3 CMD curl --fail http://localhost:7860/sdapi/v1/status || exit 1
|
||||
|
||||
# stop signal
|
||||
STOPSIGNAL SIGINT
|
||||
@@ -0,0 +1,88 @@
|
||||
# SD.Next OpenVINO Dockerfile
|
||||
# docs: <https://github.com/vladmandic/sdnext/wiki/Docker>
|
||||
|
||||
# base image
|
||||
FROM ubuntu:noble
|
||||
|
||||
# metadata
|
||||
LABEL org.opencontainers.image.vendor="SD.Next"
|
||||
LABEL org.opencontainers.image.authors="disty0"
|
||||
LABEL org.opencontainers.image.url="https://github.com/vladmandic/sdnext/"
|
||||
LABEL org.opencontainers.image.documentation="https://github.com/vladmandic/sdnext/wiki/Docker"
|
||||
LABEL org.opencontainers.image.source="https://github.com/vladmandic/sdnext/"
|
||||
LABEL org.opencontainers.image.licenses="AGPL-3.0"
|
||||
LABEL org.opencontainers.image.title="SD.Next OpenVINO"
|
||||
LABEL org.opencontainers.image.description="SD.Next: Advanced Implementation of Stable Diffusion and other Diffusion-based generative image models"
|
||||
LABEL org.opencontainers.image.base.name="https://hub.docker.com/_/ubuntu:noble"
|
||||
LABEL org.opencontainers.image.version="latest"
|
||||
|
||||
# essentials
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends --fix-missing \
|
||||
software-properties-common \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
wget \
|
||||
gpg \
|
||||
git
|
||||
|
||||
# intel compute runtime
|
||||
RUN wget -qO - https://repositories.intel.com/gpu/intel-graphics.key | gpg --yes --dearmor --output /usr/share/keyrings/intel-graphics.gpg
|
||||
RUN echo "deb [arch=amd64,i386 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/gpu/ubuntu noble client" | tee /etc/apt/sources.list.d/intel-gpu-noble.list
|
||||
RUN apt-get update
|
||||
|
||||
RUN apt-get install -y --no-install-recommends --fix-missing \
|
||||
intel-opencl-icd \
|
||||
libze-intel-gpu1 \
|
||||
libze1
|
||||
|
||||
# required by pytorch / ipex
|
||||
RUN apt-get install -y --no-install-recommends --fix-missing \
|
||||
libgl1 \
|
||||
libglib2.0-0 \
|
||||
libgomp1
|
||||
|
||||
# python3.12
|
||||
RUN apt-get install -y --no-install-recommends --fix-missing \
|
||||
python3 \
|
||||
python3-dev \
|
||||
python3-venv \
|
||||
python3-pip
|
||||
|
||||
# cleanup
|
||||
RUN /usr/sbin/ldconfig
|
||||
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# stop pip and uv from caching
|
||||
ENV PIP_NO_CACHE_DIR=true
|
||||
ENV UV_NO_CACHE=true
|
||||
|
||||
# set paths to use with sdnext
|
||||
ENV SD_DOCKER=true
|
||||
ENV SD_DATADIR="/mnt/data"
|
||||
ENV SD_MODELSDIR="/mnt/models"
|
||||
ENV venv_dir="/mnt/python/venv"
|
||||
|
||||
# paths used by sdnext can be a volume if necessary
|
||||
#VOLUME [ "/app" ]
|
||||
#VOLUME [ "/mnt/data" ]
|
||||
#VOLUME [ "/mnt/models" ]
|
||||
#VOLUME [ "/mnt/python" ]
|
||||
#VOLUME [ "/root/.cache/huggingface" ]
|
||||
|
||||
# git clone and run sdnext
|
||||
RUN echo '#!/bin/bash\ngit status || git clone https://github.com/vladmandic/sdnext.git .\n/app/webui.sh "$@"' | tee /bin/startup.sh
|
||||
RUN chmod 755 /bin/startup.sh
|
||||
|
||||
# actually run sdnext
|
||||
WORKDIR /app
|
||||
ENTRYPOINT [ "startup.sh", "-f", "--use-openvino", "--uv", "--listen", "--debug", "--api-log", "--log", "sdnext.log" ]
|
||||
|
||||
# expose port
|
||||
EXPOSE 7860
|
||||
|
||||
# healthcheck function
|
||||
# HEALTHCHECK --interval=60s --timeout=10s --start-period=60s --retries=3 CMD curl --fail http://localhost:7860/sdapi/v1/status || exit 1
|
||||
|
||||
# stop signal
|
||||
STOPSIGNAL SIGINT
|
||||
@@ -0,0 +1,84 @@
|
||||
# SD.Next ROCm Dockerfile
|
||||
# docs: <https://github.com/vladmandic/sdnext/wiki/Docker>
|
||||
|
||||
# base image
|
||||
|
||||
# rocm runtime (3gb)
|
||||
FROM rocm/dev-ubuntu-24.04:6.3.2
|
||||
|
||||
# rocm complete (32gb), required to build flash_atten
|
||||
#FROM rocm/dev-ubuntu-24.04:6.3.2-complete
|
||||
|
||||
# metadata
|
||||
LABEL org.opencontainers.image.vendor="SD.Next"
|
||||
LABEL org.opencontainers.image.authors="disty0"
|
||||
LABEL org.opencontainers.image.url="https://github.com/vladmandic/sdnext/"
|
||||
LABEL org.opencontainers.image.documentation="https://github.com/vladmandic/sdnext/wiki/Docker"
|
||||
LABEL org.opencontainers.image.source="https://github.com/vladmandic/sdnext/"
|
||||
LABEL org.opencontainers.image.licenses="AGPL-3.0"
|
||||
LABEL org.opencontainers.image.title="SD.Next ROCm"
|
||||
LABEL org.opencontainers.image.description="SD.Next: Advanced Implementation of Stable Diffusion and other Diffusion-based generative image models"
|
||||
LABEL org.opencontainers.image.base.name="https://hub.docker.com/r/rocm/dev-ubuntu-24.04:6.3.2"
|
||||
LABEL org.opencontainers.image.version="latest"
|
||||
|
||||
# essentials
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends --fix-missing \
|
||||
software-properties-common \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
wget \
|
||||
gpg \
|
||||
git
|
||||
|
||||
# python3.12
|
||||
RUN apt-get install -y --no-install-recommends --fix-missing \
|
||||
python3 \
|
||||
python3-dev \
|
||||
python3-venv \
|
||||
python3-pip
|
||||
|
||||
# jemalloc is not required but it is highly recommended
|
||||
RUN apt-get install -y --no-install-recommends --fix-missing libjemalloc-dev
|
||||
ENV LD_PRELOAD=libjemalloc.so.2
|
||||
|
||||
# cleanup
|
||||
RUN /usr/sbin/ldconfig
|
||||
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# stop pip and uv from caching
|
||||
ENV PIP_NO_CACHE_DIR=true
|
||||
ENV UV_NO_CACHE=true
|
||||
|
||||
# set paths to use with sdnext
|
||||
ENV SD_DOCKER=true
|
||||
ENV SD_DATADIR="/mnt/data"
|
||||
ENV SD_MODELSDIR="/mnt/models"
|
||||
ENV venv_dir="/mnt/python/venv"
|
||||
|
||||
# paths used by sdnext can be a volume if necessary
|
||||
#VOLUME [ "/app" ]
|
||||
#VOLUME [ "/mnt/data" ]
|
||||
#VOLUME [ "/mnt/models" ]
|
||||
#VOLUME [ "/mnt/python" ]
|
||||
#VOLUME [ "/root/.cache/huggingface" ]
|
||||
|
||||
# override gpu architecture for unsupported gpus
|
||||
#ENV HSA_OVERRIDE_GFX_VERSION=10.0.0
|
||||
|
||||
# git clone and run sdnext
|
||||
RUN echo '#!/bin/bash\ngit status || git clone https://github.com/vladmandic/sdnext.git .\n/app/webui.sh "$@"' | tee /bin/startup.sh
|
||||
RUN chmod 755 /bin/startup.sh
|
||||
|
||||
# actually run sdnext
|
||||
WORKDIR /app
|
||||
ENTRYPOINT [ "startup.sh", "-f", "--use-rocm", "--uv", "--listen", "--debug", "--api-log", "--log", "sdnext.log" ]
|
||||
|
||||
# expose port
|
||||
EXPOSE 7860
|
||||
|
||||
# healthcheck function
|
||||
# HEALTHCHECK --interval=60s --timeout=10s --start-period=60s --retries=3 CMD curl --fail http://localhost:7860/sdapi/v1/status || exit 1
|
||||
|
||||
# stop signal
|
||||
STOPSIGNAL SIGINT
|
||||
Submodule extensions-builtin/sdnext-modernui updated: 7c2ff11f74...d2a3e992be
+7556
File diff suppressed because it is too large
Load Diff
+1134
-194
File diff suppressed because it is too large
Load Diff
+7556
File diff suppressed because it is too large
Load Diff
+7556
File diff suppressed because it is too large
Load Diff
+7556
File diff suppressed because it is too large
Load Diff
+7556
File diff suppressed because it is too large
Load Diff
+7556
File diff suppressed because it is too large
Load Diff
+7556
-644
File diff suppressed because it is too large
Load Diff
+7556
File diff suppressed because it is too large
Load Diff
+7556
File diff suppressed because it is too large
Load Diff
+7556
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
[
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
[
|
||||
{ "id": "", "label": "Reprocess", "localized": "Ponovi", "hint": "Ponovno obradite prethodne generacije koristeći različite parametre" }
|
||||
]
|
||||
File diff suppressed because one or more lines are too long
@@ -179,6 +179,13 @@
|
||||
"skip": true,
|
||||
"extras": "sampler: Default, cfg_scale: 3.5"
|
||||
},
|
||||
"Ostris Flex.1 Alpha": {
|
||||
"path": "ostris/Flex.1-alpha",
|
||||
"preview": "ostris--Flex.1-alpha.jpg",
|
||||
"desc": "Flex.1 alpha is a pre-trained base 8 billion parameter rectified flow transformer capable of generating images from text descriptions. It has a similar architecture to FLUX.1-dev, but with fewer double transformer blocks (8 vs 19)",
|
||||
"skip": true,
|
||||
"extras": "sampler: Default, cfg_scale: 3.5"
|
||||
},
|
||||
|
||||
"NVLabs Sana 1.6B 4k": {
|
||||
"path": "Efficient-Large-Model/Sana_1600M_4Kpx_BF16_diffusers",
|
||||
@@ -302,6 +309,13 @@
|
||||
"skip": true,
|
||||
"extras": "sampler: Default"
|
||||
},
|
||||
"AlphaVLLM Lumina 2": {
|
||||
"path": "Alpha-VLLM/Lumina-Image-2.0",
|
||||
"desc": "A Unified and Efficient Image Generative Model. Lumina-Image-2.0 is a 2 billion parameter flow-based diffusion transformer capable of generating images from text descriptions.",
|
||||
"preview": "Alpha-VLLM--Lumina-Image-2.0.jpg",
|
||||
"skip": true,
|
||||
"extras": "sampler: Default"
|
||||
},
|
||||
|
||||
"Kwai Kolors": {
|
||||
"path": "Kwai-Kolors/Kolors-diffusers",
|
||||
|
||||
+48
-32
@@ -4,6 +4,7 @@ import sys
|
||||
import json
|
||||
import time
|
||||
import shutil
|
||||
import locale
|
||||
import logging
|
||||
import platform
|
||||
import subprocess
|
||||
@@ -250,7 +251,7 @@ def package_spec(package):
|
||||
|
||||
# check if package is installed
|
||||
@lru_cache()
|
||||
def installed(package, friendly: str = None, reload = False, quiet = False):
|
||||
def installed(package, friendly: str = None, reload = False, quiet = False): # pylint: disable=redefined-outer-name
|
||||
t_start = time.time()
|
||||
ok = True
|
||||
try:
|
||||
@@ -307,6 +308,16 @@ def uninstall(package, quiet = False):
|
||||
return res
|
||||
|
||||
|
||||
def run(cmd: str, arg: str):
|
||||
result = subprocess.run(f'"{cmd}" {arg}', shell=True, check=False, env=os.environ, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
txt = result.stdout.decode(encoding="utf8", errors="ignore")
|
||||
if len(result.stderr) > 0:
|
||||
txt += ('\n' if len(txt) > 0 else '') + result.stderr.decode(encoding="utf8", errors="ignore")
|
||||
txt = txt.strip()
|
||||
debug(f'Exec {cmd}: {txt}')
|
||||
return txt
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def pip(arg: str, ignore: bool = False, quiet: bool = True, uv = True):
|
||||
t_start = time.time()
|
||||
@@ -343,15 +354,16 @@ def pip(arg: str, ignore: bool = False, quiet: bool = True, uv = True):
|
||||
|
||||
# install package using pip if not already installed
|
||||
@lru_cache()
|
||||
def install(package, friendly: str = None, ignore: bool = False, reinstall: bool = False, no_deps: bool = False, quiet: bool = False):
|
||||
def install(package, friendly: str = None, ignore: bool = False, reinstall: bool = False, no_deps: bool = False, quiet: bool = False, force: bool = False):
|
||||
t_start = time.time()
|
||||
res = ''
|
||||
if args.reinstall or args.upgrade:
|
||||
global quick_allowed # pylint: disable=global-statement
|
||||
quick_allowed = False
|
||||
if args.reinstall or reinstall or not installed(package, friendly, quiet=quiet):
|
||||
if (args.reinstall) or (reinstall) or (not installed(package, friendly, quiet=quiet)):
|
||||
deps = '' if not no_deps else '--no-deps '
|
||||
res = pip(f"install{' --upgrade' if not args.uv else ''} {deps}{package}", ignore=ignore, uv=package != "uv" and not package.startswith('git+'))
|
||||
cmd = f"install{' --upgrade' if not args.uv else ''}{' --force' if force else ''} {deps}{package}"
|
||||
res = pip(cmd, ignore=ignore, uv=package != "uv" and not package.startswith('git+'))
|
||||
try:
|
||||
importlib.reload(pkg_resources)
|
||||
except Exception:
|
||||
@@ -362,13 +374,12 @@ def install(package, friendly: str = None, ignore: bool = False, reinstall: bool
|
||||
|
||||
# execute git command
|
||||
@lru_cache()
|
||||
def git(arg: str, folder: str = None, ignore: bool = False, optional: bool = False):
|
||||
def git(arg: str, folder: str = None, ignore: bool = False, optional: bool = False): # pylint: disable=unused-argument
|
||||
t_start = time.time()
|
||||
if args.skip_git:
|
||||
return ''
|
||||
if optional:
|
||||
if 'google.colab' in sys.modules:
|
||||
return ''
|
||||
if 'google.colab' in sys.modules:
|
||||
return ''
|
||||
git_cmd = os.environ.get('GIT', "git")
|
||||
if git_cmd != "git":
|
||||
git_cmd = os.path.abspath(git_cmd)
|
||||
@@ -481,6 +492,7 @@ def get_platform():
|
||||
'system': platform.system(),
|
||||
'release': release,
|
||||
'python': platform.python_version(),
|
||||
'locale': locale.getlocale(),
|
||||
'docker': os.environ.get('SD_DOCKER', None) is not None,
|
||||
# 'host': platform.node(),
|
||||
# 'version': platform.version(),
|
||||
@@ -520,7 +532,7 @@ def check_diffusers():
|
||||
t_start = time.time()
|
||||
if args.skip_all or args.skip_git:
|
||||
return
|
||||
sha = 'f63d32233f402bd603da8f3aa385aecb9c3d8809' # diffusers commit hash
|
||||
sha = 'b75b204a584e29ebf4e80a61be11458e9ed56e3e' # diffusers commit hash
|
||||
pkg = pkg_resources.working_set.by_key.get('diffusers', None)
|
||||
minor = int(pkg.version.split('.')[1] if pkg is not None else 0)
|
||||
cur = opts.get('diffusers_version', '') if minor > 0 else ''
|
||||
@@ -553,16 +565,17 @@ def install_cuda():
|
||||
log.info('CUDA: nVidia toolkit detected')
|
||||
ts('cuda', t_start)
|
||||
if args.use_nightly:
|
||||
cmd = os.environ.get('TORCH_COMMAND', 'pip install --pre torch torchvision --index-url https://download.pytorch.org/whl/nightly/cu128 --extra-index-url https://download.pytorch.org/whl/nightly/cu126')
|
||||
cmd = os.environ.get('TORCH_COMMAND', 'pip install --upgrade --pre torch torchvision --index-url https://download.pytorch.org/whl/nightly/cu128 --extra-index-url https://download.pytorch.org/whl/nightly/cu126')
|
||||
else:
|
||||
cmd = os.environ.get('TORCH_COMMAND', 'torch==2.6.0+cu126 torchvision==0.21.0+cu126 --index-url https://download.pytorch.org/whl/cu126')
|
||||
return cmd
|
||||
|
||||
|
||||
def install_rocm_zluda():
|
||||
torch_command = ''
|
||||
t_start = time.time()
|
||||
if args.skip_all or args.skip_requirements:
|
||||
return None
|
||||
return torch_command
|
||||
from modules import rocm
|
||||
if not rocm.is_installed:
|
||||
log.warning('ROCm: could not find ROCm toolkit installed')
|
||||
@@ -604,7 +617,6 @@ def install_rocm_zluda():
|
||||
if device is not None:
|
||||
msg += f', using agent {device.name}'
|
||||
log.info(msg)
|
||||
torch_command = ''
|
||||
|
||||
if sys.platform == "win32": # TODO install: enable ROCm for windows when available
|
||||
check_python(supported_minors=[10, 11], reason='ZLUDA backend requires Python 3.10 or 3.11')
|
||||
@@ -617,11 +629,11 @@ def install_rocm_zluda():
|
||||
|
||||
error = None
|
||||
from modules import zluda_installer
|
||||
zluda_installer.set_default_agent(device)
|
||||
try:
|
||||
if args.reinstall or zluda_installer.is_old_zluda():
|
||||
if args.reinstall or zluda_installer.is_reinstall_needed():
|
||||
zluda_installer.uninstall()
|
||||
zluda_installer.install()
|
||||
zluda_installer.set_default_agent(device)
|
||||
except Exception as e:
|
||||
error = e
|
||||
log.warning(f'Failed to install ZLUDA: {e}')
|
||||
@@ -633,7 +645,7 @@ def install_rocm_zluda():
|
||||
zluda_installer.set_blaslt_enabled(device.blaslt_supported)
|
||||
zluda_installer.make_copy()
|
||||
zluda_installer.load()
|
||||
torch_command = os.environ.get('TORCH_COMMAND', f'torch=={zluda_installer.get_default_torch_version(device)} torchvision --index-url https://download.pytorch.org/whl/cu118')
|
||||
torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.6.0 torchvision --index-url https://download.pytorch.org/whl/cu118')
|
||||
log.info(f'Using ZLUDA in {zluda_installer.path}')
|
||||
except Exception as e:
|
||||
error = e
|
||||
@@ -643,6 +655,10 @@ def install_rocm_zluda():
|
||||
torch_command = os.environ.get('TORCH_COMMAND', 'torch torchvision')
|
||||
else:
|
||||
check_python(supported_minors=[9, 10, 11, 12], reason='ROCm backend requires a Python version between 3.9 and 3.12')
|
||||
|
||||
if os.environ.get("TORCH_ROCM_AOTRITON_ENABLE_EXPERIMENTAL", None) is None:
|
||||
os.environ.setdefault('TORCH_ROCM_AOTRITON_ENABLE_EXPERIMENTAL', '1')
|
||||
|
||||
if args.use_nightly:
|
||||
if rocm.version is None or float(rocm.version) >= 6.3: # assume the latest if version check fails
|
||||
torch_command = os.environ.get('TORCH_COMMAND', '--pre torch torchvision --index-url https://download.pytorch.org/whl/nightly/rocm6.3')
|
||||
@@ -667,13 +683,6 @@ def install_rocm_zluda():
|
||||
# older rocm (5.7) uses torch 2.3 or older
|
||||
torch_command = os.environ.get('TORCH_COMMAND', f'torch torchvision --index-url https://download.pytorch.org/whl/rocm{rocm.version}')
|
||||
|
||||
if installed("torch") and device is not None:
|
||||
if 'Flash attention' in opts.get('sdp_options', ''):
|
||||
if not installed('flash-attn'):
|
||||
install(rocm.get_flash_attention_command(device), reinstall=True)
|
||||
#elif not args.experimental:
|
||||
# uninstall('flash-attn')
|
||||
|
||||
if device is not None and rocm.version != "6.2" and rocm.get_blaslt_enabled():
|
||||
log.debug(f'ROCm hipBLASLt: arch={device.name} available={device.blaslt_supported}')
|
||||
rocm.set_blaslt_enabled(device.blaslt_supported)
|
||||
@@ -719,8 +728,6 @@ def install_ipex(torch_command):
|
||||
else:
|
||||
torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.6.0+xpu torchvision==0.21.0+xpu --index-url https://download.pytorch.org/whl/xpu')
|
||||
|
||||
install(os.environ.get('OPENVINO_COMMAND', 'openvino==2024.6.0'), 'openvino', ignore=True)
|
||||
install('nncf==2.7.0', ignore=True, no_deps=True) # requires older pandas
|
||||
ts('ipex', t_start)
|
||||
return torch_command
|
||||
|
||||
@@ -730,12 +737,12 @@ def install_openvino(torch_command):
|
||||
check_python(supported_minors=[9, 10, 11, 12], reason='OpenVINO backend requires a Python version between 3.9 and 3.12')
|
||||
log.info('OpenVINO: selected')
|
||||
if sys.platform == 'darwin':
|
||||
torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.3.1 torchvision==0.18.1')
|
||||
torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.6.0 torchvision==0.21.0')
|
||||
else:
|
||||
torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.3.1+cpu torchvision==0.18.1+cpu --index-url https://download.pytorch.org/whl/cpu')
|
||||
torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.6.0+cpu torchvision==0.21.0+cpu --index-url https://download.pytorch.org/whl/cpu')
|
||||
|
||||
install(os.environ.get('OPENVINO_COMMAND', 'openvino==2024.6.0'), 'openvino')
|
||||
install('nncf==2.14.1', 'nncf')
|
||||
install(os.environ.get('OPENVINO_COMMAND', 'openvino==2025.0.0'), 'openvino')
|
||||
install(os.environ.get('NNCF_COMMAND', 'nncf==2.15.0'), 'nncf')
|
||||
os.environ.setdefault('PYTORCH_TRACING_MODE', 'TORCHFX')
|
||||
if os.environ.get("NEOReadDebugKeys", None) is None:
|
||||
os.environ.setdefault('NEOReadDebugKeys', '1')
|
||||
@@ -824,14 +831,12 @@ def check_torch():
|
||||
torch_command = install_ipex(torch_command)
|
||||
elif allow_openvino and args.use_openvino: # prioritize openvino
|
||||
torch_command = install_openvino(torch_command)
|
||||
|
||||
elif is_cuda_available:
|
||||
torch_command = install_cuda()
|
||||
elif is_rocm_available:
|
||||
torch_command = install_rocm_zluda()
|
||||
elif is_ipex_available:
|
||||
torch_command = install_ipex(torch_command)
|
||||
|
||||
else:
|
||||
machine = platform.machine()
|
||||
if sys.platform == 'darwin':
|
||||
@@ -1074,6 +1079,14 @@ def install_submodules(force=True):
|
||||
return '\n'.join(res)
|
||||
|
||||
|
||||
def reload(package):
|
||||
modules = [m for m in sys.modules if m.startswith(package)]
|
||||
for m in modules:
|
||||
del sys.modules[m]
|
||||
sys.modules[package] = importlib.import_module(package)
|
||||
log.debug(f'Reload: package={package} version={sys.modules[package].__version__ if hasattr(sys.modules[package], "__version__") else "N/A"}')
|
||||
|
||||
|
||||
def ensure_base_requirements():
|
||||
t_start = time.time()
|
||||
setuptools_version = '69.5.1'
|
||||
@@ -1121,7 +1134,10 @@ def install_optional():
|
||||
install('pynvml', ignore=True)
|
||||
install('ultralytics==8.3.40', ignore=True)
|
||||
install('Cython', ignore=True)
|
||||
install('insightface', ignore=True) # problematic build
|
||||
install('insightface==0.7.3', ignore=True) # problematic build
|
||||
install('albumentations==1.4.3', ignore=True)
|
||||
install('pydantic==1.10.21', ignore=True)
|
||||
reload('pydantic')
|
||||
install('nncf==2.7.0', ignore=True, no_deps=True) # requires older pandas
|
||||
# install('flash-attn', ignore=True) # requires cuda and nvcc to be installed
|
||||
install('gguf', ignore=True)
|
||||
@@ -1308,7 +1324,7 @@ def check_venv():
|
||||
t_start = time.time()
|
||||
import site
|
||||
pkg_path = [try_relpath(p) for p in site.getsitepackages() if os.path.exists(p)]
|
||||
log.debug(f'Packages: venv={try_relpath(sys.prefix)} site={pkg_path}')
|
||||
log.debug(f'Packages: prefix={try_relpath(sys.prefix)} site={pkg_path}')
|
||||
for p in pkg_path:
|
||||
invalid = []
|
||||
for f in os.listdir(p):
|
||||
|
||||
@@ -294,7 +294,7 @@ function selectHistory(id) {
|
||||
const headers = new Headers();
|
||||
headers.set('Content-Type', 'application/json');
|
||||
const init = { method: 'POST', body: { name: id }, headers };
|
||||
fetch('/sdapi/v1/history', { method: 'POST', body: JSON.stringify({ name: id }), headers });
|
||||
fetch(`${window.api}/history`, { method: 'POST', body: JSON.stringify({ name: id }), headers });
|
||||
}
|
||||
|
||||
let enDirty = false;
|
||||
@@ -464,13 +464,13 @@ function setupExtraNetworksForTab(tabname) {
|
||||
en.style.position = 'absolute';
|
||||
en.style.height = 'auto';
|
||||
en.style.width = `${window.opts.extra_networks_sidebar_width}vw`;
|
||||
en.style.maxWidth = '655px';
|
||||
en.style.maxWidth = '50vw';
|
||||
en.style.right = '0';
|
||||
en.style.top = '13em';
|
||||
en.style.transition = 'width 0.3s ease';
|
||||
en.style.zIndex = 100;
|
||||
// gradioApp().getElementById(`${tabname}_settings`).parentNode.style.width = `${100 - 2 - window.opts.extra_networks_sidebar_width}vw`;
|
||||
gradioApp().getElementById(`${tabname}_settings`).parentNode.style.width = `calc(100vw - 2em - min(${window.opts.extra_networks_sidebar_width}vw, 655px))`;
|
||||
gradioApp().getElementById(`${tabname}_settings`).parentNode.style.width = `calc(100vw - 2em - min(${window.opts.extra_networks_sidebar_width}vw, 50vw))`;
|
||||
} else {
|
||||
en.style.position = 'relative';
|
||||
en.style.height = 'unset';
|
||||
@@ -515,5 +515,6 @@ async function setupExtraNetworks() {
|
||||
registerPrompt('img2img', 'img2img_neg_prompt');
|
||||
registerPrompt('control', 'control_prompt');
|
||||
registerPrompt('control', 'control_neg_prompt');
|
||||
log('initNetworks');
|
||||
log('initNetworks', window.opts.extra_networks_card_size);
|
||||
document.documentElement.style.setProperty('--card-size', `${window.opts.extra_networks_card_size}px`);
|
||||
}
|
||||
|
||||
+17
-16
@@ -23,19 +23,20 @@ class GalleryFolder extends HTMLElement {
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
.gallery-folder {
|
||||
cursor: pointer;
|
||||
padding: 8px 6px 8px 6px;
|
||||
}
|
||||
.gallery-folder:hover {
|
||||
background-color: var(--button-primary-background-fill-hover);
|
||||
}
|
||||
.gallery-folder-selected {
|
||||
background-color: var(--button-primary-background-fill);
|
||||
}
|
||||
`;
|
||||
const style = document.createElement('style'); // silly but necessasry since we're inside shadowdom
|
||||
if (window.opts.theme_type === 'Modern') {
|
||||
style.textContent = `
|
||||
.gallery-folder { cursor: pointer; padding: 8px 6px 8px 6px; background-color: var(--sd-secondary-color); }
|
||||
.gallery-folder:hover { background-color: var(--button-primary-background-fill-hover); }
|
||||
.gallery-folder-selected { background-color: var(--sd-button-selected-color); color: var(--sd-button-selected-text-color); }
|
||||
`;
|
||||
} else {
|
||||
style.textContent = `
|
||||
.gallery-folder { cursor: pointer; padding: 8px 6px 8px 6px; }
|
||||
.gallery-folder:hover { background-color: var(--button-primary-background-fill-hover); }
|
||||
.gallery-folder-selected { background-color: var(--button-primary-background-fill); }
|
||||
`;
|
||||
}
|
||||
this.shadow.appendChild(style);
|
||||
const div = document.createElement('div');
|
||||
div.className = 'gallery-folder';
|
||||
@@ -92,7 +93,7 @@ async function addSeparators() {
|
||||
async function delayFetchThumb(fn) {
|
||||
while (outstanding > 16) await new Promise((resolve) => setTimeout(resolve, 50)); // eslint-disable-line no-promise-executor-return
|
||||
outstanding++;
|
||||
const res = await fetch(`/sdapi/v1/browser/thumb?file=${encodeURI(fn)}`, { priority: 'low' });
|
||||
const res = await fetch(`${window.api}/browser/thumb?file=${encodeURI(fn)}`, { priority: 'low' });
|
||||
if (!res.ok) {
|
||||
error(`fetchThumb: ${res.statusText}`);
|
||||
outstanding--;
|
||||
@@ -334,7 +335,7 @@ async function fetchFilesHT(evt) {
|
||||
el.status.innerText = `Folder | ${evt.target.name} | in-progress`;
|
||||
let numFiles = 0;
|
||||
|
||||
const res = await fetch(`/sdapi/v1/browser/files?folder=${encodeURI(evt.target.name)}`);
|
||||
const res = await fetch(`${window.api}/browser/files?folder=${encodeURI(evt.target.name)}`);
|
||||
if (!res || res.status !== 200) {
|
||||
el.status.innerText = `Folder | ${evt.target.name} | failed: ${res?.statusText}`;
|
||||
return;
|
||||
@@ -412,7 +413,7 @@ async function pruneImages() {
|
||||
|
||||
async function galleryVisible() {
|
||||
// if (el.folders.children.length > 0) return;
|
||||
const res = await fetch('/sdapi/v1/browser/folders');
|
||||
const res = await fetch(`${window.api}/browser/folders`);
|
||||
if (!res || res.status !== 200) return;
|
||||
el.folders.innerHTML = '';
|
||||
url = res.url.split('/sdapi')[0].replace('http', 'ws'); // update global url as ws need fqdn
|
||||
|
||||
@@ -37,7 +37,7 @@ async function createSplash() {
|
||||
await preloadImages();
|
||||
const imgEl = `<div id="spash-img" class="splash-img" alt="logo" style="background-image: url(file=html/logo-bg-${dark ? 'dark' : 'light'}.jpg), url(file=html/logo-bg-${num}.jpg); background-blend-mode: ${dark ? 'multiply' : 'lighten'}"></div>`;
|
||||
document.getElementById('splash').insertAdjacentHTML('afterbegin', imgEl);
|
||||
fetch('/sdapi/v1/motd')
|
||||
fetch(`${window.api}/motd`)
|
||||
.then((res) => res.text())
|
||||
.then((text) => {
|
||||
const motdEl = document.getElementById('motd');
|
||||
@@ -52,7 +52,7 @@ async function removeSplash() {
|
||||
log('removeSplash');
|
||||
const t = Math.round(performance.now() - appStartTime) / 1000;
|
||||
log('startupTime', t);
|
||||
xhrPost('/sdapi/v1/log', { message: `ready time=${t}` });
|
||||
xhrPost(`${window.api}/log`, { message: `ready time=${t}` });
|
||||
}
|
||||
|
||||
window.onload = createSplash;
|
||||
|
||||
@@ -38,14 +38,18 @@ async function logMonitor() {
|
||||
while (logMonitorEl.childElementCount > 100) logMonitorEl.removeChild(logMonitorEl.firstChild);
|
||||
if (atBottom) logMonitorEl.scrollTop = logMonitorEl.scrollHeight;
|
||||
else logMonitorEl.parentElement.style = 'border-bottom: 2px solid var(--highlight-color);';
|
||||
document.getElementById('logWarnings').innerText = logWarnings;
|
||||
document.getElementById('logErrors').innerText = logErrors;
|
||||
const elWarn = document.getElementById('logWarnings');
|
||||
const elErr = document.getElementById('logErrors');
|
||||
const modenUIBtn = document.getElementById('btn_console');
|
||||
if (elWarn) elWarn.innerText = logWarnings;
|
||||
if (elErr) elErr.innerText = logErrors;
|
||||
if (modenUIBtn) modenUIBtn.setAttribute('error-count', logErrors > 0 ? logErrors : '');
|
||||
};
|
||||
|
||||
document.getElementById('txt2img_gallery').style.height = opts.logmonitor_show ? '50vh' : '55vh';
|
||||
document.getElementById('img2img_gallery').style.height = opts.logmonitor_show ? '50vh' : '55vh';
|
||||
const txtGallery = document.getElementById('txt2img_gallery');
|
||||
if (txtGallery) txtGallery.style.height = opts.logmonitor_show ? '50vh' : '55vh';
|
||||
const imgGallery = document.getElementById('img2img_gallery');
|
||||
if (imgGallery) imgGallery.style.height = opts.logmonitor_show ? '50vh' : '55vh';
|
||||
|
||||
if (!opts.logmonitor_show) {
|
||||
Array.from(document.getElementsByClassName('log-monitor')).forEach((el) => el.style.display = 'none');
|
||||
@@ -66,7 +70,7 @@ async function logMonitor() {
|
||||
if (!logMonitorEl) return;
|
||||
const atBottom = logMonitorEl.scrollHeight <= (logMonitorEl.scrollTop + logMonitorEl.clientHeight);
|
||||
try {
|
||||
const res = await fetch('/sdapi/v1/log?clear=True');
|
||||
const res = await fetch(`${window.api}/log?clear=True`);
|
||||
if (res?.ok) {
|
||||
logMonitorStatus = true;
|
||||
const lines = await res.json();
|
||||
@@ -112,7 +116,7 @@ async function initLogMonitor() {
|
||||
</table>
|
||||
`;
|
||||
el.style.display = 'none';
|
||||
fetch(`/sdapi/v1/start?agent=${encodeURI(navigator.userAgent)}`);
|
||||
fetch(`${window.api}/start?agent=${encodeURI(navigator.userAgent)}`);
|
||||
logMonitor();
|
||||
log('initLogMonitor');
|
||||
}
|
||||
|
||||
+20
-8
@@ -1,29 +1,41 @@
|
||||
const timeout = 30000;
|
||||
const scrollBottom = async (el) => {
|
||||
const lastChild = el.lastElementChild;
|
||||
if (lastChild) lastChild.scrollIntoView({ behavior: 'smooth' });
|
||||
};
|
||||
|
||||
const log = async (...msg) => {
|
||||
const dt = new Date();
|
||||
const ts = `${dt.getHours().toString().padStart(2, '0')}:${dt.getMinutes().toString().padStart(2, '0')}:${dt.getSeconds().toString().padStart(2, '0')}.${dt.getMilliseconds().toString().padStart(3, '0')}`;
|
||||
if (window.logger) window.logger.innerHTML += window.logPrettyPrint(...msg);
|
||||
if (window.logger) {
|
||||
window.logger.innerHTML += window.logPrettyPrint(...msg);
|
||||
scrollBottom(window.logger);
|
||||
}
|
||||
console.log(ts, ...msg); // eslint-disable-line no-console
|
||||
};
|
||||
|
||||
const debug = async (...msg) => {
|
||||
const dt = new Date();
|
||||
const ts = `${dt.getHours().toString().padStart(2, '0')}:${dt.getMinutes().toString().padStart(2, '0')}:${dt.getSeconds().toString().padStart(2, '0')}.${dt.getMilliseconds().toString().padStart(3, '0')}`;
|
||||
if (window.logger) window.logger.innerHTML += window.logPrettyPrint(...msg);
|
||||
if (window.logger) {
|
||||
window.logger.innerHTML += window.logPrettyPrint(...msg);
|
||||
scrollBottom(window.logger);
|
||||
}
|
||||
console.debug(ts, ...msg); // eslint-disable-line no-console
|
||||
};
|
||||
|
||||
const error = async (...msg) => {
|
||||
const dt = new Date();
|
||||
const ts = `${dt.getHours().toString().padStart(2, '0')}:${dt.getMinutes().toString().padStart(2, '0')}:${dt.getSeconds().toString().padStart(2, '0')}.${dt.getMilliseconds().toString().padStart(3, '0')}`;
|
||||
if (window.logger) window.logger.innerHTML += window.logPrettyPrint(...msg);
|
||||
if (window.logger) {
|
||||
window.logger.innerHTML += window.logPrettyPrint(...msg);
|
||||
scrollBottom(window.logger);
|
||||
}
|
||||
console.error(ts, ...msg); // eslint-disable-line no-console
|
||||
// const txt = msg.join(' ');
|
||||
// if (!txt.includes('asctime') && !txt.includes('xhr.')) xhrPost('/sdapi/v1/log', { error: txt }); // eslint-disable-line no-use-before-define
|
||||
};
|
||||
|
||||
const xhrInternal = (xhrObj, data, handler = undefined, errorHandler = undefined, ignore = false, serverTimeout = timeout) => {
|
||||
const xhrInternal = (xhrObj, data, handler = undefined, errorHandler = undefined, ignore = false, serverTimeout = opts.ui_request_timeout || 30000) => {
|
||||
const err = (msg) => {
|
||||
if (!ignore) {
|
||||
error(`${msg}: state=${xhrObj.readyState} status=${xhrObj.status} response=${xhrObj.responseText}`);
|
||||
@@ -32,7 +44,7 @@ const xhrInternal = (xhrObj, data, handler = undefined, errorHandler = undefined
|
||||
};
|
||||
|
||||
xhrObj.setRequestHeader('Content-Type', 'application/json');
|
||||
xhrObj.timeout = timeout;
|
||||
xhrObj.timeout = opts.ui_request_timeout || 30000;
|
||||
xhrObj.ontimeout = () => err('xhr.ontimeout');
|
||||
xhrObj.onerror = () => err('xhr.onerror');
|
||||
xhrObj.onabort = () => err('xhr.onabort');
|
||||
@@ -54,14 +66,14 @@ const xhrInternal = (xhrObj, data, handler = undefined, errorHandler = undefined
|
||||
xhrObj.send(req);
|
||||
};
|
||||
|
||||
const xhrGet = (url, data, handler = undefined, errorHandler = undefined, ignore = false, serverTimeout = timeout) => {
|
||||
const xhrGet = (url, data, handler = undefined, errorHandler = undefined, ignore = false, serverTimeout = opts.ui_request_timeout || 30000) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
const args = Object.keys(data).map((k) => `${encodeURIComponent(k)}=${encodeURIComponent(data[k])}`).join('&');
|
||||
xhr.open('GET', `${url}?${args}`, true);
|
||||
xhrInternal(xhr, data, handler, errorHandler, ignore, serverTimeout);
|
||||
};
|
||||
|
||||
function xhrPost(url, data, handler = undefined, errorHandler = undefined, ignore = false, serverTimeout = timeout) {
|
||||
function xhrPost(url, data, handler = undefined, errorHandler = undefined, ignore = false, serverTimeout = opts.ui_request_timeout || 30000) {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', url, true);
|
||||
xhrInternal(xhr, data, handler, errorHandler, ignore, serverTimeout);
|
||||
|
||||
+4
-4
@@ -25,7 +25,7 @@ const loginHTML = `
|
||||
function forceLogin() {
|
||||
const form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.action = '/login';
|
||||
form.action = `${location.href}login`;
|
||||
form.id = 'loginForm';
|
||||
form.style.cssText = loginCSS;
|
||||
form.innerHTML = loginHTML;
|
||||
@@ -39,8 +39,8 @@ function forceLogin() {
|
||||
const formData = new FormData(form);
|
||||
formData.append('username', username.value);
|
||||
formData.append('password', password.value);
|
||||
console.warn('login', formData);
|
||||
fetch('/login', {
|
||||
console.warn('login', location.href, formData);
|
||||
fetch(`${location.href}login`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
})
|
||||
@@ -59,7 +59,7 @@ function forceLogin() {
|
||||
}
|
||||
|
||||
function loginCheck() {
|
||||
fetch('/login_check', {})
|
||||
fetch(`${location.href}login_check`, {})
|
||||
.then((res) => {
|
||||
if (res.status === 200) console.log('login ok');
|
||||
else forceLogin();
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ async function updateNVMLChart(mem, load) {
|
||||
|
||||
async function updateNVML() {
|
||||
try {
|
||||
const res = await fetch('/sdapi/v1/nvml');
|
||||
const res = await fetch(`${window.api}/nvml`);
|
||||
if (!res.ok) {
|
||||
clearInterval(nvmlInterval);
|
||||
nvmlEl.style.display = 'none';
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
--color-info: #D4D4D4;
|
||||
--color-warning: #FF9900;
|
||||
--color-error: #BE0000;
|
||||
--card-size: 160px;
|
||||
}
|
||||
a { font-weight: bold; cursor: pointer; }
|
||||
h2 { margin-top: 1em !important; font-size: var(--text-xxl) !important; }
|
||||
@@ -132,6 +133,7 @@ div#extras_scale_to_tab div.form { flex-direction: row; }
|
||||
width: 22em; min-height: 1.3em; font-size: var(--text-xs); transition: opacity 0.2s ease-in; pointer-events: none; opacity: 0; z-index: 999; }
|
||||
.tooltip-show { opacity: 0.9; }
|
||||
.toolbutton-selected { background: var(--background-fill-primary) !important; }
|
||||
.locale { position: fixed; top: 0.5em; right: 0.5em; background-color: var(--input-background-fill); color: var(--body-text-color); padding: 0.1em; width: 1.2em; height: 1.2em; cursor: pointer; font-size: 0.8em; font-weight: 800; font-family: monospace; opacity: 50%; }
|
||||
#txt2img_hdr_color_row > div { min-width: unset !important; max-width: unset !important; }
|
||||
#txt2img_advanced_options, #img2img_advanced_options, #control_advanced_options { min-width: 100%; }
|
||||
#txt2img_advanced_options .gradio-checkbox, #img2img_advanced_options .gradio-checkbox, #control_advanced_options .gradio-checkbox { min-width: unset !important; max-width: fit-content; }
|
||||
|
||||
+137
-37
@@ -1,17 +1,44 @@
|
||||
const allLocales = ['en', 'de', 'es', 'fr', 'it', 'ja', 'ko', 'pt', 'hr', 'ru', 'zh'];
|
||||
const localeData = {
|
||||
prev: null,
|
||||
locale: null,
|
||||
data: [],
|
||||
timeout: null,
|
||||
finished: false,
|
||||
initial: true,
|
||||
type: 2,
|
||||
el: null,
|
||||
hint: null,
|
||||
btn: null,
|
||||
};
|
||||
|
||||
async function cycleLocale() {
|
||||
log('cycleLocale', localeData.prev, localeData.locale);
|
||||
const index = allLocales.indexOf(localeData.prev);
|
||||
localeData.locale = allLocales[(index + 1) % allLocales.length];
|
||||
localeData.btn.innerText = localeData.locale;
|
||||
// localeData.btn.style.backgroundColor = localeData.locale !== 'en' ? 'var(--primary-500)' : '';
|
||||
localeData.finished = false;
|
||||
localeData.data = [];
|
||||
localeData.prev = localeData.locale;
|
||||
window.opts.ui_locale = localeData.locale;
|
||||
await setHints(); // eslint-disable-line no-use-before-define
|
||||
}
|
||||
|
||||
async function tooltipCreate() {
|
||||
localeData.el = document.createElement('div');
|
||||
localeData.el.className = 'tooltip';
|
||||
localeData.el.id = 'tooltip-container';
|
||||
localeData.el.innerText = 'this is a hint';
|
||||
gradioApp().appendChild(localeData.el);
|
||||
localeData.hint = document.createElement('div');
|
||||
localeData.hint.className = 'tooltip';
|
||||
localeData.hint.id = 'tooltip-container';
|
||||
localeData.hint.innerText = 'this is a hint';
|
||||
gradioApp().appendChild(localeData.hint);
|
||||
localeData.btn = gradioApp().getElementById('locale-container');
|
||||
if (!localeData.btn) {
|
||||
localeData.btn = document.createElement('div');
|
||||
localeData.btn.className = 'locale';
|
||||
localeData.btn.id = 'locale-container';
|
||||
gradioApp().appendChild(localeData.btn);
|
||||
}
|
||||
localeData.btn.innerText = localeData.locale;
|
||||
localeData.btn.onclick = cycleLocale;
|
||||
if (window.opts.tooltips === 'None') localeData.type = 0;
|
||||
if (window.opts.tooltips === 'Browser default') localeData.type = 1;
|
||||
if (window.opts.tooltips === 'UI tooltips') localeData.type = 2;
|
||||
@@ -19,31 +46,56 @@ async function tooltipCreate() {
|
||||
|
||||
async function tooltipShow(e) {
|
||||
if (e.target.dataset.hint) {
|
||||
localeData.el.classList.add('tooltip-show');
|
||||
localeData.el.innerHTML = `<b>${e.target.textContent}</b><br>${e.target.dataset.hint}`;
|
||||
localeData.hint.classList.add('tooltip-show');
|
||||
localeData.hint.innerHTML = `<b>${e.target.textContent}</b><br>${e.target.dataset.hint}`;
|
||||
if (e.clientX > window.innerWidth / 2) {
|
||||
localeData.el.classList.add('tooltip-left');
|
||||
localeData.hint.classList.add('tooltip-left');
|
||||
} else {
|
||||
localeData.el.classList.remove('tooltip-left');
|
||||
localeData.hint.classList.remove('tooltip-left');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function tooltipHide(e) {
|
||||
localeData.el.classList.remove('tooltip-show');
|
||||
localeData.hint.classList.remove('tooltip-show');
|
||||
}
|
||||
|
||||
async function validateHints(elements, data) {
|
||||
let original = elements.map((e) => e.textContent.toLowerCase().trim()).sort((a, b) => a > b);
|
||||
async function validateHints(json, elements) {
|
||||
json.missing = [];
|
||||
const data = Object.values(json).flat().filter((e) => e.hint.length > 0);
|
||||
for (const e of data) e.label = e.label.trim();
|
||||
let original = elements.map((e) => e.textContent.toLowerCase().trim()).sort(); // should be case sensitive
|
||||
let duplicateUI = original.filter((e, i, a) => a.indexOf(e.toLowerCase()) !== i).sort();
|
||||
original = [...new Set(original)]; // remove duplicates
|
||||
const current = data.map((e) => e.label.toLowerCase().trim()).sort((a, b) => a > b);
|
||||
duplicateUI = [...new Set(duplicateUI)]; // remove duplicates
|
||||
const current = data.map((e) => e.label.toLowerCase().trim()).sort(); // should be case sensitive
|
||||
log('all elements:', original);
|
||||
log('all hints:', current);
|
||||
log('hints-differences', { elements: original.length, hints: current.length });
|
||||
const missingLocale = original.filter((e) => !current.includes(e));
|
||||
log('missing in locale:', missingLocale);
|
||||
const missingUI = current.filter((e) => !original.includes(e));
|
||||
log('in locale but not ui:', missingUI);
|
||||
const missingHints = original.filter((e) => !current.includes(e.toLowerCase())).sort();
|
||||
const orphanedHints = current.filter((e) => !original.includes(e.toLowerCase())).sort();
|
||||
const duplicateHints = current.filter((e, i, a) => a.indexOf(e.toLowerCase()) !== i).sort();
|
||||
log('duplicate hints:', duplicateHints);
|
||||
log('duplicate labels:', duplicateUI);
|
||||
return [missingHints, orphanedHints];
|
||||
}
|
||||
|
||||
async function addMissingHints(json, missingHints) {
|
||||
if (missingHints.length === 0) return;
|
||||
json.missing = [];
|
||||
for (const h of missingHints.sort()) {
|
||||
if (h.length <= 1) continue;
|
||||
json.missing.push({ id: '', label: h, localized: '', hint: h });
|
||||
}
|
||||
log('missing hints', missingHints);
|
||||
log('added missing hints:', { missing: json.missing });
|
||||
}
|
||||
|
||||
async function removeOrphanedHints(json, orphanedHints) {
|
||||
const data = Object.values(json).flat().filter((e) => e.hint.length > 0);
|
||||
for (const e of data) e.label = e.label.trim();
|
||||
const orphaned = data.filter((e) => orphanedHints.includes(e.label.toLowerCase()));
|
||||
log('orphaned hints:', { orphaned });
|
||||
}
|
||||
|
||||
async function replaceButtonText(el) {
|
||||
@@ -65,30 +117,72 @@ async function replaceButtonText(el) {
|
||||
}
|
||||
}
|
||||
|
||||
async function setHints() {
|
||||
if (localeData.finished) return;
|
||||
if (localeData.data.length === 0) {
|
||||
const res = await fetch('/file=html/locale_en.json');
|
||||
const json = await res.json();
|
||||
localeData.data = Object.values(json).flat().filter((e) => e.hint.length > 0);
|
||||
for (const e of localeData.data) e.label = e.label.toLowerCase().trim();
|
||||
async function getLocaleData(desiredLocale = null) {
|
||||
if (desiredLocale) desiredLocale = desiredLocale.split(':')[0];
|
||||
if (desiredLocale === 'Auto') {
|
||||
try {
|
||||
localeData.locale = navigator.languages && navigator.languages.length ? navigator.languages[0] : navigator.language;
|
||||
localeData.locale = localeData.locale.split('-')[0];
|
||||
localeData.prev = localeData.locale;
|
||||
} catch (e) {
|
||||
localeData.locale = 'en';
|
||||
log('getLocale', e);
|
||||
}
|
||||
} else {
|
||||
localeData.locale = desiredLocale || 'en';
|
||||
localeData.prev = localeData.locale;
|
||||
}
|
||||
log('getLocale', desiredLocale, localeData.locale);
|
||||
// primary
|
||||
let json = {};
|
||||
try {
|
||||
let res = await fetch(`${window.subpath}/file=html/locale_${localeData.locale}.json`);
|
||||
if (!res || !res.ok) {
|
||||
localeData.locale = 'en';
|
||||
res = await fetch(`${window.subpath}/file=html/locale_${localeData.locale}.json`);
|
||||
}
|
||||
json = await res.json();
|
||||
} catch { /**/ }
|
||||
|
||||
try {
|
||||
const res = await fetch(`${window.subpath}/file=html/override_${localeData.locale}.json`);
|
||||
if (res && res.ok) json.override = await res.json();
|
||||
} catch { /**/ }
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
async function setHints(analyze = false) {
|
||||
let json = {};
|
||||
let overrideData = [];
|
||||
if (localeData.finished) return;
|
||||
if (Object.keys(opts).length === 0) return;
|
||||
const elements = [
|
||||
...Array.from(gradioApp().querySelectorAll('button')),
|
||||
...Array.from(gradioApp().querySelectorAll('label > span')),
|
||||
...Array.from(gradioApp().querySelectorAll('.label-wrap > span')),
|
||||
];
|
||||
if (elements.length === 0) return;
|
||||
if (Object.keys(opts).length === 0) return;
|
||||
if (!localeData.el) tooltipCreate();
|
||||
if (localeData.data.length === 0) {
|
||||
json = await getLocaleData(window.opts.ui_locale);
|
||||
overrideData = Object.values(json.override || {}).flat().filter((e) => e.hint.length > 0);
|
||||
const jsonData = Object.values(json).flat().filter((e) => e.hint.length > 0);
|
||||
localeData.data = [...overrideData, ...jsonData];
|
||||
}
|
||||
if (!localeData.hint) tooltipCreate();
|
||||
let localized = 0;
|
||||
let hints = 0;
|
||||
localeData.finished = true;
|
||||
const t0 = performance.now();
|
||||
for (const el of elements) {
|
||||
const found = localeData.data.find((l) => l.label === el.textContent.toLowerCase().trim());
|
||||
let found;
|
||||
if (el.dataset.original) found = localeData.data.find((l) => l.label.toLowerCase().trim() === el.dataset.original.toLowerCase().trim());
|
||||
else found = localeData.data.find((l) => l.label.toLowerCase().trim() === el.textContent.toLowerCase().trim());
|
||||
if (found?.localized?.length > 0) {
|
||||
if (!el.dataset.original) el.dataset.original = el.textContent;
|
||||
localized++;
|
||||
el.textContent = found.localized;
|
||||
} else if (found?.label && !localeData.initial && (localeData.locale === 'en')) { // reset to english
|
||||
el.textContent = found.label;
|
||||
}
|
||||
// replaceButtonText(el);
|
||||
if (found?.hint?.length > 0) {
|
||||
@@ -104,15 +198,21 @@ async function setHints() {
|
||||
}
|
||||
}
|
||||
}
|
||||
localeData.finished = true;
|
||||
localeData.initial = false;
|
||||
const t1 = performance.now();
|
||||
log('setHints', { type: localeData.type, elements: elements.length, localized, hints, data: localeData.data.length, time: t1 - t0 });
|
||||
// localeData.btn.style.backgroundColor = localeData.locale !== 'en' ? 'var(--primary-500)' : '';
|
||||
log('setHints', { type: localeData.type, locale: localeData.locale, elements: elements.length, localized, hints, data: localeData.data.length, override: overrideData.length, time: Math.round(t1 - t0) });
|
||||
// sortUIElements();
|
||||
// validateHints(elements, localeData.data);
|
||||
if (analyze) {
|
||||
const [missingHints, orphanedHints] = await validateHints(json, elements);
|
||||
await addMissingHints(json, missingHints);
|
||||
await removeOrphanedHints(json, orphanedHints);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
onAfterUiUpdate(async () => {
|
||||
if (localeData.timeout) clearTimeout(localeData.timeout);
|
||||
localeData.timeout = setTimeout(setHints, 250);
|
||||
});
|
||||
*/
|
||||
const analyzeHints = async () => {
|
||||
localeData.finished = false;
|
||||
localeData.data = [];
|
||||
await setHints(true);
|
||||
};
|
||||
|
||||
@@ -117,8 +117,10 @@ onAfterUiUpdate(async () => {
|
||||
});
|
||||
|
||||
const settingsSearch = gradioApp().querySelectorAll('#settings_search > label > textarea')[0];
|
||||
let settingsTimer;
|
||||
settingsSearch.oninput = (e) => {
|
||||
setTimeout(() => {
|
||||
if (settingsTimer) clearTimeout(settingsTimer);
|
||||
settingsTimer = setTimeout(() => {
|
||||
log('settingsSearch', e.target.value);
|
||||
showAllSettings();
|
||||
getSettingsTabs().forEach((section) => {
|
||||
@@ -128,7 +130,7 @@ onAfterUiUpdate(async () => {
|
||||
else setting.style.removeProperty('display');
|
||||
});
|
||||
});
|
||||
}, 50);
|
||||
}, 250);
|
||||
};
|
||||
});
|
||||
|
||||
@@ -150,7 +152,7 @@ async function initModels() {
|
||||
const el = gradioApp().getElementById('main_info');
|
||||
const en = gradioApp().getElementById('txt2img_extra_networks');
|
||||
if (!el || !en) return;
|
||||
const req = await fetch('/sdapi/v1/sd-models');
|
||||
const req = await fetch(`${window.api}/sd-models`);
|
||||
const res = req.ok ? await req.json() : [];
|
||||
log('initModels', res.length);
|
||||
const ready = () => `
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/* eslint-disable no-undef */
|
||||
window.api = '/sdapi/v1';
|
||||
window.subpath = '';
|
||||
|
||||
async function initStartup() {
|
||||
log('initStartup');
|
||||
@@ -23,6 +25,11 @@ async function initStartup() {
|
||||
|
||||
// make sure all of the ui is ready and options are loaded
|
||||
while (Object.keys(window.opts).length === 0) await sleep(50);
|
||||
log('mountURL', window.opts.subpath);
|
||||
if (window.opts.subpath?.length > 0) {
|
||||
window.subpath = window.opts.subpath;
|
||||
window.api = `${window.subpath}/sdapi/v1`;
|
||||
}
|
||||
executeCallbacks(uiReadyCallbacks);
|
||||
initLogMonitor();
|
||||
setupExtraNetworks();
|
||||
|
||||
+10
-5
@@ -127,13 +127,13 @@ function switch_to_img2img(...args) {
|
||||
return Array.from(arguments);
|
||||
}
|
||||
|
||||
function switch_to_sketch(...args) {
|
||||
function switch_to_inpaint(...args) {
|
||||
switchToTab('Image');
|
||||
switch_to_img2img_tab(1);
|
||||
return Array.from(arguments);
|
||||
}
|
||||
|
||||
function switch_to_inpaint(...args) {
|
||||
function switch_to_sketch(...args) {
|
||||
switchToTab('Image');
|
||||
switch_to_img2img_tab(2);
|
||||
return Array.from(arguments);
|
||||
@@ -155,6 +155,11 @@ function switch_to_control(...args) {
|
||||
return Array.from(arguments);
|
||||
}
|
||||
|
||||
function switch_to_caption(...args) {
|
||||
switchToTab('Caption');
|
||||
return Array.from(arguments);
|
||||
}
|
||||
|
||||
function get_tab_index(tabId) {
|
||||
let res = 0;
|
||||
gradioApp().getElementById(tabId).querySelector('div').querySelectorAll('button')
|
||||
@@ -385,7 +390,7 @@ function monitorServerStatus() {
|
||||
<h1>Waiting for server...</h1>
|
||||
<script>
|
||||
function monitorServerStatus() {
|
||||
fetch('/sdapi/v1/progress?skip_current_image=true')
|
||||
fetch('${window.api}/progress?skip_current_image=true')
|
||||
.then((res) => { !res?.ok ? setTimeout(monitorServerStatus, 1000) : location.reload(); })
|
||||
.catch((e) => setTimeout(monitorServerStatus, 1000))
|
||||
}
|
||||
@@ -400,7 +405,7 @@ function monitorServerStatus() {
|
||||
function restartReload() {
|
||||
document.body.style = 'background: #222222; font-size: 1rem; font-family:monospace; margin-top:20%; color:lightgray; text-align:center';
|
||||
document.body.innerHTML = '<h1>Server shutdown in progress...</h1>';
|
||||
fetch('/sdapi/v1/progress?skip_current_image=true')
|
||||
fetch(`${window.api}/progress?skip_current_image=true`)
|
||||
.then((res) => setTimeout(restartReload, 1000))
|
||||
.catch((e) => setTimeout(monitorServerStatus, 500));
|
||||
return [];
|
||||
@@ -479,7 +484,7 @@ function toggleCompact(val, old) {
|
||||
|
||||
function previewTheme() {
|
||||
let name = gradioApp().getElementById('setting_gradio_theme').querySelectorAll('input')?.[0].value || '';
|
||||
fetch('/file=html/themes.json')
|
||||
fetch(`${window.subpath}/file=html/themes.json`)
|
||||
.then((res) => {
|
||||
res.json()
|
||||
.then((themes) => {
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
+9
-6
@@ -32,7 +32,11 @@ class Api:
|
||||
self.generate = generate.APIGenerate(queue_lock)
|
||||
self.process = process.APIProcess(queue_lock)
|
||||
self.control = control.APIControl(queue_lock)
|
||||
# compatibility api
|
||||
self.text2imgapi = self.generate.post_text2img
|
||||
self.img2imgapi = self.generate.post_img2img
|
||||
|
||||
def register(self):
|
||||
# server api
|
||||
self.add_api_route("/sdapi/v1/motd", server.get_motd, methods=["GET"], response_model=str)
|
||||
self.add_api_route("/sdapi/v1/log", server.get_log, methods=["GET"], response_model=List[str])
|
||||
@@ -97,16 +101,15 @@ class Api:
|
||||
self.add_api_route("/sdapi/v1/refresh-loras", endpoints.post_refresh_loras, methods=["POST"])
|
||||
|
||||
# gallery api
|
||||
gallery.register_api(app)
|
||||
gallery.register_api(self.app)
|
||||
|
||||
# compatibility api
|
||||
self.text2imgapi = self.generate.post_text2img
|
||||
self.img2imgapi = self.generate.post_img2img
|
||||
|
||||
def add_api_route(self, path: str, endpoint, **kwargs):
|
||||
if (shared.cmd_opts.auth or shared.cmd_opts.auth_file) and shared.cmd_opts.api_only:
|
||||
return self.app.add_api_route(path, endpoint, dependencies=[Depends(self.auth)], **kwargs)
|
||||
return self.app.add_api_route(path, endpoint, **kwargs)
|
||||
kwargs['dependencies'] = [Depends(self.auth)]
|
||||
if shared.opts.subpath is not None and len(shared.opts.subpath) > 0:
|
||||
self.app.add_api_route(f'{shared.opts.subpath}{path}', endpoint, **kwargs)
|
||||
self.app.add_api_route(path, endpoint, **kwargs)
|
||||
|
||||
def auth(self, credentials: HTTPBasicCredentials = Depends(HTTPBasic())):
|
||||
# this is only needed for api-only since otherwise auth is handled in gradio/routes.py
|
||||
|
||||
@@ -113,7 +113,7 @@ def post_vqa(req: models.ReqVQA):
|
||||
image = helpers.decode_base64_to_image(req.image)
|
||||
image = image.convert('RGB')
|
||||
from modules.interrogate import vqa
|
||||
answer = vqa.interrogate(req.question, image, req.model)
|
||||
answer = vqa.interrogate(req.question, '', image, req.model)
|
||||
return models.ResVQA(answer=answer)
|
||||
|
||||
def post_unload_checkpoint():
|
||||
|
||||
@@ -126,7 +126,7 @@ def register_api(app: FastAPI): # register api
|
||||
shared.log.error(f'Gallery image: file="{filepath}" {e}')
|
||||
return {}
|
||||
|
||||
@app.get('/sdapi/v1/browser/folders', response_model=List[str])
|
||||
# @app.get('/sdapi/v1/browser/folders', response_model=List[str])
|
||||
def get_folders():
|
||||
folders = [shared.opts.data.get(f, '') for f in OPTS_FOLDERS]
|
||||
folders += list(shared.opts.browser_folders.split(','))
|
||||
@@ -141,7 +141,7 @@ def register_api(app: FastAPI): # register api
|
||||
debug(f'Browser folders: {folders}')
|
||||
return JSONResponse(content=folders)
|
||||
|
||||
@app.get("/sdapi/v1/browser/thumb", response_model=dict)
|
||||
# @app.get("/sdapi/v1/browser/thumb", response_model=dict)
|
||||
async def get_thumb(file: str):
|
||||
try:
|
||||
decoded = unquote(file).replace('%3A', ':')
|
||||
@@ -154,7 +154,7 @@ def register_api(app: FastAPI): # register api
|
||||
content = { 'error': str(e) }
|
||||
return JSONResponse(content=content)
|
||||
|
||||
@app.get("/sdapi/v1/browser/files", response_model=list)
|
||||
# @app.get("/sdapi/v1/browser/files", response_model=list)
|
||||
async def ht_files(folder: str):
|
||||
try:
|
||||
t0 = time.time()
|
||||
@@ -172,6 +172,10 @@ def register_api(app: FastAPI): # register api
|
||||
shared.log.error(f'Gallery: {folder} {e}')
|
||||
return []
|
||||
|
||||
shared.api.add_api_route("/sdapi/v1/browser/folders", get_folders, methods=["GET"], response_model=List[str])
|
||||
shared.api.add_api_route("/sdapi/v1/browser/thumb", get_thumb, methods=["GET"], response_model=dict)
|
||||
shared.api.add_api_route("/sdapi/v1/browser/files", ht_files, methods=["GET"], response_model=list)
|
||||
|
||||
@app.websocket("/sdapi/v1/browser/files")
|
||||
async def ws_files(ws: WebSocket):
|
||||
try:
|
||||
@@ -180,7 +184,7 @@ def register_api(app: FastAPI): # register api
|
||||
folder = unquote(folder).replace('%3A', ':')
|
||||
t0 = time.time()
|
||||
numFiles = 0
|
||||
files = files_cache.directory_files(folder, recursive=True)
|
||||
files = files_cache.list_files(folder, recursive=True)
|
||||
# files = list(files_cache.directory_files(folder, recursive=True))
|
||||
# files.sort(key=os.path.getmtime)
|
||||
for f in files:
|
||||
|
||||
@@ -71,6 +71,8 @@ def setup_middleware(app: FastAPI, cmd_opts):
|
||||
}
|
||||
if err['code'] == 401 and 'file=' in req.url.path: # dont spam with unauth
|
||||
return JSONResponse(status_code=err['code'], content=jsonable_encoder(err))
|
||||
if err['code'] == 404 and 'file=html/' in req.url.path: # dont spam with locales
|
||||
return JSONResponse(status_code=err['code'], content=jsonable_encoder(err))
|
||||
|
||||
log.error(f"API error: {req.method}: {req.url} {err}")
|
||||
|
||||
|
||||
@@ -328,6 +328,8 @@ class ReqInterrogate(BaseModel):
|
||||
clip_model: str = Field(default="", title="CLiP Model", description="The interrogate model used.")
|
||||
blip_model: str = Field(default="", title="BLiP Model", description="The interrogate model used.")
|
||||
|
||||
InterrogateRequest = ReqInterrogate # alias for backwards compatibility
|
||||
|
||||
class ResInterrogate(BaseModel):
|
||||
caption: Optional[str] = Field(default=None, title="Caption", description="The generated caption for the image.")
|
||||
medium: Optional[str] = Field(default=None, title="Medium", description="Image medium.")
|
||||
|
||||
+1
-2
@@ -39,6 +39,7 @@ def main_args():
|
||||
|
||||
group_http = parser.add_argument_group('HTTP')
|
||||
group_http.add_argument('--theme', type=str, default=os.environ.get("SD_THEME", None), help='Override UI theme')
|
||||
group_http.add_argument('--locale', type=str, default=os.environ.get("SD_LOCALE", None), help='Override UI locale')
|
||||
group_http.add_argument("--server-name", type=str, default=os.environ.get("SD_SERVERNAME", None), help="Sets hostname of server, default: %(default)s")
|
||||
group_http.add_argument("--tls-keyfile", type=str, default=os.environ.get("SD_TLSKEYFILE", None), help="Enable TLS and specify key file, default: %(default)s")
|
||||
group_http.add_argument("--tls-certfile", type=str, default=os.environ.get("SD_TLSCERTFILE", None), help="Enable TLS and specify cert file, default: %(default)s")
|
||||
@@ -68,7 +69,6 @@ def compatibility_args():
|
||||
group_compat.add_argument("--ui-settings-file", type=str, help=argparse.SUPPRESS, default=os.path.join(data_path, 'config.json'))
|
||||
group_compat.add_argument("--ui-config-file", type=str, help=argparse.SUPPRESS, default=os.path.join(data_path, 'ui-config.json'))
|
||||
group_compat.add_argument("--hide-ui-dir-config", action='store_true', help=argparse.SUPPRESS, default=False)
|
||||
group_compat.add_argument("--theme", type=str, help=argparse.SUPPRESS, default=None)
|
||||
group_compat.add_argument("--disable-console-progressbars", action='store_true', help=argparse.SUPPRESS, default=True)
|
||||
group_compat.add_argument("--disable-safe-unpickle", action='store_true', help=argparse.SUPPRESS, default=True)
|
||||
group_compat.add_argument("--lowram", action='store_true', help=argparse.SUPPRESS)
|
||||
@@ -88,7 +88,6 @@ def settings_args(opts, args):
|
||||
group_compat.add_argument("--ui-settings-file", type=str, help=argparse.SUPPRESS, default=os.path.join(data_path, 'config.json'))
|
||||
group_compat.add_argument("--ui-config-file", type=str, help=argparse.SUPPRESS, default=os.path.join(data_path, 'ui-config.json'))
|
||||
group_compat.add_argument("--hide-ui-dir-config", action='store_true', help=argparse.SUPPRESS, default=False)
|
||||
group_compat.add_argument("--theme", type=str, help=argparse.SUPPRESS, default=None)
|
||||
group_compat.add_argument("--disable-console-progressbars", action='store_true', help=argparse.SUPPRESS, default=True)
|
||||
group_compat.add_argument("--disable-safe-unpickle", action='store_true', help=argparse.SUPPRESS, default=True)
|
||||
group_compat.add_argument("--lowram", action='store_true', help=argparse.SUPPRESS)
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ def load_model(device, model_path, model_type="dpt_large_384", optimize=True, he
|
||||
network input
|
||||
"""
|
||||
if "openvino" in model_type:
|
||||
from openvino.runtime import Core
|
||||
from openvino import Core
|
||||
|
||||
keep_aspect_ratio = not square
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import os
|
||||
import time
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from modules.shared import log
|
||||
from installer import log
|
||||
from modules.errors import display
|
||||
from modules import devices, images
|
||||
|
||||
|
||||
@@ -611,6 +611,8 @@ def control_run(state: str = '',
|
||||
elif input_type == 1: # Init image same as control
|
||||
if 'control_image' in possible:
|
||||
p.task_args['control_image'] = p.init_images # switch image and control_image
|
||||
if 'control_mode' in possible:
|
||||
p.task_args['control_mode'] = getattr(p, 'control_mode', None)
|
||||
if 'strength' in possible:
|
||||
p.task_args['strength'] = p.denoising_strength
|
||||
p.init_images = [p.override or input_image] * len(active_model)
|
||||
@@ -620,9 +622,14 @@ def control_run(state: str = '',
|
||||
init_image = input_image
|
||||
if 'control_image' in possible:
|
||||
p.task_args['control_image'] = p.init_images # switch image and control_image
|
||||
if 'control_mode' in possible:
|
||||
p.task_args['control_mode'] = getattr(p, 'control_mode', None)
|
||||
if 'strength' in possible:
|
||||
p.task_args['strength'] = p.denoising_strength
|
||||
p.init_images = [init_image] * len(active_model)
|
||||
if hasattr(shared.sd_model, 'controlnet') and hasattr(p.task_args, 'control_image') and len(p.task_args['control_image']) > 1 and (shared.sd_model.__class__.__name__ == 'StableDiffusionXLControlNetUnionPipeline'): # special case for controlnet-union
|
||||
p.task_args['control_image'] = [[x] for x in p.task_args['control_image']]
|
||||
p.task_args['control_mode'] = [[x] for x in p.task_args['control_mode']]
|
||||
|
||||
if is_generator:
|
||||
image_txt = f'{blended_image.width}x{blended_image.height}' if blended_image is not None else 'None'
|
||||
@@ -664,8 +671,6 @@ def control_run(state: str = '',
|
||||
if unit_type == 'lite':
|
||||
p.init_image = [input_image]
|
||||
instance.apply(selected_models, processed_image, control_conditioning)
|
||||
if getattr(p, 'control_mode', None) is not None:
|
||||
p.task_args['control_mode'] = getattr(p, 'control_mode', None)
|
||||
if hasattr(p, 'init_images') and p.init_images is None: # delete empty
|
||||
del p.init_images
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import Union
|
||||
from PIL import Image
|
||||
import gradio as gr
|
||||
from modules.shared import log
|
||||
from installer import log
|
||||
from modules.control import processors
|
||||
from modules.control.units import controlnet
|
||||
from modules.control.units import xs
|
||||
|
||||
@@ -338,12 +338,11 @@ class ControlNetPipeline():
|
||||
classes = [c.__class__.__name__ for c in controlnets]
|
||||
if any(c == 'ControlNetUnionModel' for c in classes):
|
||||
if not all(c == 'ControlNetUnionModel' for c in classes):
|
||||
log.warning(f'Control {what}: units={classes} mixed type')
|
||||
log.warning(f'Control {what}: units={classes} mixed type is not supported')
|
||||
return
|
||||
if isinstance(controlnets, list) and len(controlnets) == 1:
|
||||
controlnets = controlnets[0]
|
||||
cls = StableDiffusionXLControlNetUnionPipeline
|
||||
if len(controlnets) > 1:
|
||||
# TODO controlnet-union multi-unit
|
||||
log.warning(f'Control {what}: units={classes} supports single unit only')
|
||||
controlnets = controlnets[0]
|
||||
else:
|
||||
cls = StableDiffusionXLControlNetPipeline
|
||||
self.pipeline = cls(
|
||||
|
||||
@@ -3,7 +3,7 @@ import time
|
||||
from typing import Union
|
||||
import threading
|
||||
from diffusers import StableDiffusionPipeline, StableDiffusionXLPipeline, T2IAdapter, MultiAdapter, StableDiffusionAdapterPipeline, StableDiffusionXLAdapterPipeline # pylint: disable=unused-import
|
||||
from modules.shared import log
|
||||
from installer import log
|
||||
from modules import errors, sd_models
|
||||
from modules.control.units import detect
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ def HWC3(x):
|
||||
|
||||
def make_noise_disk(H, W, C, F):
|
||||
noise = np.random.uniform(low=0, high=1, size=((H // F) + 2, (W // F) + 2, C))
|
||||
noise = cv2.resize(noise, (W + 2 * F, H + 2 * F), interpolation=cv2.INTER_CUBIC)
|
||||
noise = cv2.resize(noise, (W + 2 * F, H + 2 * F), interpolation=cv2.INTER_LANCZOS4)
|
||||
noise = noise[F: F + H, F: F + W]
|
||||
noise -= np.min(noise)
|
||||
noise /= np.max(noise)
|
||||
@@ -77,7 +77,7 @@ def img2mask(img, H, W, low=10, high=90):
|
||||
y = img[:, :, random.randrange(0, img.shape[2])]
|
||||
else:
|
||||
y = img
|
||||
y = cv2.resize(y, (W, H), interpolation=cv2.INTER_CUBIC)
|
||||
y = cv2.resize(y, (W, H), interpolation=cv2.INTER_LANCZOS4)
|
||||
if random.uniform(0, 1) < 0.5:
|
||||
y = 255 - y
|
||||
return y < np.percentile(y, random.randrange(low, high))
|
||||
@@ -92,7 +92,7 @@ def resize_image(input_image, resolution):
|
||||
W *= k
|
||||
H = int(np.round(H / 64.0)) * 64
|
||||
W = int(np.round(W / 64.0)) * 64
|
||||
img = cv2.resize(input_image, (W, H), interpolation=cv2.INTER_LANCZOS4 if k > 1 else cv2.INTER_AREA)
|
||||
img = cv2.resize(input_image, (W, H), interpolation=cv2.INTER_LANCZOS4)
|
||||
return img
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ def blend(images):
|
||||
y = np.zeros((images[0].shape[0], images[0].shape[1], 3), dtype=np.float32)
|
||||
for img in images:
|
||||
if img.shape[0] != y.shape[0] or img.shape[1] != y.shape[1]:
|
||||
img = cv2.resize(img, (y.shape[1], y.shape[0]), interpolation=cv2.INTER_CUBIC)
|
||||
img = cv2.resize(img, (y.shape[1], y.shape[0]), interpolation=cv2.INTER_LANCZOS4)
|
||||
if len(img.shape) == 3 and img.shape[2] == 4: # rgba to rgb
|
||||
img = cv2.cvtColor(img, cv2.COLOR_RGBA2RGB)
|
||||
if len(img.shape) == 2: # grayscale to rgb
|
||||
|
||||
+49
-37
@@ -6,7 +6,7 @@ from functools import wraps
|
||||
import torch
|
||||
from modules import rocm
|
||||
from modules.errors import log, display, install as install_traceback
|
||||
from installer import install
|
||||
from installer import install, installed
|
||||
|
||||
|
||||
debug = os.environ.get('SD_DEVICE_DEBUG', None) is not None
|
||||
@@ -409,36 +409,57 @@ def set_sdpa_params():
|
||||
try:
|
||||
if opts.cross_attention_optimization != "Scaled-Dot-Product":
|
||||
return
|
||||
try:
|
||||
torch.backends.cuda.enable_flash_sdp('Flash attention' in opts.sdp_options)
|
||||
torch.backends.cuda.enable_mem_efficient_sdp('Memory attention' in opts.sdp_options)
|
||||
torch.backends.cuda.enable_math_sdp('Math attention' in opts.sdp_options)
|
||||
except Exception as e:
|
||||
log.warning(f'Torch attention: {e}')
|
||||
try:
|
||||
global sdpa_original # pylint: disable=global-statement
|
||||
if sdpa_original is not None:
|
||||
torch.nn.functional.scaled_dot_product_attention = sdpa_original
|
||||
else:
|
||||
sdpa_original = torch.nn.functional.scaled_dot_product_attention
|
||||
except Exception as e:
|
||||
log.warning(f'Torch SDPA: {e}')
|
||||
if backend == "rocm":
|
||||
if 'Flash attention' in opts.sdp_options:
|
||||
try:
|
||||
# https://github.com/huggingface/diffusers/discussions/7172
|
||||
from flash_attn import flash_attn_func
|
||||
sdpa_pre_flash_atten = torch.nn.functional.scaled_dot_product_attention
|
||||
@wraps(sdpa_pre_flash_atten)
|
||||
def sdpa_flash_atten(query, key, value, attn_mask=None, dropout_p=0.0, is_causal=False, scale=None):
|
||||
if query.shape[-1] <= 128 and attn_mask is None and query.dtype != torch.float32:
|
||||
return flash_attn_func(q=query.transpose(1, 2), k=key.transpose(1, 2), v=value.transpose(1, 2), dropout_p=dropout_p, causal=is_causal, softmax_scale=scale).transpose(1, 2)
|
||||
else:
|
||||
return sdpa_pre_flash_atten(query=query, key=key, value=value, attn_mask=attn_mask, dropout_p=dropout_p, is_causal=is_causal, scale=scale)
|
||||
torch.nn.functional.scaled_dot_product_attention = sdpa_flash_atten
|
||||
log.debug('Torch ROCm Flash Attention')
|
||||
except Exception as err:
|
||||
log.error(f'Torch ROCm Flash Attention: {err}')
|
||||
except Exception as err:
|
||||
log.warning(f'Torch attention: type="sdpa" {err}')
|
||||
|
||||
try:
|
||||
torch.backends.cuda.enable_flash_sdp('Flash attention' in opts.sdp_options)
|
||||
torch.backends.cuda.enable_mem_efficient_sdp('Memory attention' in opts.sdp_options)
|
||||
torch.backends.cuda.enable_math_sdp('Math attention' in opts.sdp_options)
|
||||
log.debug(f'Torch attention: type="sdpa" flash={"Flash attention" in opts.sdp_options} memory={"Memory attention" in opts.sdp_options} math={"Math attention" in opts.sdp_options}')
|
||||
except Exception as err:
|
||||
log.warning(f'Torch attention: type="sdpa" {err}')
|
||||
|
||||
# Stack hijcaks in reverse order. This gives priority to the last added hijack.
|
||||
# If the last hijack is not compatible, it will use the one before it and so on.
|
||||
|
||||
if 'Dynamic attention' in opts.sdp_options:
|
||||
try:
|
||||
global sdpa_pre_dyanmic_atten # pylint: disable=global-statement
|
||||
sdpa_pre_dyanmic_atten = torch.nn.functional.scaled_dot_product_attention
|
||||
from modules.sd_hijack_dynamic_atten import dynamic_scaled_dot_product_attention
|
||||
torch.nn.functional.scaled_dot_product_attention = dynamic_scaled_dot_product_attention
|
||||
log.debug('Torch attention: type="dynamic attention"')
|
||||
except Exception as err:
|
||||
log.error(f'Torch attention: type="dynamic attention" {err}')
|
||||
|
||||
if 'CK Flash attention' in opts.sdp_options:
|
||||
try:
|
||||
if backend == "rocm":
|
||||
if not installed('flash-attn'):
|
||||
agent = rocm.Agent(getattr(torch.cuda.get_device_properties(device), "gcnArchName", "gfx0000"))
|
||||
install(rocm.get_flash_attention_command(agent), reinstall=True)
|
||||
else:
|
||||
install('flash-attn')
|
||||
from flash_attn import flash_attn_func
|
||||
sdpa_pre_flash_atten = torch.nn.functional.scaled_dot_product_attention
|
||||
@wraps(sdpa_pre_flash_atten)
|
||||
def sdpa_flash_atten(query, key, value, attn_mask=None, dropout_p=0.0, is_causal=False, scale=None):
|
||||
if query.shape[-1] <= 128 and attn_mask is None and query.dtype != torch.float32:
|
||||
return flash_attn_func(q=query.transpose(1, 2), k=key.transpose(1, 2), v=value.transpose(1, 2), dropout_p=dropout_p, causal=is_causal, softmax_scale=scale).transpose(1, 2)
|
||||
else:
|
||||
return sdpa_pre_flash_atten(query=query, key=key, value=value, attn_mask=attn_mask, dropout_p=dropout_p, is_causal=is_causal, scale=scale)
|
||||
torch.nn.functional.scaled_dot_product_attention = sdpa_flash_atten
|
||||
log.debug('Torch attention: type="ck flash attention"')
|
||||
except Exception as err:
|
||||
log.error(f'Torch attention: type="ck flash attention" {err}')
|
||||
|
||||
if 'Sage attention' in opts.sdp_options:
|
||||
try:
|
||||
install('sageattention')
|
||||
@@ -446,23 +467,14 @@ def set_sdpa_params():
|
||||
sdpa_pre_sage_atten = torch.nn.functional.scaled_dot_product_attention
|
||||
@wraps(sdpa_pre_sage_atten)
|
||||
def sdpa_sage_atten(query, key, value, attn_mask=None, dropout_p=0.0, is_causal=False, scale=None):
|
||||
if query.shape[-1] in {128, 96, 64} and attn_mask is None and query.dtype != torch.float32:
|
||||
if (query.shape[-1] in {128, 96, 64}) and (attn_mask is None) and (query.dtype != torch.float32):
|
||||
return sageattn(q=query, k=key, v=value, attn_mask=attn_mask, dropout_p=dropout_p, is_causal=is_causal, scale=scale)
|
||||
else:
|
||||
return sdpa_pre_sage_atten(query=query, key=key, value=value, attn_mask=attn_mask, dropout_p=dropout_p, is_causal=is_causal, scale=scale)
|
||||
torch.nn.functional.scaled_dot_product_attention = sdpa_sage_atten
|
||||
log.debug('Torch SDPA Sage Attention')
|
||||
log.debug('Torch attention: type="sage attention"')
|
||||
except Exception as err:
|
||||
log.error(f'Torch SDPA Sage Attention: {err}')
|
||||
if 'Dynamic attention' in opts.sdp_options:
|
||||
try:
|
||||
global sdpa_pre_dyanmic_atten # pylint: disable=global-statement
|
||||
sdpa_pre_dyanmic_atten = torch.nn.functional.scaled_dot_product_attention
|
||||
from modules.sd_hijack_dynamic_atten import dynamic_scaled_dot_product_attention
|
||||
torch.nn.functional.scaled_dot_product_attention = dynamic_scaled_dot_product_attention
|
||||
log.debug('Torch SDPA Dynamic Attention')
|
||||
except Exception as err:
|
||||
log.error(f'Torch SDPA Dynamic Attention: {err}')
|
||||
log.error(f'Torch attention: type="sage attention" {err}')
|
||||
except Exception as e:
|
||||
log.warning(f'Torch SDPA: {e}')
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import math
|
||||
import torch
|
||||
from modules.postprocess.realesrgan_model_arch import RealESRGANer
|
||||
from modules.shared import log
|
||||
from installer import log
|
||||
|
||||
|
||||
# DML Solution: Some of contents of output tensor turn to 0 after Extended Slices. Move it to cpu.
|
||||
|
||||
@@ -162,7 +162,10 @@ def parse_prompt(prompt):
|
||||
args = m.group(2)
|
||||
res[name].append(ExtraNetworkParams(items=args.split(":")))
|
||||
return ""
|
||||
prompt = re.sub(re_extra_net, found, prompt)
|
||||
if isinstance(prompt, list):
|
||||
prompt = [re.sub(re_extra_net, found, p) for p in prompt]
|
||||
else:
|
||||
prompt = re.sub(re_extra_net, found, prompt)
|
||||
return prompt, res
|
||||
|
||||
|
||||
|
||||
@@ -10,11 +10,12 @@ instightface_mp = None
|
||||
def get_app(mp_name, threshold=0.5, resolution=640):
|
||||
global insightface_app, instightface_mp # pylint: disable=global-statement
|
||||
|
||||
from installer import install, installed
|
||||
from installer import install, installed, reload
|
||||
if not installed('insightface', reload=False, quiet=True):
|
||||
install('insightface', 'insightface', ignore=False)
|
||||
install('albumentations==1.4.3', 'albumentations', ignore=False, reinstall=True)
|
||||
install('pydantic==1.10.21', 'pydantic', ignore=False, reinstall=True)
|
||||
install('insightface==0.7.3', ignore=False)
|
||||
install('albumentations==1.4.3', ignore=False, reinstall=True)
|
||||
install('pydantic==1.10.21', ignore=False, reinstall=True, force=True)
|
||||
reload('pydantic')
|
||||
if not installed('ip_adapter', reload=False, quiet=True):
|
||||
install('git+https://github.com/tencent-ailab/IP-Adapter.git', 'ip_adapter', ignore=False)
|
||||
|
||||
|
||||
@@ -101,6 +101,8 @@ def create_buttons(tabs_list):
|
||||
name = 'Process'
|
||||
elif name == 'control':
|
||||
name = 'Control'
|
||||
elif name == 'caption':
|
||||
name = 'Caption'
|
||||
buttons[tab] = gr.Button(f"➠ {name}", elem_id=f"{tab}_tab")
|
||||
return buttons
|
||||
|
||||
|
||||
@@ -32,12 +32,14 @@ def apply(p, model_type):
|
||||
hidiffusion.switching_threshold_ratio_dict['sdxl_4096']['T2_ratio'] = t2
|
||||
hidiffusion.switching_threshold_ratio_dict['sdxl_turbo_1024']['T2_ratio'] = t2
|
||||
p.extra_generation_params['HiDiffusion Ratios'] = f'{shared.opts.hidiffusion_t1}/{shared.opts.hidiffusion_t2}'
|
||||
hidiffusion.apply_hidiffusion(shared.sd_model, apply_raunet=shared.opts.hidiffusion_raunet, apply_window_attn=shared.opts.hidiffusion_attn, model_type=model_type)
|
||||
pipe = shared.sd_model.pipe if hasattr(shared.sd_model, 'pipe') else shared.sd_model
|
||||
hidiffusion.apply_hidiffusion(pipe, apply_raunet=shared.opts.hidiffusion_raunet, apply_window_attn=shared.opts.hidiffusion_attn, model_type=model_type, steps=p.steps)
|
||||
p.extra_generation_params['HiDiffusion'] = f'{shared.opts.hidiffusion_raunet}/{shared.opts.hidiffusion_attn}/{shared.opts.hidiffusion_steps > 0}:{shared.opts.hidiffusion_steps}'
|
||||
t1 = time.time()
|
||||
shared.log.debug(f'HiDiffusion apply: raunet={shared.opts.hidiffusion_raunet} attn={shared.opts.hidiffusion_attn} aggressive={shared.opts.hidiffusion_steps > 0}:{shared.opts.hidiffusion_steps} t1={shared.opts.hidiffusion_t1} t2={shared.opts.hidiffusion_t2} time={t1-t0:.2f} type={shared.sd_model_type} width={p.width} height={p.height}')
|
||||
|
||||
|
||||
def unapply():
|
||||
if hasattr(shared.sd_model, "unet"):
|
||||
hidiffusion.remove_hidiffusion(shared.sd_model)
|
||||
pipe = shared.sd_model.pipe if hasattr(shared.sd_model, 'pipe') else shared.sd_model
|
||||
if hasattr(pipe, 'unet'):
|
||||
hidiffusion.remove_hidiffusion(pipe)
|
||||
|
||||
@@ -6,6 +6,7 @@ from diffusers.utils.torch_utils import is_torch_version
|
||||
from diffusers.pipelines import auto_pipeline
|
||||
|
||||
|
||||
current_steps = 50
|
||||
def sd15_hidiffusion_key():
|
||||
modified_key = dict()
|
||||
modified_key['down_module_key'] = ['down_blocks.0.downsamplers.0.conv']
|
||||
@@ -163,12 +164,14 @@ def make_diffusers_transformer_block(block_class: Type[torch.nn.Module]) -> Type
|
||||
widow_size = (math.ceil(H/2), math.ceil(W/2))
|
||||
if rand_num <= 0.25:
|
||||
shift_size = (0,0)
|
||||
if rand_num > 0.25 and rand_num <= 0.5:
|
||||
elif rand_num > 0.25 and rand_num <= 0.5:
|
||||
shift_size = (widow_size[0]//4, widow_size[1]//4)
|
||||
if rand_num > 0.5 and rand_num <= 0.75:
|
||||
elif rand_num > 0.5 and rand_num <= 0.75:
|
||||
shift_size = (widow_size[0]//4*2, widow_size[1]//4*2)
|
||||
if rand_num > 0.75 and rand_num <= 1:
|
||||
elif rand_num > 0.75 and rand_num <= 1:
|
||||
shift_size = (widow_size[0]//4*3, widow_size[1]//4*3)
|
||||
else:
|
||||
shift_size = (0,0)
|
||||
norm_hidden_states = window_partition(norm_hidden_states, widow_size, shift_size, H, W)
|
||||
|
||||
# 1. Retrieve lora scale.
|
||||
@@ -261,7 +264,7 @@ def make_diffusers_cross_attn_down_block(block_class: Type[torch.nn.Module]) ->
|
||||
T1_start = 0
|
||||
T1_end = 0
|
||||
T1 = 0 # to avoid confict with sdxl-turbo
|
||||
max_timestep = 50
|
||||
max_timestep = current_steps
|
||||
|
||||
def forward(
|
||||
self,
|
||||
@@ -273,6 +276,8 @@ def make_diffusers_cross_attn_down_block(block_class: Type[torch.nn.Module]) ->
|
||||
encoder_attention_mask: Optional[torch.FloatTensor] = None,
|
||||
additional_residuals: Optional[torch.FloatTensor] = None,
|
||||
) -> Tuple[torch.FloatTensor, Tuple[torch.FloatTensor, ...]]:
|
||||
if not hasattr(self.info['pipeline'], '_num_timesteps'):
|
||||
self.info['pipeline']._num_timesteps = self.max_timestep # pylint: disable=protected-access
|
||||
self.max_timestep = self.info['pipeline']._num_timesteps # pylint: disable=protected-access
|
||||
# self.max_timestep = len(self.info['scheduler'].timesteps)
|
||||
ori_H, ori_W = self.info['size']
|
||||
@@ -618,13 +623,15 @@ def apply_hidiffusion(
|
||||
model: torch.nn.Module,
|
||||
apply_raunet: bool = True,
|
||||
apply_window_attn: bool = True,
|
||||
model_type: str = 'None'):
|
||||
model_type: str = 'None',
|
||||
steps: int = 50):
|
||||
"""
|
||||
model: diffusers model. We support SD 1.5, 2.1, XL, XL Turbo.
|
||||
apply_raunet: whether to apply RAU-Net
|
||||
apply_window_attn: whether to apply MSW-MSA.
|
||||
"""
|
||||
|
||||
global current_steps # pylint: disable=global-statement
|
||||
current_steps = steps
|
||||
if hasattr(model, 'controlnet'):
|
||||
from .hidiffusion_controlnet import make_diffusers_sdxl_contrtolnet_ppl, make_diffusers_unet_2d_condition
|
||||
make_ppl_fn = make_diffusers_sdxl_contrtolnet_ppl
|
||||
|
||||
@@ -26,6 +26,22 @@ except Exception:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def sanitize_filename_part(text, replace_spaces=True):
|
||||
if text is None:
|
||||
return None
|
||||
if replace_spaces:
|
||||
text = text.replace(' ', '_')
|
||||
invalid_filename_chars = '#<>:"/\\|?*\n\r\t'
|
||||
invalid_filename_prefix = ' '
|
||||
invalid_filename_postfix = ' .'
|
||||
max_filename_part_length = 64
|
||||
text = text.translate({ord(x): '_' for x in invalid_filename_chars})
|
||||
text = text.lstrip(invalid_filename_prefix)[:max_filename_part_length]
|
||||
text = text.rstrip(invalid_filename_postfix)
|
||||
return text
|
||||
|
||||
|
||||
def atomically_save_image():
|
||||
Image.MAX_IMAGE_PIXELS = None # disable check in Pillow and rely on check below to allow large custom image sizes
|
||||
while True:
|
||||
|
||||
@@ -71,6 +71,8 @@ class FilenameGenerator:
|
||||
self.prompt = prompt
|
||||
else:
|
||||
self.prompt = p.prompt if p is not None else ''
|
||||
if isinstance(self.prompt, list):
|
||||
self.prompt = ' '.join(self.prompt)
|
||||
self.image = image
|
||||
if not grid:
|
||||
self.batch_number = NOTHING if self.p is None or getattr(self.p, 'batch_size', 1) == 1 else (self.p.batch_index + 1 if hasattr(self.p, 'batch_index') else NOTHING)
|
||||
@@ -205,6 +207,8 @@ class FilenameGenerator:
|
||||
break
|
||||
pattern, arg = m.groups()
|
||||
pattern_args.insert(0, arg)
|
||||
if isinstance(pattern, list):
|
||||
pattern = ' '.join(pattern)
|
||||
fun = self.replacements.get(pattern.lower(), None)
|
||||
if fun is not None:
|
||||
try:
|
||||
@@ -212,6 +216,7 @@ class FilenameGenerator:
|
||||
replacement = fun(self, *pattern_args)
|
||||
except Exception as e:
|
||||
replacement = None
|
||||
errors.display(e, 'Filename apply pattern')
|
||||
shared.log.error(f'Filename apply pattern: {x} {e}')
|
||||
if replacement == NOTHING:
|
||||
continue
|
||||
|
||||
@@ -90,6 +90,9 @@ def parse(infotext):
|
||||
# debug(f'Negative: {negative}')
|
||||
|
||||
params = dict(re_param.findall(remaining))
|
||||
if len(list(params)) == 0:
|
||||
params['Prompt'] = infotext
|
||||
return params
|
||||
params['Prompt'] = prompt
|
||||
params['Negative prompt'] = negative
|
||||
for key, val in params.copy().items():
|
||||
|
||||
@@ -189,6 +189,7 @@ def ipex_init(): # pylint: disable=too-many-statements
|
||||
torch._C._XpuDeviceProperties.minor = 1
|
||||
|
||||
# Fix functions with ipex:
|
||||
# torch.xpu.mem_get_info always returns the total memory as free memory
|
||||
torch.xpu.mem_get_info = lambda device=None: [(torch.xpu.get_device_properties(device).total_memory - torch.xpu.memory_reserved(device)), torch.xpu.get_device_properties(device).total_memory]
|
||||
torch.cuda.mem_get_info = torch.xpu.mem_get_info
|
||||
torch._utils._get_available_device_type = lambda: "xpu"
|
||||
|
||||
@@ -3,10 +3,10 @@ import sys
|
||||
import torch
|
||||
import nncf
|
||||
|
||||
from openvino.frontend import FrontEndManager
|
||||
from openvino.frontend.pytorch.fx_decoder import TorchFXPythonDecoder
|
||||
from openvino.frontend.pytorch.torchdynamo.partition import Partitioner
|
||||
from openvino.runtime import Core, Type, PartialShape, serialize
|
||||
from openvino.frontend.pytorch.fx_decoder import TorchFXPythonDecoder
|
||||
from openvino.frontend import FrontEndManager
|
||||
from openvino import Core, Type, PartialShape, serialize
|
||||
from openvino.properties import hint as ov_hints
|
||||
|
||||
from torch._dynamo.backends.common import fake_tensor_unsupported
|
||||
@@ -23,6 +23,23 @@ import functools
|
||||
from modules import shared, devices, sd_models
|
||||
|
||||
|
||||
# importing openvino.runtime forces DeprecationWarning to "always"
|
||||
# And Intel's own libs (NNCF) imports the deprecated module
|
||||
# Reset the warnings back to ignore:
|
||||
try:
|
||||
import warnings
|
||||
import openvino.runtime # pylint: disable=unused-import
|
||||
warnings.filterwarnings(action="ignore", category=DeprecationWarning)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
torch._dynamo.config.cache_size_limit = 64 # pylint: disable=protected-access
|
||||
torch._dynamo.eval_frame.check_if_dynamo_supported = lambda: True # pylint: disable=protected-access
|
||||
if hasattr(torch._dynamo.config, "inline_inbuilt_nn_modules"):
|
||||
torch._dynamo.config.inline_inbuilt_nn_modules = False # pylint: disable=protected-access
|
||||
|
||||
|
||||
DEFAULT_OPENVINO_PYTHON_CONFIG = MappingProxyType(
|
||||
{
|
||||
"use_python_fusion_cache": True,
|
||||
@@ -114,9 +131,9 @@ def cached_model_name(model_hash_str, device, args, cache_root, reversed = False
|
||||
for input_data in args:
|
||||
if isinstance(input_data, torch.SymInt):
|
||||
if reversed:
|
||||
inputs_str = "_" + "torch.SymInt1" + inputs_str
|
||||
inputs_str = "_" + "torch.SymInt[]" + inputs_str
|
||||
else:
|
||||
inputs_str += "_" + "torch.SymInt1"
|
||||
inputs_str += "_" + "torch.SymInt[]"
|
||||
elif isinstance(input_data, int):
|
||||
pass
|
||||
else:
|
||||
@@ -176,7 +193,7 @@ def openvino_compile(gm: GraphModule, *example_inputs, model_hash_str: str = Non
|
||||
for input_data in example_inputs:
|
||||
if isinstance(input_data, torch.SymInt):
|
||||
input_types.append(torch.SymInt)
|
||||
input_shapes.append(torch.Size([1]))
|
||||
input_shapes.append(torch.Size([]))
|
||||
elif isinstance(input_data, int):
|
||||
pass
|
||||
else:
|
||||
@@ -426,9 +443,8 @@ def get_subgraph_type(tensor):
|
||||
return tensor
|
||||
|
||||
|
||||
@register_backend
|
||||
@fake_tensor_unsupported
|
||||
def openvino_fx(subgraph, example_inputs):
|
||||
def openvino_fx(subgraph, example_inputs, options=None):
|
||||
global dont_use_4bit_nncf
|
||||
global dont_use_nncf
|
||||
global dont_use_quant
|
||||
@@ -528,6 +544,8 @@ def openvino_fx(subgraph, example_inputs):
|
||||
for node in model.graph.nodes:
|
||||
if node.target == torch.ops.aten.mul_.Tensor:
|
||||
node.target = torch.ops.aten.mul.Tensor
|
||||
elif node.target == torch.ops.aten._unsafe_index.Tensor:
|
||||
node.target = torch.ops.aten.index.Tensor
|
||||
with devices.inference_context():
|
||||
model.eval()
|
||||
partitioner = Partitioner(options=None)
|
||||
@@ -543,3 +561,7 @@ def openvino_fx(subgraph, example_inputs):
|
||||
res = execute(compiled_model, *args, executor="openvino", executor_parameters=executor_parameters, file_name=maybe_fs_cached_name)
|
||||
return res
|
||||
return _call
|
||||
|
||||
|
||||
if "openvino_fx" not in torch.compiler.list_backends():
|
||||
register_backend(compiler_fn=openvino_fx, name="openvino_fx")
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
# converted from <https://github.com/city96/SD-Latent-Interposer>
|
||||
|
||||
import os
|
||||
import time
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from safetensors.torch import load_file
|
||||
|
||||
|
||||
# v1 = Stable Diffusion 1.x
|
||||
# xl = Stable Diffusion Extra Large (SDXL)
|
||||
# v3 = Stable Diffusion Version Three (SD3)
|
||||
# fx = Black Forest Labs Flux dot One
|
||||
# cc = Stable Cascade (Stage C) [not used]
|
||||
# ca = Stable Cascade (Stage A/B)
|
||||
config = {
|
||||
"v1-to-xl": {"ch_in": 4, "ch_out": 4, "ch_mid": 64, "scale": 1.0, "blocks": 12},
|
||||
"v1-to-v3": {"ch_in": 4, "ch_out":16, "ch_mid": 64, "scale": 1.0, "blocks": 12},
|
||||
"xl-to-v1": {"ch_in": 4, "ch_out": 4, "ch_mid": 64, "scale": 1.0, "blocks": 12},
|
||||
"xl-to-v3": {"ch_in": 4, "ch_out":16, "ch_mid": 64, "scale": 1.0, "blocks": 12},
|
||||
"v3-to-v1": {"ch_in":16, "ch_out": 4, "ch_mid": 64, "scale": 1.0, "blocks": 12},
|
||||
"v3-to-xl": {"ch_in":16, "ch_out": 4, "ch_mid": 64, "scale": 1.0, "blocks": 12},
|
||||
"fx-to-v1": {"ch_in":16, "ch_out": 4, "ch_mid": 64, "scale": 1.0, "blocks": 12},
|
||||
"fx-to-xl": {"ch_in":16, "ch_out": 4, "ch_mid": 64, "scale": 1.0, "blocks": 12},
|
||||
"fx-to-v3": {"ch_in":16, "ch_out":16, "ch_mid": 64, "scale": 1.0, "blocks": 12},
|
||||
"ca-to-v1": {"ch_in": 4, "ch_out": 4, "ch_mid": 64, "scale": 0.5, "blocks": 12},
|
||||
"ca-to-xl": {"ch_in": 4, "ch_out": 4, "ch_mid": 64, "scale": 0.5, "blocks": 12},
|
||||
"ca-to-v3": {"ch_in": 4, "ch_out":16, "ch_mid": 64, "scale": 0.5, "blocks": 12},
|
||||
}
|
||||
|
||||
|
||||
class ResBlock(nn.Module):
|
||||
"""Block with residuals"""
|
||||
def __init__(self, ch):
|
||||
super().__init__()
|
||||
self.join = nn.ReLU()
|
||||
self.norm = nn.BatchNorm2d(ch)
|
||||
self.long = nn.Sequential(
|
||||
nn.Conv2d(ch, ch, kernel_size=3, stride=1, padding=1),
|
||||
nn.SiLU(),
|
||||
nn.Conv2d(ch, ch, kernel_size=3, stride=1, padding=1),
|
||||
nn.SiLU(),
|
||||
nn.Conv2d(ch, ch, kernel_size=3, stride=1, padding=1),
|
||||
nn.Dropout(0.1)
|
||||
)
|
||||
def forward(self, x):
|
||||
x = self.norm(x)
|
||||
return self.join(self.long(x) + x)
|
||||
|
||||
|
||||
class ExtractBlock(nn.Module):
|
||||
"""Increase no. of channels by [out/in]"""
|
||||
def __init__(self, ch_in, ch_out):
|
||||
super().__init__()
|
||||
self.join = nn.ReLU()
|
||||
self.short = nn.Conv2d(ch_in, ch_out, kernel_size=3, stride=1, padding=1)
|
||||
self.long = nn.Sequential(
|
||||
nn.Conv2d( ch_in, ch_out, kernel_size=3, stride=1, padding=1),
|
||||
nn.SiLU(),
|
||||
nn.Conv2d(ch_out, ch_out, kernel_size=3, stride=1, padding=1),
|
||||
nn.SiLU(),
|
||||
nn.Conv2d(ch_out, ch_out, kernel_size=3, stride=1, padding=1),
|
||||
nn.Dropout(0.1)
|
||||
)
|
||||
def forward(self, x):
|
||||
return self.join(self.long(x) + self.short(x))
|
||||
|
||||
|
||||
class InterposerModel(nn.Module):
|
||||
"""
|
||||
NN layout, ported from:
|
||||
https://github.com/city96/SD-Latent-Interposer/blob/main/interposer.py
|
||||
"""
|
||||
def __init__(self, ch_in=4, ch_out=4, ch_mid=64, scale=1.0, blocks=12):
|
||||
super().__init__()
|
||||
self.ch_in = ch_in
|
||||
self.ch_out = ch_out
|
||||
self.ch_mid = ch_mid
|
||||
self.blocks = blocks
|
||||
self.scale = scale
|
||||
|
||||
self.head = ExtractBlock(self.ch_in, self.ch_mid)
|
||||
self.core = nn.Sequential(
|
||||
nn.Upsample(scale_factor=self.scale, mode="nearest"),
|
||||
*[ResBlock(self.ch_mid) for _ in range(blocks)],
|
||||
nn.BatchNorm2d(self.ch_mid),
|
||||
nn.SiLU(),
|
||||
)
|
||||
self.tail = nn.Conv2d(self.ch_mid, self.ch_out, kernel_size=3, stride=1, padding=1)
|
||||
|
||||
def forward(self, x):
|
||||
y = self.head(x)
|
||||
z = self.core(y)
|
||||
return self.tail(z)
|
||||
|
||||
|
||||
def map_model_name(name: str):
|
||||
if name == 'sd':
|
||||
return 'v1'
|
||||
if name == 'sdxl':
|
||||
return 'xl'
|
||||
if name == 'sd3':
|
||||
return 'v3'
|
||||
if name == 'f1':
|
||||
return 'fx'
|
||||
return name
|
||||
|
||||
|
||||
class Interposer:
|
||||
def __init__(self):
|
||||
self.version = 4.0 # network revision
|
||||
self.loaded = None # current model name
|
||||
self.model = None # current model
|
||||
self.vae = None # current VAE
|
||||
|
||||
def convert(self, src: str, dst: str, latents: torch.Tensor):
|
||||
from diffusers import AutoencoderKL
|
||||
from huggingface_hub import hf_hub_download
|
||||
from modules import shared, devices
|
||||
|
||||
src = map_model_name(src)
|
||||
dst = map_model_name(dst)
|
||||
if src == dst:
|
||||
return None
|
||||
model_name = f"{src}-to-{dst}"
|
||||
if model_name not in config:
|
||||
shared.log.error(f'Interposer: model="{model_name}" unknown')
|
||||
return None
|
||||
if (self.loaded != model_name) or (self.model is None):
|
||||
model_fn = hf_hub_download(
|
||||
repo_id="city96/SD-Latent-Interposer",
|
||||
subfolder=f"v{self.version}",
|
||||
filename=f"{model_name}_interposer-v{self.version}.safetensors",
|
||||
cache_dir=shared.opts.hfcache_dir,
|
||||
)
|
||||
self.model = InterposerModel(**config[model_name])
|
||||
self.model = self.model.to(device=devices.cpu, dtype=torch.float32)
|
||||
self.model.eval()
|
||||
self.model.load_state_dict(load_file(model_fn))
|
||||
self.loaded = model_name
|
||||
if dst == 'v1':
|
||||
vae_repo = 'stable-diffusion-v1-5/stable-diffusion-v1-5'
|
||||
self.vae = AutoencoderKL.from_pretrained(vae_repo, subfolder='vae', cache_dir=shared.opts.hfcache_dir, torch_dtype=devices.dtype)
|
||||
elif dst == 'xl':
|
||||
vae_repo = 'madebyollin/sdxl-vae-fp16-fix'
|
||||
self.vae = AutoencoderKL.from_pretrained(vae_repo, cache_dir=shared.opts.hfcache_dir, torch_dtype=devices.dtype)
|
||||
elif dst == 'v3':
|
||||
vae_repo = 'stabilityai/stable-diffusion-3.5-large'
|
||||
self.vae = AutoencoderKL.from_pretrained(vae_repo, subfolder='vae', cache_dir=shared.opts.hfcache_dir, torch_dtype=devices.dtype)
|
||||
elif dst == 'fx':
|
||||
vae_repo = 'black-forest-labs/FLUX.1-dev'
|
||||
self.vae = AutoencoderKL.from_pretrained(vae_repo, subfolder='vae', cache_dir=shared.opts.hfcache_dir, torch_dtype=devices.dtype)
|
||||
|
||||
t0 = time.time()
|
||||
if self.model is None or self.vae is None:
|
||||
return None
|
||||
with torch.no_grad():
|
||||
latent = latents.clone().cpu().float() # force fp32, always run on CPU
|
||||
output = self.model(latent)
|
||||
output = output.to(device=latents.device, dtype=latents.dtype)
|
||||
t1 = time.time()
|
||||
shared.log.debug(f'Interposer: src={src}/{list(latents.shape)} dst={dst}/{list(output.shape)} model="{os.path.basename(model_fn)}" vae="{vae_repo}" time={t1-t0:.2f}')
|
||||
# shared.log.debug(f'Interposer: src={latents.aminmax()} dst={output.aminmax()}')
|
||||
return output
|
||||
@@ -81,8 +81,8 @@ class DeepDanbooru:
|
||||
tag_outformat = tag_outformat.replace('_', ' ')
|
||||
if shared.opts.deepbooru_escape:
|
||||
tag_outformat = re.sub(re_special, r'\\\1', tag_outformat)
|
||||
if shared.opts.deepbooru_clip_score and not force_disable_ranks:
|
||||
tag_outformat = f"({tag_outformat}:{probability:.3f})"
|
||||
if shared.opts.interrogate_score and not force_disable_ranks:
|
||||
tag_outformat = f"({tag_outformat}:{probability:.2f})"
|
||||
res.append(tag_outformat)
|
||||
if len(res) > shared.opts.deepbooru_max_tags:
|
||||
res = res[:shared.opts.deepbooru_max_tags]
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
# source: <https://huggingface.co/deepseek-ai/deepseek-vl2-tiny>
|
||||
# implementation: <https://github.com/deepseek-ai/DeepSeek-VL2/tree/main/deepseek_vl2/serve>
|
||||
"""
|
||||
- run `git clone https://github.com/deepseek-ai/DeepSeek-VL2 repositories/deepseek-vl2 --depth 1`
|
||||
- remove hardcoded `python==3.9` requirement due to obsolete attrdict package dependency
|
||||
- patch transformers due to internal changes as deepseek requires obsolete `transformers==4.38.2`
|
||||
- deepseek requires `xformers`
|
||||
- broken flash_attention
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import importlib
|
||||
from transformers import AutoModelForCausalLM
|
||||
from modules import shared, devices, paths
|
||||
|
||||
|
||||
# model_path = "deepseek-ai/deepseek-vl2-small"
|
||||
vl_gpt = None
|
||||
vl_chat_processor = None
|
||||
|
||||
|
||||
class fake_attrdict():
|
||||
class AttrDict(dict): # dot notation access to dictionary attributes
|
||||
__getattr__ = dict.get
|
||||
__setattr__ = dict.__setitem__
|
||||
__delattr__ = dict.__delitem__
|
||||
|
||||
# def fake_is_flash_attn_2_available():
|
||||
# return False
|
||||
|
||||
|
||||
def predict(question, image, repo):
|
||||
global vl_gpt, vl_chat_processor # pylint: disable=global-statement
|
||||
if not shared.cmd_opts.experimental:
|
||||
shared.log.error(f'Interrogate: type=vlm model="DeepSeek VL2" repo="{repo}" is experimental-only')
|
||||
return ''
|
||||
folder = os.path.join(paths.script_path, 'repositories', 'deepseek-vl2')
|
||||
if not os.path.exists(folder):
|
||||
shared.log.error(f'Interrogate: type=vlm model="DeepSeek VL2" repo="{repo}" deepseek-vl2 repo not found')
|
||||
return ''
|
||||
if vl_gpt is None:
|
||||
sys.modules['attrdict'] = fake_attrdict
|
||||
from transformers.models.llama import modeling_llama
|
||||
modeling_llama.LlamaFlashAttention2 = modeling_llama.LlamaAttention
|
||||
_deekseek_vl = importlib.import_module('repositories.deepseek-vl2.deepseek_vl2')
|
||||
deekseek_vl_models = importlib.import_module('repositories.deepseek-vl2.deepseek_vl2.models')
|
||||
vl_chat_processor = deekseek_vl_models.DeepseekVLV2Processor.from_pretrained(repo, cache_dir=shared.opts.hfcache_dir)
|
||||
vl_gpt = AutoModelForCausalLM.from_pretrained(
|
||||
repo,
|
||||
trust_remote_code=True,
|
||||
cache_dir=shared.opts.hfcache_dir,
|
||||
)
|
||||
vl_gpt = vl_gpt.to(device=devices.device, dtype=devices.dtype).eval()
|
||||
|
||||
if len(question) < 2:
|
||||
question = "Describe the image."
|
||||
question = question.replace('<', '').replace('>', '')
|
||||
conversation = [
|
||||
{
|
||||
"role": "<|User|>",
|
||||
"content": f"<image>\n<|ref|>{question}<|/ref|>.",
|
||||
# "images": [image],
|
||||
},
|
||||
{"role": "<|Assistant|>", "content": ""},
|
||||
]
|
||||
|
||||
prepare_inputs = vl_chat_processor(
|
||||
conversations=conversation,
|
||||
images=[image],
|
||||
force_batchify=True,
|
||||
system_prompt=""
|
||||
).to(device=devices.device, dtype=devices.dtype)
|
||||
inputs_embeds = vl_gpt.prepare_inputs_embeds(**prepare_inputs)
|
||||
inputs_embeds = inputs_embeds.to(device=devices.device, dtype=devices.dtype)
|
||||
vl_gpt = vl_gpt.to(devices.device)
|
||||
with devices.inference_context():
|
||||
outputs = vl_gpt.language.generate(
|
||||
inputs_embeds=inputs_embeds,
|
||||
attention_mask=prepare_inputs.attention_mask,
|
||||
pad_token_id=vl_chat_processor.tokenizer.eos_token_id,
|
||||
bos_token_id=vl_chat_processor.tokenizer.bos_token_id,
|
||||
eos_token_id=vl_chat_processor.tokenizer.eos_token_id,
|
||||
max_new_tokens=shared.opts.interrogate_vlm_max_length,
|
||||
do_sample=False,
|
||||
use_cache=True
|
||||
)
|
||||
vl_gpt = vl_gpt.to(devices.cpu)
|
||||
answer = vl_chat_processor.tokenizer.decode(outputs[0].cpu().tolist(), skip_special_tokens=True)
|
||||
print('inputs', prepare_inputs['sft_format'][0])
|
||||
print('answer', answer)
|
||||
return answer
|
||||
@@ -28,7 +28,7 @@ def interrogate(image):
|
||||
elif shared.opts.interrogate_default_type == 'VLM':
|
||||
shared.log.info(f'Interrogate: type={shared.opts.interrogate_default_type} vlm="{shared.opts.interrogate_vlm_model}" prompt="{shared.opts.interrogate_vlm_prompt}"')
|
||||
from modules.interrogate import vqa
|
||||
prompt = vqa.interrogate(image=image, model_name=shared.opts.interrogate_vlm_model, question=shared.opts.interrogate_vlm_prompt)
|
||||
prompt = vqa.interrogate(image=image, model_name=shared.opts.interrogate_vlm_model, question=shared.opts.interrogate_vlm_prompt, prompt=None)
|
||||
shared.log.debug(f'Interrogate: time={time.time()-t0:.2f} answer="{prompt}"')
|
||||
return prompt
|
||||
else:
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
# based on <https://huggingface.co/fancyfeast/llama-joycaption-alpha-two-hf-llava>
|
||||
|
||||
from dataclasses import dataclass
|
||||
import torch
|
||||
from transformers import AutoProcessor, LlavaForConditionalGeneration
|
||||
from modules import shared, devices
|
||||
|
||||
|
||||
"""
|
||||
Example prompts
|
||||
Short description: Write a short description of the image.
|
||||
Detailed descriptive: Please provide a detailed description of the image.
|
||||
Descriptive: Write a descriptive caption for this image in a formal tone.
|
||||
Descriptive (Informal): Write a descriptive caption for this image in a casual tone.
|
||||
Training Prompt: Write a stable diffusion prompt for this image.
|
||||
MidJourney: Write a MidJourney prompt for this image.
|
||||
Booru tag list: Write a list of Booru tags for this image.
|
||||
Booru-like tag list: Write a list of Booru-like tags for this image.
|
||||
Art Critic: Analyze this image like an art critic would with information about its composition, style, symbolism, the use of color, light, any artistic movement it might belong to, etc.
|
||||
Product Listing: Write a caption for this image as though it were a product listing.
|
||||
Social Media Post: Write a caption for this image as if it were being used for a social media post.
|
||||
Extra Options:
|
||||
- If there is a person/character in the image you must refer to them as {name}.
|
||||
- Do NOT include information about people/characters that cannot be changed (like ethnicity, gender, etc), but do still include changeable attributes (like hair style).
|
||||
- Include information about lighting.
|
||||
- Include information about camera angle.
|
||||
- Include information about whether there is a watermark or not.
|
||||
- Include information about whether there are JPEG artifacts or not.
|
||||
- If it is a photo you MUST include information about what camera was likely used and details such as aperture, shutter speed, ISO, etc.
|
||||
- Do NOT include anything sexual; keep it PG.
|
||||
- Do NOT mention the image's resolution.
|
||||
- You MUST include information about the subjective aesthetic quality of the image from low to very high.
|
||||
- Include information on the image's composition style, such as leading lines, rule of thirds, or symmetry.
|
||||
- Do NOT mention any text that is in the image.
|
||||
- Specify the depth of field and whether the background is in focus or blurred.
|
||||
- If applicable, mention the likely use of artificial or natural lighting sources.
|
||||
- Do NOT use any ambiguous language.
|
||||
- Include whether the image is sfw, suggestive, or nsfw.
|
||||
- ONLY describe the most important elements of the image.
|
||||
"""
|
||||
|
||||
@dataclass
|
||||
class JoyOptions():
|
||||
repo: str = "fancyfeast/llama-joycaption-alpha-two-hf-llava"
|
||||
temp: float = 0.5
|
||||
top_k: float = 10
|
||||
top_p: float = 0.9
|
||||
max_new_tokens: int = 512
|
||||
sample: bool = True
|
||||
|
||||
def __str__(self):
|
||||
return f'repo="{self.repo}" temp={self.temp} top_k={self.top_k} top_p={self.top_p} sample={self.sample} tokens={self.max_new_tokens}'
|
||||
|
||||
|
||||
processor: AutoProcessor = None
|
||||
llava_model: LlavaForConditionalGeneration = None
|
||||
opts = JoyOptions()
|
||||
|
||||
|
||||
@torch.no_grad()
|
||||
def predict(question: str, image):
|
||||
global llava_model, processor # pylint: disable=global-statement
|
||||
opts.max_new_tokens = shared.opts.interrogate_vlm_max_length
|
||||
if llava_model is None:
|
||||
shared.log.info(f'Interrogate: type=vlm model="JoyCaption" {str(opts)}')
|
||||
processor = AutoProcessor.from_pretrained(opts.repo)
|
||||
llava_model = LlavaForConditionalGeneration.from_pretrained(opts.repo, torch_dtype=devices.dtype, device_map="auto", cache_dir=shared.opts.hfcache_dir)
|
||||
llava_model.eval()
|
||||
|
||||
if len(question) < 2:
|
||||
question = "Describe the image."
|
||||
question = question.replace('<', '').replace('>', '')
|
||||
convo = [
|
||||
{ "role": "system", "content": "You are a helpful image captioner." },
|
||||
{ "role": "user", "content": question },
|
||||
]
|
||||
convo_string = processor.apply_chat_template(convo, tokenize=False, add_generation_prompt=True)
|
||||
inputs = processor(text=[convo_string], images=[image], return_tensors="pt").to(devices.device) # Process the inputs
|
||||
inputs['pixel_values'] = inputs['pixel_values'].to(devices.dtype)
|
||||
llava_model = llava_model.to(devices.device)
|
||||
with devices.inference_context():
|
||||
generate_ids = llava_model.generate( # Generate the captions
|
||||
**inputs,
|
||||
# input_ids=inputs['input_ids'],
|
||||
# pixel_values=inputs['pixel_values'],
|
||||
# attention_mask=inputs['attention_mask'],
|
||||
max_new_tokens=opts.max_new_tokens,
|
||||
suppress_tokens=None,
|
||||
use_cache=True,
|
||||
do_sample=opts.sample,
|
||||
temperature=opts.temp,
|
||||
top_k=opts.top_k,
|
||||
top_p=opts.top_p,
|
||||
)[0]
|
||||
generate_ids = generate_ids[inputs['input_ids'].shape[1]:] # Trim off the prompt
|
||||
caption = processor.tokenizer.decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False) # Decode the caption
|
||||
llava_model = llava_model.to(devices.cpu)
|
||||
caption = caption.replace('\n\n', '\n').strip()
|
||||
return caption
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,21 +6,13 @@ import threading
|
||||
import re
|
||||
import torch
|
||||
import torch.hub # pylint: disable=ungrouped-imports
|
||||
import gradio as gr
|
||||
from PIL import Image
|
||||
from torchvision import transforms
|
||||
from torchvision.transforms.functional import InterpolationMode
|
||||
from modules import devices, paths, shared, lowvram, errors, sd_models
|
||||
|
||||
|
||||
config = {
|
||||
"caption_max_length": 74,
|
||||
"chunk_size": 1024,
|
||||
"flavor_intermediate_count": 1024,
|
||||
"min_flavors": 2,
|
||||
"max_flavors": 8,
|
||||
"clip_offload": True,
|
||||
"caption_offload": True,
|
||||
}
|
||||
caption_models = {
|
||||
'blip-base': 'Salesforce/blip-image-captioning-base',
|
||||
'blip-large': 'Salesforce/blip-image-captioning-large',
|
||||
@@ -213,8 +205,8 @@ class InterrogateModels:
|
||||
for _name, topn, items in self.categories():
|
||||
matches = self.rank(image_features, items, top_count=topn)
|
||||
for match, score in matches:
|
||||
if shared.opts.interrogate_clip_score:
|
||||
res += f", ({match}:{score/100:.3f})"
|
||||
if shared.opts.interrogate_score:
|
||||
res += f", ({match}:{score/100:.2f})"
|
||||
else:
|
||||
res += f", {match}"
|
||||
except Exception as e:
|
||||
@@ -227,13 +219,17 @@ class InterrogateModels:
|
||||
# --------- interrrogate ui
|
||||
|
||||
class BatchWriter:
|
||||
def __init__(self, folder):
|
||||
def __init__(self, folder, mode='w'):
|
||||
self.folder = folder
|
||||
self.csv, self.file = None, None
|
||||
self.csv = None
|
||||
self.file = None
|
||||
self.mode = mode
|
||||
|
||||
def add(self, file, prompt):
|
||||
txt_file = os.path.splitext(file)[0] + ".txt"
|
||||
with open(os.path.join(self.folder, txt_file), 'w', encoding='utf-8') as f:
|
||||
if self.mode == 'a':
|
||||
prompt = '\n' + prompt
|
||||
with open(os.path.join(self.folder, txt_file), self.mode, encoding='utf-8') as f:
|
||||
f.write(prompt)
|
||||
|
||||
def close(self):
|
||||
@@ -241,21 +237,13 @@ class BatchWriter:
|
||||
self.file.close()
|
||||
|
||||
|
||||
def update_interrogate_params(caption_max_length:int=None, chunk_size:int=None, min_flavors:int=None, max_flavors:int=None, flavor_intermediate_count:int=None):
|
||||
config["caption_max_length"] = int(caption_max_length or shared.opts.interrogate_clip_max_length)
|
||||
config["clip_offload"] = shared.opts.interrogate_offload
|
||||
config["caption_offload"] = shared.opts.interrogate_offload
|
||||
config["min_flavors"] = int(min_flavors or shared.opts.interrogate_clip_min_flavors)
|
||||
config["max_flavors"] = int(max_flavors or shared.opts.interrogate_clip_max_flavors)
|
||||
if chunk_size is not None:
|
||||
config["chunk_size"] = int(chunk_size)
|
||||
if flavor_intermediate_count is not None:
|
||||
config["flavor_intermediate_count"] = int(flavor_intermediate_count)
|
||||
def update_interrogate_params():
|
||||
if ci is not None:
|
||||
ci.config.caption_max_length = config["caption_max_length"]
|
||||
ci.config.chunk_size = config["chunk_size"]
|
||||
ci.config.flavor_intermediate_count = config["flavor_intermediate_count"]
|
||||
shared.log.debug(f'Interrogate: type={shared.opts.interrogate_default_type} config={config}')
|
||||
ci.caption_max_length=shared.opts.interrogate_clip_max_length
|
||||
ci.chunk_size=shared.opts.interrogate_clip_chunk_size
|
||||
ci.flavor_intermediate_count=shared.opts.interrogate_clip_flavor_count
|
||||
ci.clip_offload=shared.opts.interrogate_offload
|
||||
ci.caption_offload=shared.opts.interrogate_offload
|
||||
|
||||
|
||||
def get_clip_models():
|
||||
@@ -285,11 +273,11 @@ def load_interrogator(clip_model, blip_model):
|
||||
clip_model_name=clip_model,
|
||||
caption_model_name=blip_model,
|
||||
quiet=True,
|
||||
caption_max_length=config['caption_max_length'],
|
||||
chunk_size=config['chunk_size'],
|
||||
flavor_intermediate_count=config['flavor_intermediate_count'],
|
||||
clip_offload=config['clip_offload'],
|
||||
caption_offload=config['caption_offload'],
|
||||
caption_max_length=shared.opts.interrogate_clip_max_length,
|
||||
chunk_size=shared.opts.interrogate_clip_chunk_size,
|
||||
flavor_intermediate_count=shared.opts.interrogate_clip_flavor_count,
|
||||
clip_offload=shared.opts.interrogate_offload,
|
||||
caption_offload=shared.opts.interrogate_offload,
|
||||
)
|
||||
ci = clip_interrogator.Interrogator(interrogator_config)
|
||||
elif clip_model != ci.config.clip_model_name or blip_model != ci.config.caption_model_name:
|
||||
@@ -319,15 +307,15 @@ def interrogate(image, mode, caption=None):
|
||||
return ''
|
||||
image = image.convert("RGB")
|
||||
if mode == 'best':
|
||||
prompt = ci.interrogate(image, caption=caption, min_flavors=config["min_flavors"], max_flavors=config["max_flavors"])
|
||||
prompt = ci.interrogate(image, caption=caption, min_flavors=shared.opts.interrogate_clip_min_flavors, max_flavors=shared.opts.interrogate_clip_max_flavors, )
|
||||
elif mode == 'caption':
|
||||
prompt = ci.generate_caption(image) if caption is None else caption
|
||||
elif mode == 'classic':
|
||||
prompt = ci.interrogate_classic(image, caption=caption, max_flavors=config["max_flavors"])
|
||||
prompt = ci.interrogate_classic(image, caption=caption, max_flavors=shared.opts.interrogate_clip_max_flavors)
|
||||
elif mode == 'fast':
|
||||
prompt = ci.interrogate_fast(image, caption=caption, max_flavors=config["max_flavors"])
|
||||
prompt = ci.interrogate_fast(image, caption=caption, max_flavors=shared.opts.interrogate_clip_max_flavors)
|
||||
elif mode == 'negative':
|
||||
prompt = ci.interrogate_negative(image, max_flavors=config["max_flavors"])
|
||||
prompt = ci.interrogate_negative(image, max_flavors=shared.opts.interrogate_clip_max_flavors)
|
||||
else:
|
||||
raise RuntimeError(f"Unknown mode {mode}")
|
||||
return prompt
|
||||
@@ -339,7 +327,7 @@ def interrogate_image(image, clip_model, blip_model, mode):
|
||||
if not shared.native and (shared.cmd_opts.lowvram or shared.cmd_opts.medvram):
|
||||
lowvram.send_everything_to_cpu()
|
||||
devices.torch_gc()
|
||||
if shared.native:
|
||||
if shared.native and shared.sd_loaded:
|
||||
sd_models.apply_balanced_offload(shared.sd_model)
|
||||
load_interrogator(clip_model, blip_model)
|
||||
image = image.convert('RGB')
|
||||
@@ -353,58 +341,45 @@ def interrogate_image(image, clip_model, blip_model, mode):
|
||||
return prompt
|
||||
|
||||
|
||||
def interrogate_batch(batch_files, batch_folder, batch_str, clip_model, blip_model, mode, write):
|
||||
def interrogate_batch(batch_files, batch_folder, batch_str, clip_model, blip_model, mode, write, append, recursive):
|
||||
files = []
|
||||
if batch_files is not None:
|
||||
files += [f.name for f in batch_files]
|
||||
if batch_folder is not None:
|
||||
files += [f.name for f in batch_folder]
|
||||
if batch_str is not None and len(batch_str) > 0 and os.path.exists(batch_str) and os.path.isdir(batch_str):
|
||||
files += [os.path.join(batch_str, f) for f in os.listdir(batch_str) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.webp'))]
|
||||
from modules.files_cache import list_files
|
||||
files += list(list_files(batch_str, ext_filter=['.png', '.jpg', '.jpeg', '.webp'], recursive=recursive))
|
||||
if len(files) == 0:
|
||||
shared.log.error('Interrogate batch no images')
|
||||
shared.log.warning('Interrogate batch: type=clip no images')
|
||||
return ''
|
||||
shared.state.begin('Batch interrogate')
|
||||
shared.state.begin('Interrogate batch')
|
||||
prompts = []
|
||||
try:
|
||||
if not shared.native and (shared.cmd_opts.lowvram or shared.cmd_opts.medvram):
|
||||
lowvram.send_everything_to_cpu()
|
||||
devices.torch_gc()
|
||||
load_interrogator(clip_model, blip_model)
|
||||
shared.log.info(f'Interrogate batch: images={len(files)} mode={mode} config={ci.config}')
|
||||
captions = []
|
||||
# first pass: generate captions
|
||||
|
||||
load_interrogator(clip_model, blip_model)
|
||||
if write:
|
||||
file_mode = 'w' if not append else 'a'
|
||||
writer = BatchWriter(os.path.dirname(files[0]), mode=file_mode)
|
||||
import rich.progress as rp
|
||||
pbar = rp.Progress(rp.TextColumn('[cyan]Caption:'), rp.BarColumn(), rp.MofNCompleteColumn(), rp.TaskProgressColumn(), rp.TimeRemainingColumn(), rp.TimeElapsedColumn(), rp.TextColumn('[cyan]{task.description}'), console=shared.console)
|
||||
with pbar:
|
||||
task = pbar.add_task(total=len(files), description='starting...')
|
||||
for file in files:
|
||||
caption = ""
|
||||
pbar.update(task, advance=1, description=file)
|
||||
try:
|
||||
if shared.state.interrupted:
|
||||
break
|
||||
image = Image.open(file).convert('RGB')
|
||||
caption = ci.generate_caption(image)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Interrogate caption: {e}')
|
||||
finally:
|
||||
captions.append(caption)
|
||||
# second pass: interrogate
|
||||
if write:
|
||||
writer = BatchWriter(os.path.dirname(files[0]))
|
||||
for idx, file in enumerate(files):
|
||||
try:
|
||||
if shared.state.interrupted:
|
||||
break
|
||||
image = Image.open(file).convert('RGB')
|
||||
prompt = interrogate(image, mode, caption=captions[idx])
|
||||
prompt = interrogate(image, mode)
|
||||
prompts.append(prompt)
|
||||
if write:
|
||||
writer.add(file, prompt)
|
||||
except OSError as e:
|
||||
shared.log.error(f'Interrogate batch: {e}')
|
||||
if write:
|
||||
writer.close()
|
||||
ci.config.quiet = False
|
||||
unload_clip_model()
|
||||
except Exception as e:
|
||||
shared.log.error(f'Interrogate batch: {e}')
|
||||
if write:
|
||||
writer.close()
|
||||
ci.config.quiet = False
|
||||
unload_clip_model()
|
||||
shared.state.end()
|
||||
return '\n\n'.join(prompts)
|
||||
|
||||
@@ -423,7 +398,13 @@ def analyze_image(image, clip_model, blip_model):
|
||||
movement_ranks = dict(zip(top_movements, ci.similarities(image_features, top_movements)))
|
||||
trending_ranks = dict(zip(top_trendings, ci.similarities(image_features, top_trendings)))
|
||||
flavor_ranks = dict(zip(top_flavors, ci.similarities(image_features, top_flavors)))
|
||||
return medium_ranks, artist_ranks, movement_ranks, trending_ranks, flavor_ranks
|
||||
return [
|
||||
gr.update(value=medium_ranks, visible=True),
|
||||
gr.update(value=artist_ranks, visible=True),
|
||||
gr.update(value=movement_ranks, visible=True),
|
||||
gr.update(value=trending_ranks, visible=True),
|
||||
gr.update(value=flavor_ranks, visible=True),
|
||||
]
|
||||
|
||||
|
||||
interrogator = InterrogateModels()
|
||||
|
||||
+225
-33
@@ -1,4 +1,5 @@
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import base64
|
||||
@@ -6,12 +7,7 @@ import torch
|
||||
import transformers
|
||||
import transformers.dynamic_module_utils
|
||||
from PIL import Image
|
||||
from modules import shared, devices, errors
|
||||
|
||||
# TODO vlm: add additional models
|
||||
# https://huggingface.co/nvidia/Eagle2-1B not compatible with latest transformers
|
||||
# https://huggingface.co/deepseek-ai/deepseek-vl2-tiny requires custom code
|
||||
|
||||
from modules import shared, devices, errors, sd_models
|
||||
|
||||
processor = None
|
||||
model = None
|
||||
@@ -36,7 +32,17 @@ vlm_models = {
|
||||
"Microsoft GIT VQA Base": "microsoft/git-base-vqav2", # 0.7GB
|
||||
"Microsoft GIT VQA Large": "microsoft/git-large-vqav2", # 1.6GB
|
||||
"ToriiGate 0.4 2B": "Minthy/ToriiGate-v0.4-2B",
|
||||
"ToriiGate 0.4 7B": "Minthy/ToriiGate-v0.4-7B",
|
||||
"ViLT Base": "dandelin/vilt-b32-finetuned-vqa", # 0.5GB
|
||||
"Google PaliGemma 2 3B": "google/paligemma2-3b-pt-224",
|
||||
"JoyCaption": "fancyfeast/llama-joycaption-alpha-two-hf-llava", # 0.7GB
|
||||
"JoyTag": "fancyfeast/joytag", # 17.4GB
|
||||
"AIDC Ovis2 1B": "AIDC-AI/Ovis2-1B",
|
||||
"AIDC Ovis2 2B": "AIDC-AI/Ovis2-2B",
|
||||
"AIDC Ovis2 4B": "AIDC-AI/Ovis2-4B",
|
||||
# "OpenGVLab InternVL 2.5 1B": "OpenGVLab/InternVL2_5-1B"
|
||||
# "DeepSeek VL2 Tiny": "deepseek-ai/deepseek-vl2-tiny", # broken
|
||||
# "nVidia Eagle 2 1B": "nvidia/Eagle2-1B", # not compatible with latest transformers
|
||||
}
|
||||
vlm_prompts = [
|
||||
'<CAPTION>',
|
||||
@@ -72,25 +78,46 @@ def clean(response, question):
|
||||
response = json.dumps(response)
|
||||
if isinstance(response, list):
|
||||
response = response[0]
|
||||
question = question.replace('<', '').replace('>', '')
|
||||
question = question.replace('<', '').replace('>', '').replace('_', ' ')
|
||||
if question in response:
|
||||
response = response.split(question, 1)[1]
|
||||
response = response.replace('\n', '').replace('\r', '').replace('\t', '').strip()
|
||||
if response.startswith('"'):
|
||||
response = response[1:]
|
||||
if response.endswith('"'):
|
||||
response = response[:-1]
|
||||
response = response.replace('Assistant:', '').strip()
|
||||
return response
|
||||
|
||||
|
||||
def get_kwargs():
|
||||
kwargs = {
|
||||
'max_new_tokens': shared.opts.interrogate_vlm_max_length,
|
||||
'do_sample': shared.opts.interrogate_vlm_do_sample,
|
||||
}
|
||||
if shared.opts.interrogate_vlm_num_beams > 0:
|
||||
kwargs['num_beams'] = shared.opts.interrogate_vlm_num_beams
|
||||
if shared.opts.interrogate_vlm_temperature > 0:
|
||||
kwargs['temperature'] = shared.opts.interrogate_vlm_temperature
|
||||
if shared.opts.interrogate_vlm_top_k > 0:
|
||||
kwargs['top_k'] = shared.opts.interrogate_vlm_top_k
|
||||
if shared.opts.interrogate_vlm_top_p > 0:
|
||||
kwargs['top_p'] = shared.opts.interrogate_vlm_top_p
|
||||
return kwargs
|
||||
|
||||
|
||||
def qwen(question: str, image: Image.Image, repo: str = None):
|
||||
global processor, model, loaded # pylint: disable=global-statement
|
||||
if model is None or loaded != repo:
|
||||
shared.log.debug(f'Interrogate load: vlm="{repo}"')
|
||||
model = transformers.Qwen2VLForConditionalGeneration.from_pretrained(repo, cache_dir=shared.opts.hfcache_dir)
|
||||
model = transformers.Qwen2VLForConditionalGeneration.from_pretrained(
|
||||
repo,
|
||||
cache_dir=shared.opts.hfcache_dir
|
||||
)
|
||||
processor = transformers.AutoProcessor.from_pretrained(repo, cache_dir=shared.opts.hfcache_dir)
|
||||
loaded = repo
|
||||
model.to(devices.device, devices.dtype)
|
||||
if len(question) < 2:
|
||||
question = "Describe the image."
|
||||
question = question.replace('<', '').replace('>', '')
|
||||
model = model.to(devices.device, devices.dtype)
|
||||
question = question.replace('<', '').replace('>', '').replace('_', ' ')
|
||||
conversation = [
|
||||
{
|
||||
"role": "system",
|
||||
@@ -112,7 +139,7 @@ def qwen(question: str, image: Image.Image, repo: str = None):
|
||||
inputs = inputs.to(devices.device, devices.dtype)
|
||||
output_ids = model.generate(
|
||||
**inputs,
|
||||
max_new_tokens=shared.opts.interrogate_vlm_max_length,
|
||||
**get_kwargs(),
|
||||
)
|
||||
generated_ids = [
|
||||
output_ids[len(input_ids) :]
|
||||
@@ -122,6 +149,76 @@ def qwen(question: str, image: Image.Image, repo: str = None):
|
||||
return response
|
||||
|
||||
|
||||
def paligemma(question: str, image: Image.Image, repo: str = None):
|
||||
global processor, model, loaded # pylint: disable=global-statement
|
||||
if model is None or loaded != repo:
|
||||
shared.log.debug(f'Interrogate load: vlm="{repo}"')
|
||||
processor = transformers.PaliGemmaProcessor.from_pretrained(repo, cache_dir=shared.opts.hfcache_dir)
|
||||
model = transformers.PaliGemmaForConditionalGeneration.from_pretrained(
|
||||
repo,
|
||||
cache_dir=shared.opts.hfcache_dir,
|
||||
torch_dtype=devices.dtype,
|
||||
)
|
||||
loaded = repo
|
||||
model = model.to(devices.device, devices.dtype)
|
||||
question = question.replace('<', '').replace('>', '').replace('_', ' ')
|
||||
model_inputs = processor(text=question, images=image, return_tensors="pt").to(devices.device, devices.dtype)
|
||||
input_len = model_inputs["input_ids"].shape[-1]
|
||||
with devices.inference_context():
|
||||
generation = model.generate(
|
||||
**model_inputs,
|
||||
**get_kwargs(),
|
||||
)
|
||||
generation = generation[0][input_len:]
|
||||
response = processor.decode(generation, skip_special_tokens=True)
|
||||
return response
|
||||
|
||||
|
||||
def ovis(question: str, image: Image.Image, repo: str = None):
|
||||
try:
|
||||
import flash_attn # pylint: disable=unused-import
|
||||
except Exception:
|
||||
shared.log.error(f'Interrogate: vlm="{repo}" flash-attn is not available')
|
||||
return ''
|
||||
global model, loaded # pylint: disable=global-statement
|
||||
if model is None or loaded != repo:
|
||||
shared.log.debug(f'Interrogate load: vlm="{repo}"')
|
||||
model = transformers.AutoModelForCausalLM.from_pretrained(
|
||||
repo,
|
||||
torch_dtype=devices.dtype,
|
||||
multimodal_max_length=32768,
|
||||
trust_remote_code=True,
|
||||
cache_dir=shared.opts.hfcache_dir,
|
||||
)
|
||||
loaded = repo
|
||||
model = model.to(devices.device, devices.dtype)
|
||||
text_tokenizer = model.get_text_tokenizer()
|
||||
visual_tokenizer = model.get_visual_tokenizer()
|
||||
max_partition = 9
|
||||
question = question.replace('<', '').replace('>', '').replace('_', ' ')
|
||||
question = f'<image>\n{question}'
|
||||
_prompt, input_ids, pixel_values = model.preprocess_inputs(question, [image], max_partition=max_partition)
|
||||
attention_mask = torch.ne(input_ids, text_tokenizer.pad_token_id)
|
||||
input_ids = input_ids.unsqueeze(0).to(device=model.device)
|
||||
attention_mask = attention_mask.unsqueeze(0).to(device=model.device)
|
||||
if pixel_values is not None:
|
||||
pixel_values = pixel_values.to(dtype=visual_tokenizer.dtype, device=visual_tokenizer.device)
|
||||
pixel_values = [pixel_values]
|
||||
with devices.inference_context():
|
||||
output_ids = model.generate(
|
||||
input_ids,
|
||||
pixel_values=pixel_values,
|
||||
attention_mask=attention_mask,
|
||||
repetition_penalty=None,
|
||||
eos_token_id=model.generation_config.eos_token_id,
|
||||
pad_token_id=text_tokenizer.pad_token_id,
|
||||
use_cache=True,
|
||||
**get_kwargs())
|
||||
response = text_tokenizer.decode(output_ids[0], skip_special_tokens=True)
|
||||
print(f'Output:\n{response}')
|
||||
return response
|
||||
|
||||
|
||||
def smol(question: str, image: Image.Image, repo: str = None):
|
||||
global processor, model, loaded # pylint: disable=global-statement
|
||||
if model is None or loaded != repo:
|
||||
@@ -135,9 +232,7 @@ def smol(question: str, image: Image.Image, repo: str = None):
|
||||
processor = transformers.AutoProcessor.from_pretrained(repo, cache_dir=shared.opts.hfcache_dir)
|
||||
loaded = repo
|
||||
model.to(devices.device, devices.dtype)
|
||||
if len(question) < 2:
|
||||
question = "Describe the image."
|
||||
question = question.replace('<', '').replace('>', '')
|
||||
question = question.replace('<', '').replace('>', '').replace('_', ' ')
|
||||
conversation = [
|
||||
{
|
||||
"role": "system",
|
||||
@@ -159,7 +254,7 @@ def smol(question: str, image: Image.Image, repo: str = None):
|
||||
inputs = inputs.to(devices.device, devices.dtype)
|
||||
output_ids = model.generate(
|
||||
**inputs,
|
||||
max_new_tokens=shared.opts.interrogate_vlm_max_length,
|
||||
**get_kwargs(),
|
||||
)
|
||||
response = processor.batch_decode(output_ids,skip_special_tokens=True)
|
||||
return response
|
||||
@@ -169,7 +264,10 @@ def git(question: str, image: Image.Image, repo: str = None):
|
||||
global processor, model, loaded # pylint: disable=global-statement
|
||||
if model is None or loaded != repo:
|
||||
shared.log.debug(f'Interrogate load: vlm="{repo}"')
|
||||
model = transformers.GitForCausalLM.from_pretrained(repo, cache_dir=shared.opts.hfcache_dir)
|
||||
model = transformers.GitForCausalLM.from_pretrained(
|
||||
repo,
|
||||
cache_dir=shared.opts.hfcache_dir,
|
||||
)
|
||||
processor = transformers.GitProcessor.from_pretrained(repo, cache_dir=shared.opts.hfcache_dir)
|
||||
loaded = repo
|
||||
model.to(devices.device, devices.dtype)
|
||||
@@ -191,7 +289,10 @@ def blip(question: str, image: Image.Image, repo: str = None):
|
||||
global processor, model, loaded # pylint: disable=global-statement
|
||||
if model is None or loaded != repo:
|
||||
shared.log.debug(f'Interrogate load: vlm="{repo}"')
|
||||
model = transformers.BlipForQuestionAnswering.from_pretrained(repo, cache_dir=shared.opts.hfcache_dir)
|
||||
model = transformers.BlipForQuestionAnswering.from_pretrained(
|
||||
repo,
|
||||
cache_dir=shared.opts.hfcache_dir,
|
||||
)
|
||||
processor = transformers.BlipProcessor.from_pretrained(repo, cache_dir=shared.opts.hfcache_dir)
|
||||
loaded = repo
|
||||
model.to(devices.device, devices.dtype)
|
||||
@@ -207,7 +308,10 @@ def vilt(question: str, image: Image.Image, repo: str = None):
|
||||
global processor, model, loaded # pylint: disable=global-statement
|
||||
if model is None or loaded != repo:
|
||||
shared.log.debug(f'Interrogate load: vlm="{repo}"')
|
||||
model = transformers.ViltForQuestionAnswering.from_pretrained(repo, cache_dir=shared.opts.hfcache_dir)
|
||||
model = transformers.ViltForQuestionAnswering.from_pretrained(
|
||||
repo,
|
||||
cache_dir=shared.opts.hfcache_dir,
|
||||
)
|
||||
processor = transformers.ViltProcessor.from_pretrained(repo, cache_dir=shared.opts.hfcache_dir)
|
||||
loaded = repo
|
||||
model.to(devices.device)
|
||||
@@ -225,7 +329,10 @@ def pix(question: str, image: Image.Image, repo: str = None):
|
||||
global processor, model, loaded # pylint: disable=global-statement
|
||||
if model is None or loaded != repo:
|
||||
shared.log.debug(f'Interrogate load: vlm="{repo}"')
|
||||
model = transformers.Pix2StructForConditionalGeneration.from_pretrained(repo, cache_dir=shared.opts.hfcache_dir)
|
||||
model = transformers.Pix2StructForConditionalGeneration.from_pretrained(
|
||||
repo,
|
||||
cache_dir=shared.opts.hfcache_dir,
|
||||
)
|
||||
processor = transformers.Pix2StructProcessor.from_pretrained(repo, cache_dir=shared.opts.hfcache_dir)
|
||||
loaded = repo
|
||||
model.to(devices.device)
|
||||
@@ -253,9 +360,7 @@ def moondream(question: str, image: Image.Image, repo: str = None):
|
||||
loaded = repo
|
||||
model.eval()
|
||||
model.to(devices.device, devices.dtype)
|
||||
if len(question) < 2:
|
||||
question = "Describe the image."
|
||||
question = question.replace('<', '').replace('>', '')
|
||||
question = question.replace('<', '').replace('>', '').replace('_', ' ')
|
||||
encoded = model.encode_image(image)
|
||||
with devices.inference_context():
|
||||
response = model.answer_question(encoded, question, processor)
|
||||
@@ -272,11 +377,16 @@ def florence(question: str, image: Image.Image, repo: str = None, revision: str
|
||||
return R
|
||||
revision = None
|
||||
if '@' in repo:
|
||||
repo, revision = model.split('@')
|
||||
repo, revision = repo.split('@')
|
||||
if model is None or loaded != repo:
|
||||
shared.log.debug(f'Interrogate load: vlm="{repo}" path="{shared.opts.hfcache_dir}"')
|
||||
transformers.dynamic_module_utils.get_imports = get_imports
|
||||
model = transformers.AutoModelForCausalLM.from_pretrained(repo, trust_remote_code=True, revision=revision, cache_dir=shared.opts.hfcache_dir)
|
||||
model = transformers.AutoModelForCausalLM.from_pretrained(
|
||||
repo,
|
||||
trust_remote_code=True,
|
||||
revision=revision,
|
||||
cache_dir=shared.opts.hfcache_dir,
|
||||
)
|
||||
processor = transformers.AutoProcessor.from_pretrained(repo, trust_remote_code=True, revision=revision, cache_dir=shared.opts.hfcache_dir)
|
||||
transformers.dynamic_module_utils.get_imports = _get_imports
|
||||
loaded = repo
|
||||
@@ -286,7 +396,6 @@ def florence(question: str, image: Image.Image, repo: str = None, revision: str
|
||||
task = question.split('>', 1)[0] + '>'
|
||||
else:
|
||||
task = '<MORE_DETAILED_CAPTION>'
|
||||
# question = task + question
|
||||
inputs = processor(text=task, images=image, return_tensors="pt")
|
||||
input_ids = inputs['input_ids'].to(devices.device)
|
||||
pixel_values = inputs['pixel_values'].to(devices.device, devices.dtype)
|
||||
@@ -294,16 +403,16 @@ def florence(question: str, image: Image.Image, repo: str = None, revision: str
|
||||
generated_ids = model.generate(
|
||||
input_ids=input_ids,
|
||||
pixel_values=pixel_values,
|
||||
max_new_tokens=shared.opts.interrogate_vlm_max_length,
|
||||
num_beams=shared.opts.interrogate_vlm_num_beams,
|
||||
do_sample=False
|
||||
**get_kwargs()
|
||||
)
|
||||
generated_text = processor.batch_decode(generated_ids, skip_special_tokens=False)[0]
|
||||
response = processor.post_process_generation(generated_text, task="task", image_size=(image.width, image.height))
|
||||
return response
|
||||
|
||||
|
||||
def interrogate(question, image, model_name):
|
||||
def interrogate(question, prompt, image, model_name, quiet:bool=False):
|
||||
if not quiet:
|
||||
shared.state.begin('Interrogate')
|
||||
t0 = time.time()
|
||||
if isinstance(image, list):
|
||||
image = image[0] if len(image) > 0 else None
|
||||
@@ -312,9 +421,17 @@ def interrogate(question, image, model_name):
|
||||
if image is None:
|
||||
return ''
|
||||
if image.width > 768 or image.height > 768:
|
||||
image.thumbnail((768, 768), Image.Resampling.HAMMING)
|
||||
image.thumbnail((768, 768), Image.Resampling.LANCZOS)
|
||||
if image.mode != 'RGB':
|
||||
image = image.convert('RGB')
|
||||
if prompt is not None and len(prompt) > 0:
|
||||
question = prompt
|
||||
if len(question) < 2:
|
||||
question = "Describe the image."
|
||||
if shared.native and shared.sd_loaded:
|
||||
sd_models.apply_balanced_offload(shared.sd_model)
|
||||
from modules import modelloader
|
||||
modelloader.hf_login()
|
||||
try:
|
||||
if model_name is None:
|
||||
shared.log.error(f'Interrogate: type=vlm model="{model_name}" no model selected')
|
||||
@@ -342,6 +459,19 @@ def interrogate(question, image, model_name):
|
||||
answer = qwen(question, image, vqa_model)
|
||||
elif 'smol' in vqa_model.lower():
|
||||
answer = smol(question, image, vqa_model)
|
||||
elif 'joytag' in vqa_model.lower():
|
||||
from modules.interrogate import joytag
|
||||
answer = joytag.predict(image)
|
||||
elif 'joycaption' in vqa_model.lower():
|
||||
from modules.interrogate import joycaption
|
||||
answer = joycaption.predict(question, image)
|
||||
elif 'deepseek' in vqa_model.lower():
|
||||
from modules.interrogate import deepseek
|
||||
answer = deepseek.predict(question, image, vqa_model)
|
||||
elif 'paligemma' in vqa_model.lower():
|
||||
answer = paligemma(question, image, vqa_model)
|
||||
elif 'ovis' in vqa_model.lower():
|
||||
answer = ovis(question, image, vqa_model)
|
||||
else:
|
||||
answer = 'unknown model'
|
||||
except Exception as e:
|
||||
@@ -352,5 +482,67 @@ def interrogate(question, image, model_name):
|
||||
devices.torch_gc()
|
||||
answer = clean(answer, question)
|
||||
t1 = time.time()
|
||||
shared.log.debug(f'Interrogate: type=vlm model="{model_name}" repo="{vqa_model}" time={t1-t0:.2f}')
|
||||
if not quiet:
|
||||
shared.log.debug(f'Interrogate: type=vlm model="{model_name}" repo="{vqa_model}" args={get_kwargs()} time={t1-t0:.2f}')
|
||||
shared.state.end()
|
||||
return answer
|
||||
|
||||
|
||||
def batch(model_name, batch_files, batch_folder, batch_str, question, prompt, write, append, recursive):
|
||||
class BatchWriter:
|
||||
def __init__(self, folder, mode='w'):
|
||||
self.folder = folder
|
||||
self.csv = None
|
||||
self.file = None
|
||||
self.mode = mode
|
||||
|
||||
def add(self, file, prompt):
|
||||
txt_file = os.path.splitext(file)[0] + ".txt"
|
||||
if self.mode == 'a':
|
||||
prompt = '\n' + prompt
|
||||
with open(os.path.join(self.folder, txt_file), self.mode, encoding='utf-8') as f:
|
||||
f.write(prompt)
|
||||
|
||||
def close(self):
|
||||
if self.file is not None:
|
||||
self.file.close()
|
||||
|
||||
files = []
|
||||
if batch_files is not None:
|
||||
files += [f.name for f in batch_files]
|
||||
if batch_folder is not None:
|
||||
files += [f.name for f in batch_folder]
|
||||
if batch_str is not None and len(batch_str) > 0 and os.path.exists(batch_str) and os.path.isdir(batch_str):
|
||||
from modules.files_cache import list_files
|
||||
files += list(list_files(batch_str, ext_filter=['.png', '.jpg', '.jpeg', '.webp'], recursive=recursive))
|
||||
if len(files) == 0:
|
||||
shared.log.warning('Interrogate batch: type=vlm no images')
|
||||
return ''
|
||||
shared.state.begin('Interrogate batch')
|
||||
prompts = []
|
||||
if write:
|
||||
mode = 'w' if not append else 'a'
|
||||
writer = BatchWriter(os.path.dirname(files[0]), mode=mode)
|
||||
orig_offload = shared.opts.interrogate_offload
|
||||
shared.opts.interrogate_offload = False
|
||||
import rich.progress as rp
|
||||
pbar = rp.Progress(rp.TextColumn('[cyan]Caption:'), rp.BarColumn(), rp.MofNCompleteColumn(), rp.TaskProgressColumn(), rp.TimeRemainingColumn(), rp.TimeElapsedColumn(), rp.TextColumn('[cyan]{task.description}'), console=shared.console)
|
||||
with pbar:
|
||||
task = pbar.add_task(total=len(files), description='starting...')
|
||||
for file in files:
|
||||
pbar.update(task, advance=1, description=file)
|
||||
try:
|
||||
if shared.state.interrupted:
|
||||
break
|
||||
image = Image.open(file)
|
||||
prompt = interrogate(question, prompt, image, model_name, quiet=True)
|
||||
prompts.append(prompt)
|
||||
if write:
|
||||
writer.add(file, prompt)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Interrogate batch: {e}')
|
||||
if write:
|
||||
writer.close()
|
||||
shared.opts.interrogate_offload = orig_offload
|
||||
shared.state.end()
|
||||
return '\n\n'.join(prompts)
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@ import numpy as np
|
||||
from torch.hub import download_url_to_file, get_dir
|
||||
from PIL import Image
|
||||
from modules import devices
|
||||
from modules.shared import log
|
||||
from installer import log
|
||||
|
||||
|
||||
LAMA_MODEL_URL = "https://github.com/enesmsahin/simple-lama-inpainting/releases/download/v0.1.0/big-lama.pt"
|
||||
@@ -38,7 +38,7 @@ def prepare_img_and_mask(image, mask, device, pad_out_to_modulo=8, scale_factor=
|
||||
mode="symmetric",
|
||||
)
|
||||
|
||||
def scale_image(img, factor, interpolation=cv2.INTER_AREA):
|
||||
def scale_image(img, factor, interpolation=cv2.INTER_LANCZOS4):
|
||||
if img.shape[0] == 1:
|
||||
img = img[0]
|
||||
else:
|
||||
@@ -54,7 +54,7 @@ def prepare_img_and_mask(image, mask, device, pad_out_to_modulo=8, scale_factor=
|
||||
out_mask = get_image(mask)
|
||||
if scale_factor is not None:
|
||||
out_image = scale_image(out_image, scale_factor)
|
||||
out_mask = scale_image(out_mask, scale_factor, interpolation=cv2.INTER_NEAREST)
|
||||
out_mask = scale_image(out_mask, scale_factor, interpolation=cv2.INTER_LANCZOS4)
|
||||
if pad_out_to_modulo is not None and pad_out_to_modulo > 1:
|
||||
out_image = pad_img_to_modulo(out_image, pad_out_to_modulo)
|
||||
out_mask = pad_img_to_modulo(out_mask, pad_out_to_modulo)
|
||||
|
||||
@@ -272,7 +272,7 @@ class TransparentVAEDecoder(AutoencoderKL):
|
||||
|
||||
B, H, W, C = fg.shape
|
||||
cb = checkerboard(shape=(H // 64, W // 64))
|
||||
cb = cv2.resize(cb, (W, H), interpolation=cv2.INTER_NEAREST)
|
||||
cb = cv2.resize(cb, (W, H), interpolation=cv2.INTER_LANCZOS4)
|
||||
cb = (0.5 + (cb - 0.5) * 0.1)[None, ..., None]
|
||||
cb = torch.from_numpy(cb).to(fg)
|
||||
|
||||
|
||||
+1
-1
@@ -140,4 +140,4 @@ except ImportError:
|
||||
pass # shrug...
|
||||
|
||||
errors.log.info(f'Torch: torch=={torch.__version__} torchvision=={torchvision.__version__}')
|
||||
errors.log.info(f'Packages: diffusers=={diffusers.__version__} transformers=={transformers.__version__} accelerate=={accelerate.__version__} gradio=={gradio.__version__}')
|
||||
errors.log.info(f'Packages: diffusers=={diffusers.__version__} transformers=={transformers.__version__} accelerate=={accelerate.__version__} gradio=={gradio.__version__} pydantic=={pydantic.__version__}')
|
||||
|
||||
+35
-26
@@ -238,7 +238,7 @@ def run_segment(input_image: gr.Image, input_mask: np.ndarray):
|
||||
overlap = 0
|
||||
if input_mask_size > 0:
|
||||
if mask.shape != input_mask.shape:
|
||||
mask = cv2.resize(mask, (input_mask.shape[1], input_mask.shape[0]), interpolation=cv2.INTER_CUBIC)
|
||||
mask = cv2.resize(mask, (input_mask.shape[1], input_mask.shape[0]), interpolation=cv2.INTER_LANCZOS4)
|
||||
overlap = cv2.bitwise_and(mask, input_mask)
|
||||
overlap = np.count_nonzero(overlap)
|
||||
if overlap == 0:
|
||||
@@ -278,7 +278,7 @@ def run_rembg(input_image: Image, input_mask: np.ndarray):
|
||||
binary_input = cv2.threshold(input_mask, 127, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
|
||||
binary_output = cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
|
||||
if binary_input.shape != binary_output.shape:
|
||||
binary_output = cv2.resize(binary_output, binary_input.shape[:2], interpolation=cv2.INTER_LINEAR)
|
||||
binary_output = cv2.resize(binary_output, binary_input.shape[:2], interpolation=cv2.INTER_LANCZOS4)
|
||||
binary_overlap = cv2.bitwise_and(binary_input, binary_output)
|
||||
input_size = np.count_nonzero(binary_input)
|
||||
overlap_size = np.count_nonzero(binary_overlap)
|
||||
@@ -335,6 +335,8 @@ def get_mask(input_image: gr.Image, input_mask: gr.Image):
|
||||
|
||||
|
||||
def outpaint(input_image: Image.Image, outpaint_type: str = 'Edge'):
|
||||
fn = f'{sys._getframe(2).f_code.co_name}:{sys._getframe(1).f_code.co_name}' # pylint: disable=protected-access
|
||||
debug(f'Run outpaint: fn={fn}') # pylint: disable=protected-access
|
||||
image = cv2.cvtColor(np.array(input_image), cv2.COLOR_RGB2BGR)
|
||||
h0, w0 = image.shape[:2]
|
||||
empty = (image == 0).all(axis=2)
|
||||
@@ -342,35 +344,43 @@ def outpaint(input_image: Image.Image, outpaint_type: str = 'Edge'):
|
||||
x1, x2 = min(x0), max(x0)
|
||||
y1, y2 = min(y0), max(y0)
|
||||
cropped = image[y1:y2, x1:x2]
|
||||
h1, w1 = cropped.shape[:2]
|
||||
mask = None
|
||||
|
||||
if opts.mask_only:
|
||||
mask = cv2.copyMakeBorder(cropped, y1, h0-y2, x1, w0-x2, cv2.BORDER_CONSTANT, value=(0, 0, 0))
|
||||
mask = cv2.resize(mask, (w0, h0))
|
||||
mask = cv2.cvtColor(np.array(mask), cv2.COLOR_BGR2GRAY)
|
||||
mask = cv2.threshold(mask, 0, 255, cv2.THRESH_BINARY)[1]
|
||||
sigmaX, sigmaY = int((h0-h1)/3), int((w0-w1)/3)
|
||||
sigmaX, sigmaY = max(1, sigmaX), max(1, sigmaY)
|
||||
kernel = np.ones((5, 5), np.uint8)
|
||||
mask = cv2.erode(mask, kernel, iterations=max(sigmaX, sigmaY) // 3) # increase overlap area
|
||||
mask = cv2.GaussianBlur(mask, (0, 0), sigmaX=sigmaX, sigmaY=sigmaY) # blur mask
|
||||
mask = Image.fromarray(mask)
|
||||
|
||||
mask = cv2.copyMakeBorder(cropped, y1, h0-y2, x1, w0-x2, cv2.BORDER_CONSTANT, value=(0, 0, 0))
|
||||
mask = cv2.resize(mask, (w0, h0))
|
||||
mask = cv2.cvtColor(np.array(mask), cv2.COLOR_BGR2GRAY)
|
||||
mask = cv2.threshold(mask, 0, 255, cv2.THRESH_BINARY)[1]
|
||||
"""
|
||||
size = min(input_image.width, input_image.height)
|
||||
if opts.mask_erode > 0:
|
||||
try:
|
||||
kernel = np.ones((int(opts.mask_erode * size / 4) + 1, int(opts.mask_erode * size / 4) + 1), np.uint8)
|
||||
mask = cv2.erode(mask, kernel, iterations=opts.kernel_iterations) # remove noise
|
||||
debug(f'Mask erode={opts.mask_erode:.3f} kernel={kernel.shape} mask={mask.shape}')
|
||||
except Exception as e:
|
||||
shared.log.error(f'Mask erode: {e}')
|
||||
if opts.mask_dilate > 0:
|
||||
try:
|
||||
kernel = np.ones((int(opts.mask_dilate * size / 4) + 1, int(opts.mask_dilate * size / 4) + 1), np.uint8)
|
||||
mask = cv2.dilate(mask, kernel, iterations=opts.kernel_iterations) # expand area
|
||||
debug(f'Mask dilate={opts.mask_dilate:.3f} kernel={kernel.shape} mask={mask.shape}')
|
||||
except Exception as e:
|
||||
shared.log.error(f'Mask dilate: {e}')
|
||||
if opts.mask_blur > 0:
|
||||
try:
|
||||
sigmax, sigmay = 1 + int(opts.mask_blur * size / 4), 1 + int(opts.mask_blur * size / 4)
|
||||
mask = cv2.GaussianBlur(mask, (0, 0), sigmaX=sigmax, sigmaY=sigmay) # blur mask
|
||||
debug(f'Mask blur={opts.mask_blur:.3f} x={sigmax} y={sigmay} mask={mask.shape}')
|
||||
except Exception as e:
|
||||
shared.log.error(f'Mask blur: {e}')
|
||||
"""
|
||||
if outpaint_type == 'Edge':
|
||||
bordered = cv2.copyMakeBorder(cropped, y1, h0-y2, x1, w0-x2, cv2.BORDER_REPLICATE)
|
||||
bordered = cv2.resize(bordered, (w0, h0))
|
||||
image = bordered
|
||||
# noise = np.random.normal(1, variation, bordered.shape)
|
||||
# noised = (noise * bordered).astype(np.uint8)
|
||||
# h, w = cropped.shape[:2]
|
||||
# noised[y1:y1 + h, x1:x1 + w] = cropped # overlay original over initialized
|
||||
# image = noised
|
||||
|
||||
# mask = Image.new('L', (w0, h0), 0)
|
||||
|
||||
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
||||
image = Image.fromarray(image)
|
||||
mask = Image.fromarray(mask)
|
||||
return image, mask
|
||||
|
||||
|
||||
@@ -409,9 +419,9 @@ def run_mask(input_image: Image.Image, input_mask: Image.Image = None, return_ty
|
||||
mask = run_rembg(input_image, input_mask)
|
||||
else:
|
||||
mask = run_segment(input_image, input_mask)
|
||||
mask = cv2.resize(mask, (input_image.width, input_image.height), interpolation=cv2.INTER_LINEAR)
|
||||
mask = cv2.resize(mask, (input_image.width, input_image.height), interpolation=cv2.INTER_LANCZOS4)
|
||||
|
||||
debug(f'Mask shape={mask.shape} opts={opts}')
|
||||
shared.log.trace(f'Mask shape={mask.shape} opts={opts} fn={fn}')
|
||||
if opts.mask_erode > 0:
|
||||
try:
|
||||
kernel = np.ones((int(opts.mask_erode * size / 4) + 1, int(opts.mask_erode * size / 4) + 1), np.uint8)
|
||||
@@ -436,7 +446,6 @@ def run_mask(input_image: Image.Image, input_mask: Image.Image = None, return_ty
|
||||
if opts.invert:
|
||||
mask = np.invert(mask)
|
||||
|
||||
|
||||
return_type = return_type or opts.preview_type
|
||||
|
||||
# mask_size = np.count_nonzero(mask)
|
||||
|
||||
@@ -7,7 +7,7 @@ import torch
|
||||
from tensordict import TensorDict
|
||||
import modules.memstats
|
||||
import modules.devices as devices
|
||||
from modules.shared import log, console
|
||||
from installer import log, console
|
||||
from modules.sd_models import read_state_dict
|
||||
from modules.merging import merge_methods
|
||||
from modules.merging.merge_utils import WeightClass
|
||||
|
||||
@@ -4,7 +4,7 @@ from random import shuffle
|
||||
from typing import NamedTuple
|
||||
import torch
|
||||
from scipy.optimize import linear_sum_assignment
|
||||
from modules.shared import log
|
||||
from installer import log
|
||||
|
||||
|
||||
SPECIAL_KEYS = [
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+20
-17
@@ -144,24 +144,27 @@ def quant_flux_bnb(checkpoint_info, transformer, text_encoder_2):
|
||||
|
||||
|
||||
def load_quants(kwargs, repo_id, cache_dir, allow_quant):
|
||||
if not allow_quant:
|
||||
return kwargs
|
||||
quant_args = {}
|
||||
quant_args = model_quant.create_bnb_config(quant_args)
|
||||
if quant_args:
|
||||
model_quant.load_bnb(f'Load model: type=FLUX quant={quant_args}')
|
||||
if not quant_args:
|
||||
quant_args = model_quant.create_ao_config(quant_args)
|
||||
try:
|
||||
if not allow_quant:
|
||||
return kwargs
|
||||
quant_args = {}
|
||||
quant_args = model_quant.create_bnb_config(quant_args)
|
||||
if quant_args:
|
||||
model_quant.load_torchao(f'Load model: type=FLUX quant={quant_args}')
|
||||
if not quant_args:
|
||||
return kwargs
|
||||
if 'transformer' not in kwargs and ('Model' in shared.opts.bnb_quantization or 'Model' in shared.opts.torchao_quantization):
|
||||
kwargs['transformer'] = diffusers.FluxTransformer2DModel.from_pretrained(repo_id, subfolder="transformer", cache_dir=cache_dir, torch_dtype=devices.dtype, **quant_args)
|
||||
shared.log.debug(f'Quantization: module=transformer type=bnb dtype={shared.opts.bnb_quantization_type} storage={shared.opts.bnb_quantization_storage}')
|
||||
if 'text_encoder_2' not in kwargs and ('Text Encoder' in shared.opts.bnb_quantization or 'Text Encoder' in shared.opts.torchao_quantization):
|
||||
kwargs['text_encoder_2'] = transformers.T5EncoderModel.from_pretrained(repo_id, subfolder="text_encoder_2", cache_dir=cache_dir, torch_dtype=devices.dtype, **quant_args)
|
||||
shared.log.debug(f'Quantization: module=t5 type=bnb dtype={shared.opts.bnb_quantization_type} storage={shared.opts.bnb_quantization_storage}')
|
||||
model_quant.load_bnb(f'Load model: type=FLUX quant={quant_args}')
|
||||
if not quant_args:
|
||||
quant_args = model_quant.create_ao_config(quant_args)
|
||||
if quant_args:
|
||||
model_quant.load_torchao(f'Load model: type=FLUX quant={quant_args}')
|
||||
if not quant_args:
|
||||
return kwargs
|
||||
if 'transformer' not in kwargs and ('Model' in shared.opts.bnb_quantization or 'Model' in shared.opts.torchao_quantization):
|
||||
kwargs['transformer'] = diffusers.FluxTransformer2DModel.from_pretrained(repo_id, subfolder="transformer", cache_dir=cache_dir, torch_dtype=devices.dtype, **quant_args)
|
||||
shared.log.debug(f'Quantization: module=transformer type=bnb dtype={shared.opts.bnb_quantization_type} storage={shared.opts.bnb_quantization_storage}')
|
||||
if 'text_encoder_2' not in kwargs and ('Text Encoder' in shared.opts.bnb_quantization or 'Text Encoder' in shared.opts.torchao_quantization):
|
||||
kwargs['text_encoder_2'] = transformers.T5EncoderModel.from_pretrained(repo_id, subfolder="text_encoder_2", cache_dir=cache_dir, torch_dtype=devices.dtype, **quant_args)
|
||||
shared.log.debug(f'Quantization: module=t5 type=bnb dtype={shared.opts.bnb_quantization_type} storage={shared.opts.bnb_quantization_storage}')
|
||||
except Exception as e:
|
||||
shared.log.error(f'Quantization: {e}')
|
||||
return kwargs
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import transformers
|
||||
import diffusers
|
||||
|
||||
|
||||
@@ -22,3 +23,23 @@ def load_lumina(_checkpoint_info, diffusers_load_config={}):
|
||||
)
|
||||
devices.torch_gc()
|
||||
return pipe
|
||||
|
||||
|
||||
def load_lumina2(checkpoint_info, diffusers_load_config={}):
|
||||
from modules import shared, devices, sd_models, model_quant
|
||||
quant_args = {}
|
||||
quant_args = model_quant.create_bnb_config(quant_args)
|
||||
if quant_args:
|
||||
model_quant.load_bnb(f'Load model: type=Lumina quant={quant_args}')
|
||||
if not quant_args:
|
||||
quant_args = model_quant.create_ao_config(quant_args)
|
||||
if quant_args:
|
||||
model_quant.load_torchao(f'Load model: type=Lumina quant={quant_args}')
|
||||
kwargs = {}
|
||||
repo_id = sd_models.path_to_repo(checkpoint_info.name)
|
||||
if ('Model' in shared.opts.bnb_quantization or 'Model' in shared.opts.torchao_quantization):
|
||||
kwargs['transformer'] = diffusers.Lumina2Transformer2DModel.from_pretrained(repo_id, subfolder="transformer", cache_dir=shared.opts.diffusers_dir, torch_dtype=devices.dtype, **quant_args)
|
||||
if ('Text Encoder' in shared.opts.bnb_quantization or 'Text Encoder' in shared.opts.torchao_quantization):
|
||||
kwargs['text_encoder'] = transformers.AutoModel.from_pretrained(repo_id, subfolder="text_encoder", cache_dir=shared.opts.diffusers_dir, torch_dtype=devices.dtype, **quant_args)
|
||||
sd_model = diffusers.Lumina2Text2ImgPipeline.from_pretrained(checkpoint_info.path, cache_dir=shared.opts.diffusers_dir, **diffusers_load_config, **quant_args, **kwargs)
|
||||
return sd_model
|
||||
|
||||
@@ -30,8 +30,6 @@ def get_quant(name):
|
||||
return 'none'
|
||||
|
||||
|
||||
|
||||
|
||||
def create_bnb_config(kwargs = None, allow_bnb: bool = True):
|
||||
from modules import shared, devices
|
||||
if len(shared.opts.bnb_quantization) > 0 and allow_bnb:
|
||||
@@ -77,7 +75,7 @@ def load_torchao(msg='', silent=False):
|
||||
global ao # pylint: disable=global-statement
|
||||
if ao is not None:
|
||||
return ao
|
||||
install('torchao==0.7.0', quiet=True)
|
||||
install('torchao==0.8.0', quiet=True)
|
||||
try:
|
||||
import torchao
|
||||
ao = torchao
|
||||
|
||||
@@ -31,6 +31,8 @@ def get_model_type(pipe):
|
||||
model_type = 'f1'
|
||||
elif "Mochi" in name:
|
||||
model_type = 'mochi'
|
||||
elif "Lumina2" in name:
|
||||
model_type = 'lumina2'
|
||||
elif "Lumina" in name:
|
||||
model_type = 'lumina'
|
||||
elif "OmniGen" in name:
|
||||
|
||||
@@ -110,6 +110,8 @@ def download_civit_model_thread(model_name: str, model_url: str, model_path: str
|
||||
if os.path.isfile(temp_file):
|
||||
starting_pos = os.path.getsize(temp_file)
|
||||
headers['Range'] = f'bytes={starting_pos}-'
|
||||
if token is None:
|
||||
token = shared.opts.civitai_token
|
||||
if token is not None and len(token) > 0:
|
||||
headers['Authorization'] = f'Bearer {token}'
|
||||
|
||||
@@ -396,7 +398,7 @@ def load_civitai(model: str, url: str):
|
||||
return name # already downloaded
|
||||
else:
|
||||
shared.log.debug(f'Reference download start: model="{name}"')
|
||||
download_civit_model_thread(model_name=model, model_url=url, model_path='', model_type='safetensors', token=None)
|
||||
download_civit_model_thread(model_name=model, model_url=url, model_path='', model_type='safetensors', token=shared.opts.civitai_token)
|
||||
shared.log.debug(f'Reference download complete: model="{name}"')
|
||||
sd_models.list_models()
|
||||
info = sd_models.get_closet_checkpoint_match(name)
|
||||
|
||||
@@ -45,12 +45,12 @@ def center_crop_arr(pil_image, image_size):
|
||||
"""
|
||||
while min(*pil_image.size) >= 2 * image_size:
|
||||
pil_image = pil_image.resize(
|
||||
tuple(x // 2 for x in pil_image.size), resample=Image.BOX
|
||||
tuple(x // 2 for x in pil_image.size), resample=Image.Resampling.LANCZOS
|
||||
)
|
||||
|
||||
scale = image_size / min(*pil_image.size)
|
||||
pil_image = pil_image.resize(
|
||||
tuple(round(x * scale) for x in pil_image.size), resample=Image.BICUBIC
|
||||
tuple(round(x * scale) for x in pil_image.size), resample=Image.Resampling.LANCZOS
|
||||
)
|
||||
|
||||
arr = np.array(pil_image)
|
||||
@@ -63,19 +63,19 @@ def center_crop_arr(pil_image, image_size):
|
||||
def crop_arr(pil_image, max_image_size):
|
||||
while min(*pil_image.size) >= 2 * max_image_size:
|
||||
pil_image = pil_image.resize(
|
||||
tuple(x // 2 for x in pil_image.size), resample=Image.BOX
|
||||
tuple(x // 2 for x in pil_image.size), resample=Image.Resampling.LANCZOS
|
||||
)
|
||||
|
||||
if max(*pil_image.size) > max_image_size:
|
||||
scale = max_image_size / max(*pil_image.size)
|
||||
pil_image = pil_image.resize(
|
||||
tuple(round(x * scale) for x in pil_image.size), resample=Image.BICUBIC
|
||||
tuple(round(x * scale) for x in pil_image.size), resample=Image.Resampling.LANCZOS
|
||||
)
|
||||
|
||||
if min(*pil_image.size) < 16:
|
||||
scale = 16 / min(*pil_image.size)
|
||||
pil_image = pil_image.resize(
|
||||
tuple(round(x * scale) for x in pil_image.size), resample=Image.BICUBIC
|
||||
tuple(round(x * scale) for x in pil_image.size), resample=Image.Resampling.LANCZOS
|
||||
)
|
||||
|
||||
arr = np.array(pil_image)
|
||||
|
||||
@@ -103,7 +103,7 @@ def setup_model(dirname):
|
||||
restored_img = self.face_helper.paste_faces_to_input_image()
|
||||
restored_img = restored_img[:, :, ::-1]
|
||||
if original_resolution != restored_img.shape[0:2]:
|
||||
restored_img = cv2.resize(restored_img, (0, 0), fx=original_resolution[1]/restored_img.shape[1], fy=original_resolution[0]/restored_img.shape[0], interpolation=cv2.INTER_LINEAR)
|
||||
restored_img = cv2.resize(restored_img, (0, 0), fx=original_resolution[1]/restored_img.shape[1], fy=original_resolution[0]/restored_img.shape[0], interpolation=cv2.INTER_LANCZOS4)
|
||||
self.face_helper.clean_all()
|
||||
if shared.opts.detailer_unload:
|
||||
self.send_model_to(devices.cpu)
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
import numpy as np
|
||||
|
||||
|
||||
def DetectDirect(A, dcc_type, k, T):
|
||||
if dcc_type == 1:
|
||||
# 45 degree diagonal direction
|
||||
t1 = abs(A[2,0]-A[0,2])
|
||||
t2 = abs(A[4,0]-A[2,2])+abs(A[2,2]-A[0,4])
|
||||
t3 = abs(A[6,0]-A[4,2])+abs(A[4,2]-A[2,4])+abs(A[2,4]-A[0,6])
|
||||
t4 = abs(A[6,2]-A[4,4])+abs(A[4,4]-A[2,6])
|
||||
t5 = abs(A[6,4]-A[4,6])
|
||||
d1 = t1+t2+t3+t4+t5
|
||||
|
||||
# 135 degree diagonal direction
|
||||
t1 = abs(A[0,4]-A[2,6])
|
||||
t2 = abs(A[0,2]-A[2,4])+abs(A[2,4]-A[4,6])
|
||||
t3 = abs(A[0,0]-A[2,2])+abs(A[2,2]-A[4,4])+abs(A[4,4]-A[6,6])
|
||||
t4 = abs(A[2,0]-A[4,2])+abs(A[4,2]-A[6,4])
|
||||
t5 = abs(A[4,0]-A[6,2])
|
||||
d2 = t1+t2+t3+t4+t5
|
||||
else:
|
||||
# horizontal direction
|
||||
t1 = abs(A[0,1]-A[0,3])+abs(A[2,1]-A[2,3])+abs(A[4,1]-A[4,3])
|
||||
t2 = abs(A[1,0]-A[1,2])+abs(A[1,2]-A[1,4])
|
||||
t3 = abs(A[3,0]-A[3,2])+abs(A[3,2]-A[3,4])
|
||||
d1 = t1+t2+t3
|
||||
|
||||
# vertical direction
|
||||
t1 = abs(A[1,0]-A[3,0])+abs(A[1,2]-A[3,2])+abs(A[1,4]-A[3,4])
|
||||
t2 = abs(A[0,1]-A[2,1])+abs(A[2,1]-A[4,1])
|
||||
t3 = abs(A[0,3]-A[2,3])+abs(A[2,3]-A[4,3])
|
||||
d2 = t1+t2+t3
|
||||
# Compute the weight vector
|
||||
w = np.array([1/(1+d1**k), 1/(1+d2**k)])
|
||||
# Compute the directional index
|
||||
n = 3
|
||||
if (1+d1)/(1+d2) > T:
|
||||
n = 1
|
||||
elif (1+d2)/(1+d1) > T:
|
||||
n = 2
|
||||
return w, n
|
||||
|
||||
def PixelValue(A, mode, w, n, f):
|
||||
if mode == 1:
|
||||
v1 = np.diag(np.fliplr(A))[::2]
|
||||
v2 = np.diag(A)[::2]
|
||||
else:
|
||||
v1 = A[3,::2]
|
||||
v2 = A[::2,3]
|
||||
if n == 1:
|
||||
p = np.dot(v2, f)
|
||||
elif n == 2:
|
||||
p = np.dot(v1, f)
|
||||
else:
|
||||
p1 = np.dot(v1, f)
|
||||
p2 = np.dot(v2, f)
|
||||
p = (w[0]*p1+w[1]*p2)/(w[0]+w[1])
|
||||
return p
|
||||
|
||||
def PadLeftTop(img_pad, H, W):
|
||||
img = img_pad[3:-3,3:-3]
|
||||
# Pad the first/last three col and row
|
||||
img_pad[3:H+3,1]=img[:,0]
|
||||
img_pad[H+3::2,3:W+3]=img[H-2:H-1,:]
|
||||
img_pad[3:H+3,W+3::2]=img[:,W-2:W-1]
|
||||
img_pad[1,3:W+3]=img[0,:]
|
||||
# Pad the missing nine points
|
||||
img_pad[1,1]=img[0,0]
|
||||
img_pad[H+3::2,1]=img[H-2,0]
|
||||
img_pad[H+3::2,W+3::2]=img[H-2,W-2]
|
||||
img_pad[1,W+3::2]=img[0,W-2]
|
||||
return img_pad
|
||||
|
||||
def PadRightBottom(img_pad, H, W):
|
||||
img = img_pad[3:-3,3:-3]
|
||||
# Pad the first/last three col and row
|
||||
img_pad[3:H+3,0:3:2]=img[:,1:2]
|
||||
img_pad[H+4::2,3:W+3]=img[H-1:H,:]
|
||||
img_pad[3:H+3,W+4::2]=img[:,W-1:W]
|
||||
img_pad[0:3:2,3:W+3]=img[1,:]
|
||||
# Pad the missing nine points
|
||||
img_pad[0:3:2,0:3:2]=img[1,1]
|
||||
img_pad[H+4,0:3:2]=img[H-1,1]
|
||||
img_pad[H+4,W+4]=img[H-1,W-1]
|
||||
img_pad[0:3:2,W+4]=img[0,W-1]
|
||||
return img_pad
|
||||
|
||||
def _DCC(I, k, T):
|
||||
m, n = I.shape
|
||||
nRow = 2*m
|
||||
nCol = 2*n
|
||||
A = np.zeros([nRow+6, nCol+6])
|
||||
A[0+3:-1-3:2, 0+3:-1-3:2] = I
|
||||
A = PadLeftTop(A, nRow, nCol)
|
||||
f = np.array([-1, 9, 9, -1])/16
|
||||
for i in range(4,nRow+3,2):
|
||||
for j in range(4,nCol+3,2):
|
||||
[w,n] = DetectDirect(A[i-3:i+4,j-3:j+4],1,k,T)
|
||||
A[i,j] = PixelValue(A[i-3:i+4,j-3:j+4],1,w,n,f)
|
||||
A = PadRightBottom(A, nRow, nCol)
|
||||
for i in range(3,nRow+3,2):
|
||||
for j in range(4,nCol+3,2):
|
||||
[w,n] = DetectDirect(A[i-2:i+3,j-2:j+3],2,k,T)
|
||||
A[i,j] = PixelValue(A[i-3:i+4,j-3:j+4],2,w,n,f)
|
||||
for i in range(4,nRow+3,2):
|
||||
for j in range(3,nCol+3,2):
|
||||
[w,n] = DetectDirect(A[i-2:i+3,j-2:j+3],3,k,T)
|
||||
A[i,j] = PixelValue(A[i-3:i+4,j-3:j+4],3,w,n,f)
|
||||
return A[3:-3,3:-3]
|
||||
|
||||
|
||||
'''
|
||||
img: Shape[H,W,C], Value Range[0-1]
|
||||
level: super resolution level
|
||||
Return: super resolution img who shape is the same with input
|
||||
'''
|
||||
def DCC(img, level):
|
||||
# hyper parameters
|
||||
k, T = 5, 1.15
|
||||
sr_img = img
|
||||
# get the high resolution image channel by channel
|
||||
for channel in range(img.shape[-1]):
|
||||
sr_img_simple = img[:,:,channel]
|
||||
for _ in range(level):
|
||||
sr_img_simple = _DCC(sr_img_simple, k, T)
|
||||
sr_img[:,:,channel] = sr_img_simple
|
||||
return sr_img
|
||||
@@ -253,7 +253,7 @@ class RealESRGANer():
|
||||
output_alpha = cv2.cvtColor(output_alpha, cv2.COLOR_BGR2GRAY)
|
||||
else: # use the cv2 resize for alpha channel
|
||||
h, w = alpha.shape[0:2]
|
||||
output_alpha = cv2.resize(alpha, (w * self.scale, h * self.scale), interpolation=cv2.INTER_LINEAR)
|
||||
output_alpha = cv2.resize(alpha, (w * self.scale, h * self.scale), interpolation=cv2.INTER_LANCZOS4)
|
||||
|
||||
# merge the alpha channel
|
||||
output_img = cv2.cvtColor(output_img, cv2.COLOR_BGR2BGRA)
|
||||
|
||||
@@ -34,7 +34,7 @@ def restore(np_image, name, session, strength): # pylint: disable=unused-argumen
|
||||
|
||||
detected_faces = len(face_helper.cropped_faces)
|
||||
for cropped_face in face_helper.cropped_faces:
|
||||
cropped_face = cv2.resize(cropped_face, resolution, interpolation=cv2.INTER_LINEAR)
|
||||
cropped_face = cv2.resize(cropped_face, resolution, interpolation=cv2.INTER_LANCZOS4)
|
||||
cropped_face = cropped_face.astype(np.float16)[:,:,::-1] / 255.0
|
||||
cropped_face = cropped_face.transpose((2, 0, 1))
|
||||
cropped_face = (cropped_face - 0.5) / 0.5
|
||||
@@ -52,7 +52,7 @@ def restore(np_image, name, session, strength): # pylint: disable=unused-argumen
|
||||
restored_img = face_helper.paste_faces_to_input_image()
|
||||
restored_img = restored_img[:, :, ::-1]
|
||||
if original_resolution != restored_img.shape[0:2]:
|
||||
restored_img = cv2.resize(restored_img, (0, 0), fx=original_resolution[1]/restored_img.shape[1], fy=original_resolution[0]/restored_img.shape[0], interpolation=cv2.INTER_LINEAR)
|
||||
restored_img = cv2.resize(restored_img, (0, 0), fx=original_resolution[1]/restored_img.shape[1], fy=original_resolution[0]/restored_img.shape[0], interpolation=cv2.INTER_LANCZOS4)
|
||||
|
||||
face_helper.clean_all()
|
||||
t1 = time.time()
|
||||
|
||||
@@ -346,10 +346,10 @@ class YoloRestorer(Detailer):
|
||||
with gr.Row():
|
||||
enabled = gr.Checkbox(label="Enable detailer pass", elem_id=f"{tab}_detailer_enabled", value=False)
|
||||
with gr.Row():
|
||||
detailers = gr.Dropdown(label="Detailers", elem_id=f"{tab}_detailers", choices=self.list, value=shared.opts.detailer_models, multiselect=True)
|
||||
detailers = gr.Dropdown(label="Detailer models", elem_id=f"{tab}_detailers", choices=self.list, value=shared.opts.detailer_models, multiselect=True)
|
||||
ui_common.create_refresh_button(detailers, self.enumerate, {}, elem_id=f"{tab}_detailers_refresh")
|
||||
with gr.Row():
|
||||
classes = gr.Textbox(label="Classes", placeholder="Classes", elem_id=f"{tab}_detailer_classes")
|
||||
classes = gr.Textbox(label="Detailer classes", placeholder="Classes", elem_id=f"{tab}_detailer_classes")
|
||||
with gr.Row():
|
||||
prompt = gr.Textbox(label="Detailer prompt", value='', placeholder='Detailer prompt', lines=2, elem_id=f"{tab}_detailer_prompt")
|
||||
with gr.Row():
|
||||
|
||||
@@ -260,9 +260,10 @@ def process_init(p: StableDiffusionProcessing):
|
||||
else:
|
||||
p.all_subseeds = [int(subseed) + x for x in range(len(p.all_prompts))]
|
||||
if reset_prompts:
|
||||
p.all_prompts, p.all_negative_prompts = shared.prompt_styles.apply_styles_to_prompts(p.all_prompts, p.all_negative_prompts, p.styles, p.all_seeds)
|
||||
p.prompts = p.all_prompts[p.iteration * p.batch_size:(p.iteration+1) * p.batch_size]
|
||||
p.negative_prompts = p.all_negative_prompts[p.iteration * p.batch_size:(p.iteration+1) * p.batch_size]
|
||||
if not hasattr(p, 'keep_prompts'):
|
||||
p.all_prompts, p.all_negative_prompts = shared.prompt_styles.apply_styles_to_prompts(p.all_prompts, p.all_negative_prompts, p.styles, p.all_seeds)
|
||||
p.prompts = p.all_prompts[p.iteration * p.batch_size:(p.iteration+1) * p.batch_size]
|
||||
p.negative_prompts = p.all_negative_prompts[p.iteration * p.batch_size:(p.iteration+1) * p.batch_size]
|
||||
p.prompts, _ = extra_networks.parse_prompts(p.prompts)
|
||||
|
||||
|
||||
@@ -312,8 +313,9 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
if shared.native:
|
||||
from modules import ipadapter
|
||||
ipadapter.apply(shared.sd_model, p)
|
||||
p.prompts = p.all_prompts[n * p.batch_size:(n+1) * p.batch_size]
|
||||
p.negative_prompts = p.all_negative_prompts[n * p.batch_size:(n+1) * p.batch_size]
|
||||
if not hasattr(p, 'keep_prompts'):
|
||||
p.prompts = p.all_prompts[n * p.batch_size:(n+1) * p.batch_size]
|
||||
p.negative_prompts = p.all_negative_prompts[n * p.batch_size:(n+1) * p.batch_size]
|
||||
p.seeds = p.all_seeds[n * p.batch_size:(n+1) * p.batch_size]
|
||||
p.subseeds = p.all_subseeds[n * p.batch_size:(n+1) * p.batch_size]
|
||||
if p.scripts is not None and isinstance(p.scripts, scripts.ScriptRunner):
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user