diff --git a/CHANGELOG.md b/CHANGELOG.md index f790956f0..ccf8b36c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,13 +3,17 @@ ## Update for 2025-08-04 - **Models** + - [Qwen-Image](https://qwenlm.github.io/blog/qwen-image/) + new image foundational model with 20B params and using Qwen-2.5 as text-encoder! + *note*: this model is almost 2x the size of Flux, quantization and offloading are highly recommended! + available via *networks -> models -> reference* - [FLUX.1-Krea-Dev](https://www.krea.ai/blog/flux-krea-open-source-release) new 12B base model compatible with FLUX.1-Dev from *Black Forest Labs* with opinionated aesthetics and aesthetic preferences in mind - simply select in *networks -> models -> reference* + available via *networks -> models -> reference* - [Chroma](https://huggingface.co/lodestones/Chroma) great model based on FLUX.1 and then redesigned and retrained by *lodestones* update with latest **v48**, **v48 Detail Calibrated** and **v46 Flash** variants - simply select in *networks -> models -> reference* + available via *networks -> models -> reference* - **UI** - new embedded docs/wiki search! **Docs** search: fully-local and works in real-time on all document pages diff --git a/html/reference.json b/html/reference.json index 4f21017ac..e4d8e0d4c 100644 --- a/html/reference.json +++ b/html/reference.json @@ -195,6 +195,14 @@ "extras": "sampler: Default, cfg_scale: 1.0" }, + "Qwen-Image": { + "path": "Qwen/Qwen-Image", + "preview": "Qwen--Qwen-Image.jpg", + "desc": " Qwen-Image, an image generation foundation model in the Qwen series that achieves significant advances in complex text rendering and precise image editing.", + "skip": true, + "extras": "" + }, + "Ostris Flex.2 Preview": { "path": "ostris/Flex.2-preview", "preview": "ostris--Flex.2-preview.jpg", diff --git a/installer.py b/installer.py index a0bbc2828..f5f692666 100644 --- a/installer.py +++ b/installer.py @@ -593,7 +593,7 @@ def check_diffusers(): t_start = time.time() if args.skip_all or args.skip_git: return - sha = '0c71189abeaa8ab4b28dd7e5a309ac75c64968a2' # diffusers commit hash + sha = '7ea065c5070a5278259e6f1effa9dccea232e62a' # diffusers commit hash pkg = pkg_resources.working_set.by_key.get('diffusers', None) minor = int(pkg.version.split('.')[1] if pkg is not None else -1) cur = opts.get('diffusers_version', '') if minor > -1 else '' diff --git a/models/Reference/Qwen--Qwen-Image.jpg b/models/Reference/Qwen--Qwen-Image.jpg new file mode 100644 index 000000000..b55838835 Binary files /dev/null and b/models/Reference/Qwen--Qwen-Image.jpg differ diff --git a/modules/sd_detect.py b/modules/sd_detect.py index b16478580..b5767956e 100644 --- a/modules/sd_detect.py +++ b/modules/sd_detect.py @@ -101,6 +101,8 @@ def guess_by_name(fn, current_guess): return 'WanAI' elif 'bria' in fn.lower(): return 'Bria' + elif 'qwen' in fn.lower(): + return 'Qwen' return current_guess diff --git a/modules/sd_models.py b/modules/sd_models.py index 1ba384732..152ad0bb8 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -368,6 +368,10 @@ def load_diffuser_force(model_type, checkpoint_info, diffusers_load_config, op=' from pipelines.model_bria import load_bria sd_model = load_bria(checkpoint_info, diffusers_load_config) allow_post_quant = False + elif model_type in ['Qwen']: + from pipelines.model_qwen import load_qwen + sd_model = load_qwen(checkpoint_info, diffusers_load_config) + allow_post_quant = False except Exception as e: shared.log.error(f'Load {op}: path="{checkpoint_info.path}" {e}') if debug_load: diff --git a/modules/shared_items.py b/modules/shared_items.py index 74b087739..ede653f07 100644 --- a/modules/shared_items.py +++ b/modules/shared_items.py @@ -45,6 +45,7 @@ pipelines = { 'OmniGenPipeline': getattr(diffusers, 'OmniGenPipeline', None), 'Cosmos': getattr(diffusers, 'Cosmos2TextToImagePipeline', None), 'WanAI': getattr(diffusers, 'WanPipeline', None), + 'Qwen': getattr(diffusers, 'QwenImagePipeline', None), # dynamically imported and redefined later 'Meissonic': getattr(diffusers, 'DiffusionPipeline', None), diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 8640e285d..1cb4b4cc0 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -932,7 +932,7 @@ def create_ui(container, button_parent, tabname, skip_indexing = False): return pages def ui_scan_click(title): - from modules.models_civitai import civit_search_metadata + from modules.civitai.metadata_civitai import civit_search_metadata civit_search_metadata(title) return ui_refresh_click(title) diff --git a/pipelines/model_qwen.py b/pipelines/model_qwen.py new file mode 100644 index 000000000..622b35bc9 --- /dev/null +++ b/pipelines/model_qwen.py @@ -0,0 +1,67 @@ +import transformers +import diffusers +from modules import shared, devices, sd_models, model_quant, sd_hijack_te + + +def load_transformer(repo_id, diffusers_load_config={}): + load_args, quant_args = model_quant.get_dit_args(diffusers_load_config, module='Model', device_map=True) + shared.log.debug(f'Load model: type=Qwen transformer="{repo_id}" quant="{model_quant.get_quant_type(quant_args)}" args={load_args}') + transformer = diffusers.QwenImageTransformer2DModel.from_pretrained( + repo_id, + subfolder="transformer", + cache_dir=shared.opts.hfcache_dir, + **load_args, + **quant_args, + ) + if shared.opts.diffusers_offload_mode != 'none' and transformer is not None: + sd_models.move_model(transformer, devices.cpu) + return transformer + + +def load_text_encoder(repo_id, diffusers_load_config={}): + load_args, quant_args = model_quant.get_dit_args(diffusers_load_config, module='TE', device_map=True) + shared.log.debug(f'Load model: type=Qwen te="{repo_id}" quant="{model_quant.get_quant_type(quant_args)}" args={load_args}') + text_encoder = transformers.Qwen2_5_VLForConditionalGeneration.from_pretrained( + repo_id, + subfolder="text_encoder", + cache_dir=shared.opts.hfcache_dir, + **load_args, + **quant_args, + ) + if shared.opts.diffusers_offload_mode != 'none' and text_encoder is not None: + sd_models.move_model(text_encoder, devices.cpu) + return text_encoder + + +def load_qwen(checkpoint_info, diffusers_load_config={}): + repo_id = sd_models.path_to_repo(checkpoint_info) + sd_models.hf_auth_check(checkpoint_info) + + transformer = load_transformer(repo_id, diffusers_load_config) + text_encoder = load_text_encoder(repo_id, diffusers_load_config) + + load_args, _quant_args = model_quant.get_dit_args(diffusers_load_config, module='Model') + shared.log.debug(f'Load model: type=Qwen model="{checkpoint_info.name}" repo="{repo_id}" offload={shared.opts.diffusers_offload_mode} dtype={devices.dtype} args={load_args}') + + cls = diffusers.QwenImagePipeline + pipe = cls.from_pretrained( + repo_id, + transformer=transformer, + text_encoder=text_encoder, + cache_dir=shared.opts.diffusers_dir, + **load_args, + ) + pipe.task_args = { + 'output_type': 'np', + } + + del text_encoder + del transformer + + sd_hijack_te.init_hijack(pipe) + from modules.video_models import video_vae + pipe.vae.orig_decode = pipe.vae.decode + pipe.vae.decode = video_vae.hijack_vae_decode + + devices.torch_gc() + return pipe