diff --git a/.gitignore b/.gitignore index 9bc6d5a24..3bb999538 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ tunableop_results*.csv /*.txt /*.mp3 /*.lnk +/*.swp !webui.bat !webui.sh !package.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 620c2c730..357042c9c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,15 +1,6 @@ -# To use: -# -# pre-commit run -a -# -# Or: -# -# pre-commit install # (runs every time you commit in git) -# -# To update this file: -# -# pre-commit autoupdate -# +# To use: pre-commit run -a +# Or: pre-commit install # (runs every time you commit in git) +# To update this file: pre-commit autoupdate # See https://github.com/pre-commit/pre-commit ci: @@ -19,7 +10,7 @@ ci: repos: # Standard hooks - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: check-added-large-files - id: check-case-conflict @@ -35,6 +26,7 @@ repos: - id: check-json - id: check-toml - id: check-xml + - id: debug-statements - id: end-of-file-fixer - id: mixed-line-ending - id: check-executables-have-shebangs diff --git a/.pylintrc b/.pylintrc index 5dc22b99c..27d3e7a52 100644 --- a/.pylintrc +++ b/.pylintrc @@ -2,6 +2,7 @@ analyse-fallback-blocks=no clear-cache-post-run=no extension-pkg-allow-list= +prefer-stubs=yes extension-pkg-whitelist= fail-on= fail-under=10 @@ -44,6 +45,7 @@ ignore-paths=/usr/lib/.*$, pipelines/omnigen2, pipelines/segmoe, pipelines/xomni, + pipelines/chrono, scripts/consistory, scripts/ctrlx, scripts/daam, @@ -69,11 +71,11 @@ ignore-patterns=.*test*.py$, .*_model_arch.py*, .*_model_arch_v2.py$, ignored-modules= -jobs=0 +jobs=8 limit-inference-results=100 load-plugins= -persistent=yes -py-version=3.9 +persistent=no +py-version=3.10 recursive=no source-roots= unsafe-load-any-extension=no @@ -207,6 +209,8 @@ disable=abstract-method, unnecessary-lambda-assigment, unnecessary-lambda, unused-wildcard-import, + unpacking-non-sequence, + unsubscriptable-object, useless-return, use-dict-literal, use-symbolic-message-instead, diff --git a/.ruff.toml b/.ruff.toml index de4e0f7a5..7b4f4fb57 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -24,6 +24,7 @@ exclude = [ "pipelines/hdm", "pipelines/segmoe", "pipelines/xomni", + "pipelines/chrono", "scripts/lbm", "scripts/daam", diff --git a/CHANGELOG.md b/CHANGELOG.md index 7deaed7a1..13fb9c654 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,114 @@ # Change Log for SD.Next +## Update for 2025-10-31 + +### Highlights for 2025-10-31 + +Less than 2 weeks since last release, here's a service-pack style update with a lot of fixes and improvements: +- Reorganization of **Reference Models** into *Base, Quantized, Distilled and Community* sections for easier navigation + and introduction of optimized **pre-quantized** variants for many popular models - use this as your quick start! +- New models: + **HunyuanImage 2.1** capable of 2K images natively, **HunyuanImage 3.0** large unified multimodal autoregressive model, + **ChronoEdit** that re-purposes temporal consistency of generation for image editing + **Pony 7** based on AuraFlow architecture, **Kandinsky 5** 10s video models +- New **offline mode** to use previously downloaded models without internet connection +- Optimizations to **WAN-2.2** given its popularity + plus addition of native **VAE Upscaler** and optimized **pre-quantized** variants +- New SOTA model loader using **Run:ai streamer** +- Updates to `rocm` and `xpu` backends +- Fixes, fixes, fixes... too many to list here! + +![Screenshot](https://github.com/user-attachments/assets/d6119a63-6ee5-4597-95f6-29ed0701d3b5) + +[ReadMe](https://github.com/vladmandic/automatic/blob/master/README.md) | [ChangeLog](https://github.com/vladmandic/automatic/blob/master/CHANGELOG.md) | [Docs](https://vladmandic.github.io/sdnext-docs/) | [WiKi](https://github.com/vladmandic/automatic/wiki) | [Discord](https://discord.com/invite/sd-next-federal-batch-inspectors-1101998836328697867) | [Sponsor](https://github.com/sponsors/vladmandic) + +### Details for 2025-10-31 + +- **Reference** networks section is now split into actual *Base* models plus: + - **Quantized**: pre-quantized variants of the base models using SDNQ-SVD quantization for optimal quality and smallest possible resource usage + examples: *FLUX.1-Dev/Krea/Kontext/Schnell, Qwen-Image/Edit/2509, Chroma1-HD, WAN-2.2-A44B, etc.* + *note*: pre-quantized *WAN-2.2-14B* is also available in video models and runs with only 12GB VRAM! + - **Distilled**: distilled variants of base models + examples: *Turbo, Lightning, Lite, SRPO, Distill, Pruning, etc.* + - **Community**: community highlights + examples: *Tempest, Juggernaut, Illustrious, Pony, NoobAI, etc.* + and all reference models have new preview images, thanks @liutyi +- **Models Reference** + - [Tencent HunyuanImage 2.1](https://huggingface.co/tencent/HunyuanImage-2.1) in *full*, *distilled* and *refiner* variants + *HunyuanImage-2.1* is a large (51GB) T2I model capable of natively generating 2K images and uses Qwen2.5 + T5 text-encoders and 32x VAE + - [Tencent HunyuanImage 3.0](https://huggingface.co/tencent/HunyuanImage-3.0) in [pre-quant](https://huggingface.co/Disty0/HunyuanImage3-SDNQ-uint4-svd-r32) only variant due to massive size + *HunyuanImage 3.0* is very large at 47GB pre-quantized (oherwise its 157GB) that unifies multimodal understanding and generation within an autoregressive framework + - [nVidia ChronoEdit](https://huggingface.co/nvidia/ChronoEdit-14B-Diffusers) + *ChronoEdit* is a 14B image editing model based on *WAN* + this model reframes image editing as a video generation task, using input and edited images as start/end frames to leverage pretrained video models with temporal consistency + to extend temporal consistency for image editing, set *settings -> model options -> chrono temporal steps* to desired number of temporaly reasoning steps + - [Kandinsky 5 Lite 10s](https://huggingface.co/ai-forever/Kandinsky-5.0-T2V-Lite-sft-10s-Diffusers') in *SFT, CFG-distilled and Steps-distilled* variants + second series of models in *Kandinsky5* series is T2V model optimized for 10sec videos and uses Qwen2.5 text encoder + - [Pony 7](https://huggingface.co/purplesmartai/pony-v7-base) + Pony 7 steps in a different direction from previous Pony models and is based on AuraFlow architecture and UMT5 encoder +- **Models Auxiliary** + - [Qwen 3-VL](https://huggingface.co/Qwen/Qwen3-VL-4B-Instruct) VLM for interrogate and prompt enhance, thanks @CalamitousFelicitousness + this includes *2B, 4B and 8B* variants + - [WAN Asymettric Upscale](https://huggingface.co/spacepxl/Wan2.1-VAE-upscale2x) + available as general purpose upscaler that can be used during standard workflow or process tab + available as VAE for compatible video models: *WAN-2.x-14B, SkyReels-v2* models + - [Apple DepthPro](https://huggingface.co/apple/DepthPro) controlnet processor, thanks @nolbert82 + - [LibreFlux controlnet](https://huggingface.co/neuralvfx/LibreFlux-ControlNet) segmentation controlnet for FLUX.1 +- **Features** + - **offline mode**: enable in *settings -> hugginface* + enables fully offline mode where previously downloaded models can be used as-is + *note*: must be enabled only after all packages have been installed and model has been run online at least once + - **model load**: SOTA method using nVidia's [Run:ai streamer](https://github.com/run-ai/runai-model-streamer) + enable in *settings -> model options -> runai streamer* + applies to *diffusers, transformers and sdnq* loaders, note this is linux-only feature + *experimental* but shows significant model load speedups, 20-40% depending on model and hardware +- **Backend** + - switch to `torch==2.9` for *ipex, rocm and openvino* + - switch to `rocm==7.0` for nightlies + - log `triton` availability on startup + - add `xpu` stats in gpu monitor +- **Other** + - improved **SDNQ SVD** and low-bit matmul performance + - reduce RAM usage on model load using **SDNQ SVD** + - change default **schedulers** for sdxl + - warn on `python==3.9` end-of-life and `python==3.10` not actively supported + - **scheduler** add base and max shift parameters for flow-matching samplers + - enhance `--optional` flag to pre-install optional packages + - add `[lora]` to recognized filename patterns + - when using **shared-t5** *(default)*, it will load standard or pre-quant depending on model + - enhanced LoRA support for **Wan-2.2-14B** + - log available attention mechanisms on startup + - support for switching back-and-forth **t2i** and **t2v** for *wan-2.x* models + - control `api` cache controlnets + - additional model modules **deduplication** for both normal and pre-quant models: *umt5, qwen25-vl* +- **Fixes** + - startup error with `--profile` enabled if using `--skip` + - restore orig init image for each batch sequence + - fix modernui hints layout + - fix `wan-2.2-a14b` stage selection + - fix `wan-2.2-5b` vae decode + - disabling live preview should not disable progress updates + - video tab create `params.txt` with metadata + - fix full-screen image-viewer toolbar actions with control tab + - improve filename sanitization + - lora auto-detect low/high stage if not specified + - lora disable fuse on partially applied network + - fix networks display with extended characters, thanks @awsr + - installer handle different `opencv` package variants + - fix using pre-quantized shared-t5 + - fix `wan-2.2-14b-vace` single-stage exectution + - fix `wan-2.2-5b` tiled vae decode + - fix `controlnet` loading with quantization + - video use pre-quantized text-encoder if selected model is pre-quantized + - handle sparse `controlnet` models + - catch `xet` warnings + - avoid unnecessary pipe variant switching + - validate pipelines on import + - fix `nudenet` process tab operations + - `controlnet` input validation + - log metadata keys that cannot be applied + - fix `framepack` with image input + ## Update for 2025-10-18 - **Models** diff --git a/TODO.md b/TODO.md index 56ece93cd..9405eb231 100644 --- a/TODO.md +++ b/TODO.md @@ -1,71 +1,85 @@ # TODO -Main ToDo list can be found at [GitHub projects](https://github.com/users/vladmandic/projects) +## Project Board -## Future Candidates +- -- Transformers unified cache handler -- Remote TE -- [Canvas](https://konvajs.org/) +## Internal + +- UI: New inpaint/outpaint interface + [Kanvas](https://github.com/vladmandic/kanvas) +- Deploy: Create executable for SD.Next +- Feature: Integrate natural language imagesearch + [ImageDB](https://github.com/vladmandic/imagedb) +- Feature: Transformers unified cache handler +- Feature: Remote Text-Encoder support - Refactor: [Modular pipelines and guiders](https://github.com/huggingface/diffusers/issues/11915) -- Refactor: Sampler options +- Refactor: move sampler options to settings to config - Refactor: [GGUF](https://huggingface.co/docs/diffusers/main/en/quantization/gguf) - Feature: LoRA add OMI format support for SD35/FLUX.1 -- Video Core: API -- Video LTX: TeaCache and others, API, Conditioning preprocess Video: LTX API - -### Under Consideration - -- [Inf-DiT](https://github.com/zai-org/Inf-DiT) -- [DiffSynth Studio](https://github.com/modelscope/DiffSynth-Studio) -- [IPAdapter negative guidance](https://github.com/huggingface/diffusers/discussions/7167) -- [IPAdapter composition](https://huggingface.co/ostris/ip-composition-adapter) -- [STG](https://github.com/huggingface/diffusers/blob/main/examples/community/README.md#spatiotemporal-skip-guidance) -- [SmoothCache](https://github.com/huggingface/diffusers/issues/11135) -- [MagCache](https://github.com/lllyasviel/FramePack/pull/673/files) -- [Dream0 guidance](https://huggingface.co/ByteDance/DreamO) -- [ByteDance OneReward](https://github.com/bytedance/OneReward) -- [ByteDance USO](https://github.com/bytedance/USO) -- Remove: `CodeFormer` -- Remove: `GFPGAN` -- ModernUI: Lite vs Expert mode +- Refactor: remove `CodeFormer` +- Refactor: remove `GFPGAN` +- UI: Lite vs Expert mode +- Video tab: add full API support +- Control tab: add overrides handling - Engine: TensorRT acceleration -### New models +## Features -- [Ovi](https://github.com/character-ai/Ovi) -- [Kandinsky 5](https://github.com/ai-forever/Kandinsky-5) +- [IPAdapter composition](https://huggingface.co/ostris/ip-composition-adapter) +- [IPAdapter negative guidance](https://github.com/huggingface/diffusers/discussions/7167) +- [MagCache](https://github.com/lllyasviel/FramePack/pull/673/files) +- [SmoothCache](https://github.com/huggingface/diffusers/issues/11135) +- [STG](https://github.com/huggingface/diffusers/blob/main/examples/community/README.md#spatiotemporal-skip-guidance) +- [Video Inpaint Pipeline](https://github.com/huggingface/diffusers/pull/12506) + +### New models / Pipelines + +TODO: *Prioritize*! + +- [Bria FIBO](https://huggingface.co/briaai/FIBO) - [Bytedance Lynx](https://github.com/bytedance/lynx) -- [HunyuanImage](https://huggingface.co/tencent/HunyuanImage-2.1) -- [Phantom HuMo](https://github.com/Phantom-video/Phantom) -- [Lumina-DiMOO](https://huggingface.co/Alpha-VLLM/Lumina-DiMOO) -- [Wan2.2-Animate-14B](https://huggingface.co/Wan-AI/Wan2.2-Animate-14B) -- [Magi](https://github.com/SandAI-org/MAGI-1)(https://github.com/huggingface/diffusers/pull/11713) -- [SEVA](https://github.com/huggingface/diffusers/pull/11440) -- [Ming](https://github.com/inclusionAI/Ming) -- [Liquid](https://github.com/FoundationVision/Liquid) -- [Step1X](https://github.com/stepfun-ai/Step1X-Edit) -- [LucyEdit](https://github.com/huggingface/diffusers/pull/12340) -- [SD3 UltraEdit](https://github.com/HaozheZhao/UltraEdit) -- [WAN2GP](https://github.com/deepbeepmeep/Wan2GP) -- [SelfForcing](https://github.com/guandeh17/Self-Forcing) +- [ByteDance OneReward](https://github.com/bytedance/OneReward) +- [ByteDance USO](https://github.com/bytedance/USO) +- [Chroma1 Radiance](https://huggingface.co/lodestones/Chroma1-Radiance) +- [DiffSynth Studio](https://github.com/modelscope/DiffSynth-Studio) - [DiffusionForcing](https://github.com/kwsong0113/diffusion-forcing-transformer) -- [LanDiff](https://github.com/landiff/landiff) -- [HunyuanCustom](https://github.com/Tencent-Hunyuan/HunyuanCustom) +- [Dream0 guidance](https://huggingface.co/ByteDance/DreamO) - [HunyuanAvatar](https://huggingface.co/tencent/HunyuanVideo-Avatar) -- [WAN-CausVid](https://huggingface.co/lightx2v/Wan2.1-T2V-14B-CausVid) +- [HunyuanCustom](https://github.com/Tencent-Hunyuan/HunyuanCustom) +- [Inf-DiT](https://github.com/zai-org/Inf-DiT) +- [Krea Realtime Video](https://huggingface.co/krea/krea-realtime-video) +- [LanDiff](https://github.com/landiff/landiff) +- [Liquid](https://github.com/FoundationVision/Liquid) +- [LongCat-Video](https://huggingface.co/meituan-longcat/LongCat-Video) +- [LucyEdit](https://github.com/huggingface/diffusers/pull/12340) +- [Lumina-DiMOO](https://huggingface.co/Alpha-VLLM/Lumina-DiMOO) +- [Magi](https://github.com/SandAI-org/MAGI-1)(https://github.com/huggingface/diffusers/pull/11713) +- [Ming](https://github.com/inclusionAI/Ming) +- [MUG-V 10B](https://huggingface.co/MUG-V/MUG-V-inference) +- [Ovi](https://github.com/character-ai/Ovi) +- [Phantom HuMo](https://github.com/Phantom-video/Phantom) +- [SD3 UltraEdit](https://github.com/HaozheZhao/UltraEdit) +- [SelfForcing](https://github.com/guandeh17/Self-Forcing) +- [SEVA](https://github.com/huggingface/diffusers/pull/11440) +- [Step1X](https://github.com/stepfun-ai/Step1X-Edit) +- [Wan-2.2 Animate](https://github.com/huggingface/diffusers/pull/12526) +- [Wan-2.2 S2V](https://github.com/huggingface/diffusers/pull/12258) - [WAN-CausVid-Plus t2v](https://github.com/goatWu/CausVid-Plus/) +- [WAN-CausVid](https://huggingface.co/lightx2v/Wan2.1-T2V-14B-CausVid) - [WAN-StepDistill](https://huggingface.co/lightx2v/Wan2.1-T2V-14B-StepDistill-CfgDistill) +- [Wan2.2-Animate-14B](https://huggingface.co/Wan-AI/Wan2.2-Animate-14B) +- [WAN2GP](https://github.com/deepbeepmeep/Wan2GP) ## Code TODO -> pnpm lint | grep W0511 | awk -F'TODO ' '{print "- "$NF}' | sed 's/ (fixme)//g' | sort +> npm run todo - control: support scripts via api - fc: autodetect distilled based on model - fc: autodetect tensor format based on model - hypertile: vae breaks when using non-standard sizes -- install: enable ROCm for windows when available +- install: switch to pytorch source when it becomes available - loader: load receipe - loader: save receipe - lora: add other quantization types @@ -75,5 +89,6 @@ Main ToDo list can be found at [GitHub projects](https://github.com/users/vladma - model load: implement model in-memory caching - modernui: monkey-patch for missing tabs.select event - modules/lora/lora_extract.py:188:9: W0511: TODO: lora: support pre-quantized flux +- modules/modular_guiders.py:65:58: W0511: TODO: guiders - processing: remove duplicate mask params - resize image: enable full VAE mode for resize-latent diff --git a/cli/api-control.py b/cli/api-control.py index 925c77599..0e73e94be 100755 --- a/cli/api-control.py +++ b/cli/api-control.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# example: api-control.py --prompt "anime girl" --control "Canny:Canny:1.0:0.1:0.9:/home/vlado/generative/Samples/anime1.jpg,None:Depth:0.9:0.0:1.0:/home/vlado/generative/Samples/anime1.jpg" --hires --detailer --output /tmp/anime.jpg import os import io import time @@ -70,10 +71,12 @@ def generate(args): # pylint: disable=redefined-outer-name options['negative_prompt'] = args.negative options['steps'] = int(args.steps) options['seed'] = int(args.seed) - options['sampler_name'] = args.sampler + if args.sampler is not None: + options['sampler_name'] = args.sampler if args.control is not None: - options['unit_type'] = args.type + if args.type is not None: + options['unit_type'] = args.type options['control'] = [] for control in args.control.split(','): u = control.split(':') @@ -86,7 +89,9 @@ def generate(args): # pylint: disable=redefined-outer-name 'strength': float(u[2].strip()) if len(u) > 2 else 1.0, 'start': float(u[3].strip()) if len(u) > 3 else 0.0, 'end': float(u[4].strip()) if len(u) > 4 else 1.0, + 'override': encode(u[5].strip()) if len(u) > 5 else None, }) + log.info(f'added control: {options["control"]}') if args.ipadapter is not None: options['ip_adapter'] = [] @@ -109,6 +114,20 @@ def generate(args): # pylint: disable=redefined-outer-name if args.mask is not None: options['mask'] = encode(args.mask) + if args.detailer: + options['detailer_enabled'] = True + + if args.hires: + options['enable_hr'] = True + options['hr_force'] = True + + if args.upscaler is not None: + options['enable_hr'] = True + options['hr_force'] = True + options['hr_scale'] = 2 + options['hr_resize_mode'] = 1 + options['hr_upscaler'] = args.upscaler + data = post('/sdapi/v1/control', options) t1 = time.time() if 'info' in data: @@ -141,13 +160,16 @@ if __name__ == "__main__": parser.add_argument('--negative', required=False, default='', help='negative prompt text') parser.add_argument('--steps', required=False, default=20, help='number of steps') parser.add_argument('--seed', required=False, default=-1, help='initial seed') - parser.add_argument('--sampler', required=False, default='UniPC', help='sampler name') + parser.add_argument('--sampler', required=False, default=None, help='sampler name') parser.add_argument('--output', required=False, default=None, help='output image file') parser.add_argument('--processed', required=False, default=None, help='processed output file') parser.add_argument('--model', required=False, help='model name') - parser.add_argument('--type', required=False, help='control type') + parser.add_argument('--type', required=False, default="controlnet", help='control type') parser.add_argument('--control', required=False, help='control units') parser.add_argument('--ipadapter', required=False, help='ipadapter units') + parser.add_argument('--detailer', required=False, default=False, action='store_true', help='force detailer') + parser.add_argument('--hires', required=False, default=False, action='store_true', help='force hires') + parser.add_argument('--upscaler', required=False, default=None, help='upscaler name') args = parser.parse_args() log.info(f'api-control: {args}') generate(args) diff --git a/cli/hf-search.py b/cli/hf-search.py index f69c215eb..9ee696602 100755 --- a/cli/hf-search.py +++ b/cli/hf-search.py @@ -7,12 +7,12 @@ from rich import print # pylint: disable=redefined-builtin if __name__ == "__main__": sys.argv.pop(0) keyword = sys.argv[0] if len(sys.argv) > 0 else '' + hf.logging.set_verbosity_info() hf_api = hf.HfApi() - model_filter = hf.ModelFilter( - model_name=keyword, - # task='text-to-image', - library=['diffusers'], - ) - res = hf_api.list_models(filter=model_filter, full=True, limit=50, sort="downloads", direction=-1) - models = [{ 'name': m.id, 'downloads': m.downloads, 'mtime': m.lastModified, 'url': f'https://huggingface.co/{m.id}', 'pipeline': m.pipeline_tag, 'tags': m.tags } for m in res] - print(models) + res = hf_api.list_models(model_name=keyword, full=True, limit=100, sort="downloads", direction=-1) + res = sorted(res, key=lambda x: x.id) + for m in res: + meta = hf_api.model_info(m.id, files_metadata=True) + m.files = [f.rfilename for f in meta.siblings if f.rfilename.endswith('.bin') or f.rfilename.endswith('.safetensors')] + m.size = sum([f.size for f in meta.siblings]) / 1024 / 1024 / 1024 # in GB + print({ 'name': m.id, 'files': len(m.files), 'size': m.size, 'downloads': m.downloads, 'mtime': m.lastModified, 'url': f'https://huggingface.co/{m.id}', 'pipeline': m.pipeline_tag }) diff --git a/configs/sdxl/scheduler/scheduler_config.json b/configs/sdxl/scheduler/scheduler_config.json index e5bc8421e..a55b71ccb 100644 --- a/configs/sdxl/scheduler/scheduler_config.json +++ b/configs/sdxl/scheduler/scheduler_config.json @@ -1,6 +1,6 @@ { - "_class_name": "EulerDiscreteScheduler", - "_diffusers_version": "0.19.0.dev0", + "_class_name": "EulerAncestralDiscreteScheduler", + "_diffusers_version": "0.35.1", "beta_end": 0.012, "beta_schedule": "scaled_linear", "beta_start": 0.00085, @@ -8,11 +8,12 @@ "interpolation_type": "linear", "num_train_timesteps": 1000, "prediction_type": "epsilon", + "rescale_betas_zero_snr": false, "sample_max_value": 1.0, "set_alpha_to_one": false, "skip_prk_steps": true, "steps_offset": 1, - "timestep_spacing": "leading", + "timestep_spacing": "trailing", "trained_betas": null, "use_karras_sigmas": false } diff --git a/extensions-builtin/sdnext-modernui b/extensions-builtin/sdnext-modernui index 8f6427aa0..eea7fc5d9 160000 --- a/extensions-builtin/sdnext-modernui +++ b/extensions-builtin/sdnext-modernui @@ -1 +1 @@ -Subproject commit 8f6427aa037b654ae664a0197c794e48fdbbc648 +Subproject commit eea7fc5d94564e5d1eda76b6db7e906fc792cffe diff --git a/html/locale_en.json b/html/locale_en.json index 42d81e431..5b8b75e5b 100644 --- a/html/locale_en.json +++ b/html/locale_en.json @@ -197,13 +197,16 @@ {"id":"","label":"Init Image Same As Control","localized":"","reload":"","hint":"Will additionally treat any image placed into the Control input window as a source for img2img type tasks, an image to modify for example."}, {"id":"","label":"Separate Init Image","localized":"","reload":"","hint":"Creates an additional window next to Control input labeled Init input, so you can have a separate image for both Control operations and an init source."}, {"id":"","label":"Override settings","localized":"","reload":"","hint":"If generation parameters deviate from your system settings override settings populated with those settings to override your system configuration for this workflow"}, - {"id":"","label":"sigma method","localized":"","reload":"","hint":"Controls how noise levels (sigmas) are distributed across diffusion steps. Options:\n- default: the model default\n- karras: smoother noise schedule, higher quality with fewer steps\n- beta: based on beta schedule values\n- exponential: exponential decay of noise\n- lambdas: experimental, balances signal-to-noise\n- flowmatch: tuned for flow-matching models"}, - {"id":"","label":"timestep spacing","localized":"","reload":"","hint":"Determines how timesteps are spaced across the diffusion process. Options:\n- default: the model default\n- leading: creates evenly spaced steps\n- linspace: includes the first and last steps and evenly selects the remaining intermediate steps\n- trailing: only includes the last step and evenly selects the remaining intermediate steps starting from the end"}, - {"id":"","label":"beta schedule","localized":"","reload":"","hint":"Defines how beta (noise strength per step) grows. Options:\n- default: the model default\n- linear: evenly decays noise per step\n- scaled: squared version of linear, used only by Stable Diffusion\n- cosine: smoother decay, often better results with fewer steps\n- sigmoid: sharp transition, experimental"}, - {"id":"","label":"prediction method","localized":"","reload":"","hint":"Defines what the model predicts at each step. Options:\n- default: the model default\n- epsilon: noise (most common for Stable Diffusion)\n- sample: direct denoised image prediction, also called as x0 prediction\n- v_prediction: velocity prediction, used by CosXL and NoobAI VPred models\n- flow_prediction: used with newer flow-matching models like SD3 and Flux"}, + {"id":"","label":"sigma method","localized":"","reload":"","hint":"Controls how noise levels (sigmas) are distributed across diffusion steps. Options:
- default: the model default
- karras: smoother noise schedule, higher quality with fewer steps
- beta: based on beta schedule values
- exponential: exponential decay of noise
- lambdas: experimental, balances signal-to-noise
- flowmatch: tuned for flow-matching models"}, + {"id":"","label":"timestep spacing","localized":"","reload":"","hint":"Determines how timesteps are spaced across the diffusion process. Options:
- default: the model default
- leading: creates evenly spaced steps
- linspace: includes the first and last steps and evenly selects the remaining intermediate steps
- trailing: only includes the last step and evenly selects the remaining intermediate steps starting from the end"}, + {"id":"","label":"beta schedule","localized":"","reload":"","hint":"Defines how beta (noise strength per step) grows. Options:
- default: the model default
- linear: evenly decays noise per step
- scaled: squared version of linear, used only by Stable Diffusion
- cosine: smoother decay, often better results with fewer steps
- sigmoid: sharp transition, experimental"}, + {"id":"","label":"prediction method","localized":"","reload":"","hint":"Defines what the model predicts at each step. Options:
- default: the model default
- epsilon: noise (most common for Stable Diffusion)
- sample: direct denoised image prediction, also called as x0 prediction
- v_prediction: velocity prediction, used by CosXL and NoobAI VPred models
- flow_prediction: used with newer flow-matching models like SD3 and Flux"}, {"id":"","label":"sampler order","localized":"","reload":"","hint":"Order of solver updates in the sampler. Higher order improves stability/accuracy but increases compute cost."}, - {"id":"","label":"flow shift","localized":"","reload":"","hint":"Adjustment for flow-based samplers. Shifts noise distribution during generation, useful for fine-tuning balance between detail and consistency."}, - {"id":"","label":"resize mode","localized":"","reload":"","hint":"Defines how the input is resized or adapted in second-pass refinement:\n- none: no resizing, keep original resolution\n- fixed: force resize to target resolution (may distort)\n- crop: center-crop to fit target while keeping aspect ratio\n- fill: resize to fit and pad empty space with borders\n- outpaint: extend canvas beyond image borders\n- context aware: smart resize that blends or adapts surrounding areas"} + {"id":"","label":"flow shift","localized":"","reload":"","hint":"Shift value for flowmatching models. Controls the distribution of denoising steps.

Values:
- >1.0: allocate more steps to early denoising (better structure)
-<1.0: allocate more steps to late denoising (better fine details)
- 1.0: balanced schedule

Most flowmatching models use the value of 3 as default. Effectively inactive if dynamic shift is enabled."}, + {"id":"","label":"dynamic","localized":"","reload":"","hint":"Dynamic shifting automatically adjusts the denoising schedule based on your image resolution.

The scheduler interpolates between base_shift and max_shift based on actual image resolution.

Enabling disables static Flow shift."}, + {"id":"","label":"base shift","localized":"","reload":"","hint":"Minimum shift value for low resolutions when using dynamic shifting."}, + {"id":"","label":"max shift","localized":"","reload":"","hint":"Maximum shift value for high resolutions when using dynamic shifting."}, + {"id":"","label":"resize mode","localized":"","reload":"","hint":"Defines how the input is resized or adapted in second-pass refinement:
- none: no resizing, keep original resolution
- fixed: force resize to target resolution (may distort)
- crop: center-crop to fit target while keeping aspect ratio
- fill: resize to fit and pad empty space with borders
- outpaint: extend canvas beyond image borders
- context aware: smart resize that blends or adapts surrounding areas"} ], "other": [ {"id":"","label":"Install","localized":"","reload":"","hint":"Install"}, @@ -277,7 +280,7 @@ {"id":"","label":"Final denoising strength","localized":"","reload":"","hint":"The denoising strength for the final loop of each image in the batch"}, {"id":"","label":"Denoising strength curve","localized":"","reload":"","hint":"The denoising curve controls the rate of denoising strength change each loop. Aggressive: Most of the change will happen towards the start of the loops. Linear: Change will be constant through all loops. Lazy: Most of the change will happen towards the end of the loops"}, {"id":"","label":"Tile overlap","localized":"","reload":"","hint":"For SD upscale, how much overlap in pixels should there be between tiles. Tiles overlap so that when they are merged back into one picture, there is no clearly visible seam"}, - {"id":"","label":"ACI: Color to Mask","localized":"","reload":"","hint":"Pick the color you want to mask and inpaint. Click on the color in the image to automatically select it.\n Advised to use images like green screens to get precise results."}, + {"id":"","label":"ACI: Color to Mask","localized":"","reload":"","hint":"Pick the color you want to mask and inpaint. Click on the color in the image to automatically select it.
Advised to use images like green screens to get precise results."}, {"id":"","label":"ACI: Color Tolerance","localized":"","reload":"","hint":"Adjust the tolerance to include similar colors in the mask. Lower values = mask only very similar colors. Higher = values mask a wider range of similar colors."}, {"id":"","label":"ACI: Mask Erode","localized":"","reload":"","hint":"Adjust padding to apply a inside offset to the mask. (Recommended value = 2 to remove leftovers at edges)"}, {"id":"","label":"ACI: Mask Blur","localized":"","reload":"","hint":"Adjust blur to apply a smooth transition between image and inpainted area. (Recommended value = 0 for sharpness)"}, @@ -651,7 +654,6 @@ {"id":"","label":"dropdown","localized":"","reload":"","hint":"dropdown"}, {"id":"","label":"duration","localized":"","reload":"","hint":"duration"}, {"id":"","label":"dwpose","localized":"","reload":"","hint":"dwpose"}, - {"id":"","label":"dynamic","localized":"","reload":"","hint":"dynamic"}, {"id":"","label":"dynamic attention slicing rate in gb","localized":"","reload":"","hint":"dynamic attention slicing rate in gb"}, {"id":"","label":"dynamic attention trigger rate in gb","localized":"","reload":"","hint":"dynamic attention trigger rate in gb"}, {"id":"","label":"edge","localized":"","reload":"","hint":"edge"}, @@ -1070,7 +1072,6 @@ {"id":"","label":"same latent","localized":"","reload":"","hint":"same latent"}, {"id":"","label":"sample","localized":"","reload":"","hint":"sample"}, {"id":"","label":"sampler","localized":"","reload":"","hint":"sampler"}, - {"id":"","label":"sampler dynamic shift","localized":"","reload":"","hint":"sampler dynamic shift"}, {"id":"","label":"sampler shift","localized":"","reload":"","hint":"sampler shift"}, {"id":"","label":"sana: use complex human instructions","localized":"","reload":"","hint":"sana: use complex human instructions"}, {"id":"","label":"saturation","localized":"","reload":"","hint":"saturation"}, diff --git a/html/reference.json b/html/reference.json index fced58542..7d573ba54 100644 --- a/html/reference.json +++ b/html/reference.json @@ -1,37 +1,4 @@ { - "Tempest-by-Vlad XL": { - "path": "tempestByVlad_baseV01.safetensors@https://civitai.com/api/download/models/1301775", - "preview": "tempestByVlad_baseV01.jpg", - "desc": "Flexible SDXL model with custom encoder and finetuned for larger landscape resolutions with high details and high contrast.", - "extras": "" - }, - "Tempest-by-Vlad XL Hyper": { - "path": "tempestByVlad_hyperV01.safetensors@https://civitai.com/api/download/models/1343512", - "preview": "tempestByVlad_hyperV01.jpg", - "desc": "Custom distilled variant with goal to get as-normal-as-possible model that works with low steps and guidance-free", - "extras": "" - }, - - "Juggernaut XL XI": { - "path": "juggernautXL_juggXIByRundiffusion.safetensors@https://civitai.com/api/download/models/782002", - "preview": "juggernautXL_juggXIByRundiffusion.jpg", - "desc": "Showcase finetuned model based on Stable diffusion XL", - "extras": "sampler: DEIS, steps: 20, cfg_scale: 6.0" - }, - "Juggernaut XL XI Lightning": { - "path": "juggernautXL_juggXILightningByRD.safetensors@https://civitai.com/api/download/models/920957", - "preview": "juggernautXL_juggXILightningByRD.jpg", - "desc": "Showcase finetuned model based on Stable diffusion XL", - "extras": "sampler: DPM SDE, steps: 6, cfg_scale: 2.0" - }, - "Juggernaut SD Reborn": { - "original": true, - "path": "juggernaut_reborn.safetensors@https://civitai.com/api/download/models/274039", - "preview": "juggernaut_reborn.jpg", - "desc": "Showcase finetuned model based on Stable diffusion 1.5", - "extras": "width: 512, height: 512, sampler: DEIS, steps: 20, cfg_scale: 6.0" - }, - "RunwayML StableDiffusion 1.5": { "original": true, "path": "v1-5-pruned-fp16-emaonly.safetensors@https://huggingface.co/Aptronym/SDNext/resolve/main/Reference/v1-5-pruned-fp16-emaonly.safetensors?download=true", @@ -56,6 +23,8 @@ "preview": "stabilityai--stable-diffusion-2-1.jpg", "skip": true, "variant": "fp16", + "size": 2.58, + "date": "2022 December", "desc": "This stable-diffusion-2 model is resumed from stable-diffusion-2-base (512-base-ema.ckpt) and trained for 150k steps using a v-objective on the same dataset. Resumed for another 140k steps on 768x768 images", "extras": "width: 768, height: 768, sampler: DEIS, steps: 20, cfg_scale: 6.0" }, @@ -75,6 +44,7 @@ "desc": "SDXL-Turbo is a fast generative text-to-image model that can synthesize photorealistic images from a text prompt in a 1-4 steps.", "skip": true, "variant": "fp16", + "tags": "distilled", "extras": "steps: 4, cfg_scale: 0.0" }, "StabilityAI Stable Cascade": { @@ -95,9 +65,10 @@ "preview": "stabilityai--stable-cascade-lite.jpg", "extras": "sampler: Default, cfg_scale: 4.0, image_cfg_scale: 1.0", "size": 4.97, + "tags": "distilled", "date": "2024 February" }, - "StabilityAI Stable Diffusion 3 Medium": { + "StabilityAI Stable Diffusion 3.0 Medium": { "path": "stabilityai/stable-diffusion-3-medium-diffusers", "skip": true, "variant": "fp16", @@ -133,6 +104,7 @@ "variant": "fp16", "desc": "Stable Diffusion 3.5 Large Turbo is a Multimodal Diffusion Transformer (MMDiT) text-to-image model with Adversarial Diffusion Distillation (ADD) that features improved performance in image quality, typography, complex prompt understanding, and resource-efficiency, with a focus on fewer inference steps.", "preview": "stabilityai--stable-diffusion-3_5-large-turbo.jpg", + "tags": "distilled", "extras": "sampler: Default, cfg_scale: 7.0" }, @@ -176,6 +148,7 @@ "path": "vladmandic/flux.1-dev-SRPO", "preview": "vladmandic--flux.1-dev-SRPO.jpg", "desc": "FLUX.1 Dev SRPO is Tencent trained with specific technique: Directly Aligning the Full Diffusion Trajectory with Fine-Grained Human Preference", + "tags": "distilled", "skip": true, "extras": "sampler: Default, cfg_scale: 4.5" }, @@ -214,6 +187,7 @@ "skip": true, "extras": "steps: 8", "size": 56.1, + "tags": "distilled", "date": "2025 August" }, "Qwen-Image-Distill": { @@ -223,6 +197,7 @@ "skip": true, "extras": "steps: 15", "size": 56.1, + "tags": "distilled", "date": "2025 August" }, "Qwen-Image-Lightning-Edit": { @@ -232,6 +207,7 @@ "skip": true, "extras": "steps: 8", "size": 56.1, + "tags": "distilled", "date": "2025 August" }, "Qwen-Image Pruning-12B": { @@ -240,6 +216,7 @@ "preview": "OPPOer--Qwen-Image-Pruning.jpg", "desc": "This open-source project is based on Qwen-Image and has attempted model pruning, removing 20 layers while retaining the weights of 40 layers, resulting in a model size of 12B parameters.", "skip": true, + "tags": "distilled", "date": "2025 Ocotober" }, "Qwen-Image-Edit Pruning-13B": { @@ -248,6 +225,7 @@ "preview": "OPPOer--Qwen-Image-Edit-Pruning.jpg", "desc": "This open-source project is based on Qwen-Image-Edit and has attempted model pruning, removing 20 layers while retaining the weights of 40 layers, resulting in a model size of 13.6B parameters.", "skip": true, + "tags": "distilled", "date": "2025 Ocotober" }, "Qwen-Image-Edit-2509 Pruning-13B": { @@ -256,6 +234,7 @@ "preview": "OPPOer--Qwen-Image-Edit-2509-Pruning.jpg", "desc": "This open-source project is based on Qwen-Image-Edit and has attempted model pruning, removing 20 layers while retaining the weights of 40 layers, resulting in a model size of 13.6B parameters.", "skip": true, + "tags": "distilled", "date": "2025 Ocotober" }, @@ -284,6 +263,7 @@ "skip": true, "extras": "", "size": 26.84, + "tags": "distilled", "date": "2025 July" }, "lodestones Chroma1 v50 Preview Annealed": { @@ -419,6 +399,7 @@ "preview": "SDXL-Flash_Mini.jpg", "desc": "Introducing the new fast model SDXL Flash (Mini), we learned that all fast XL models work fast, but the quality decreases, and we also made a fast model, but it is not as fast as LCM, Turbo, Lightning and Hyper, but the quality is higher.", "extras": "width: 2048, height: 1024, sampler: DEIS, steps: 40, cfg_scale: 6.0", + "tags": "distilled", "experimental": true }, @@ -442,6 +423,7 @@ "path": "Efficient-Large-Model/Sana_Sprint_1.6B_1024px_diffusers", "desc": "SANA-Sprint is an ultra-efficient diffusion model for text-to-image (T2I) generation, reducing inference steps from 20 to 1-4 while achieving state-of-the-art performance.", "preview": "Efficient-Large-Model--Sana15_Sprint_1600M_1024px_diffusers.jpg", + "tags": "distilled", "skip": true }, "NVLabs Sana 1.0 1.6B 4k": { @@ -476,6 +458,13 @@ "size": 7.51, "date": "2024 November" }, + "nVidia ChronoEdit": { + "path": "nvidia/ChronoEdit-14B-Diffusers", + "preview": "nvidia--ChronoEdit-14B-Diffusers.jpg", + "desc": "ChronoEdit reframes image editing as a video generation task, using input and edited images as start/end frames to leverage pretrained video models with temporal consistency.", + "skip": true, + "extras": "" + }, "nVidia Cosmos-Predict2 T2I 2B": { "path": "nvidia/Cosmos-Predict2-2B-Text2Image", "desc": "Cosmos-Predict2: A family of highly performant pre-trained world foundation models purpose-built for generating physics-aware images, videos and world states for physical AI development.", @@ -555,6 +544,7 @@ "skip": true, "extras": "sampler: Default, cfg_scale: 9.0", "size": 8.72, + "tags": "distilled", "date": "2023 October" }, "Segmind Tiny": { @@ -563,6 +553,7 @@ "desc": "Segmind's Tiny-SD offers a compact, efficient, and distilled version of Realistic Vision 4.0 and is up to 80% faster than SD1.5", "extras": "width: 512, height: 512, sampler: Default, cfg_scale: 9.0", "size": 1.03, + "tags": "distilled", "date": "2023 July" }, "Segmind SegMoE SD 4x2": { @@ -615,6 +606,34 @@ "date": "2024 April" }, + "Tencent HunyuanImage 2.1": { + "path": "hunyuanvideo-community/HunyuanImage-2.1-Diffusers", + "desc": "HunyuanImage-2.1, a highly efficient text-to-image model that is capable of generating 2K (2048 × 2048) resolution images.", + "preview": "hunyuanvideo-community--HunyuanImage-2.1-Diffusers.jpg", + "extras": "", + "skip": true, + "size": 0, + "date": "2025 August" + }, + "Tencent HunyuanImage 2.1 Distilled": { + "path": "hunyuanvideo-community/HunyuanImage-2.1-Distilled-Diffusers", + "desc": "HunyuanImage-2.1, a highly efficient text-to-image model that is capable of generating 2K (2048 × 2048) resolution images.", + "preview": "hunyuanvideo-community--HunyuanImage-2.1-Distilled-Diffusers.jpg", + "extras": "", + "tags": "distilled", + "skip": true, + "size": 0, + "date": "2025 August" + }, + "Tencent HunyuanImage 2.1 Refiner": { + "path": "hunyuanvideo-community/HunyuanImage-2.1-Refiner-Diffusers", + "desc": "HunyuanImage-2.1, a highly efficient text-to-image model that is capable of generating 2K (2048 × 2048) resolution images.", + "preview": "hunyuanvideo-community--HunyuanImage-2.1-Diffusers.jpg", + "extras": "", + "skip": true, + "size": 0, + "date": "2025 August" + }, "Tencent HunyuanDiT 1.2": { "path": "Tencent-Hunyuan/HunyuanDiT-v1.2-Diffusers", "desc": "Hunyuan-DiT : A Powerful Multi-Resolution Diffusion Transformer with Fine-Grained Chinese Understanding.", @@ -627,6 +646,7 @@ "path": "Tencent-Hunyuan/HunyuanDiT-v1.2-Diffusers-Distilled", "desc": "Hunyuan-DiT : A Powerful Multi-Resolution Diffusion Transformer with Fine-Grained Chinese Understanding.", "preview": "Tencent-Hunyuan--HunyuanDiT-v1.2-Diffusers-Distilled.jpg", + "tags": "distilled", "extras": "sampler: Default, cfg_scale: 2.0" }, "Tencent HunyuanDiT 1.1": { @@ -639,6 +659,7 @@ "path": "Tencent-Hunyuan/HunyuanDiT-v1.1-Diffusers-Distilled", "desc": "Hunyuan-DiT : A Powerful Multi-Resolution Diffusion Transformer with Fine-Grained Chinese Understanding.", "preview": "Tencent-Hunyuan--HunyuanDiT-v1.1-Diffusers-Distilled.jpg", + "tags": "distilled", "extras": "sampler: Default, cfg_scale: 2.0" }, @@ -698,7 +719,7 @@ "HiDream-E1.1": { "path": "HiDream-ai/HiDream-E1-1", "desc": "HiDream-E1 is an image editing model built on HiDream-I1.", - "preview": "HiDream-ai--HiDream-E1-Full.jpg", + "preview": "HiDream-ai--HiDream-E1-1.jpg", "skip": true, "extras": "sampler: Default" }, @@ -792,25 +813,6 @@ "date": "2024 October" }, - "ShuttleAI Shuttle 3.0 Diffusion": { - "path": "shuttleai/shuttle-3-diffusion", - "desc": "Shuttle uses Flux.1 Schnell as its base. It can produce images similar to Flux Dev or Pro in just 4 steps, and it is licensed under Apache 2. The model was partially de-distilled during training. When used beyond 10 steps, it enters refiner mode enhancing image details without altering the composition", - "preview": "shuttleai--shuttle-3-diffusion.jpg", - "skip": true - }, - "ShuttleAI Shuttle 3.1 Aesthetic": { - "path": "shuttleai/shuttle-3.1-aesthetic", - "desc": "Shuttle uses Flux.1 Schnell as its base. It can produce images similar to Flux Dev or Pro in just 4 steps, and it is licensed under Apache 2. The model was partially de-distilled during training. When used beyond 10 steps, it enters refiner mode enhancing image details without altering the composition", - "preview": "shuttleai--shuttle-3_1-aestetic.jpg", - "skip": true - }, - "ShuttleAI Shuttle Jaguar": { - "path": "shuttleai/shuttle-jaguar", - "desc": "Shuttle uses Flux.1 Schnell as its base. It can produce images similar to Flux Dev or Pro in just 4 steps, and it is licensed under Apache 2. The model was partially de-distilled during training. When used beyond 10 steps, it enters refiner mode enhancing image details without altering the composition", - "preview": "shuttleai--shuttle-jaguar.jpg", - "skip": true - }, - "Bria 3.2": { "path": "briaai/BRIA-3.2", "desc": "Bria 3.2 is the next-generation commercial-ready text-to-image model. With just 4 billion parameters, it provides exceptional aesthetics and text rendering, evaluated to provide on par results to leading open-source models, and outperforming other licensed models.", @@ -909,6 +911,285 @@ "extras": "sampler: Default", "size": 15.48, "date": "2023 April" + }, + + "FLUX.1-Dev sdnq-svd-uint4": { + "path": "Disty0/FLUX.1-dev-SDNQ-uint4-svd-r32", + "preview": "Disty0--FLUX.1-dev-SDNQ-uint4-svd-r32.jpg", + "desc": "Quantization of black-forest-labs/FLUX.1-dev using SDNQ: sdnq-svd 4-bit uint with svd rank 32", + "skip": true, + "tags": "quantized", + "size": 12.60, + "date": "2025 October", + "extras": "" + }, + "FLUX.1-Schnell sdnq-svd-uint4": { + "path": "Disty0/FLUX.1-schnell-SDNQ-uint4-svd-r32", + "preview": "Disty0--FLUX.1-dev-SDNQ-uint4-svd-r32.jpg", + "desc": "Quantization of black-forest-labs/FLUX.1-schnell using SDNQ: sdnq-svd 4-bit uint with svd rank 32", + "skip": true, + "tags": "quantized", + "size": 12.60, + "date": "2025 October", + "extras": "" + }, + "FLUX.1-Dev Krea sdnq-svd-uint4": { + "path": "Disty0/FLUX.1-Krea-dev-SDNQ-uint4-svd-r32", + "preview": "Disty0--FLUX.1-dev-SDNQ-uint4-svd-r32.jpg", + "desc": "Quantization of black-forest-labs/FLUX.1-Krea-dev using SDNQ: sdnq-svd 4-bit uint with svd rank 32", + "skip": true, + "tags": "quantized", + "size": 12.60, + "date": "2025 October", + "extras": "" + }, + "FLUX.1-Dev Kontext sdnq-svd-uint4": { + "path": "Disty0/FLUX.1-Kontext-dev-SDNQ-uint4-svd-r32", + "preview": "Disty0--FLUX.1-dev-SDNQ-uint4-svd-r32.jpg", + "desc": "Quantization of black-forest-labs/FLUX.1-Kontext-dev using SDNQ: sdnq-svd 4-bit uint with svd rank 32", + "skip": true, + "tags": "quantized", + "size": 12.60, + "date": "2025 October", + "extras": "" + }, + "Chroma1-HD sdnq-svd-uint4": { + "path": "Disty0/Chroma1-HD-SDNQ-uint4-svd-r32", + "preview": "Disty0--Chroma1-HD-SDNQ-uint4-svd-r32.jpg", + "desc": "Quantization of lodestones/Chroma1-HD using SDNQ: sdnq-svd 4-bit uint with svd rank 32", + "skip": true, + "tags": "quantized", + "size": 11.89, + "date": "2025 October", + "extras": "" + }, + "Wan-AI Wan2.2 A14B T2I sdnq-svd-uint4": { + "path": "Disty0/Wan2.2-T2V-A14B-SDNQ-uint4-svd-r32", + "preview": "Wan-AI--Wan2.2-T2V-A14B-Diffusers.jpg", + "desc": "Quantization of black-forest-labs/FLUX.1-dev using SDNQ: sdnq-svd 4-bit uint with svd rank 32", + "skip": true, + "tags": "quantized", + "date": "2025 October", + "size": 23.54, + "extras": "" + }, + "Wan-AI Wan2.2 A14B I2I sdnq-svd-uint4": { + "path": "Disty0/Wan2.2-I2V-A14B-SDNQ-uint4-svd-r32", + "preview": "Wan-AI--Wan2.2-T2V-A14B-Diffusers.jpg", + "desc": "Quantization of Laxhar/noobai-XL-1.1 using SDNQ: sdnq-svd 4-bit uint with svd rank 128", + "skip": true, + "tags": "quantized", + "date": "2025 October", + "size": 23.55, + "extras": "" + }, + "Qwen-Image sdnq-svd-uint4": { + "path": "Disty0/Qwen-Image-SDNQ-uint4-svd-r32", + "preview": "Qwen--Qwen-Image.jpg", + "desc": "Quantization of Qwen/Qwen-Image using SDNQ: sdnq-svd 4-bit uint with svd rank 32", + "skip": true, + "tags": "quantized", + "date": "2025 October", + "size": 16.09, + "extras": "" + }, + "Qwen-Image-Edit sdnq-svd-uint4": { + "path": "Disty0/Qwen-Image-Edit-SDNQ-uint4-svd-r32", + "preview": "Qwen--Qwen-Image-Edit.jpg", + "desc": "Quantization of Qwen/Qwen-Image-Edit using SDNQ: sdnq-svd 4-bit uint with svd rank 32", + "skip": true, + "tags": "quantized", + "date": "2025 October", + "size": 16.10, + "extras": "" + }, + "Qwen-Image-Edit-2509 sdnq-svd-uint4": { + "path": "Disty0/Qwen-Image-Edit-2509-SDNQ-uint4-svd-r32", + "preview": "Qwen--Qwen-Image-Edit-2509.jpg", + "desc": "Quantization of Qwen/Qwen-Image-Edit-2509 using SDNQ: sdnq-svd 4-bit uint with svd rank 32", + "skip": true, + "tags": "quantized", + "date": "2025 October", + "size": 16.10, + "extras": "" + }, + "nVidia ChronoEdit sdnq-svd-uint4": { + "path": "Disty0/ChronoEdit-14B-SDNQ-uint4-svd-r32", + "preview": "nvidia--ChronoEdit-14B-Diffusers.jpg", + "desc": "Quantization of nvidia/ChronoEdit-14B-Diffusers using SDNQ: sdnq-svd 4-bit uint with svd rank 32.", + "skip": true, + "tags": "quantized", + "date": "2025 October", + "size": 18.10, + "extras": "" + }, + "Tencent HunyuanImage 3.0 sdnq-svd-uint4": { + "path": "Disty0/HunyuanImage3-SDNQ-uint4-svd-r32", + "desc": "Quantization of tencent/HunyuanImage-3.0 using SDNQ: sdnq-svd 4-bit uint with svd rank 32.", + "preview": "Disty0--HunyuanImage3-SDNQ-uint4-svd-r32.jpg", + "extras": "", + "skip": true, + "tags": "quantized", + "size": 57.06, + "date": "2025 September" + }, + "Tempest-by-Vlad XL sdnq-svd-uint4": { + "path": "vladmandic/tempestByVlad_baseV01-SDNQ-uint4-svd", + "preview": "tempestByVlad_baseV01.jpg", + "desc": "Quantization of vladmandic/tempestByVlad_baseV01 using SDNQ: sdnq-svd 4-bit uint with svd rank 128", + "tags": "quantized", + "size": 3.37, + "date": "2025 October", + "extras": "" + }, + "NoobAI-XL v1.1 epsilon sdnq-svd-uint4": { + "path": "Disty0/NoobAI-XL-v1.1-SDNQ-uint4-svd-r128", + "preview": "Disty0--NoobAI-XL-v1.1-SDNQ-uint4-svd-r128.jpg", + "desc": "Quantization of Laxhar/noobai-XL-1.1 using SDNQ: sdnq-svd 4-bit uint with svd rank 128", + "tags": "quantized", + "size": 3.37, + "date": "2025 October", + "extras": "" + }, + "NoobAI-XL v1.0 v-pred sdnq-svd-uint4": { + "path": "Disty0/NoobAI-XL-Vpred-v1.0-SDNQ-uint4-svd-r128", + "preview": "Disty0--NoobAI-XL-Vpred-v1.0-SDNQ-uint4-svd-r128.jpg", + "desc": "Quantization of Laxhar/noobai-XL-Vpred-1.0 using SDNQ: sdnq-svd 4-bit uint with svd rank 128", + "tags": "quantized", + "size": 3.37, + "date": "2025 October", + "extras": "" + }, + + "Tempest-by-Vlad XL": { + "path": "tempestByVlad_baseV01.safetensors@https://civitai.com/api/download/models/1301775", + "preview": "tempestByVlad_baseV01.jpg", + "desc": "Flexible SDXL model with custom encoder and finetuned for larger landscape resolutions with high details and high contrast.", + "tags": "community", + "size": 6.94, + "date": "2025 January", + "extras": "" + }, + "Tempest-by-Vlad XL Hyper": { + "path": "tempestByVlad_hyperV01.safetensors@https://civitai.com/api/download/models/1343512", + "preview": "tempestByVlad_hyperV01.jpg", + "desc": "Custom distilled variant with goal to get as-normal-as-possible model that works with low steps and guidance-free", + "tags": "community", + "size": 6.94, + "date": "2025 January", + "extras": "" + }, + "Juggernaut XL XI": { + "path": "juggernautXL_juggXIByRundiffusion.safetensors@https://civitai.com/api/download/models/782002", + "preview": "juggernautXL_juggXIByRundiffusion.jpg", + "desc": "Showcase finetuned model based on Stable diffusion XL", + "date": "2024 August", + "size": 6.94, + "tags": "community", + "extras": "sampler: DEIS, steps: 20, cfg_scale: 6.0" + }, + "Juggernaut XL XI Lightning": { + "path": "juggernautXL_juggXILightningByRD.safetensors@https://civitai.com/api/download/models/920957", + "preview": "juggernautXL_juggXILightningByRD.jpg", + "desc": "Showcase finetuned model based on Stable diffusion XL", + "date": "2024 August", + "size": 6.94, + "tags": "community", + "extras": "sampler: DPM SDE, steps: 6, cfg_scale: 2.0" + }, + "Juggernaut SD Reborn": { + "original": true, + "path": "juggernaut_reborn.safetensors@https://civitai.com/api/download/models/274039", + "preview": "juggernaut_reborn.jpg", + "desc": "Showcase finetuned model based on Stable diffusion 1.5", + "date": "2023 December", + "size": 2.28, + "tags": "community", + "extras": "width: 512, height: 512, sampler: DEIS, steps: 20, cfg_scale: 6.0" + }, + "WAI Illustrious XL v15": { + "path": "waiIllustriousSDXL_v150.safetensors@https://civitai.com/api/download/models/2167369", + "preview": "waiIllustriousSDXL_v150.jpg", + "desc": "", + "tags": "community", + "size": 6.94, + "date": "2025 August", + "extras": "" + }, + "Pony Realism XL v2.3": { + "path": "ponyRealism_V23.safetensors@https://civitai.com/api/download/models/2167369", + "preview": "ponyRealism_V23.jpg", + "desc": "", + "tags": "community", + "size": 6.94, + "date": "2025 May", + "extras": "" + }, + "NoobAI XL 1.0 V-Pred": { + "path": "noobaiXLNAIXL_vPred10Version.safetensors@https://huggingface.co/Laxhar/noobai-XL-Vpred-1.0/resolve/main/NoobAI-XL-Vpred-v1.0.safetensors", + "preview": "noobaiXLNAIXL_vPred10Version.jpg", + "desc": "", + "tags": "community", + "size": 6.94, + "date": "2024 December", + "extras": "" + }, + "NoobAI XL 1.1 Epsilon": { + "path": "noobaiXLNAIXL_epsilonPred11Version.safetensors@https://huggingface.co/Laxhar/noobai-XL-1.1/resolve/main/NoobAI-XL-v1.1.safetensors", + "preview": "noobaiXLNAIXL_epsilonPred11Version.jpg", + "desc": "", + "tags": "community", + "size": 6.94, + "date": "2024 November", + "extras": "" + }, + "WAI-Ani-Pony XL v14": { + "path": "waiANIPONYXL_v140.safetensors.safetensors@https://civitai.com/api/download/models/1767402", + "preview": "waiANIPONYXL_v140.jpg", + "desc": "", + "tags": "community", + "size": 6.94, + "date": "2025 May", + "extras": "" + }, + "Tiwaz CenKreChro": { + "path": "Tiwaz/CenKreChro", + "preview": "Tiwaz--CenKreChro.jpg", + "skip": true, + "desc": "Based Centerfold Flux 5, trying to merge in Chroma and Krea.", + "extras": "", + "tags": "community", + "date": "2025 September" + }, + "purplesmartai Pony 7": { + "path": "purplesmartai/pony-v7-base", + "preview": "purplesmartai--pony-v7-base.jpg", + "skip": true, + "desc": "Pony V7 is a versatile character generation model based on AuraFlow architecture. It supports a wide range of styles and species types (humanoid, anthro, feral, and more) and handles character interactions through natural language prompts.", + "extras": "", + "tags": "community", + "date": "October September" + }, + "ShuttleAI Shuttle 3.0 Diffusion": { + "path": "shuttleai/shuttle-3-diffusion", + "desc": "Shuttle uses Flux.1 Schnell as its base. It can produce images similar to Flux Dev or Pro in just 4 steps, and it is licensed under Apache 2. The model was partially de-distilled during training. When used beyond 10 steps, it enters refiner mode enhancing image details without altering the composition", + "preview": "shuttleai--shuttle-3-diffusion.jpg", + "tags": "community", + "skip": true + }, + "ShuttleAI Shuttle 3.1 Aesthetic": { + "path": "shuttleai/shuttle-3.1-aesthetic", + "desc": "Shuttle uses Flux.1 Schnell as its base. It can produce images similar to Flux Dev or Pro in just 4 steps, and it is licensed under Apache 2. The model was partially de-distilled during training. When used beyond 10 steps, it enters refiner mode enhancing image details without altering the composition", + "preview": "shuttleai--shuttle-3_1-aestetic.jpg", + "tags": "community", + "skip": true + }, + "ShuttleAI Shuttle Jaguar": { + "path": "shuttleai/shuttle-jaguar", + "desc": "Shuttle uses Flux.1 Schnell as its base. It can produce images similar to Flux Dev or Pro in just 4 steps, and it is licensed under Apache 2. The model was partially de-distilled during training. When used beyond 10 steps, it enters refiner mode enhancing image details without altering the composition", + "preview": "shuttleai--shuttle-jaguar.jpg", + "tags": "community", + "skip": true } } diff --git a/installer.py b/installer.py index 71ec4cbe0..2a398952f 100644 --- a/installer.py +++ b/installer.py @@ -241,7 +241,10 @@ def setup_logging(): log.addHandler(fh) global log_rolled # pylint: disable=global-statement if not log_rolled and args.debug and not args.log: - fh.doRollover() + try: + fh.doRollover() + except Exception: + pass log_rolled = True rb = RingBuffer(100) # 100 entries default in log ring buffer @@ -386,6 +389,9 @@ def pip(arg: str, ignore: bool = False, quiet: bool = True, uv = True): t_start = time.time() originalArg = arg arg = arg.replace('>=', '==') + if opts.get('offline_mode', False): + log.warning('Offline mode enabled') + return 'offline' package = arg.replace("install", "").replace("--upgrade", "").replace("--no-deps", "").replace("--force-reinstall", "").replace(" ", " ").strip() uv = uv and args.uv and not package.startswith('git+') pipCmd = "uv pip" if uv else "pip" @@ -586,8 +592,10 @@ def check_python(supported_minors=[], experimental_minors=[], reason=None): sys.exit(1) if int(sys.version_info.minor) == 12: os.environ.setdefault('SETUPTOOLS_USE_DISTUTILS', 'local') # hack for python 3.11 setuptools + if int(sys.version_info.minor) == 10: + log.warning(f"Python: version={platform.python_version()} is not actively supported") if int(sys.version_info.minor) == 9: - log.warning("Python 3.9 support is scheduled to be removed") + log.warning(f"Python: version={platform.python_version()} is end-of-life") if not args.skip_git: git_cmd = os.environ.get('GIT', "git") if shutil.which(git_cmd) is None: @@ -605,10 +613,7 @@ def check_diffusers(): t_start = time.time() if args.skip_all: return - if args.skip_git: - install('diffusers') - return - sha = '23ebbb4bc81a17ebea17cb7cb94f301199e49a7f' # diffusers commit hash + sha = '9f3c0fdcd859905c2c13ec47f10eb0250d2576ac' # diffusers commit hash # if args.use_rocm or args.use_zluda or args.use_directml: # sha = '043ab2520f6a19fce78e6e060a68dbc947edb9f9' # lock diffusers versions for now pkg = pkg_resources.working_set.by_key.get('diffusers', None) @@ -620,6 +625,8 @@ def check_diffusers(): else: log.info(f'Diffusers update: current={pkg.version} hash={cur} target={sha}') pip('uninstall --yes diffusers', ignore=True, quiet=True, uv=False) + if args.skip_git: + log.warning('Git: marked as not available but required for diffusers installation') pip(f'install --upgrade git+https://github.com/huggingface/diffusers@{sha}', ignore=False, quiet=True, uv=False) global diffusers_commit # pylint: disable=global-statement diffusers_commit = sha @@ -756,28 +763,24 @@ def install_rocm_zluda(): except Exception as e: log.warning(f'Failed to load ZLUDA: {e}') else: - #check_python(supported_minors=[10, 11, 12, 13], reason='ROCm backend requires a Python version between 3.10 and 3.13') - + #check_python(supported_minors=[10, 11, 12, 13, 14], reason='ROCm backend requires a Python version between 3.10 and 3.13') if args.use_nightly: - if rocm.version is None or float(rocm.version) >= 6.4: # assume the latest if version check fails + if rocm.version is None or float(rocm.version) >= 7.0: # assume the latest if version check fails + torch_command = os.environ.get('TORCH_COMMAND', '--upgrade --pre torch torchvision --index-url https://download.pytorch.org/whl/nightly/rocm7.0') + else: # oldest rocm version on nightly is 6.4 torch_command = os.environ.get('TORCH_COMMAND', '--upgrade --pre torch torchvision --index-url https://download.pytorch.org/whl/nightly/rocm6.4') - else: # oldest rocm version on nightly is 6.3 - torch_command = os.environ.get('TORCH_COMMAND', '--upgrade --pre torch torchvision --index-url https://download.pytorch.org/whl/nightly/rocm6.3') else: if rocm.version is None or float(rocm.version) >= 6.4: # assume the latest if version check fails - # Torch 2.8 with ROCm has common segfaults, memory access violations and accuracy issues - #torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.8.0+rocm6.4 torchvision==0.23.0+rocm6.4 --index-url https://download.pytorch.org/whl/rocm6.4') - torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.7.1+rocm6.3 torchvision==0.22.1+rocm6.3 --index-url https://download.pytorch.org/whl/rocm6.3') + torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.9.0+rocm6.4 torchvision==0.24.0+rocm6.4 --index-url https://download.pytorch.org/whl/rocm6.4') elif rocm.version == "6.3": - #torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.8.0+rocm6.3 torchvision==0.23.0+rocm6.3 --index-url https://download.pytorch.org/whl/rocm6.3') - torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.7.1+rocm6.3 torchvision==0.22.1+rocm6.3 --index-url https://download.pytorch.org/whl/rocm6.3') + torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.9.0+rocm6.3 torchvision==0.24.0+rocm6.3 --index-url https://download.pytorch.org/whl/rocm6.3') elif rocm.version == "6.2": # use rocm 6.2.4 instead of 6.2 as torch==2.7.1+rocm6.2 doesn't exists torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.7.1+rocm6.2.4 torchvision==0.22.1+rocm6.2.4 --index-url https://download.pytorch.org/whl/rocm6.2.4') elif rocm.version == "6.1": torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.6.0+rocm6.1 torchvision==0.21.0+rocm6.1 --index-url https://download.pytorch.org/whl/rocm6.1') else: - # lock to 2.4.1 instead of 2.5.1 for performance reasons there are no support for torch 2.6.0 for rocm 6.0 + # lock to 2.4.1 instead of 2.5.1 for performance reasons there are no support for torch 2.6 for rocm 6.0 torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.4.1+rocm6.0 torchvision==0.19.1+rocm6.0 --index-url https://download.pytorch.org/whl/rocm6.0') if float(rocm.version) < 6.0: log.warning(f"ROCm: unsupported version={rocm.version}") @@ -797,15 +800,14 @@ def install_rocm_zluda(): def install_ipex(): t_start = time.time() - #check_python(supported_minors=[10, 11, 12, 13], reason='IPEX backend requires a Python version between 3.10 and 3.13') + #check_python(supported_minors=[10, 11, 12, 13, 14], reason='IPEX backend requires a Python version between 3.10 and 3.13') args.use_ipex = True # pylint: disable=attribute-defined-outside-init log.info('IPEX: Intel OneAPI toolkit detected') if args.use_nightly: torch_command = os.environ.get('TORCH_COMMAND', '--upgrade --pre torch torchvision --index-url https://download.pytorch.org/whl/nightly/xpu') else: - # torch 2.8 segfaults with torch.compile: https://github.com/pytorch/pytorch/issues/159974 - torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.7.1+xpu torchvision==0.22.1+xpu --index-url https://download.pytorch.org/whl/xpu') + torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.9.0+xpu torchvision==0.24.0+xpu --index-url https://download.pytorch.org/whl/xpu') ts('ipex', t_start) return torch_command @@ -818,9 +820,9 @@ def install_openvino(): #check_python(supported_minors=[10, 11, 12, 13], reason='OpenVINO backend requires a Python version between 3.10 and 3.13') if sys.platform == 'darwin': - torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.8.0 torchvision==0.23.0') + torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.9.0 torchvision==0.24.0') else: - torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.8.0+cpu torchvision==0.23.0 --index-url https://download.pytorch.org/whl/cpu') + torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.9.0+cpu torchvision==0.24.0 --index-url https://download.pytorch.org/whl/cpu') if not (args.skip_all or args.skip_requirements): install(os.environ.get('OPENVINO_COMMAND', 'openvino==2025.3.0'), 'openvino') @@ -855,7 +857,7 @@ def install_torch_addons(): if len(opts.get('torchao_quantization', [])): install('torchao==0.10.0', 'torchao') if opts.get('samples_format', 'jpg') == 'jxl' or opts.get('grid_format', 'jpg') == 'jxl': - install('pillow-jxl-plugin==1.3.4', 'pillow-jxl-plugin') + install('pillow-jxl-plugin==1.3.5', 'pillow-jxl-plugin') if not args.experimental: uninstall('wandb', quiet=True) uninstall('pynvml', quiet=True) @@ -1261,6 +1263,13 @@ def install_pydantic(): reload('pydantic', '1.10.21') +def install_opencv(): + install('opencv-python==4.12.0.88', ignore=True, quiet=True) + install('opencv-python-headless==4.12.0.88', ignore=True, quiet=True) + install('opencv-contrib-python==4.12.0.88', ignore=True, quiet=True) + install('opencv-contrib-python-headless==4.12.0.88', ignore=True, quiet=True) + + def install_insightface(): install('git+https://github.com/deepinsight/insightface@29b6cd65aa0e9ae3b6602de3c52e9d8949c8ee86#subdirectory=python-package', 'insightface') # insightface==0.7.3 with patches if args.new: @@ -1275,18 +1284,23 @@ def install_insightface(): def install_optional(): t_start = time.time() log.info('Installing optional requirements...') - install('--no-build-isolation git+https://github.com/Disty0/BasicSR@23c1fb6f5c559ef5ce7ad657f2fa56e41b121754', 'basicsr') - install('--no-build-isolation git+https://github.com/Disty0/GFPGAN@ae0f7e44fafe0ef4716f3c10067f8f379b74c21c', 'gfpgan') - install('clean-fid', quiet=True) - install('pillow-jxl-plugin==1.3.4', ignore=True, quiet=True) - install('optimum-quanto==0.2.7', ignore=True, quiet=True) - install('torchao==0.10.0', ignore=True, quiet=True) - install('bitsandbytes==0.47.0', ignore=True, quiet=True) - install('nvidia-ml-py', ignore=True, quiet=True) - install('ultralytics==8.3.40', ignore=True, quiet=True) - install('Cython', ignore=True, quiet=True) + install('--no-build-isolation git+https://github.com/Disty0/BasicSR@23c1fb6f5c559ef5ce7ad657f2fa56e41b121754', 'basicsr', ignore=True, quiet=True) + install('--no-build-isolation git+https://github.com/Disty0/GFPGAN@ae0f7e44fafe0ef4716f3c10067f8f379b74c21c', 'gfpgan', ignore=True, quiet=True) install('av', ignore=True, quiet=True) - install('gguf', ignore=True) + install('beautifulsoup4', ignore=True, quiet=True) + install('bitsandbytes==0.47.0', ignore=True, quiet=True) + install('clean-fid', ignore=True, quiet=True) + install('clip_interrogator==0.6.0', ignore=True, quiet=True) + install('Cython', ignore=True, quiet=True) + install('gguf', ignore=True, quiet=True) + install('git+https://github.com/tencent-ailab/IP-Adapter.git', 'ip_adapter', ignore=True, quiet=True) + install('hf_transfer', ignore=True, quiet=True) + install('hf_xet', ignore=True, quiet=True) + install('nvidia-ml-py', ignore=True, quiet=True) + install('optimum-quanto==0.2.7', ignore=True, quiet=True) + install('pillow-jxl-plugin==1.3.5', ignore=True, quiet=True) + install('torchao==0.10.0', ignore=True, quiet=True) + install('ultralytics==8.3.40', ignore=True, quiet=True) try: import gguf scripts_dir = os.path.join(os.path.dirname(gguf.__file__), '..', 'scripts') @@ -1299,11 +1313,11 @@ def install_optional(): def install_requirements(): t_start = time.time() + if args.skip_requirements and not args.requirements: + return if args.profile: pr = cProfile.Profile() pr.enable() - if args.skip_requirements and not args.requirements: - return if int(sys.version_info.minor) >= 13: install('audioop-lts') if not installed('diffusers', quiet=True): # diffusers are not installed, so run initial installation @@ -1324,6 +1338,7 @@ def install_requirements(): if not installed(line, quiet=True): _res = install(line) install_pydantic() + install_opencv() if args.profile: pr.disable() print_profile(pr, 'Requirements') @@ -1356,6 +1371,8 @@ def set_environment(): os.environ.setdefault('UV_INDEX_STRATEGY', 'unsafe-any-match') os.environ.setdefault('UV_NO_BUILD_ISOLATION', '1') os.environ.setdefault('UVICORN_TIMEOUT_KEEP_ALIVE', '60') + os.environ.setdefault('RUNAI_STREAMER_CHUNK_BYTESIZE', '2097152') + os.environ.setdefault('RUNAI_STREAMER_MEMORY_LIMIT', '-1') allocator = f'garbage_collection_threshold:{opts.get("torch_gc_threshold", 80)/100:0.2f},max_split_size_mb:512' if opts.get("torch_malloc", "native") == 'cudaMallocAsync': allocator += ',backend:cudaMallocAsync' @@ -1501,12 +1518,17 @@ def check_venv(): # check version of the main repo and optionally upgrade it -def check_version(offline=False, reset=True): # pylint: disable=unused-argument +def check_version(reset=True): # pylint: disable=unused-argument + if opts.get('offline_mode', False): + log.warning('Offline mode enabled') + args.skip_git = True # pylint: disable=attribute-defined-outside-init + args.skip_all = True # pylint: disable=attribute-defined-outside-init + return t_start = time.time() if args.skip_all: return if not os.path.exists('.git'): - log.warning('Not a git repository, all git operations are disabled') + log.warning('Not a git repository') args.skip_git = True # pylint: disable=attribute-defined-outside-init ver = get_version() log.info(f'Version: {print_dict(ver)}') @@ -1543,7 +1565,7 @@ def check_version(offline=False, reset=True): # pylint: disable=unused-argument else: log.warning('Repository: retrying upgrade...') git_reset() - check_version(offline=offline, reset=False) + check_version(reset=False) else: dt = commits["commit"]["commit"]["author"]["date"] commit = commits["commit"]["sha"][:8] diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 95b5aca10..aa9b71246 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -144,7 +144,19 @@ async function filterExtraNetworksForTab(searchTerm) { } else if (searchTerm === 'reference/') { cards.forEach((elem) => elem.style.display = elem.dataset.name .toLowerCase() - .includes('reference/') ? '' : 'none'); + .includes('reference/') && elem.dataset.tags === '' ? '' : 'none'); + } else if (searchTerm === 'distilled/') { + cards.forEach((elem) => elem.style.display = elem.dataset.tags + .toLowerCase() + .includes('distilled') ? '' : 'none'); + } else if (searchTerm === 'community/') { + cards.forEach((elem) => elem.style.display = elem.dataset.tags + .toLowerCase() + .includes('community') ? '' : 'none'); + } else if (searchTerm === 'quantized/') { + cards.forEach((elem) => elem.style.display = elem.dataset.tags + .toLowerCase() + .includes('quantized') ? '' : 'none'); } else if (searchTerm === 'local/') { cards.forEach((elem) => elem.style.display = elem.dataset.name .toLowerCase() diff --git a/javascript/imageViewer.js b/javascript/imageViewer.js index e7eaca7a8..8788ca689 100644 --- a/javascript/imageViewer.js +++ b/javascript/imageViewer.js @@ -52,12 +52,15 @@ function modalImageSwitch(offset) { } function modalSaveImage(event) { - if (gradioApp().getElementById('tab_txt2img').style.display !== 'none') gradioApp().getElementById('save_txt2img').click(); - else if (gradioApp().getElementById('tab_img2img').style.display !== 'none') gradioApp().getElementById('save_img2img').click(); - else if (gradioApp().getElementById('tab_process').style.display !== 'none') gradioApp().getElementById('save_extras').click(); + const tabName = getENActiveTab(); + const saveBtn = gradioApp().getElementById(`save_${tabName}`); + log('modalSaveImage', tabName, saveBtn); + if (saveBtn) saveBtn.click(); + modalImageSwitch(0); } function modalKeyHandler(event) { + log('modalKeyHandler', event.key); switch (event.key) { case 's': modalSaveImage(); @@ -158,6 +161,7 @@ function modalZoomToggle(event) { const modalImage = gradioApp().getElementById('modalImage'); modalZoomSet(modalImage, !modalImage.classList.contains('modalImageFullscreen')); event.stopPropagation(); + modalImageSwitch(0); } function modalTileToggle(event) { @@ -172,12 +176,15 @@ function modalTileToggle(event) { modal.style.setProperty('background-image', `url(${modalImage.src})`); } event.stopPropagation(); + modalImageSwitch(0); } function modalResetInstance(event) { const modalImage = document.getElementById('modalImage'); previewInstance.dispose(); previewInstance = panzoom(modalImage, { zoomSpeed: 0.05, minZoom: 0.1, maxZoom: 5.0, filterKey: (/* e, dx, dy, dz */) => true }); + event.stopPropagation(); + modalImageSwitch(0); } function modalToggleParams(event) { @@ -188,6 +195,7 @@ function modalToggleParams(event) { modalExif.style.display = 'none'; } event.stopPropagation(); + modalImageSwitch(0); } function galleryClickEventHandler(event) { diff --git a/javascript/progressBar.js b/javascript/progressBar.js index 5c148836b..5062517e0 100644 --- a/javascript/progressBar.js +++ b/javascript/progressBar.js @@ -132,7 +132,7 @@ function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgres }; const start = (id_task, id_live_preview) => { // eslint-disable-line no-shadow - if (!opts.live_previews_enable || opts.live_preview_refresh_period === 0 || opts.show_progress_every_n_steps === 0) return; + if (opts.live_preview_refresh_period === 0) return; const request_id = document.hidden ? -1 : id_live_preview; const onProgressHandler = (res) => { diff --git a/javascript/settings.js b/javascript/settings.js index c4170f936..f82f0a692 100644 --- a/javascript/settings.js +++ b/javascript/settings.js @@ -170,7 +170,7 @@ async function initModels() { if (en.classList.contains('hide')) gradioApp().getElementById('txt2img_extra_networks_btn').click(); const repeat = setInterval(() => { const buttons = Array.from(gradioApp().querySelectorAll('#txt2img_model_subdirs > button')) || []; - const reference = buttons.find((b) => b.innerText === 'Reference'); + const reference = buttons.find((b) => (b.innerText === 'Reference') || (b.innerText === 'Distilled') || (b.innerText === 'Community') || (b.innerText === 'Quantized')); if (reference) { clearInterval(repeat); reference.click(); diff --git a/launch.py b/launch.py index 40e1946fc..57a1930af 100755 --- a/launch.py +++ b/launch.py @@ -297,10 +297,9 @@ def main(): installer.log.info(f'Installer time: {init_summary()}') get_custom_args() + uv, instance = start_server(immediate=True, server=None) if installer.restart_required: installer.log.warning('Restart is recommended due to packages updates...') - - uv, instance = start_server(immediate=True, server=None) t_server = time.time() t_monitor = time.time() while True: diff --git a/models/Reference/Disty0--Chroma1-HD-SDNQ-uint4-svd-r32.jpg b/models/Reference/Disty0--Chroma1-HD-SDNQ-uint4-svd-r32.jpg new file mode 100644 index 000000000..94c8fd8d5 Binary files /dev/null and b/models/Reference/Disty0--Chroma1-HD-SDNQ-uint4-svd-r32.jpg differ diff --git a/models/Reference/Disty0--FLUX.1-dev-SDNQ-uint4-svd-r32.jpg b/models/Reference/Disty0--FLUX.1-dev-SDNQ-uint4-svd-r32.jpg new file mode 100644 index 000000000..c77642703 Binary files /dev/null and b/models/Reference/Disty0--FLUX.1-dev-SDNQ-uint4-svd-r32.jpg differ diff --git a/models/Reference/Disty0--HunyuanImage3-SDNQ-uint4-svd-r32.jpg b/models/Reference/Disty0--HunyuanImage3-SDNQ-uint4-svd-r32.jpg new file mode 100644 index 000000000..d0c28e944 Binary files /dev/null and b/models/Reference/Disty0--HunyuanImage3-SDNQ-uint4-svd-r32.jpg differ diff --git a/models/Reference/Disty0--NoobAI-XL-Vpred-v1.0-SDNQ-uint4-svd-r128.jpg b/models/Reference/Disty0--NoobAI-XL-Vpred-v1.0-SDNQ-uint4-svd-r128.jpg new file mode 100644 index 000000000..46d04b1c4 Binary files /dev/null and b/models/Reference/Disty0--NoobAI-XL-Vpred-v1.0-SDNQ-uint4-svd-r128.jpg differ diff --git a/models/Reference/Disty0--NoobAI-XL-v1.1-SDNQ-uint4-svd-r128.jpg b/models/Reference/Disty0--NoobAI-XL-v1.1-SDNQ-uint4-svd-r128.jpg new file mode 100644 index 000000000..2c96c3cda Binary files /dev/null and b/models/Reference/Disty0--NoobAI-XL-v1.1-SDNQ-uint4-svd-r128.jpg differ diff --git a/models/Reference/HiDream-ai--HiDream-E1-1.jpg b/models/Reference/HiDream-ai--HiDream-E1-1.jpg new file mode 100644 index 000000000..b10b7dc66 Binary files /dev/null and b/models/Reference/HiDream-ai--HiDream-E1-1.jpg differ diff --git a/models/Reference/HiDream-ai--HiDream-E1-Full.jpg b/models/Reference/HiDream-ai--HiDream-E1-Full.jpg index 8de72fe43..dfe65dfc5 100644 Binary files a/models/Reference/HiDream-ai--HiDream-E1-Full.jpg and b/models/Reference/HiDream-ai--HiDream-E1-Full.jpg differ diff --git a/models/Reference/HiDream-ai--HiDream-I1-Dev.jpg b/models/Reference/HiDream-ai--HiDream-I1-Dev.jpg index 863bd705b..243abc370 100644 Binary files a/models/Reference/HiDream-ai--HiDream-I1-Dev.jpg and b/models/Reference/HiDream-ai--HiDream-I1-Dev.jpg differ diff --git a/models/Reference/HiDream-ai--HiDream-I1-Fast.jpg b/models/Reference/HiDream-ai--HiDream-I1-Fast.jpg index 7a3ed13d3..b0c61bd11 100644 Binary files a/models/Reference/HiDream-ai--HiDream-I1-Fast.jpg and b/models/Reference/HiDream-ai--HiDream-I1-Fast.jpg differ diff --git a/models/Reference/HiDream-ai--HiDream-I1-Full.jpg b/models/Reference/HiDream-ai--HiDream-I1-Full.jpg index 21f6b653b..a921fc073 100644 Binary files a/models/Reference/HiDream-ai--HiDream-I1-Full.jpg and b/models/Reference/HiDream-ai--HiDream-I1-Full.jpg differ diff --git a/models/Reference/Qwen--Qwen-Image.jpg b/models/Reference/Qwen--Qwen-Image.jpg index 1d3fdd499..6f6e4b30e 100644 Binary files a/models/Reference/Qwen--Qwen-Image.jpg and b/models/Reference/Qwen--Qwen-Image.jpg differ diff --git a/models/Reference/Tiwaz--CenKreChro.jpg b/models/Reference/Tiwaz--CenKreChro.jpg new file mode 100644 index 000000000..fd489e694 Binary files /dev/null and b/models/Reference/Tiwaz--CenKreChro.jpg differ diff --git a/models/Reference/hunyuanvideo-community--HunyuanImage-2.1-Diffusers.jpg b/models/Reference/hunyuanvideo-community--HunyuanImage-2.1-Diffusers.jpg new file mode 100644 index 000000000..cac5e02df Binary files /dev/null and b/models/Reference/hunyuanvideo-community--HunyuanImage-2.1-Diffusers.jpg differ diff --git a/models/Reference/hunyuanvideo-community--HunyuanImage-2.1-Distilled-Diffusers.jpg b/models/Reference/hunyuanvideo-community--HunyuanImage-2.1-Distilled-Diffusers.jpg new file mode 100644 index 000000000..1144266a4 Binary files /dev/null and b/models/Reference/hunyuanvideo-community--HunyuanImage-2.1-Distilled-Diffusers.jpg differ diff --git a/models/Reference/noobaiXLNAIXL_epsilonPred11Version.jpg b/models/Reference/noobaiXLNAIXL_epsilonPred11Version.jpg new file mode 100644 index 000000000..cbb7cc1fa Binary files /dev/null and b/models/Reference/noobaiXLNAIXL_epsilonPred11Version.jpg differ diff --git a/models/Reference/noobaiXLNAIXL_vPred10Version.jpg b/models/Reference/noobaiXLNAIXL_vPred10Version.jpg new file mode 100644 index 000000000..17dd2c8d4 Binary files /dev/null and b/models/Reference/noobaiXLNAIXL_vPred10Version.jpg differ diff --git a/models/Reference/nvidia--ChronoEdit-14B-Diffusers.jpg b/models/Reference/nvidia--ChronoEdit-14B-Diffusers.jpg new file mode 100644 index 000000000..0b8f4d04e Binary files /dev/null and b/models/Reference/nvidia--ChronoEdit-14B-Diffusers.jpg differ diff --git a/models/Reference/ponyRealism_V23.jpg b/models/Reference/ponyRealism_V23.jpg new file mode 100644 index 000000000..c92446fd6 Binary files /dev/null and b/models/Reference/ponyRealism_V23.jpg differ diff --git a/models/Reference/purplesmartai--pony-v7-base.jpg b/models/Reference/purplesmartai--pony-v7-base.jpg new file mode 100644 index 000000000..958e14007 Binary files /dev/null and b/models/Reference/purplesmartai--pony-v7-base.jpg differ diff --git a/models/Reference/waiANIPONYXL_v140.jpg b/models/Reference/waiANIPONYXL_v140.jpg new file mode 100644 index 000000000..7888d1eeb Binary files /dev/null and b/models/Reference/waiANIPONYXL_v140.jpg differ diff --git a/models/Reference/waiIllustriousSDXL_v150.jpg b/models/Reference/waiIllustriousSDXL_v150.jpg new file mode 100644 index 000000000..4d697cf70 Binary files /dev/null and b/models/Reference/waiIllustriousSDXL_v150.jpg differ diff --git a/modules/api/control.py b/modules/api/control.py index 1f74c9ba0..bc7e1efa9 100644 --- a/modules/api/control.py +++ b/modules/api/control.py @@ -37,28 +37,6 @@ ReqControl = models.create_model_from_signature( if not hasattr(ReqControl, "__config__"): ReqControl.__config__ = models.DummyConfig -""" -ReqControl = models.PydanticModelGenerator( - "StableDiffusionProcessingControl", - StableDiffusionProcessingControl, - [ - {"key": "sampler_index", "type": Union[int, str], "default": 0}, - {"key": "sampler_name", "type": str, "default": "Default"}, - {"key": "script_name", "type": Optional[str], "default": ""}, - {"key": "script_args", "type": list, "default": []}, - {"key": "send_images", "type": bool, "default": True}, - {"key": "save_images", "type": bool, "default": False}, - {"key": "alwayson_scripts", "type": dict, "default": {}}, - {"key": "ip_adapter", "type": Optional[List[models.ItemIPAdapter]], "default": None, "exclude": True}, - {"key": "face", "type": Optional[models.ItemFace], "default": None, "exclude": True}, - {"key": "control", "type": Optional[List[ItemControl]], "default": [], "exclude": True}, - {"key": "extra", "type": Optional[dict], "default": {}, "exclude": True}, - ] -).generate_model() -if not hasattr(ReqControl, "__config__"): - ReqControl.__config__ = models.DummyConfig -""" - class ResControl(BaseModel): images: List[str] = Field(default=None, title="Images", description="") @@ -71,6 +49,7 @@ class APIControl(): def __init__(self, queue_lock: Lock): self.queue_lock = queue_lock self.default_script_arg = [] + self.units = [] def sanitize_args(self, args: dict): args = vars(args) @@ -142,29 +121,38 @@ class APIControl(): from modules.control.unit import Unit, unit_types req.units = [] if req.unit_type is None: - return req.control + req.unit_type = 'controlnet' if req.unit_type not in unit_types: shared.log.error(f'Control uknown unit type: type={req.unit_type} available={unit_types}') - return req.control - for u in req.control: - unit = Unit( - enabled = True, - unit_type = req.unit_type, - model_id = u.model, - process_id = u.process, - strength = u.strength, - start = u.start, - end = u.end, - ) + return + for i in range(len(req.control)): + u = req.control[i] + if (len(self.units) > i) and (self.units[i].process_id == u.process) and (self.units[i].model_id == u.model): + unit = self.units[i] + unit.enabled = True + unit.strength = u.strength + unit.start = u.start + unit.end = u.end + else: + unit = Unit( + enabled = True, + unit_type = req.unit_type, + model_id = u.model, + process_id = u.process, + strength = u.strength, + start = u.start, + end = u.end, + ) if u.override is not None: unit.override = helpers.decode_base64_to_image(u.override) req.units.append(unit) - return req.control + self.units = req.units + del req.control def post_control(self, req: ReqControl): self.prepare_face_module(req) - orig_control = self.prepare_control(req) - del req.control + requested = req.control + self.prepare_control(req) # prepare args args = req.copy(update={ # Override __init__ params @@ -203,5 +191,5 @@ class APIControl(): b64images = list(map(helpers.encode_pil_to_base64, output_images)) if send_images else [] b64processed = list(map(helpers.encode_pil_to_base64, output_processed)) if send_images else [] self.sanitize_b64(req) - req.units = orig_control + req.units = requested return ResControl(images=b64images, processed=b64processed, params=vars(req), info=output_info) diff --git a/modules/api/gpu.py b/modules/api/gpu.py index 7f33156c4..e5290d96e 100644 --- a/modules/api/gpu.py +++ b/modules/api/gpu.py @@ -20,6 +20,9 @@ def get_gpu_status(): elif 'amd' in device.lower(): from modules.api import rocm_smi return rocm_smi.get_rocm_smi() + elif 'arc' in device.lower(): + from modules.api import xpu_smi + return xpu_smi.get_xpu_smi() return [] diff --git a/modules/api/xpu_smi.py b/modules/api/xpu_smi.py new file mode 100644 index 000000000..742393456 --- /dev/null +++ b/modules/api/xpu_smi.py @@ -0,0 +1,47 @@ +try: + from installer import log +except Exception: + import logging + log = logging.getLogger(__name__) + + +def get_xpu_smi(): + try: + import torch + from modules.memstats import ram_stats + + devices = [] + mem = torch.xpu.memory_stats() + ram = ram_stats() + cap = torch.xpu.get_device_capability() + prop = torch.xpu.get_device_properties() + load = { + 'gpu': 0, # no interface to get gpu load + 'memory': mem['active_bytes.all.allocated'] // (1024**3), # no interface to get gpu memory so use torch instead + } + total = prop.total_memory // (1024**2) + data = { + 'Version': cap['version'], + 'Driver': prop.driver_version, + 'Platform': prop.platform_name, + 'ID': hex(prop.device_id).removeprefix("0x"), + 'Compute Units': prop.max_compute_units, + "VRAM usage": f'{round(100 * load["memory"] / total)}% | {load["memory"]} MB used | {total - load["memory"]} MB free | {total} MB total', + "RAM usage": f'{round(100 * ram["used"] / ram["total"])}% | {round(1024 * ram["used"])} MB used | {round(1024 * ram["free"])} MB free | {round(1024 * ram["total"])} MB total', + } + chart = [load["memory"], load["gpu"]] + devices.append({ + 'name': torch.xpu.get_device_name(), + 'data': data, + 'chart': chart, + }) + return devices + except Exception as e: + log.error(f'XPU SMI: {e}') + return [] + + +if __name__ == '__main__': + from rich import print as rprint + for gpu in get_xpu_smi(): + rprint(gpu) diff --git a/modules/civitai/search_civitai.py b/modules/civitai/search_civitai.py index 0465411b9..488a05cdd 100644 --- a/modules/civitai/search_civitai.py +++ b/modules/civitai/search_civitai.py @@ -108,7 +108,7 @@ def search_civitai( global models # pylint: disable=global-statement import requests from urllib.parse import urlencode - install('bs4') # Ensure BeautifulSoup is installed + install('beautifulsoup4') if len(query) == 0: log.error('CivitAI: empty query') diff --git a/modules/control/proc/depth_anything/__init__.py b/modules/control/proc/depth_anything/__init__.py index cf7ee92e2..4709727e4 100644 --- a/modules/control/proc/depth_anything/__init__.py +++ b/modules/control/proc/depth_anything/__init__.py @@ -27,7 +27,7 @@ class DepthAnythingDetector: PrepareForNet()]) @classmethod - def from_pretrained(cls, pretrained_model_or_path: str, cache_dir: str) -> str: + def from_pretrained(cls, pretrained_model_or_path: str, cache_dir: str, local_files_only=False) -> str: from modules.control.proc.depth_anything.dpt import DPT_DINOv2 import huggingface_hub as hf model = ( @@ -40,7 +40,7 @@ class DepthAnythingDetector: .to(devices.device) .eval() ) - model_path = hf.hf_hub_download(repo_id=pretrained_model_or_path, filename="pytorch_model.bin", cache_dir=cache_dir) + model_path = hf.hf_hub_download(repo_id=pretrained_model_or_path, filename="pytorch_model.bin", cache_dir=cache_dir, local_files_only=local_files_only) model_dict = torch.load(model_path) model.load_state_dict(model_dict) return cls(model) diff --git a/modules/control/proc/depth_pro/__init__.py b/modules/control/proc/depth_pro/__init__.py new file mode 100644 index 000000000..e9bd20793 --- /dev/null +++ b/modules/control/proc/depth_pro/__init__.py @@ -0,0 +1,63 @@ +import cv2 +import torch +import torch.nn.functional as F +import numpy as np +from PIL import Image + +from modules import devices, masking +from modules.shared import opts + + +class DepthProDetector: + """Apple DepthPro detector (aligned with Depth Anything style).""" + + def __init__(self, model, processor): + self.model = model + self.processor = processor + + @classmethod + def from_pretrained(cls, pretrained_model_or_path: str = "apple/DepthPro-hf", cache_dir: str = None, local_files_only = False) -> "DepthProDetector": + from transformers import AutoImageProcessor, DepthProForDepthEstimation + + processor = AutoImageProcessor.from_pretrained(pretrained_model_or_path, cache_dir=cache_dir, local_files_only=local_files_only) + model = DepthProForDepthEstimation.from_pretrained( + pretrained_model_or_path, + cache_dir=cache_dir, + local_files_only=local_files_only, + ).to(devices.device).eval() + return cls(model, processor) + + def __call__(self, image, color_map: str = "none", output_type: str = "pil"): + self.model.to(devices.device) + if isinstance(image, Image.Image): + image = np.array(image) + h, w = image.shape[:2] + image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + pil_image = Image.fromarray(image_rgb) + + inputs = self.processor(images=pil_image, return_tensors="pt") + inputs = {k: v.to(devices.device) if isinstance(v, torch.Tensor) else v for k, v in inputs.items()} + + with devices.inference_context(): + outputs = self.model(**inputs) + results = self.processor.post_process_depth_estimation(outputs, target_sizes=[(h, w)]) + depth_tensor = results[0]["predicted_depth"].to(devices.device, dtype=torch.float32) + + if opts.control_move_processor: + self.model.to("cpu") + + depth_tensor = F.interpolate(depth_tensor[None, None], size=(h, w), mode="bilinear", align_corners=False)[0, 0] + depth_tensor = 1.0 / torch.clamp(depth_tensor, min=1e-6) + depth_tensor -= depth_tensor.min() + depth_max = depth_tensor.max() + if depth_max > 0: + depth_tensor /= depth_max + depth = (depth_tensor * 255.0).clamp(0, 255).to(torch.uint8).cpu().numpy() + + if color_map != "none": + colormap_key = color_map if color_map in masking.COLORMAP else "inferno" + depth = cv2.applyColorMap(depth, masking.COLORMAP.index(colormap_key))[:, :, ::-1] + if output_type == "pil": + mode = "RGB" if depth.ndim == 3 else "L" + depth = Image.fromarray(depth, mode=mode) + return depth diff --git a/modules/control/proc/dwpose/__init__.py b/modules/control/proc/dwpose/__init__.py index e97ade3e6..9e8466a69 100644 --- a/modules/control/proc/dwpose/__init__.py +++ b/modules/control/proc/dwpose/__init__.py @@ -51,7 +51,7 @@ def check_dependencies(): status = [installed(p, reload=False, quiet=True) for p in packages] debug(f'DWPose required={packages} status={status}') if not all(status): - log.info(f'Installing DWPose dependencies: {packages}') + log.info(f'Installing dependencies: for=dwpose packages={packages}') cmd = 'install --upgrade --no-deps --force-reinstall ' pkgs = ' '.join(packages) pip(cmd + pkgs, ignore=False, quiet=True, uv=False) diff --git a/modules/control/proc/hed.py b/modules/control/proc/hed.py index e0144c41f..06610921e 100644 --- a/modules/control/proc/hed.py +++ b/modules/control/proc/hed.py @@ -60,12 +60,12 @@ class HEDdetector: self.model = model @classmethod - def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None, local_files_only=False): filename = filename or "ControlNetHED.pth" if os.path.isdir(pretrained_model_or_path): model_path = os.path.join(pretrained_model_or_path, filename) else: - model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) model = ControlNetHED_Apache2() model.load_state_dict(torch.load(model_path, map_location='cpu')) model.float().eval() diff --git a/modules/control/proc/leres/__init__.py b/modules/control/proc/leres/__init__.py index 63040bc49..e2f358c7d 100644 --- a/modules/control/proc/leres/__init__.py +++ b/modules/control/proc/leres/__init__.py @@ -20,13 +20,13 @@ class LeresDetector: self.pix2pixmodel = pix2pixmodel @classmethod - def from_pretrained(cls, pretrained_model_or_path, filename=None, pix2pix_filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, filename=None, pix2pix_filename=None, cache_dir=None, local_files_only=False): filename = filename or "res101.pth" pix2pix_filename = pix2pix_filename or "latest_net_G.pth" if os.path.isdir(pretrained_model_or_path): model_path = os.path.join(pretrained_model_or_path, filename) else: - model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) checkpoint = torch.load(model_path, map_location=torch.device('cpu')) model = RelDepthModel(backbone='resnext101') model.load_state_dict(strip_prefix_if_present(checkpoint['depth_model'], "module."), strict=True) @@ -34,7 +34,7 @@ class LeresDetector: if os.path.isdir(pretrained_model_or_path): model_path = os.path.join(pretrained_model_or_path, pix2pix_filename) else: - model_path = hf_hub_download(pretrained_model_or_path, pix2pix_filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, pix2pix_filename, cache_dir=cache_dir, local_files_only=local_files_only) opt = TestOptions().parse() if not torch.cuda.is_available(): opt.gpu_ids = [] # cpu mode diff --git a/modules/control/proc/lineart.py b/modules/control/proc/lineart.py index afd55d9a7..61d3ce8b4 100644 --- a/modules/control/proc/lineart.py +++ b/modules/control/proc/lineart.py @@ -95,7 +95,7 @@ class LineartDetector: self.model_coarse = coarse_model @classmethod - def from_pretrained(cls, pretrained_model_or_path, filename=None, coarse_filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, filename=None, coarse_filename=None, cache_dir=None, local_files_only=False): filename = filename or "sk_model.pth" coarse_filename = coarse_filename or "sk_model2.pth" @@ -103,8 +103,8 @@ class LineartDetector: model_path = os.path.join(pretrained_model_or_path, filename) coarse_model_path = os.path.join(pretrained_model_or_path, coarse_filename) else: - model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) - coarse_model_path = hf_hub_download(pretrained_model_or_path, coarse_filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) + coarse_model_path = hf_hub_download(pretrained_model_or_path, coarse_filename, cache_dir=cache_dir, local_files_only=local_files_only) model = Generator(3, 1, 3) model.load_state_dict(torch.load(model_path, map_location=torch.device('cpu'))) diff --git a/modules/control/proc/lineart_anime.py b/modules/control/proc/lineart_anime.py index 541fcad61..c70da3784 100644 --- a/modules/control/proc/lineart_anime.py +++ b/modules/control/proc/lineart_anime.py @@ -117,12 +117,12 @@ class LineartAnimeDetector: self.model = model @classmethod - def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None, local_files_only=False): filename = filename or "netG.pth" if os.path.isdir(pretrained_model_or_path): model_path = os.path.join(pretrained_model_or_path, filename) else: - model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) norm_layer = functools.partial(nn.InstanceNorm2d, affine=False, track_running_stats=False) net = UnetGenerator(3, 1, 8, 64, norm_layer=norm_layer, use_dropout=False) ckpt = torch.load(model_path) diff --git a/modules/control/proc/midas/__init__.py b/modules/control/proc/midas/__init__.py index ba6d1b0e5..0ca4b4fee 100644 --- a/modules/control/proc/midas/__init__.py +++ b/modules/control/proc/midas/__init__.py @@ -17,7 +17,7 @@ class MidasDetector: self.model = model @classmethod - def from_pretrained(cls, pretrained_model_or_path, model_type="dpt_hybrid", filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, model_type="dpt_hybrid", filename=None, cache_dir=None, local_files_only=False): if pretrained_model_or_path == "lllyasviel/ControlNet": filename = filename or "annotator/ckpts/dpt_hybrid-midas-501f0c75.pt" else: @@ -25,7 +25,7 @@ class MidasDetector: if os.path.isdir(pretrained_model_or_path): model_path = os.path.join(pretrained_model_or_path, filename) else: - model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) model = MiDaSInference(model_type=model_type, model_path=model_path) return cls(model) diff --git a/modules/control/proc/mlsd/__init__.py b/modules/control/proc/mlsd/__init__.py index ea26c5b0d..e46ac865c 100644 --- a/modules/control/proc/mlsd/__init__.py +++ b/modules/control/proc/mlsd/__init__.py @@ -16,7 +16,7 @@ class MLSDdetector: self.model = model @classmethod - def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None, local_files_only=False): if pretrained_model_or_path == "lllyasviel/ControlNet": filename = filename or "annotator/ckpts/mlsd_large_512_fp32.pth" else: @@ -24,7 +24,7 @@ class MLSDdetector: if os.path.isdir(pretrained_model_or_path): model_path = os.path.join(pretrained_model_or_path, filename) else: - model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) model = MobileV2_MLSD_Large() model.load_state_dict(torch.load(model_path), strict=True) model.eval() diff --git a/modules/control/proc/normalbae/__init__.py b/modules/control/proc/normalbae/__init__.py index ba10570c6..16e613dc9 100644 --- a/modules/control/proc/normalbae/__init__.py +++ b/modules/control/proc/normalbae/__init__.py @@ -33,12 +33,12 @@ class NormalBaeDetector: self.norm = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) @classmethod - def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None, local_files_only=False): filename = filename or "scannet.pt" if os.path.isdir(pretrained_model_or_path): model_path = os.path.join(pretrained_model_or_path, filename) else: - model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) args = types.SimpleNamespace() args.mode = 'client' args.architecture = 'BN' diff --git a/modules/control/proc/openpose/__init__.py b/modules/control/proc/openpose/__init__.py index 80649e213..746351718 100644 --- a/modules/control/proc/openpose/__init__.py +++ b/modules/control/proc/openpose/__init__.py @@ -76,7 +76,7 @@ class OpenposeDetector: self.face_estimation = face_estimation @classmethod - def from_pretrained(cls, pretrained_model_or_path, filename=None, hand_filename=None, face_filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, filename=None, hand_filename=None, face_filename=None, cache_dir=None, local_files_only=False): if pretrained_model_or_path == "lllyasviel/ControlNet": filename = filename or "annotator/ckpts/body_pose_model.pth" @@ -96,9 +96,9 @@ class OpenposeDetector: hand_model_path = os.path.join(pretrained_model_or_path, hand_filename) face_model_path = os.path.join(face_pretrained_model_or_path, face_filename) else: - body_model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) - hand_model_path = hf_hub_download(pretrained_model_or_path, hand_filename, cache_dir=cache_dir) - face_model_path = hf_hub_download(face_pretrained_model_or_path, face_filename, cache_dir=cache_dir) + body_model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) + hand_model_path = hf_hub_download(pretrained_model_or_path, hand_filename, cache_dir=cache_dir, local_files_only=local_files_only) + face_model_path = hf_hub_download(face_pretrained_model_or_path, face_filename, cache_dir=cache_dir, local_files_only=local_files_only) body_estimation = Body(body_model_path) hand_estimation = Hand(hand_model_path) diff --git a/modules/control/proc/pidi.py b/modules/control/proc/pidi.py index 078525f2b..2b18eedf9 100644 --- a/modules/control/proc/pidi.py +++ b/modules/control/proc/pidi.py @@ -16,12 +16,12 @@ class PidiNetDetector: self.model = model @classmethod - def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, filename=None, cache_dir=None, local_files_only=False): filename = filename or "table5_pidinet.pth" if os.path.isdir(pretrained_model_or_path): model_path = os.path.join(pretrained_model_or_path, filename) else: - model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) model = pidinet() model.load_state_dict({k.replace('module.', ''): v for k, v in torch.load(model_path)['state_dict'].items()}) model.eval() diff --git a/modules/control/proc/segment_anything/__init__.py b/modules/control/proc/segment_anything/__init__.py index d698b46fc..121421c18 100644 --- a/modules/control/proc/segment_anything/__init__.py +++ b/modules/control/proc/segment_anything/__init__.py @@ -23,12 +23,12 @@ class SamDetector: self.model = mask_generator @classmethod - def from_pretrained(cls, model_path, filename, model_type, cache_dir=None): + def from_pretrained(cls, model_path, filename, model_type, cache_dir=None, local_files_only=False): """ Possible model_type : vit_h, vit_l, vit_b, vit_t download weights from https://github.com/facebookresearch/segment-anything """ - model_path = hf_hub_download(model_path, filename, cache_dir=cache_dir) + model_path = hf_hub_download(model_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) sam = sam_model_registry[model_type](checkpoint=model_path) sam.to(devices.device) mask_generator = SamAutomaticMaskGenerator(sam) diff --git a/modules/control/proc/zoe/__init__.py b/modules/control/proc/zoe/__init__.py index bb18201e0..a6550d605 100644 --- a/modules/control/proc/zoe/__init__.py +++ b/modules/control/proc/zoe/__init__.py @@ -20,12 +20,12 @@ class ZoeDetector: self.model = model @classmethod - def from_pretrained(cls, pretrained_model_or_path, model_type="zoedepth", filename=None, cache_dir=None): + def from_pretrained(cls, pretrained_model_or_path, model_type="zoedepth", filename=None, cache_dir=None, local_files_only=False): filename = filename or "ZoeD_M12_N.pt" if os.path.isdir(pretrained_model_or_path): model_path = os.path.join(pretrained_model_or_path, filename) else: - model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir) + model_path = hf_hub_download(pretrained_model_or_path, filename, cache_dir=cache_dir, local_files_only=local_files_only) if model_type == "zoedepth": model_cls = ZoeDepth elif model_type == "zoedepth_nk": diff --git a/modules/control/processor.py b/modules/control/processor.py index 80ca18cd9..2022c7959 100644 --- a/modules/control/processor.py +++ b/modules/control/processor.py @@ -34,6 +34,7 @@ processors = [ 'DPT Depth Hybrid', 'GLPN Depth', 'Depth Anything', + 'Depth Pro', ] diff --git a/modules/control/processors.py b/modules/control/processors.py index 24002a4fb..4d66bfac6 100644 --- a/modules/control/processors.py +++ b/modules/control/processors.py @@ -39,6 +39,7 @@ config = { 'DPT Depth Hybrid': {'class': None, 'checkpoint': False, 'params': {}}, 'GLPN Depth': {'class': None, 'checkpoint': False, 'params': {}}, 'Depth Anything': {'class': None, 'checkpoint': True, 'load_config': {'pretrained_model_or_path': 'LiheYoung/depth_anything_vitl14' }, 'params': { 'color_map': 'inferno' }}, + 'Depth Pro': {'class': None, 'checkpoint': True, 'load_config': {'pretrained_model_or_path': 'apple/DepthPro-hf'}, 'params': {'color_map': 'inferno'}}, # 'Midas Depth Large': {'class': MidasDetector, 'checkpoint': True, 'params': {'bg_th': 0.1, 'depth_and_normal': False}, 'load_config': {'pretrained_model_or_path': 'Intel/dpt-large', 'model_type': "dpt_large", 'filename': ''}}, # 'Zoe Depth Zoe': {'class': ZoeDetector, 'checkpoint': True, 'params': {}}, # 'Zoe Depth NK': {'class': ZoeDetector, 'checkpoint': True, 'params': {}, 'load_config': {'pretrained_model_or_path': 'halffried/gyre_zoedepth', 'filename': 'ZoeD_M12_NK.safetensors', 'model_type': "zoedepth_nk"}}, @@ -67,6 +68,7 @@ def delay_load_config(): from modules.control.proc.dpt import DPTDetector from modules.control.proc.glpn import GLPNDetector from modules.control.proc.depth_anything import DepthAnythingDetector + from modules.control.proc.depth_pro import DepthProDetector config = { # placeholder 'None': {}, @@ -95,6 +97,7 @@ def delay_load_config(): 'DPT Depth Hybrid': {'class': DPTDetector, 'checkpoint': False, 'params': {}}, 'GLPN Depth': {'class': GLPNDetector, 'checkpoint': False, 'params': {}}, 'Depth Anything': {'class': DepthAnythingDetector, 'checkpoint': True, 'load_config': {'pretrained_model_or_path': 'LiheYoung/depth_anything_vitl14' }, 'params': { 'color_map': 'inferno' }}, + 'Depth Pro': {'class': DepthProDetector, 'checkpoint': True, 'load_config': {'pretrained_model_or_path': 'apple/DepthPro-hf'}, 'params': {'color_map': 'inferno'}}, # 'Midas Depth Large': {'class': MidasDetector, 'checkpoint': True, 'params': {'bg_th': 0.1, 'depth_and_normal': False}, 'load_config': {'pretrained_model_or_path': 'Intel/dpt-large', 'model_type': "dpt_large", 'filename': ''}}, # 'Zoe Depth Zoe': {'class': ZoeDetector, 'checkpoint': True, 'params': {}}, # 'Zoe Depth NK': {'class': ZoeDetector, 'checkpoint': True, 'params': {}, 'load_config': {'pretrained_model_or_path': 'halffried/gyre_zoedepth', 'filename': 'ZoeD_M12_NK.safetensors', 'model_type': "zoedepth_nk"}}, @@ -155,6 +158,7 @@ def update_settings(*settings): update(['Marigold Depth', 'params', 'denoising_steps'], settings[25]) update(['Marigold Depth', 'params', 'ensemble_size'], settings[26]) update(['Depth Anything', 'params', 'color_map'], settings[27]) + update(['Depth Pro', 'params', 'color_map'], settings[28]) class Processor(): @@ -177,9 +181,15 @@ class Processor(): self.model = None self.processor_id = processor_id devices.torch_gc(force=True, reason='processor') - # self.override = None - # devices.torch_gc() self.load_config = { 'cache_dir': cache_dir } + from modules.shared import opts + if opts.offline_mode: + self.load_config["local_files_only"] = True + os.environ['HF_HUB_OFFLINE'] = '1' + else: + os.environ.pop('HF_HUB_OFFLINE', None) + os.unsetenv('HF_HUB_OFFLINE') + def config(self, processor_id = None): if processor_id is not None: diff --git a/modules/control/run.py b/modules/control/run.py index 0bdfdc994..b8078ecaa 100644 --- a/modules/control/run.py +++ b/modules/control/run.py @@ -53,9 +53,22 @@ def is_unified_model(): return shared.sd_model.__class__.__name__ in unified_models -def set_pipe(p, has_models, unit_type, selected_models, active_model, active_strength, control_conditioning, control_guidance_start, control_guidance_end, inits=None): +def has_inputs(inputs): + current = inputs or [] + current = current if isinstance(current, list) else [current] + current = [input for input in current if input is not None] + if current is None or len(current) == 0: + return False + return True + + +def set_pipe(p, has_models, unit_type, selected_models, active_model, active_strength, active_units, control_conditioning, control_guidance_start, control_guidance_end, inits=None, inputs=None): global pipe, instance # pylint: disable=global-statement pipe = None + if has_models and not has_inputs(inits) and not has_inputs(inputs): + if not any(has_inputs(u.override) for u in active_units if u.enabled): # check overrides + shared.log.error('Control: no input images') + return pipe if has_models: p.ops.append('control') p.extra_generation_params["Control type"] = unit_type # overriden later with pretty-print @@ -132,6 +145,7 @@ def check_active(p, unit_type, units): active_strength: List[float] = [] # strength factors for all active models active_start: List[float] = [] # start step for all active models active_end: List[float] = [] # end step for all active models + active_units: List[unit.Unit] = [] # all active units num_units = 0 for u in units: if u.type != unit_type: @@ -151,6 +165,7 @@ def check_active(p, unit_type, units): active_model.append(u.adapter) active_strength.append(float(u.strength)) p.adapter_conditioning_factor = u.factor + active_units.append(u) shared.log.debug(f'Control T2I-Adapter unit: i={num_units} process="{u.process.processor_id}" model="{u.adapter.model_id}" strength={u.strength} factor={u.factor}') elif unit_type == 'controlnet' and (u.controlnet.model is not None or is_unified_model()): active_process.append(u.process) @@ -159,6 +174,7 @@ def check_active(p, unit_type, units): active_start.append(float(u.start)) active_end.append(float(u.end)) p.guess_mode = u.guess + active_units.append(u) if isinstance(u.mode, str): if not hasattr(p, 'control_mode'): p.control_mode = [] @@ -173,11 +189,13 @@ def check_active(p, unit_type, units): active_strength.append(float(u.strength)) active_start.append(float(u.start)) active_end.append(float(u.end)) + active_units.append(u) shared.log.debug(f'Control ControlNet-XS unit: i={num_units} process={u.process.processor_id} model={u.controlnet.model_id} strength={u.strength} guess={u.guess} start={u.start} end={u.end}') elif unit_type == 'lite' and u.controlnet.model is not None: active_process.append(u.process) active_model.append(u.controlnet) active_strength.append(float(u.strength)) + active_units.append(u) shared.log.debug(f'Control ControlLLite unit: i={num_units} process={u.process.processor_id} model={u.controlnet.model_id} strength={u.strength} guess={u.guess} start={u.start} end={u.end}') elif unit_type == 'reference': p.override = u.override @@ -185,14 +203,16 @@ def check_active(p, unit_type, units): p.query_weight = float(u.query_weight) p.adain_weight = float(u.adain_weight) p.fidelity = u.fidelity + active_units.append(u) shared.log.debug('Control Reference unit') else: if u.process.processor_id is not None: active_process.append(u.process) + active_units.append(u) shared.log.debug(f'Control process unit: i={num_units} process={u.process.processor_id}') active_strength.append(float(u.strength)) debug_log(f'Control active: process={len(active_process)} model={len(active_model)}') - return active_process, active_model, active_strength, active_start, active_end + return active_process, active_model, active_strength, active_start, active_end, active_units def check_enabled(p, unit_type, units, active_model, active_strength, active_start, active_end): @@ -258,7 +278,7 @@ def control_run(state: str = '', # pylint: disable=keyword-arg-before-vararg guidance_name: str = 'Default', guidance_scale: float = 6.0, guidance_rescale: float = 0.0, guidance_start: float = 0.0, guidance_stop: float = 1.0, cfg_scale: float = 6.0, clip_skip: float = 1.0, image_cfg_scale: float = 6.0, diffusers_guidance_rescale: float = 0.7, pag_scale: float = 0.0, pag_adaptive: float = 0.5, cfg_end: float = 1.0, vae_type: str = 'Full', tiling: bool = False, hidiffusion: bool = False, - detailer_enabled: bool = True, detailer_prompt: str = '', detailer_negative: str = '', detailer_steps: int = 10, detailer_strength: float = 0.3, detailer_resolution: int = 1024, + detailer_enabled: bool = False, detailer_prompt: str = '', detailer_negative: str = '', detailer_steps: int = 10, detailer_strength: float = 0.3, detailer_resolution: int = 1024, hdr_mode: int = 0, hdr_brightness: float = 0, hdr_color: float = 0, hdr_sharpen: float = 0, hdr_clamp: bool = False, hdr_boundary: float = 4.0, hdr_threshold: float = 0.95, hdr_maximize: bool = False, hdr_max_center: float = 0.6, hdr_max_boundary: float = 1.0, hdr_color_picker: str = None, hdr_tint_ratio: float = 0, resize_mode_before: int = 0, resize_name_before: str = 'None', resize_context_before: str = 'None', width_before: int = 512, height_before: int = 512, scale_by_before: float = 1.0, selected_scale_tab_before: int = 0, @@ -384,8 +404,8 @@ def control_run(state: str = '', # pylint: disable=keyword-arg-before-vararg outpath_grids=shared.opts.outdir_grids or shared.opts.outdir_control_grids, # overrides override_settings=extra - ) + p.state = state p.is_tile = False p.init_control = inits or [] @@ -419,7 +439,7 @@ def control_run(state: str = '', # pylint: disable=keyword-arg-before-vararg return [], '', '', 'Error: model not loaded' unit_type = unit_type.strip().lower() if unit_type is not None else '' - active_process, active_model, active_strength, active_start, active_end = check_active(p, unit_type, units) + active_process, active_model, active_strength, active_start, active_end, active_units = check_active(p, unit_type, units) has_models, selected_models, control_conditioning, control_guidance_start, control_guidance_end = check_enabled(p, unit_type, units, active_model, active_strength, active_start, active_end) image_txt = '' @@ -429,7 +449,7 @@ def control_run(state: str = '', # pylint: disable=keyword-arg-before-vararg if is_unified_model(): p.init_images = inputs - pipe = set_pipe(p, has_models, unit_type, selected_models, active_model, active_strength, control_conditioning, control_guidance_start, control_guidance_end, inits) + pipe = set_pipe(p, has_models, unit_type, selected_models, active_model, active_strength, active_units, control_conditioning, control_guidance_start, control_guidance_end, inits, inputs) debug_log(f'Control pipeline: class={pipe.__class__.__name__} args={vars(p)}') status = True frame = None diff --git a/modules/control/unit.py b/modules/control/unit.py index 3260527c2..f47c1caac 100644 --- a/modules/control/unit.py +++ b/modules/control/unit.py @@ -31,7 +31,7 @@ class Unit(): # mashup of gradio controls and mapping to actual implementation c self.choices = ['default'] def __str__(self): - return f'Unit(index={self.index} enabled={self.enabled} type="{self.type}" strength={self.strength} start={self.start} end={self.end}{self.process}{self.controlnet})' + return f'Unit(index={self.index} enabled={self.enabled} type="{self.type}" strength={self.strength} start={self.start} end={self.end}{self.process}{self.controlnet} override={self.override})' def __init__(self, # values diff --git a/modules/control/units/controlnet.py b/modules/control/units/controlnet.py index 990ca2e4a..ca7656f76 100644 --- a/modules/control/units/controlnet.py +++ b/modules/control/units/controlnet.py @@ -89,7 +89,8 @@ predefined_f1 = { "Shakker-Labs Depth F1": 'Shakker-Labs/FLUX.1-dev-ControlNet-Depth', "XLabs-AI Canny F1": 'XLabs-AI/flux-controlnet-canny-diffusers', "XLabs-AI Depth F1": 'XLabs-AI/flux-controlnet-depth-diffusers', - "XLabs-AI HED F1": 'XLabs-AI/flux-controlnet-hed-diffusers' + "XLabs-AI HED F1": 'XLabs-AI/flux-controlnet-hed-diffusers', + "LibreFlux Segment F1": 'neuralvfx/LibreFlux-ControlNet', } predefined_sd3 = { "StabilityAI Canny SD35": 'diffusers-internal-dev/sd35-controlnet-canny-8b', @@ -119,6 +120,15 @@ variants = { 'NoobAI SoftEdge XL': 'fp16', 'TTPlanet Tile Realistic XL': 'fp16', } + +subfolders = { + "LibreFlux Segment F1": 'controlnet', +} + +remote_code = { + "LibreFlux Segment F1": True, +} + models = {} all_models = {} all_models.update(predefined_sd15) @@ -205,6 +215,12 @@ class ControlNet(): self.load_config = { 'cache_dir': cache_dir } if load_config is not None: self.load_config.update(load_config) + if opts.offline_mode: + self.load_config["local_files_only"] = True + os.environ['HF_HUB_OFFLINE'] = '1' + else: + os.environ.pop('HF_HUB_OFFLINE', None) + os.unsetenv('HF_HUB_OFFLINE') if model_id is not None: self.load() @@ -279,7 +295,7 @@ class ControlNet(): self.load_config['original_config_file '] = config_path self.model = cls.from_single_file(model_path, config=config, **self.load_config) - def load(self, model_id: str = None, force: bool = True) -> str: + def load(self, model_id: str = None, force: bool = False) -> str: with load_lock: try: t0 = time.time() @@ -320,6 +336,10 @@ class ControlNet(): self.load_config['use_safetensors'] = True if variants.get(model_id, None) is not None: kwargs['variant'] = variants[model_id] + if subfolders.get(model_id, None) is not None: + kwargs['subfolder'] = subfolders[model_id] + if remote_code.get(model_id, None) is not None: + kwargs['trust_remote_code'] = remote_code[model_id] try: self.model = cls.from_pretrained(model_path, **self.load_config, **kwargs) except Exception as e: @@ -332,6 +352,8 @@ class ControlNet(): self.model.offload_never = True if self.dtype is not None: self.model.to(self.dtype) + if self.device is not None: + self.model.to_empty(device=self.device) # model could be sparse if "Control" in opts.sdnq_quantize_weights: try: log.debug(f'Control {what} model SDNQ Compress: id="{model_id}"') @@ -356,7 +378,7 @@ class ControlNet(): except Exception as e: log.error(f'Control {what} model Torch AO: id="{model_id}" {e}') if self.device is not None: - self.model.to(self.device) + sd_models.move_model(self.model, self.device) if "Control" in opts.cuda_compile: try: from modules.sd_models_compile import compile_torch @@ -365,9 +387,9 @@ class ControlNet(): log.warning(f"Control compile error: {e}") t1 = time.time() self.model_id = model_id - log.info(f'Control {what} model loaded: id="{model_id}" path="{model_path}" cls={cls.__name__} time={t1-t0:.2f}') + log.info(f'Control {what} model loaded: id="{self.model_id}" path="{model_path}" cls={cls.__name__} time={t1-t0:.2f}') state.end(jobid) - return f'{what} loaded model: {model_id}' + return f'{what} loaded model: {self.model_id}' except Exception as e: log.error(f'Control {what} model load: id="{model_id}" {e}') errors.display(e, f'Control {what} load') @@ -507,7 +529,7 @@ class ControlNetPipeline(): if opts.diffusers_offload_mode == 'none': sd_models.move_model(self.pipeline, devices.device) sd_models.clear_caches() - sd_models.set_diffuser_offload(self.pipeline, 'model') + sd_models.set_diffuser_offload(self.pipeline, 'model', force=True) t1 = time.time() debug_log(f'Control {what} pipeline: class={self.pipeline.__class__.__name__} time={t1-t0:.2f}') diff --git a/modules/control/units/lite.py b/modules/control/units/lite.py index fbabe2280..107ebb0a0 100644 --- a/modules/control/units/lite.py +++ b/modules/control/units/lite.py @@ -108,8 +108,15 @@ class ControlLLLite(): self.model = ControlNetLLLite(model_path) else: import huggingface_hub as hf + offline_config = {} + if opts.offline_mode: + offline_config["local_files_only"] = True + os.environ['HF_HUB_OFFLINE'] = '1' + else: + os.environ.pop('HF_HUB_OFFLINE', None) + os.unsetenv('HF_HUB_OFFLINE') folder, filename = os.path.split(model_path) - model_path = hf.hf_hub_download(repo_id=folder, filename=f'{filename}.safetensors', cache_dir=cache_dir) + model_path = hf.hf_hub_download(repo_id=folder, filename=f'{filename}.safetensors', cache_dir=cache_dir, **offline_config) self.model = ControlNetLLLite(model_path) if self.device is not None: self.model.to(self.device) diff --git a/modules/control/units/t2iadapter.py b/modules/control/units/t2iadapter.py index 35ba8ab43..b9e049779 100644 --- a/modules/control/units/t2iadapter.py +++ b/modules/control/units/t2iadapter.py @@ -2,7 +2,7 @@ import os import time from typing import Union import threading -from diffusers import pipelines, StableDiffusionPipeline, StableDiffusionXLPipeline, T2IAdapter, MultiAdapter, StableDiffusionAdapterPipeline, StableDiffusionXLAdapterPipeline # pylint: disable=unused-import +from diffusers import StableDiffusionPipeline, StableDiffusionXLPipeline, T2IAdapter, MultiAdapter, StableDiffusionAdapterPipeline, StableDiffusionXLAdapterPipeline # pylint: disable=unused-import from installer import log from modules import errors, sd_models from modules.control.units import detect @@ -104,6 +104,13 @@ class Adapter(): return model_path, model_args = all_models[model_id] self.load_config.update(model_args) + from modules.shared import opts + if opts.offline_mode: + self.load_config["local_files_only"] = True + os.environ['HF_HUB_OFFLINE'] = '1' + else: + os.environ.pop('HF_HUB_OFFLINE', None) + os.unsetenv('HF_HUB_OFFLINE') if model_path is None: log.error(f'Control {what} model load failed: id="{model_id}" error=unknown model id') return @@ -168,6 +175,7 @@ class AdapterPipeline(): adapter=adapter, ) sd_models.move_model(self.pipeline, pipeline.device) + sd_models.apply_balanced_offload(self.pipeline, force=True) elif detect.is_sd15(pipeline): self.pipeline = StableDiffusionAdapterPipeline( vae=pipeline.vae, @@ -181,6 +189,7 @@ class AdapterPipeline(): adapter=adapter, ) sd_models.move_model(self.pipeline, pipeline.device) + sd_models.apply_balanced_offload(self.pipeline, force=True) else: log.error(f'Control {what} pipeline: class={pipeline.__class__.__name__} unsupported model type') return diff --git a/modules/control/units/xs.py b/modules/control/units/xs.py index f727a0111..2d56fd7ff 100644 --- a/modules/control/units/xs.py +++ b/modules/control/units/xs.py @@ -100,6 +100,12 @@ class ControlNetXS(): # log.debug(f'Control {what} model: id="{model_id}" path="{model_path}" already loaded') return self.load_config['time_embedding_mix'] = time_embedding_mix + if opts.offline_mode: + self.load_config["local_files_only"] = True + os.environ['HF_HUB_OFFLINE'] = '1' + else: + os.environ.pop('HF_HUB_OFFLINE', None) + os.unsetenv('HF_HUB_OFFLINE') log.debug(f'Control {what} model loading: id="{model_id}" path="{model_path}" {self.load_config}') if model_path.endswith('.safetensors'): self.model = ControlNetXSModel.from_single_file(model_path, **self.load_config) @@ -140,6 +146,7 @@ class ControlNetXSPipeline(): controlnet=controlnet, # can be a list ) sd_models.move_model(self.pipeline, pipeline.device) + sd_models.apply_balanced_offload(self.pipeline, force=True) elif detect.is_sd15(pipeline): self.pipeline = StableDiffusionControlNetXSPipeline( vae=pipeline.vae, @@ -153,6 +160,7 @@ class ControlNetXSPipeline(): controlnet=controlnet, # can be a list ) sd_models.move_model(self.pipeline, pipeline.device) + sd_models.apply_balanced_offload(self.pipeline, force=True) else: log.error(f'Control {what} pipeline: class={pipeline.__class__.__name__} unsupported model type') return diff --git a/modules/control/util.py b/modules/control/util.py index f528c7ac9..fccfa0abc 100644 --- a/modules/control/util.py +++ b/modules/control/util.py @@ -55,7 +55,7 @@ def nms(x, t, s): for f in [f1, f2, f3, f4]: np.putmask(y, cv2.dilate(x, kernel=f) == x, x) z = np.zeros_like(y, dtype=np.uint8) - z[y > t] = 255 + z[y > t] = 255 # pylint: disable=unsupported-assignment-operation return z def min_max_norm(x): diff --git a/modules/devices.py b/modules/devices.py index 961ffb384..fb3bce320 100644 --- a/modules/devices.py +++ b/modules/devices.py @@ -21,6 +21,7 @@ cpu = torch.device("cpu") fp16_ok = None # set once by test_fp16 bf16_ok = None # set once by test_bf16 +triton_ok = None # set once by test_triton backend = None # set by get_backend device = None # set by get_optimal_device @@ -64,12 +65,10 @@ def has_zluda() -> bool: return False -def has_triton() -> bool: - try: - from torch.utils._triton import has_triton as torch_has_triton - return torch_has_triton() - except Exception: - return False +def has_triton(early:bool=False) -> bool: + if triton_ok is not None: + return triton_ok + return test_triton(early=early) def get_backend(shared_cmd_opts): @@ -382,6 +381,36 @@ def test_bf16(): return bf16_ok +def test_triton(early: bool = False): + global triton_ok # pylint: disable=global-statement + if triton_ok is not None and early: + return triton_ok + t0 = time.time() + try: + from torch.utils._triton import has_triton as torch_has_triton + if torch_has_triton(): + if early: + return True + def test_triton_func(a,b,c): + return a * b + c + test_triton_func = torch.compile(test_triton_func, fullgraph=True) + test_triton_func(torch.randn(32, device=device), torch.randn(32, device=device), torch.randn(32, device=device)) + triton_ok = True + else: + triton_ok = False + except Exception as e: + triton_ok = False + log.warning(f"Triton test fail: {e}") + from modules import errors + errors.display(e, 'Triton') + t1 = time.time() + fn = f'{sys._getframe(2).f_code.co_name}:{sys._getframe(1).f_code.co_name}' # pylint: disable=protected-access + log.debug(f'Triton: pass={triton_ok} fn={fn} time={t1-t0:.2f}') + if not triton_ok and opts is not None: + opts.sdnq_dequantize_compile = False + return triton_ok + + def set_cudnn_params(): if not cuda_ok: return @@ -542,6 +571,7 @@ def set_sdpa_params(): except Exception as err: log.error(f'Torch attention: type="sage attention" {err}') + from importlib.metadata import version try: flash = version('flash-attn') @@ -551,7 +581,11 @@ def set_sdpa_params(): sage = version('sageattention') except Exception: sage = False - log.info(f'Torch attention: flashattn={flash} sageattention={sage}') + log.debug(f'Torch attention installed: flashattn={flash} sageattention={sage}') + + from diffusers.models import attention_dispatch as a + log.debug(f'Torch attention status: flash={a._CAN_USE_FLASH_ATTN} flash3={a._CAN_USE_FLASH_ATTN_3} aiter={a._CAN_USE_AITER_ATTN} sage={a._CAN_USE_SAGE_ATTN} flex={a._CAN_USE_FLEX_ATTN} npu={a._CAN_USE_NPU_ATTN} xla={a._CAN_USE_XLA_ATTN} xformers={a._CAN_USE_XFORMERS_ATTN}') # pylint: disable=protected-access + except Exception as e: log.warning(f'Torch SDPA: {e}') @@ -614,6 +648,7 @@ def set_cuda_params(): set_cudnn_params() set_sdpa_params() set_dtype() + test_triton() if backend == 'openvino': from modules.intel.openvino import get_device as get_raw_openvino_device device_name = get_raw_openvino_device() @@ -624,7 +659,7 @@ def set_cuda_params(): tunable = [torch.cuda.tunable.is_enabled(), torch.cuda.tunable.tuning_is_enabled()] except Exception: tunable = [False, False] - log.info(f'Torch parameters: backend={backend} device={device_name} config={opts.cuda_dtype} dtype={dtype} context={inference_context.__name__} nohalf={opts.no_half} nohalfvae={opts.no_half_vae} upcast={opts.upcast_sampling} deterministic={opts.cudnn_deterministic} tunable={tunable} fp16={"pass" if fp16_ok else "fail"} bf16={"pass" if bf16_ok else "fail"} optimization="{opts.cross_attention_optimization}"') + log.info(f'Torch parameters: backend={backend} device={device_name} config={opts.cuda_dtype} dtype={dtype} context={inference_context.__name__} nohalf={opts.no_half} nohalfvae={opts.no_half_vae} upcast={opts.upcast_sampling} deterministic={opts.cudnn_deterministic} tunable={tunable} fp16={"pass" if fp16_ok else "fail"} bf16={"pass" if bf16_ok else "fail"} triton={"pass" if triton_ok else "fail"} optimization="{opts.cross_attention_optimization}"') def randn(seed, shape=None): diff --git a/modules/face/reswapper_utils.py b/modules/face/reswapper_utils.py index ae260054d..a7451103f 100644 --- a/modules/face/reswapper_utils.py +++ b/modules/face/reswapper_utils.py @@ -1,9 +1,8 @@ +# https://github.com/somanchiu/ReSwapper/blob/GAN/Image.py import cv2 import numpy as np -### https://github.com/somanchiu/ReSwapper/blob/GAN/Image.py - input_std = 255.0 input_mean = 0.0 @@ -38,7 +37,7 @@ def blend_swapped_image(swapped_face, target_image, M): warped_face = cv2.warpAffine(swapped_face, M_inv, (w, h),borderValue=0.0) img_white = np.full((swapped_face.shape[0], swapped_face.shape[1]), 255, dtype=np.float32) img_mask = cv2.warpAffine(img_white, M_inv, (w, h), borderValue=0.0) - img_mask[img_mask > 20] = 255 + img_mask[img_mask > 20] = 255 # pylint: disable=unsupported-assignment-operation mask_h_inds, mask_w_inds = np.where(img_mask == 255) if len(mask_h_inds) > 0 and len(mask_w_inds) > 0: # safety check mask_h = np.max(mask_h_inds) - np.min(mask_h_inds) diff --git a/modules/framepack/framepack_api.py b/modules/framepack/framepack_api.py index 6cb35403b..0c2ecb25b 100644 --- a/modules/framepack/framepack_api.py +++ b/modules/framepack/framepack_api.py @@ -21,7 +21,7 @@ class ReqFramepack(BaseModel): resolution: Optional[int] = Field(default=640, title="Resolution", description="Resolution of the image") duration: Optional[float] = Field(default=4, title="Duration", description="Duration of the video in seconds") latent_ws: Optional[int] = Field(default=9, title="Latent window size", description="Size of the latent window") - steps: Optional[int] = Field(default=25, title="Steps", description="Number of steps for the model") + steps: Optional[int] = Field(default=25, title="Video steps", description="Number of steps for the video generation") cfg_scale: Optional[float] = Field(default=1.0, title="CFG scale", description="CFG scale for the model") cfg_distilled: Optional[float] = Field(default=10.0, title="Distilled CFG scale", description="Distilled CFG scale for the model") cfg_rescale: Optional[float] = Field(default=0.0, title="CFG re-scale", description="CFG re-scale for the model") diff --git a/modules/framepack/framepack_load.py b/modules/framepack/framepack_load.py index ed9fdde43..4b99126ef 100644 --- a/modules/framepack/framepack_load.py +++ b/modules/framepack/framepack_load.py @@ -1,3 +1,4 @@ +import os import time from modules import shared, devices, errors, sd_models, sd_checkpoint, model_quant @@ -128,23 +129,31 @@ def load_model(variant:str=None, pipeline:str=None, text_encoder:str=None, text_ sd_models.hf_auth_check(model["text_encoder"]["repo"]) sd_models.hf_auth_check(model["text_encoder_2"]["repo"]) + offline_config = {} + if shared.opts.offline_mode: + offline_config["local_files_only"] = True + os.environ['HF_HUB_OFFLINE'] = '1' + else: + os.environ.pop('HF_HUB_OFFLINE', None) + os.unsetenv('HF_HUB_OFFLINE') + shared.log.debug(f'FramePack load: module=llm {model["text_encoder"]}') load_args, quant_args = model_quant.get_dit_args({}, module='TE', device_map=True) - text_encoder = LlamaModel.from_pretrained(model["text_encoder"]["repo"], subfolder=model["text_encoder"]["subfolder"], cache_dir=shared.opts.hfcache_dir, **load_args, **quant_args) - tokenizer = LlamaTokenizerFast.from_pretrained(model["tokenizer"]["repo"], subfolder=model["tokenizer"]["subfolder"], cache_dir=shared.opts.hfcache_dir) + text_encoder = LlamaModel.from_pretrained(model["text_encoder"]["repo"], subfolder=model["text_encoder"]["subfolder"], cache_dir=shared.opts.hfcache_dir, **load_args, **quant_args, **offline_config) + tokenizer = LlamaTokenizerFast.from_pretrained(model["tokenizer"]["repo"], subfolder=model["tokenizer"]["subfolder"], cache_dir=shared.opts.hfcache_dir, **offline_config) text_encoder.requires_grad_(False) text_encoder.eval() sd_models.move_model(text_encoder, devices.cpu) shared.log.debug(f'FramePack load: module=te {model["text_encoder_2"]}') - text_encoder_2 = CLIPTextModel.from_pretrained(model["text_encoder_2"]["repo"], subfolder=model["text_encoder_2"]["subfolder"], torch_dtype=devices.dtype, cache_dir=shared.opts.hfcache_dir) - tokenizer_2 = CLIPTokenizer.from_pretrained(model["pipeline"]["repo"], subfolder='tokenizer_2', cache_dir=shared.opts.hfcache_dir) + text_encoder_2 = CLIPTextModel.from_pretrained(model["text_encoder_2"]["repo"], subfolder=model["text_encoder_2"]["subfolder"], torch_dtype=devices.dtype, cache_dir=shared.opts.hfcache_dir, **offline_config) + tokenizer_2 = CLIPTokenizer.from_pretrained(model["pipeline"]["repo"], subfolder='tokenizer_2', cache_dir=shared.opts.hfcache_dir, **offline_config) text_encoder_2.requires_grad_(False) text_encoder_2.eval() sd_models.move_model(text_encoder_2, devices.cpu) shared.log.debug(f'FramePack load: module=vae {model["vae"]}') - vae = AutoencoderKLHunyuanVideo.from_pretrained(model["vae"]["repo"], subfolder=model["vae"]["subfolder"], torch_dtype=devices.dtype, cache_dir=shared.opts.hfcache_dir) + vae = AutoencoderKLHunyuanVideo.from_pretrained(model["vae"]["repo"], subfolder=model["vae"]["subfolder"], torch_dtype=devices.dtype, cache_dir=shared.opts.hfcache_dir, **offline_config) vae.requires_grad_(False) vae.eval() vae.enable_slicing() @@ -152,8 +161,8 @@ def load_model(variant:str=None, pipeline:str=None, text_encoder:str=None, text_ sd_models.move_model(vae, devices.cpu) shared.log.debug(f'FramePack load: module=encoder {model["feature_extractor"]} model={model["image_encoder"]}') - feature_extractor = SiglipImageProcessor.from_pretrained(model["feature_extractor"]["repo"], subfolder=model["feature_extractor"]["subfolder"], cache_dir=shared.opts.hfcache_dir) - image_encoder = SiglipVisionModel.from_pretrained(model["image_encoder"]["repo"], subfolder=model["image_encoder"]["subfolder"], torch_dtype=devices.dtype, cache_dir=shared.opts.hfcache_dir) + feature_extractor = SiglipImageProcessor.from_pretrained(model["feature_extractor"]["repo"], subfolder=model["feature_extractor"]["subfolder"], cache_dir=shared.opts.hfcache_dir, **offline_config) + image_encoder = SiglipVisionModel.from_pretrained(model["image_encoder"]["repo"], subfolder=model["image_encoder"]["subfolder"], torch_dtype=devices.dtype, cache_dir=shared.opts.hfcache_dir, **offline_config) image_encoder.requires_grad_(False) image_encoder.eval() sd_models.move_model(image_encoder, devices.cpu) @@ -161,7 +170,7 @@ def load_model(variant:str=None, pipeline:str=None, text_encoder:str=None, text_ shared.log.debug(f'FramePack load: module=transformer {model["transformer"]}') dit_repo = model["transformer"]["repo"] load_args, quant_args = model_quant.get_dit_args({}, module='Model', device_map=True) - transformer = HunyuanVideoTransformer3DModelPacked.from_pretrained(dit_repo, subfolder=model["transformer"]["subfolder"], cache_dir=shared.opts.hfcache_dir, **load_args, **quant_args) + transformer = HunyuanVideoTransformer3DModelPacked.from_pretrained(dit_repo, subfolder=model["transformer"]["subfolder"], cache_dir=shared.opts.hfcache_dir, **load_args, **quant_args, **offline_config) transformer.high_quality_fp32_output_for_inference = False transformer.requires_grad_(False) transformer.eval() diff --git a/modules/framepack/framepack_worker.py b/modules/framepack/framepack_worker.py index cda5677f8..6558c0765 100644 --- a/modules/framepack/framepack_worker.py +++ b/modules/framepack/framepack_worker.py @@ -308,12 +308,26 @@ def worker( if is_last_section: break - total_generated_frames, _video_filename = save_video(history_pixels, mp4_fps, mp4_codec, mp4_opt, mp4_ext, mp4_sf, mp4_video, mp4_frames, mp4_interpolate, pbar=pbar, stream=stream, metadata=metadata) + total_generated_frames, _video_filename = save_video( + None, + history_pixels, + mp4_fps, + mp4_codec, + mp4_opt, + mp4_ext, + mp4_sf, + mp4_video, + mp4_frames, + mp4_interpolate, + pbar=pbar, + stream=stream, + metadata=metadata, + ) except AssertionError: shared.log.info('FramePack: interrupted') if shared.opts.keep_incomplete: - save_video(history_pixels, mp4_fps, mp4_codec, mp4_opt, mp4_ext, mp4_sf, mp4_video, mp4_frames, mp4_interpolate=0, stream=stream, metadata=metadata) + save_video(None, history_pixels, mp4_fps, mp4_codec, mp4_opt, mp4_ext, mp4_sf, mp4_video, mp4_frames, mp4_interpolate=0, stream=stream, metadata=metadata) except Exception as e: shared.log.error(f'FramePack: {e}') errors.display(e, 'FramePack') diff --git a/modules/framepack/framepack_wrappers.py b/modules/framepack/framepack_wrappers.py index 672db7806..ad4745846 100644 --- a/modules/framepack/framepack_wrappers.py +++ b/modules/framepack/framepack_wrappers.py @@ -5,6 +5,7 @@ import threading import numpy as np import torch import gradio as gr +from PIL import Image from modules import shared, processing, timer, paths, extra_networks, progress, ui_video_vlm from modules.video_models.video_utils import check_av from modules.framepack import framepack_install # pylint: disable=wrong-import-order @@ -27,6 +28,8 @@ def prepare_image(image, resolution): (416, 960), (448, 864), (480, 832), (512, 768), (544, 704), (576, 672), (608, 640), (640, 608), (672, 576), (704, 544), (768, 512), (832, 480), (864, 448), (960, 416), ] + if isinstance(image, Image.Image): + image = np.array(image) h, w, _c = image.shape min_metric = float('inf') scale_factor = resolution / 640.0 diff --git a/modules/framepack/pipeline/utils.py b/modules/framepack/pipeline/utils.py index 20108106b..9cd99571d 100644 --- a/modules/framepack/pipeline/utils.py +++ b/modules/framepack/pipeline/utils.py @@ -1,15 +1,14 @@ import os -import cv2 import json import random import glob +import datetime import torch import einops +import cv2 import numpy as np -import datetime import torchvision -import safetensors.torch as sf -from PIL import Image +from PIL import Image, ImageDraw, ImageFont def min_resize(x, m): @@ -30,7 +29,7 @@ def min_resize(x, m): def d_resize(x, y): - H, W, C = y.shape + H, W, _C = y.shape new_min = min(H, W) raw_min = min(x.shape[0], x.shape[1]) if new_min < raw_min: @@ -50,7 +49,7 @@ def resize_and_center_crop(image, target_width, target_height): scale_factor = max(target_width / original_width, target_height / original_height) resized_width = int(round(original_width * scale_factor)) resized_height = int(round(original_height * scale_factor)) - resized_image = pil_image.resize((resized_width, resized_height), Image.LANCZOS) + resized_image = pil_image.resize((resized_width, resized_height), Image.Resampling.LANCZOS) left = (resized_width - target_width) / 2 top = (resized_height - target_height) / 2 right = (resized_width + target_width) / 2 @@ -60,7 +59,7 @@ def resize_and_center_crop(image, target_width, target_height): def resize_and_center_crop_pytorch(image, target_width, target_height): - B, C, H, W = image.shape + _B, _C, H, W = image.shape if H == target_height and W == target_width: return image @@ -83,7 +82,7 @@ def resize_without_crop(image, target_width, target_height): return image pil_image = Image.fromarray(image) - resized_image = pil_image.resize((target_width, target_height), Image.LANCZOS) + resized_image = pil_image.resize((target_width, target_height), Image.Resampling.LANCZOS) return np.array(resized_image) @@ -188,7 +187,7 @@ def supress_lower_channels(m, k, alpha=0.01): def freeze_module(m): if not hasattr(m, '_forward_inside_frozen_module'): - m._forward_inside_frozen_module = m.forward + m._forward_inside_frozen_module = m.forward # pylint: disable=protected-access m.requires_grad_(False) m.forward = torch.no_grad()(m.forward) return m @@ -243,7 +242,7 @@ def soft_append_bcthw(history, current, overlap=0): def save_bcthw_as_mp4(x, output_filename, fps=10, crf=0): - b, c, t, h, w = x.shape + b, _c, _t, _h, _w = x.shape per_row = b for p in [6, 5, 4, 3, 2]: @@ -297,8 +296,6 @@ def add_tensors_with_padding(tensor1, tensor2): def visualize_txt_as_img(width, height, text, font_path='font/DejaVuSans.ttf', size=18): - from PIL import Image, ImageDraw, ImageFont - txt = Image.new("RGB", (width, height), color="white") draw = ImageDraw.Draw(txt) font = ImageFont.truetype(font_path, size=size) diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index 1427e53cc..ccb2d76c8 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -3,7 +3,6 @@ import io import os from PIL import Image import gradio as gr -from modules.paths import params_path from modules import shared, gr_tempdir, script_callbacks, images from modules.infotext import parse, mapping, quote, unquote # pylint: disable=unused-import @@ -204,6 +203,7 @@ def create_override_settings_dict(text_pairs): def connect_paste(button, local_paste_fields, input_comp, override_settings_component, tabname): def paste_func(prompt): + from modules.paths import params_path if prompt is None or len(prompt.strip()) == 0: if os.path.exists(params_path): with open(params_path, "r", encoding="utf8") as file: @@ -252,7 +252,8 @@ def connect_paste(button, local_paste_fields, input_comp, override_settings_comp val = valtype(v) res.append(gr.update(value=val)) applied[key] = val - except Exception: + except Exception as e: + shared.log.error(f'Paste param: key="{key}" value="{v}" error="{e}"') res.append(gr.update()) list_applied = [{k: v} for k, v in applied.items() if not callable(v) and not callable(k)] shared.log.debug(f"Prompt restore: apply={list_applied} skip={skipped}") diff --git a/modules/images.py b/modules/images.py index d93170be8..7a65c7df3 100644 --- a/modules/images.py +++ b/modules/images.py @@ -157,6 +157,10 @@ def save_image(image, if image is None: shared.log.warning('Image is none') return None, None, None + if isinstance(image, list): + if len(image) > 1: + shared.log.warning(f'Save: images={image} multiple images provided only the first one will be saved') + image = image[0] if not check_grid_size([image]): return None, None, None if path is None or path == '': # set default path to avoid errors when functions are triggered manually or via api and param is not set diff --git a/modules/images_grid.py b/modules/images_grid.py index 8f7b290ff..5827bdb95 100644 --- a/modules/images_grid.py +++ b/modules/images_grid.py @@ -13,7 +13,11 @@ def check_grid_size(imgs): return False mp = 0 for img in imgs: - mp += img.width * img.height if img is not None else 0 + if isinstance(img, list): + for im in img: + mp += im.width * im.height if im is not None else 0 + else: + mp += img.width * img.height if img is not None else 0 mp = round(mp / 1000000) ok = mp <= shared.opts.img_max_size_mp if not ok: diff --git a/modules/images_namegen.py b/modules/images_namegen.py index 849450d5b..7382462b8 100644 --- a/modules/images_namegen.py +++ b/modules/images_namegen.py @@ -1,6 +1,7 @@ import re import os import time +import unicodedata import uuid import string import hashlib @@ -23,8 +24,8 @@ NOTHING = object() class FilenameGenerator: replacements = { - 'width': lambda self: self.image.width, - 'height': lambda self: self.image.height, + 'width': lambda self: self.width, + 'height': lambda self: self.height, 'batch_number': lambda self: self.batch_number, 'iter_number': lambda self: self.iter_number, 'num': lambda self: NOTHING if self.p.n_iter == 1 and self.p.batch_size == 1 else self.p.iteration * self.p.batch_size + self.p.batch_index + 1, @@ -32,8 +33,8 @@ class FilenameGenerator: 'date': lambda self: datetime.datetime.now().strftime('%Y-%m-%d'), 'datetime': lambda self, *args: self.datetime(*args), # accepts formats: [datetime], [datetime], [datetime