跳至主要内容

Qwen-Image-Edit-2509 教程:生成可用于影棚的产品多角度图

使用 Qwen-Image-Edit-2509 与 LoRA,将一张照片转化为多张产品图。为亚马逊、Etsy 和网店在数秒内生成一致的影棚角度。
· 9分钟 阅读

使用 AI 探索

个体卖家和小品牌往往没有预算进行完整的产品拍摄。在本指南中,我们将构建并运行一个 Gradio 应用,使用 Qwen-Image-Edit-2509 配合基于 LoRA 的虚拟相机控制,将单张输入图片转换为适用于电商的多角度产品渲染(广角、微距、±45°、俯拍),实现可复现的 AI 产品摄影。

在底层,我们使用:

  • 基础模型:Qwen/Qwen-Image-Edit-2509(Apache-2.0)
  • 角度控制 LoRA:dx8152/Qwen-Edit-2509-Multiple-angles
  • 可选加速 LoRA: lightx2v/Qwen-Image-Lightning

在本教程中,您将学会:

  • 加载 Qwen 编辑管线并叠加多个 LoRA。
  • 用健壮的角度宏驱动相机移动。
  • 为创作者与电商团队构建简洁的 Gradio 界面。
  • 将所有结果导出为 ZIP,便于交接。

如果您想进一步了解深度学习中图像数据的处理方式,请查看Python 图像处理技能路径。

为何将 Qwen Image Edit 与多角度 LoRA 搭配使用?

Qwen-Image-Edit-2509 是 Qwen 最新的图像编辑模型迭代版本,具备更高一致性与多图编辑能力。Multiple-Angles LoRA 注入了可靠的“虚拟相机”控制,您可以这样指令:

  • 将镜头转为广角镜头 (turn to wide-angle)
  • 将镜头转为特写镜头 (turn to close-up)
  • 将镜头向左旋转45度 (rotate 45° left)
  • 将镜头转为俯视 (top-down view)

这些指令能稳定地重构同一产品的取景,而不会幻变为其他产品。

Qwen Image Edit 分步教程:打造产品图“增产器”

在本指南中,我们将构建一个应用,把单张产品图转成影棚就绪的角度组合(包括广角、特写、左右 45°、俯拍等),并提供可选的生活化预设与快速预览模式。它使用 Qwen/Qwen-Image-Edit-2509 模型和多角度 LoRA,并封装在 Gradio 界面中。

我们将从环境配置开始,依次完成模型加载、生成辅助函数与 Web UI。

步骤 1:先决条件

首先安装所有运行时依赖。我们使用带 CUDA 轮子的 PyTorch、Hugging Face 工具链、Diffusers 库、用于 UI 的 Gradio,以及用于图像处理的 Pillow/OpenCV。

pip install -q torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
pip install -q transformers>=4.44.0 accelerate>=0.33.0 safetensors>=0.4.3
pip install -q pillow>=10.2.0 huggingface_hub>=0.24.0 opencv-python-headless>=4.9.0.80
pip install -q numpy>=1.26.0 gradio>=4.0.0
pip install -q git+https://github.com/huggingface/diffusers

各依赖的作用如下:

  • CUDA 轮子:该 PyTorch 安装指向 NVIDIA CUDA 12.1 轮子,用于 GPU 加速。
  • Transformers 与 Diffusers:Transformers 管理配置与分词器,Diffusers 提供 QwenImageEditPlusPipeline。
  • Safetensors:提供安全的张量格式(比 Pickle 更快更安全)。
  • OpenCV-headless 与 Pillow:提供稳健的图像 I/O 与缩放,便于前后处理。
  • Gradio:快速构建 Web UI,可本地或在 Colab 运行。

环境就绪后,我们可以(可选)进行 Hugging Face 认证并加载模型。

步骤 2:Hugging Face 认证

某些模型或适配器可能需要 HuggingFace 身份验证。此步骤在会话中为您完成一次登录。

from huggingface_hub import login
import os
HF_TOKEN = "Your_HF_Token"  # @param {type:"string"}
if HF_TOKEN:
    login(token=HF_TOKEN)
    print("Logged in to Hugging Face")
else:
    print("WARNING: No token provided. Continuing without authentication")

先登录您的 HuggingFace 账号,在账号设置中找到 Access Tokens。点击“Create new token”,创建一个 Read 类型的新令牌。选择所需权限并生成新令牌。

HF Access code

在生产环境/Colab 中请勿硬编码令牌,改用环境变量或 Colab “Secrets”。

步骤 3:加载模型并设置管线

本节将设置 Qwen-Image-Edit 管线、加载角度 LoRA,并加入带 GPU 感知设置的“快速”开关。注意:该代码在高内存的 A100 GPU 上效率更佳。

步骤 3.1:导入与 GPU 配置

现在我们导入核心库并检测 GPU,自动设置精度、attention 切片与 VAE 切片,以提升稳定性。

import io
import zipfile
from typing import List, Tuple, Optional
import gc
import torch
from PIL import Image
from diffusers import QwenImageEditPlusPipeline
import gradio as gr
def get_gpu_config():
    if not torch.cuda.is_available():
        return {
            'device': 'cpu',
            'dtype': torch.float32,
            'gpu_name': 'CPU',
            'vram_gb': 0,
            'max_batch': 1,
            'enable_attention_slicing': True,
            'enable_vae_slicing': True,
        }
    gpu_name = torch.cuda.get_device_name(0)
    vram_gb = torch.cuda.get_device_properties(0).total_memory / 1e9 
    if 'T4' in gpu_name or vram_gb < 20:
        return {
            'device': 'cuda',
            'dtype': torch.bfloat16,
            'gpu_name': gpu_name,
            'vram_gb': vram_gb,
            'max_batch': 1,
            'enable_attention_slicing': True,
            'enable_vae_slicing': True,
        }
    else:
        return {
            'device': 'cuda',
            'dtype': torch.bfloat16,
            'gpu_name': gpu_name,
            'vram_gb': vram_gb,
            'max_batch': 2,
            'enable_attention_slicing': False,
            'enable_vae_slicing': False,
        }
gpu_config = get_gpu_config()
HF_BASE_MODEL = "Qwen/Qwen-Image-Edit-2509"
LORA_ANGLES = "dx8152/Qwen-Edit-2509-Multiple-angles"
LORA_LIGHTNING = "lightx2v/Qwen-Image-Lightning"
ANGLE_MACROS = {
    "Wide-angle": "将镜头转为广角镜头",
    "Close-up": "将镜头转为特写镜头",
    "Forward": "将镜头向前移动",
    "Left": "将镜头向左移动",
    "Right": "将镜头向右移动",
    "Down": "将镜头向下移动",
    "Rotate 45° Left": "将镜头向左旋转45度",
    "Rotate 45° Right": "将镜头向右旋转45度",
    "Top-down": "将镜头转为俯视",
}
BACKGROUND_PRESETS = {
    "(None)": None,
    "Pure Studio (white seamless)": "in a professional studio with seamless white background, soft shadows, product centered",
    "Soft Gray Studio": "in a professional studio with seamless soft gray background, gentle vignette, softbox lighting",
    "Lifestyle (cozy desk)": "on a cozy wooden desk near a window, soft natural light, minimal props",
    "Lifestyle (marble)": "on a clean white marble surface, bright daylight, subtle reflections",
    "Lifestyle (outdoor)": "outdoors on a neutral table, soft shade, bokeh background",
}
ASPECT_RATIOS = {
    "1:1 (Square)": (1024, 1024),
    "4:3 (Standard)": (1024, 768),
    "3:4 (Portrait)": (768, 1024),
    "16:9 (Widescreen)": (1024, 576),
    "9:16 (Mobile)": (576, 1024),
    "3:2 (Photo)": (1024, 683),
    "2:3 (Portrait Photo)": (683, 1024),
}

上述代码块的关键组件包括:

  • GPU 自动配置(get_gpu_config 函数):检测可用加速器并返回一个小型配置字典,用于选择设备(cpu/cuda)、数据类型(如 GPU 上的 bfloat16)以及内存行为:
    • 在 CPU 上,默认使用 float32,并启用 attention/VAE 切片以保证安全。
    • 在 T4 / 小于 20 GB 显存上,保持 bfloat16,但启用切片以防止 OOM。
    • 在 20 GB+ GPU(如 A100)上,禁用切片并允许更大批量以提速。
  • 模型 ID 与适配器:加载 Qwen-Image-Edit-2509 作为核心编辑器,配合多角度 LoRA 进行相机控制,并可在快速模式下可选混合 Lightning LoRA。
    • HF_BASE_MODEL = "Qwen/Qwen-Image-Edit-2509" 是主要的图像编辑骨干模型。
    • LORA_ANGLES = "dx8152/Qwen-Edit-2509-Multiple-angles" 提供“虚拟相机”控制。
    • LORA_LIGHTNING = "lightx2v/Qwen-Image-Lightning" 是可选的速度适配器,用于快速预览。

注:在高质量模式(HQ Mode)下,仅启用角度适配器。

  • 角度宏:ANGLE_MACROS 将“Wide-angle”“Top-down”等标签映射为该角度 LoRA 能可靠识别的中文指令。这些命令可稳定地移动虚拟相机(移动/旋转/缩放)而不改变主体身份。
  • 背景预设:BACKGROUND_PRESETS 是简短的英文片段,如“marble surface”“soft gray studio”,我们会将其合并进最终提示中。它们与角度宏正交,负责控制场景。
  • 长宽比(ASPECT_RATIOS):支持生成不同画布(1:1、4:3、16:9 等),并在推理前对输入进行缩放或中心裁剪,这样输出无需后期再裁。

至此 GPU 与适配器已就位。接下来实例化 Diffusers 管线并挂载 LoRA。

步骤 3.2:管线

接下来加载 Qwen Edit 基础管线,根据 GPU 启用内存优化,并叠加多角度 LoRA。同时实现模式切换,可选加入 Lightning LoRA 进行快速预览。

pipe = QwenImageEditPlusPipeline.from_pretrained(
    HF_BASE_MODEL,
    torch_dtype=gpu_config['dtype'],
)
pipe = pipe.to(gpu_config['device'])
if gpu_config['enable_attention_slicing']:
    pipe.enable_attention_slicing()
if gpu_config['enable_vae_slicing']:
    pipe.enable_vae_slicing()
pipe.load_lora_weights(LORA_ANGLES, adapter_name="angles")
current_mode = {"fast": False}
def set_pipeline_mode(use_lightning: bool):
    global current_mode  
    if use_lightning and not current_mode["fast"]:
        pipe.load_lora_weights(LORA_LIGHTNING, adapter_name="lightning")
        pipe.set_adapters(["angles", "lightning"], adapter_weights=[1.0, 1.0])
        current_mode["fast"] = True
    elif not use_lightning and current_mode["fast"]:
        pipe.set_adapters(["angles"], adapter_weights=[1.0])
        current_mode["fast"] = False
    elif not use_lightning and not current_mode["fast"]:
        pipe.set_adapters(["angles"], adapter_weights=[1.0])
set_pipeline_mode(False)

以上代码是产品图“增产器”应用的管线设置层,完成以下工作:

  • 管线初始化:从基础模型(Qwen/Qwen-Image-Edit-2509)实例化 QwenImageEditPlusPipeline,将其移动到检测到的设备(CPU/GPU),并根据硬件设置张量精度(torch_dtype)。
  • 内存安全:在小显存 GPU 上启用 attention 与 VAE 切片,以降低峰值显存并避免 OOM,牺牲少量速度换取稳定性。
  • LoRA 组合:加载多角度 LoRA 以提供可复现的“虚拟相机”控制(移动/旋转/缩放),并可选混合 Lightning LoRA 用于更快的预览。
  • 模式切换逻辑:set_pipeline_mode() 辅助函数与 current_mode 标志管理 HQ 与 Fast 状态,避免重复加载 Lightning,并允许快速切换适配器。

管线已就绪。接下来连接生成函数与 Gradio UI。

步骤 4:生成函数

生成函数通过构建提示、按目标长宽比调整尺寸、在所选角度上运行管线,并将结果打包为 zip 文件供下载,从而完成生成流程。

步骤 4.1:提示拼装与图像缩放辅助

在调用模型前,我们用两个无状态辅助函数规范化输入:

  • compose_prompt() 函数:把相机角度、背景预设、自定义场景与风格备注拼接为一条指令字符串。使用简洁分句有助于保持 Qwen 的指令解析稳定,避免某部分过度加权。
  • resize_image() 函数:采用“等比铺满”策略将源图适配到目标长宽比,使用 Lanczos 重采样实现高质量缩放,并进行居中裁剪,确保主体保留在画面中。
def compose_prompt(angle_phrase: str,
                   bg_preset_text: Optional[str],
                   custom_scene: str,
                   extra_style: str) -> str:
    parts = [angle_phrase]
    if bg_preset_text:
        parts.append(f"{bg_preset_text}")
    if custom_scene.strip():
        parts.append(custom_scene.strip())
    if extra_style.strip():
        parts.append(extra_style.strip())
    return " | ".join(parts)
def resize_image(img: Image.Image, target_size: Tuple[int, int]) -> Image.Image:
    target_w, target_h = target_size
    orig_w, orig_h = img.size
    scale = max(target_w / orig_w, target_h / orig_h)
    new_w = int(orig_w * scale)
    new_h = int(orig_h * scale)
    img = img.resize((new_w, new_h), Image.Resampling.LANCZOS) 
    left = (new_w - target_w) // 2
    top = (new_h - target_h) // 2
    img = img.crop((left, top, left + target_w, top + target_h))    
    return img

上述两个函数完成的任务包括:

  • 可组合提示:用 " | " 连接分句,使其在视觉上分明,Qwen-Edit 能良好处理标点并稳定解析,不会偏向末尾分句。对用户文本使用 .strip() 以避免空片段。
  • 等比铺满计算:确保缩放后图像在两个维度上都能完全覆盖目标画布。
  • Lanczos 重采样:在下采样时保留细节、上采样时降低混叠。
  • 中心裁剪:缩放后从中心截取精确目标画布,保持常见构图,避免意外偏切。

辅助函数就绪后,我们可以执行多角度生成循环并打包结果。

步骤 4.2:生成图像

接下来校验输入、将图像适配所选长宽比、遍历所选角度,并在 inference_mode 下运行管线。最后返回包含全部生成图像的 zip 文件。

def generate_images(
    source_img: Image.Image,
    angle_keys: List[str],
    bg_key: str,
    custom_scene: str,
    extra_style: str,
    aspect_ratio: str,
    use_lightning: bool,
    seed: int,
    steps: int,
    guidance_scale: float,
    true_cfg_scale: float,
    images_per_prompt: int,
    progress=gr.Progress()
) -> List[Image.Image]:   
    if source_img is None:
        return [], "WARNING: Please upload an image first!"    
    if not angle_keys:
        return [], "WARNING: Please select at least one angle!"  
    target_size = ASPECT_RATIOS[aspect_ratio]
    source_img = resize_image(source_img, target_size)  
    set_pipeline_mode(use_lightning)
    results = []
    generator = torch.manual_seed(seed)  
    bg_preset_text = BACKGROUND_PRESETS.get(bg_key)    
    total_angles = len(angle_keys)    
    for idx, angle_name in enumerate(angle_keys):
        progress((idx + 1) / total_angles, f"Generating {angle_name}...") 
        angle_phrase = ANGLE_MACROS[angle_name]
        full_prompt = compose_prompt(angle_phrase, bg_preset_text, custom_scene, extra_style)   
        inputs = {
            "image": [source_img],
            "prompt": full_prompt,
            "generator": generator,
            "true_cfg_scale": true_cfg_scale,
            "negative_prompt": " ",
            "num_inference_steps": steps,
            "guidance_scale": guidance_scale,
            "num_images_per_prompt": images_per_prompt,
            "height": target_size[1],
            "width": target_size[0],
        }     
        with torch.inference_mode():
            out = pipe(**inputs)   
        for img_idx, im in enumerate(out.images):
            results.append(im) 
        if 'T4' in gpu_config['gpu_name']:
            torch.cuda.empty_cache()
    return results
def create_zip(images: List[Image.Image]) -> Optional[str]:
    if not images:
        return None
    zip_path = "/content/product_shot_booster.zip"
    with zipfile.ZipFile(zip_path, mode="w", compression=zipfile.ZIP_DEFLATED) as zf:
        zf.writestr("manifest.txt", "Product Shot Booster Export\nGenerated angles exported as PNG files.\n")
        for idx, img in enumerate(images):
            buf = io.BytesIO()
            img.save(buf, format="PNG")
            zf.writestr(f"angle_{idx+1:03d}.png", buf.getvalue())
    return zip_path

generate_images() 函数通过以下方式实现多角度生成与导出:

  • 校验、规范与配置:检查是否上传图像与选择角度,将图像适配所选长宽比(缩放+居中裁剪),并通过 set_pipeline_mode(use_lightning) 切换 HQ 与 Fast 模式。
  • 高效推理:针对每个角度,构建包含角度宏、背景预设、场景与风格的一条指令,准备 Diffusers 输入,并在 torch.inference_mode() 下执行以节省内存。在低显存 GPU(如 T4)上,每个角度调用 torch.cuda.empty_cache() 有助于减少碎片化。
  • 聚合结果并反馈进度:随着每个角度完成,更新 Gradio 进度,并将所有输出收集为一个列表,便于后续打包。

create_zip(images) 函数提供可移植的导出:写入一个包含简要 manifest.txt 的 zip,并通过内存(BytesIO)编码图像为无损 PNG;若无图像则返回 None。

生成与打包完成后,UI 可触发一次多角度生成并提供一键下载完整角度包。

步骤 5:Gradio UI

该界面允许用户上传产品图、选择角度与长宽比、挑选背景预设、设置风格备注,并随时生成多角度图像。

with gr.Blocks(title="Product Shot Booster", theme=gr.themes.Soft()) as demo:
    gr.Markdown("<h1 style='text-align: center;font-weight: bold;'>Product Shot Booster</h1>")
    with gr.Row():
        with gr.Column(scale=1):
            input_image = gr.Image(
                label="Product Image",
                type="pil",
                height=300
            ) 
            angle_choices = gr.CheckboxGroup(
                choices=list(ANGLE_MACROS.keys()),
                value=["Wide-angle", "Close-up", "Rotate 45° Left", "Rotate 45° Right", "Top-down"],
                label="Camera Angles"
            ) 
            aspect_ratio = gr.Dropdown(
                choices=list(ASPECT_RATIOS.keys()),
                value="1:1 (Square)",
                label="Aspect Ratio"
            )
            bg_preset = gr.Dropdown(
                choices=list(BACKGROUND_PRESETS.keys()),
                value="(None)",
                label="Background"
            ) 
            custom_scene = gr.Textbox(
                label="Custom Scene",
                placeholder="e.g., 'on a matte black table with soft reflections'",
                lines=2
            )    
            extra_style = gr.Textbox(
                label="Style Notes",
                value="studio-grade lighting, high clarity, ecommerce-ready composition",
                lines=2
            )
        with gr.Column(scale=2):
            output_gallery = gr.Gallery(
                  label="Generated Images",
                  show_label=True,
                  columns=3,
                  height="auto",
                  object_fit="contain"
              )

            info_output = gr.Markdown("")
            zip_output = gr.File(label="Download ZIP")
    with gr.Accordion("Advanced Settings", open=False):
        with gr.Row():
            use_lightning = gr.Checkbox(
                label="Fast Mode (Lightning LoRA)",
                value=('T4' in gpu_config['gpu_name']),
                info="Enable for faster generation (recommended for T4)"
            )   
            seed = gr.Number(
                label="Seed",
                value=123,
                precision=0
            )  
            steps = gr.Slider(
                label="Inference Steps",
                minimum=10,
                maximum=60,
                value=28,
                step=1
            )
        with gr.Row():
            guidance_scale = gr.Slider(
                label="Guidance Scale",
                minimum=0.0,
                maximum=8.0,
                value=1.0,
                step=0.1
            )
            true_cfg_scale = gr.Slider(
                label="True CFG Scale",
                minimum=0.0,
                maximum=10.0,
                value=4.0,
                step=0.1
            ) 
            images_per_prompt = gr.Slider(
                label="Images per Angle",
                minimum=1,
                maximum=4,
                value=1,
                step=1
            )
    generate_btn = gr.Button("Generate", variant="primary", size="lg")
    def generate_and_zip(*args):
      images = generate_images(*args)
      zip_file = create_zip(images) if images else None
      return images, zip_file 
    generate_btn.click(
        fn=generate_and_zip,
        inputs=[
            input_image,
            angle_choices,
            bg_preset,
            custom_scene,
            extra_style,
            aspect_ratio,
            use_lightning,
            seed,
            steps,
            guidance_scale,
            true_cfg_scale,
            images_per_prompt
        ],
        outputs=[output_gallery, zip_output]
    )
demo.launch(share=True, debug=True, show_error=True)

Demo

以上代码构建了一个交互式 Gradio 应用,通过一次生成操作连接推理,并暴露可分享的 Web UI,包括:

  • 输入(左列):左侧面板收集模型所需的一切:用 gr.Image 上传产品图,用 gr.CheckboxGroup 选择相机角度,用两个 gr.Dropdown 设定长宽比与背景预设,并用两个 gr.Textbox 输入自定义场景与风格备注以微调观感。
  • 输出(右列):gr.Gallery 以网格展示所有生成角度,便于一眼对比构图;gr.File 提供 PNG 的 ZIP,方便快速下载。
  • 高级设置:gr.Checkbox 切换快速模式(Lightning LoRA),适合 T4 级 GPU 的快速预览;Seed 和 Steps 调节可复现性与质效平衡;Guidance 与 True-CFG 控制编辑强度与身份稳定性;Images per Angle 允许一次性为每个相机宏生成多个变体。
  • 交互连线(动作):一个Generate 按钮将所有输入连接到 generate_images()(用所选适配器运行 Qwen Edit),随后到 create_zip()(进行打包),并将画廊与 ZIP 一并返回界面。
  • 应用启动:最后 demo.launch() 启动 Web 应用并提供可公开分享的 URL——非常适合 Colab 会话、快速演示,或把链接发给相关方亲自试用。

最后点击Generate,即可渲染完整角度包。

以下是我用该模型实验得到的结果。首先,您可以看到在不同参数设置下产品的呈现效果。

在下一个视频中,您可以看到我如何与界面交互、调整各类设置,以及基于原始图像生成的结果。

总结

现在,您已经拥有一个 Product Shot Booster Gradio 应用,可借助 Qwen-Image-Edit-2509 与 LoRA “虚拟相机”控制,将单张照片转换为多角度渲染。它具备 GPU 感知能力(A100 最佳,较小 GPU 启用切片),可复现,并支持从快速预览到高质量导出的工作流。

接下来您可以尝试:

  • 上线交付:加入认证与配额,并提供一键预设导出以符合 Amazon/Etsy/PDP 图片规范。
  • 规模化:开放 /generate API 并支持批处理(SKU 的 CSV/JSON),让团队可规模化渲染角度包。

如果您希望更动手地构建 AI 驱动应用,推荐查看开发 AI 应用技能路径。

=

主题