跳至主要内容

Qwen-Image-Layered:图像分解实操指南

学习用 Python 自动化图像编辑。使用 Qwen-Image-Layered 将图像分解为可编辑 RGBA 图层,并在 Streamlit 应用中重新合成。
· 10分钟 阅读

使用 AI 探索

Photoshop 用户常常花费数小时手动抠图和绘制背景。来自阿里巴巴 Qwen 团队的全新模型 Qwen-Image-Layered 可以自动完成这些工作:输入一张图片,即可返回多个带透明背景的 RGBA 图层。

本教程介绍 Qwen-Image-Layered 的原理、与 SAM 等分割工具的差异,以及如何在 Python 中运行。您将把一张图片分解为多个图层,再编辑并重新组合这些图层生成新的合成图。最终,您将掌握一套无需手动抠图的程序化图像编辑流程。

如果您刚接触以代码处理图像数据和透明通道,建议先学习我们的Python 图像处理课程,打好基础再深入实践。我也推荐阅读我们关于 Google 最新图像生成模型 Nano Banana 2 的指南。

什么是 Qwen-Image-Layered?

Qwen-Image-Layered 构建于Qwen2.5-VL 之上,这是阿里巴巴发布的视觉-语言模型。VL 主干意味着模型能够从语义上理解图像内容:它不仅能检测边缘,还能分辨产品、本影以及其所处的表面。类似 视觉 Transformer,它通过注意力机制而非传统卷积的方式处理图像。

推理时,您可以指定所需的图层数量。请求 4 个,模型会将图像内容分配到 4 个 RGBA 输出;请求 8 个,则分离更细。模型会根据场景结构决定元素归属。

视觉示例

以下是在 Qwen 官方公告中的分解效果:

Qwen-Image-Layered 可视化示例,展示输入与输出图层

该模型将合成图像拆分为具有透明度、可单独编辑的 RGBA 图层。

这张糖果宣传图被拆分为四个独立图层:放射状背景、穿橙色衣服的人、“Sour Candy” 文案与品牌元素、以及单个糖果。每个图层都带有透明背景(棋盘格表示),可随时编辑或重排。

模型会基于景深与语义理解将视觉元素分配到不同图层。背景图形进入图层 0,主体人物进入图层 1,文本叠加进入图层 2,小的前景物体(糖果)进入图层 3。每个元素都以合适的透明度被干净地分离。

Qwen-Image-Layered 与 Segment Anything Model 对比

如果您使用过图像分割,可能会好奇 Qwen-Image-Layered 与现有工具,尤其是 Segment Anything Model(SAM),有何不同。

SAM 等分割模型输出的是掩码:用二值图标记像素属于哪个对象。这很有用,但掩码只告诉您“在哪里”。将对象抠出后,原图中仍会留下一个“洞”。

而 Qwen-Image-Layered 输出的是完整图层。前景对象带有透明通道输出,其后方的背景图层已经被补全。后续删除或移动图层时,无需再进行修补。

功能

Qwen-Image-Layered

Segment Anything Model 2(SAM 2)

主要目标

图层分解(生成可编辑素材)

分割(识别并分离像素)

关键输出

一组 RGBA 图像(透明图层)

二值掩码(黑白轮廓)

背景处理

修补遮挡:为对象背后的“空洞”生成填充像素。

无:移除对象处的背景保持为空/黑色。

输入方式

图像 + 文本提示(可选)

图像/视频 + 点、框或掩码

架构类型

生成式(基于扩散,Qwen2.5-VL)

判别式(基于 Transformer)

视频支持

有限(逐帧应用)

原生支持(业界领先的视频目标跟踪)

用户控制

可变图层数(例如“拆成 4 层或 8 层”)

可提示交互(点击或画框选择特定对象)

最佳场景

平面设计、素材制作、“无 Photoshop” 编辑

视频编辑、目标跟踪、科学分析、机器人

SAM 采用判别式方法,基于现有像素精确划定对象边界。相比之下,Qwen-Image-Layered 使用生成式、基于扩散的架构,通过学习到的图像生成来重建被遮挡或隐藏的区域。

SAM 输出的是用于“去除”的掩码;而 Qwen-Image-Layered 会生成合理的背景内容,完成端到端的图层分解。需要注意的是,将 SAM 与专门的图像修补模型组合也能实现背景填充,但 Qwen-Image-Layered 将此能力整合进了自身的分解流程。

两者的输入控制机制也不同:SAM 倚赖精确的交互式提示,需要点击或框选来指定目标;Qwen-Image-Layered 更加整体化,您只需指定图层数量(如“拆成 4 层”),模型就会自动组织整张图像的结构。

还需强调的是,SAM 2 明确面向视频设计,采用带记忆的流式架构(维护约 6 帧的近期记忆)以高时间一致性跟踪序列中的对象。Qwen-Image-Layered 专注于静态图像分解,目前尚未原生支持用于视频编辑的目标跟踪。

使用 Qwen-Image-Layered 分解图像

接下来进入实操。在编写代码之前,先考虑一个现实问题:模型运行环境。

为何使用 API?

Qwen-Image-Layered 需要超过 57GB 的模型权重,以及高端、具备大量显存的 GPU。

我曾在 Google Colab 的 T4 GPU 上本地尝试运行,模型甚至还没下完,内存就耗尽了。GPU 运行时的磁盘使用也攀升到 90GB(总计 112GB)。对免费云环境而言,这个模型实在太大。

类似 Stable Diffusion 的大型生成式模型也面临相同的托管挑战,因此托管型 API 已成常态。

在本教程中,我们将使用 Replicate 的托管 API。每次分解约 $0.03,可获得稳定、可复现的运行结果,无需担心内存限制或依赖冲突。如果您想不写代码先试用,该 Hugging Face Space 提供免费的网页界面,您可以上传图像并手动下载图层。

Qwen-Image-Layered Hugging Face Space 界面

Hugging Face Space 让您无需写代码即可试用模型,而 API 则提供可编程的控制能力。

环境搭建

创建新项目目录并安装所需包:

pip install replicate python-dotenv

在 Replicate 账户设置 获取 API 令牌,并将其保存在 .env 文件中:

REPLICATE_API_TOKEN=your_token_here

源图像

官方模型公告往往展示的是精挑细选的最佳案例。我们改用一张真实场景图片来测试,更贴近实际表现。

我们将把这张图片分解为多个图层:

粉彩背景上的医疗护理礼品产品

图片来自 Pexels 的 Karola G

图片中有三件护肤产品:薰衣草色背景前的薄荷绿色台面上,摆放着一只系黑色缎带的白色礼盒、一瓶粉色精华和一罐小面霜。构图干净、物体分明、阴影细腻——是进行图层分离的理想测试样本。

运行推理

import replicate
from dotenv import load_dotenv

load_dotenv()

with open("data/healthcare_products.jpg", "rb") as f:
	output = replicate.run(
         "qwen/qwen-image-layered",
     	    input={
         	"image": f,
         	"num_layers": 4,
         	"go_fast": True,
         	"output_format": "png",
         	"seed": 42,
     	    }
	)

参数 num_layers 控制模型的切分细度,取值范围 2 到 8。设置为 4 层可在颗粒度与易管理性之间取得较好平衡。固定 seed 值可以让输出结果可复现。

API 也支持直接传入 URL,如果图像已托管在线上,可跳过文件读写:

output = replicate.run(
	"qwen/qwen-image-layered",
	input={
     	  "image": "https://example.com/photo.jpg",
     	  "num_layers": 4,
     	  "go_fast": True,
     	  "output_format": "png",
	}
)

保存图层

API 返回一个 FileOutput 对象列表。每个对象都有 .read() 方法,可直接获取图像字节:

for i, layer in enumerate(output):
	with open(f"layer_{i}.png", "wb") as f:
     	  f.write(layer.read())

图层自下而上排序:索引 0 为背景,更高索引依次叠加在其上。每个 PNG 都是带透明通道的 RGBA 图像,其他图层会透过透明处显现。

结果如下:

Qwen-Image-Layered:图层分解展示原图与 4 个分离图层

模型将背景(薰衣草色墙面与薄荷色台面)放在图层 0,将面霜放在图层 1,礼盒放在图层 2,精华瓶放在图层 3。各产品边缘干净,透明度准确。

不过观察背景层,您会发现物体原位置仍有淡淡的阴影残留。在我的测试中这很常见:模型在对象分离上表现不错,但遇到阴影时,对背景的重建还不够理想。

源图像的阴影越明显,这类伪影越显著。作为同类中的首个模型,预计 Qwen 团队会在后续版本中持续改进。

用 Qwen-Image-Layered 重新合成图层

分解只是流程的一半。图层的价值在于编辑并重组为新的作品。

替换背景

产品摄影常需要为同一画面更换不同背景,以进行 A/B 测试、应对季节活动或满足平台要求。有了分离图层,这只需几行代码。

from PIL import Image

# 加载已分解的图层
layers = [Image.open(f"layer_{i}.png") for i in range(4)]

# 创建新背景(纯珊瑚色)
new_bg = Image.new("RGBA", layers[0].size, (255, 180, 162, 255))

# 替换原背景层
layers[0] = new_bg

# 自下而上合成
result = layers[0]
for layer in layers[1:]:
	result = Image.alpha_composite(result, layer)

result.save("coral_background.png")

alpha_composite() 会自动处理 RGBA 混合,尊重每个图层的透明度,因此前景对象能自然地叠加在您提供的任何背景之上。

PIL 使图像操作变得直观;如果需要更复杂的变换, PyTorch 的图像处理能力也能很好地与 PIL 流程集成。

效果如下:

替换为珊瑚色背景后的产品合成图

同样的产品,不同的环境,无需手动抠图。您也可以换成渐变、纹理,甚至另一张照片。流程不变:替换图层 0,再合成其余图层。

移除或重定位对象

其他编辑同理。要移除某个对象,只需在合成时跳过对应图层:

# 不包含面霜罐(图层 1)进行合成
layers_without_jar = [layers[0], layers[2], layers[3]]

result = layers_without_jar[0]
for layer in layers_without_jar[1:]:
	result = Image.alpha_composite(result, layer)

移除面霜罐后的产品图

要通过在不同坐标处粘贴来重定位对象,可使用 paste() 方法并设置所选的 x/y 偏移:

# 将精华瓶向左移动 50 像素
 canvas = Image.new("RGBA", layers[0].size, (0, 0, 0, 0))
 canvas.paste(layers[3], (-50, 0), layers[3])
 layers[3] = canvas

 # 然后按常规进行合成

工作流始终一致:

  1. 修改您关心的图层:缩放、重着色、删除、重排。
  2. 然后将所有内容重新合成。

每次操作都在独立图层上进行,编辑范围可控。

用 Streamlit 构建 Qwen-Image-Layered 编辑器

在笔记本中运行这些操作适合一次性编辑,但反馈循环较慢:写代码、运行单元、查看结果、再调整。若需频繁切换图层、对比合成并下载结果,而不想反复运行单元,轻量级 GUI 更合适。

我们将用约 160 行代码构建一个 Streamlit 应用,让您上传图像、分解、切换图层并实时预览。在项目目录中新建名为 streamlit_app.py 的文件。

设置 Streamlit

先编写导入与基础配置:

import os
import io
import time
from typing import List
import streamlit as st
from PIL import Image, ImageDraw
from dotenv import load_dotenv
import replicate

load_dotenv()

# 常量
MAX_LAYERS, MIN_LAYERS, DEFAULT_LAYERS = 8, 2, 4
THUMBNAIL_SIZE = (150, 150)

# 初始化会话状态
if "layers" not in st.session_state:
	st.session_state.layers = []
	st.session_state.layer_visibility = []
	st.session_state.original_image = None

st.set_page_config(page_title="Qwen-Image-Layered", page_icon="🎨", layout="wide")
st.title("🎨 Qwen-Image-Layered")
st.markdown("将图像分解为可编辑的 RGBA 图层")

会话状态可在 Streamlit 重跑间保持不变。否则,用户每次点击按钮图层都会丢失。

API 令牌检查

调用 Replicate 之前,先确认令牌存在:

token = os.environ.get("REPLICATE_API_TOKEN")
if not token:
	st.error("未找到 REPLICATE_API_TOKEN。请在 .env 文件中设置。")
	st.code("export REPLICATE_API_TOKEN=your_token_here", language="bash")
	st.stop()

st.stop() 会在缺少令牌时中止执行,避免继续渲染无意义的界面。

带缓存的图像分解函数

每次分解约 $0.03,耗时 30–60 秒。使用缓存可避免重复调用:

@st.cache_data(show_spinner=False)
def decompose_image(_image_bytes: bytes, num_layers: int, seed: int) -> List[bytes]:
    """调用 Replicate API,并在出现 502 错误时重试。"""
    for attempt in range(3):
        try:
            output = replicate.run(
                "qwen/qwen-image-layered",
                input={
                    "image": io.BytesIO(_image_bytes),
                    "num_layers": num_layers,
                    "go_fast": True,
                    "output_format": "png",
                    "seed": seed,
                },
            )
            return [layer.read() for layer in output]
        except Exception as e:
            if attempt < 2 and ("502" in str(e) or "Replicate" in str(type(e).__name__)):
                time.sleep(5)
                continue
            raise

对 _image_bytes 使用下划线前缀可提示 Streamlit 不对其做哈希(字节哈希代价较高)。重试逻辑通过等待 5 秒、最多重试 3 次来应对 Replicate 偶发的 502 错误。

可视化与合成功能

接着,定义用于可视化与合成的小工具函数:

def create_checkerboard(size: tuple) -> Image.Image:
    """创建棋盘格,用于展示透明区域。"""
    img = Image.new("RGBA", size, (220, 220, 220, 255))
    draw = ImageDraw.Draw(img)

    square = 10
    for y in range(0, size[1], square):
        for x in range(0, size[0], square):
            if (x // square + y // square) % 2:
                draw.rectangle(
                    [x, y, x + square, y + square],
                    fill=(255, 255, 255, 255),
                )

    return img


def composite_layers(
    layers: List[Image.Image],
    visibility: List[bool],
) -> Image.Image:
    """自下而上合成可见图层。"""
    if not layers or not any(visibility):
        return Image.new(
            "RGBA",
            layers[0].size if layers else (400, 400),
            (0, 0, 0, 0),
        )

    result = None
    for layer, visible in zip(layers, visibility):
        if visible:
            result = layer.copy() if result is None else Image.alpha_composite(result, layer)

    return result or Image.new("RGBA", layers[0].size, (0, 0, 0, 0))

棋盘格用于显示透明度(图像编辑器的通用规范)。合成函数与笔记本中的逻辑一致,但由复选框状态驱动。

图像上传与分解设置

侧边栏用于图像上传与分解配置:

with st.sidebar:
    st.header("🖼️ 上传图像")
    uploaded_file = st.file_uploader(
        "选择一张图片",
        type=["png", "jpg", "jpeg"],
        help="上传要分解的图像",
    )

    if uploaded_file is not None:
        st.session_state.original_image = uploaded_file
        st.image(uploaded_file, caption="已上传", use_container_width=True)

    st.divider()

    st.header("⚙️ 设置")
    num_layers = st.slider(
        "图层数量",
        MIN_LAYERS,
        MAX_LAYERS,
        DEFAULT_LAYERS,
    )
    seed = st.number_input(
        "随机种子",
        0,
        999999,
        42,
        help="用于结果可复现",
    )

    # 分解按钮
    if st.button(
        "🔄 分解图像",
        disabled=uploaded_file is None,
        use_container_width=True,
        type="primary",
    ):
        if st.session_state.original_image is not None:
            image_bytes = st.session_state.original_image.getvalue()

            with st.spinner(f"正在分解为 {num_layers} 个图层…(30–60 秒)"):
                try:
                    layer_bytes = decompose_image(image_bytes, num_layers, seed)
                    st.session_state.layers = [
                        Image.open(io.BytesIO(lb)).convert("RGBA") for lb in layer_bytes
                    ]
                    st.session_state.layer_visibility = [True] * len(
                        st.session_state.layers
                    )
                    st.success(
                        f"已分解为 {len(st.session_state.layers)} 个图层!"
                    )
                except Exception as e:
                    if "502" in str(e):
                        st.error(
                            "Replicate API 出现 502 错误。请重试或减少图层数量。"
                        )
                    else:
                        st.error(f"错误:{e}")

    # 重置按钮
    if st.session_state.layers and st.button("🗑️ 重置", use_container_width=True):
        st.session_state.layers = []
        st.session_state.layer_visibility = []
        st.session_state.original_image = None
        st.rerun()

在上传图像前,“分解”按钮处于禁用状态。成功后,图层存入会话状态,且默认全部可见。

添加图层控制

使用两列展示图层控制与实时预览:

col1, col2 = st.columns([1, 1])  # 两列等宽 [web:61]

with col1:
    st.subheader("📑 图层")

    if not st.session_state.layers:
        st.info("上传图像并进行分解后将在此显示图层。")
    else:
        st.caption("切换图层显隐。图层 0 为背景。")

        cols = st.columns(min(len(st.session_state.layers), 4))
        for i, layer in enumerate(st.session_state.layers):
            with cols[i % 4]:
                thumb = layer.copy()
                thumb.thumbnail(THUMBNAIL_SIZE)

                checkerboard = create_checkerboard(thumb.size)
                display = Image.alpha_composite(checkerboard, thumb)

                st.image(display, caption=f"图层 {i}", use_container_width=True)

                st.session_state.layer_visibility[i] = st.checkbox(
                    f"显示图层 {i}",
                    value=st.session_state.layer_visibility[i],
                    key=f"layer_{i}",
                )

with col2:
    st.subheader("👁️ 预览")

    if not st.session_state.layers:
        st.info("合成预览将显示在此处。")
    else:
        composite = composite_layers(
            st.session_state.layers,
            st.session_state.layer_visibility,
        )
        visible = sum(st.session_state.layer_visibility)

        st.caption(f"当前显示 {visible} / {len(st.session_state.layers)} 个图层")
        st.image(composite, caption="合成结果", use_container_width=True)

        if visible > 0:
            buf = io.BytesIO()
            composite.save(buf, format="PNG")

            st.download_button(
                "⬇️ 下载结果",
                data=buf.getvalue(),
                file_name="composite.png",
                mime="image/png",
                use_container_width=True,
            )

当您取消勾选某图层时,Streamlit 会重跑脚本。复选框更新 layer_visibility,合成函数据此重算预览,界面随之自动刷新。

运行应用

保存文件并运行:

streamlit run streamlit_app.py

Streamlit 应用界面,展示 Qwen-Image-Layered 分解的图层切换与实时预览

应用中已载入分解后的图像。关闭图层即可在预览中移除对象。

上传图像,点击分解,然后按需切换图层。预览会即时更新。找到满意的合成后即可下载。整个工作流都在浏览器中完成,无需反复运行笔记本单元。

结语

Qwen-Image-Layered 提供了另一种图像编辑思路:不是去“遮盖像素”,而是将内容分解为包含修补背景的完整 RGBA 图层。您可以在无需清理的情况下实现对象分离。该模型在复杂场景的阴影伪影上仍有不足,但对象提取本身表现良好。

Python 工作流非常直接。调用 Replicate API,获取图层列表,再用 PIL 合成。更换背景与移除对象只需几行代码。Streamlit 应用将这套逻辑封装为 GUI,便于快速迭代。

这仍是同类中的首个模型。Qwen 团队更新速度很快,预计会改进背景重建,并更好地处理阴影较重的图像。该 研究论文介绍了其架构,便于理解可变图层分解在底层如何实现。

想继续探索更多开源 AI 模型?通过 DataCamp 的 Hugging Face 基础技能路径,系统掌握支撑 Qwen 的生态。

Qwen-Image-Layered 常见问题

什么是 Qwen-Image-Layered?它如何工作?

Qwen-Image-Layered 是阿里巴巴推出的一款视觉-语言模型,可将图像分解为多个带透明度的 RGBA 图层。不同于只提供掩码的分割工具,它输出带有修补背景的完整图层,让您无需手动清理即可编辑或移除对象。

Qwen-Image-Layered 与 SAM(Segment Anything Model)有何不同?

SAM 输出的是二值掩码,指示对象所在位置,但把对象抠出后,原图会留下空洞。Qwen-Image-Layered 则输出完整的 RGBA 图层,前景对象背后的背景已被填充。您可以直接删除或移动图层,无需再做补丁修复。

我能在本地电脑上运行 Qwen-Image-Layered 吗?

该模型需要超过 57GB 的权重以及大量 GPU 显存,本地部署难度较大。即便在 Google Colab 的 T4 GPU 上,设置阶段也会耗尽内存。本文建议使用 Replicate 的托管 API(每次分解约 $0.03),或使用免费的 Hugging Face Space 进行测试。

拿到分解图层后,我可以进行哪些编辑?

拿到图层后,您可以进行背景更换用于 A/B 测试;通过跳过某个图层来移除对象;在不同坐标处粘贴以重定位元素;或按自定义方式进行合成。教程中展示了使用 PIL 进行程序化编辑的示例,并构建了一个 Streamlit 应用以交互式切换图层。

Qwen-Image-Layered 有哪些局限?

当图像中存在明显阴影时,模型在背景重建方面会有困难。您可能会在背景层看到物体原位置的淡淡阴影残留。源图像的阴影越复杂,这类伪影越明显。作为第一代模型,预计 Qwen 团队后续会持续改进。

Bexruz (Bex) Tuychiev

作者

Bexruz (Bex) Tuychiev

我是一名数据科学内容创作者,拥有超过 2 年的经验,并在 Medium 上拥有较大的读者群。我喜欢用略带讽刺的笔调撰写有关 AI 和机器学习的深度文章——毕竟总得想点办法让这些内容不那么枯燥。我已发表 130 多篇文章,并制作了一门 DataCamp 课程,另有一门正在筹备中。我的内容已被超过 500 万人次阅读,其中有 2 万人在 Medium 和 LinkedIn 上成为关注者。 

主题