From 8f151b7e1718126137d1c3e9feaa4d30d75630a9 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Mon, 18 Aug 2025 16:08:41 -0400 Subject: [PATCH] add qwen-image-edit Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 10 +++++++++- html/reference.json | 7 +++++++ installer.py | 2 +- modules/modelloader.py | 15 +++++++++------ modules/models_hf.py | 2 +- modules/processing_args.py | 2 ++ modules/processing_helpers.py | 2 +- pipelines/model_qwen.py | 18 +++++++++++++----- wiki | 2 +- 9 files changed, 44 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e7a5d252..98082745c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,14 +2,22 @@ ## Update for 2025-08-18 +- **Models** + - [Qwen-Image-Edit](https://huggingface.co/Qwen/Qwen-Image-Edit) + Image editing using natural language prompting, similar to `Flux.1-Kontext`, but based on larger 20B `Qwen-Image` model - **Features** - new setting -> huggingface -> download method default is `rust` as new `xet` is known to cause issues - support for `flux.1-kontext` lora + - support for `qwen-image` lora - **UI** - - new artwork for reference models in networks, thanks @liutyi + - new artwork for reference models in networks + thanks @liutyi - localization support for ModernUI - single-click on locale rotates current locale, double-click on locale resets locale to `en` +- **Docs** + - Models and Video pages updated with links to original model repos, model licenses and original release dates + thanks @alerikaisattera - **Fixes** - fix OpenVINO with offloading - add explicit offload calls on prompt encode diff --git a/html/reference.json b/html/reference.json index 300442507..5d634e68d 100644 --- a/html/reference.json +++ b/html/reference.json @@ -183,6 +183,13 @@ "skip": true, "extras": "" }, + "Qwen-Image-Edit": { + "path": "Qwen/Qwen-Image-Edit", + "preview": "Qwen--Qwen-Image.jpg", + "desc": " Qwen-Image-Edit, the image editing version of Qwen-Image. Built upon our 20B Qwen-Image model, Qwen-Image-Edit successfully extends Qwen-Image’s unique text rendering capabilities to image editing tasks, enabling precise text editing.", + "skip": true, + "extras": "" + }, "Qwen-Lightning": { "path": "vladmandic/Qwen-Lightning", "preview": "Qwen-Lightning.jpg", diff --git a/installer.py b/installer.py index a46b04322..f0cc9a638 100644 --- a/installer.py +++ b/installer.py @@ -598,7 +598,7 @@ def check_diffusers(): t_start = time.time() if args.skip_all or args.skip_git: return - sha = '58bf2682612bc29b7cdb8a10ba6eee28a024d6d3' # diffusers commit hash + sha = '555b6cc34f1973c36a1d168edee0960625c00c8c' # 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/modules/modelloader.py b/modules/modelloader.py index b185475e3..149c57867 100644 --- a/modules/modelloader.py +++ b/modules/modelloader.py @@ -27,17 +27,20 @@ def hf_login(token=None): log.debug('HF login: no token provided') return False if os.environ.get('HUGGING_FACE_HUB_TOKEN', None) is not None: - # log.warning('HF login: removing existing env variable: HUGGING_FACE_HUB_TOKEN') - del os.environ['HUGGING_FACE_HUB_TOKEN'] + os.environ.pop('HUGGING_FACE_HUB_TOKEN', None) + os.unsetenv('HUGGING_FACE_HUB_TOKEN') if os.environ.get('HF_TOKEN', None) is not None: - # log.warning('HF login: removing existing env variable: HF_TOKEN') - del os.environ['HF_TOKEN'] + os.environ.pop('HF_TOKEN', None) + os.unsetenv('HF_TOKEN') if loggedin != token: - os.environ.setdefault('HF_TOKEN', token) stdout = io.StringIO() - with contextlib.redirect_stdout(stdout): + try: hf.logout() + except Exception: + pass + with contextlib.redirect_stdout(stdout): hf.login(token=token, add_to_git_credential=False, write_permission=False) + os.environ['HF_TOKEN'] = token text = stdout.getvalue() or '' obfuscated_token = 'hf_...' + token[-4:] line = [l for l in text.split('\n') if 'Token' in l] diff --git a/modules/models_hf.py b/modules/models_hf.py index d810dbb6b..7eab17ef7 100644 --- a/modules/models_hf.py +++ b/modules/models_hf.py @@ -30,7 +30,7 @@ def hf_init(): obfuscated_token = None if len(opts.huggingface_token) > 0 and opts.huggingface_token.startswith('hf_'): obfuscated_token = 'hf_...' + opts.huggingface_token[-4:] - os.environ.setdefault('HF_TOKEN', opts.huggingface_token) + # os.environ.setdefault('HF_TOKEN', opts.huggingface_token) log.info(f'Huggingface init: transfer={opts.hf_transfer_mode} parallel={opts.sd_parallel_load} direct={opts.diffusers_to_gpu} token="{obfuscated_token}" cache="{opts.hfcache_dir}"') diff --git a/modules/processing_args.py b/modules/processing_args.py index c34cdc2b9..a80a3e56d 100644 --- a/modules/processing_args.py +++ b/modules/processing_args.py @@ -101,6 +101,8 @@ def task_specific_kwargs(p, model): } # model specific args + if model.__class__.__name__ == 'QwenImageEditPipeline' and len(getattr(p, 'init_images', [])) == 0: + task_args['image'] = [Image.new('RGB', (p.width, p.height), (0, 0, 0))] # monkey-patch so qwen-image-edit pipeline does not error-out on t2i if model.__class__.__name__ == 'LatentConsistencyModelPipeline' and hasattr(p, 'init_images') and len(p.init_images) > 0: p.ops.append('lcm') init_latents = [processing_vae.vae_encode(image, model=shared.sd_model, vae_type=p.vae_type).squeeze(dim=0) for image in p.init_images] diff --git a/modules/processing_helpers.py b/modules/processing_helpers.py index 8729d2465..e3210ba66 100644 --- a/modules/processing_helpers.py +++ b/modules/processing_helpers.py @@ -365,7 +365,7 @@ def calculate_base_steps(p, use_denoise_start, use_refiner_start): if len(getattr(p, 'timesteps', [])) > 0: return None cls = shared.sd_model.__class__.__name__ - if 'Flex' in cls or 'HiDreamImageEditingPipeline' in cls or 'Kontext' in cls: + if 'Flex' in cls or 'Kontext' in cls or 'Edit' in cls: steps = p.steps elif not is_txt2img(): if cls in sd_models.i2i_pipes: diff --git a/pipelines/model_qwen.py b/pipelines/model_qwen.py index 63ee989e3..eacb36da1 100644 --- a/pipelines/model_qwen.py +++ b/pipelines/model_qwen.py @@ -12,10 +12,21 @@ def load_qwen(checkpoint_info, diffusers_load_config={}): 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}') transformer = generic.load_transformer(repo_id, cls_name=diffusers.QwenImageTransformer2DModel, load_config=diffusers_load_config, modules_dtype_dict={"minimum_6bit": ["img_mod", "pos_embed", "time_text_embed", "img_in", "txt_in", "norm_out"]}) - repo_te = 'Qwen/Qwen-Image' if 'Qwen-Lightning' in repo_id else repo_id + repo_te = 'Qwen/Qwen-Image' if 'Qwen-Lightning' in repo_id or 'Qwen-Image-Edit' in repo_id else repo_id text_encoder = generic.load_text_encoder(repo_te, cls_name=transformers.Qwen2_5_VLForConditionalGeneration, load_config=diffusers_load_config) - pipe = diffusers.QwenImagePipeline.from_pretrained( + if 'Edit' in repo_id: + cls_name = diffusers.QwenImageEditPipeline + diffusers.pipelines.auto_pipeline.AUTO_TEXT2IMAGE_PIPELINES_MAPPING["qwen-image"] = diffusers.QwenImageEditPipeline + diffusers.pipelines.auto_pipeline.AUTO_IMAGE2IMAGE_PIPELINES_MAPPING["qwen-image"] = diffusers.QwenImageEditPipeline + diffusers.pipelines.auto_pipeline.AUTO_INPAINT_PIPELINES_MAPPING["qwen-image"] = diffusers.QwenImageEditPipeline + else: + cls_name = diffusers.QwenImagePipeline + diffusers.pipelines.auto_pipeline.AUTO_TEXT2IMAGE_PIPELINES_MAPPING["qwen-image"] = diffusers.QwenImagePipeline + diffusers.pipelines.auto_pipeline.AUTO_IMAGE2IMAGE_PIPELINES_MAPPING["qwen-image"] = diffusers.QwenImageImg2ImgPipeline + diffusers.pipelines.auto_pipeline.AUTO_INPAINT_PIPELINES_MAPPING["qwen-image"] = diffusers.QwenImageInpaintPipeline + + pipe = cls_name.from_pretrained( repo_id, transformer=transformer, text_encoder=text_encoder, @@ -26,9 +37,6 @@ def load_qwen(checkpoint_info, diffusers_load_config={}): 'output_type': 'np', } - diffusers.pipelines.auto_pipeline.AUTO_TEXT2IMAGE_PIPELINES_MAPPING["qwen-image"] = diffusers.QwenImagePipeline - diffusers.pipelines.auto_pipeline.AUTO_IMAGE2IMAGE_PIPELINES_MAPPING["qwen-image"] = diffusers.QwenImageImg2ImgPipeline - diffusers.pipelines.auto_pipeline.AUTO_INPAINT_PIPELINES_MAPPING["qwen-image"] = diffusers.QwenImageInpaintPipeline del text_encoder del transformer diff --git a/wiki b/wiki index f91e819d2..6bef6e0b5 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit f91e819d22603f34be0c3e8fb674d4ab89421622 +Subproject commit 6bef6e0b56e21aed3760eab32e4c69ee2a0550ea