跳至主要内容

FLUX.2 Klein 教程:使用 Gradio 构建“生成与编辑”图像应用

了解如何将本地 FLUX.2 Klein 4B 生成与基于 API 的图像编辑、多参考条件和会话历史相结合,在 Gradio 中创建一款图像编辑器。
· 14分钟 阅读

使用 AI 探索

FLUX.2 Klein 专为快速、交互式的图像工作流而设计,您可以以极低延迟进行生成、微调和迭代。在本教程中,我将使用 FLUX.2 Klein 4B 在 Google Colab 中借助 Gradio 构建一个轻量级的“生成与编辑应用”。 

在该应用中,用户首先输入生成提示词以创建基础图像,然后提供编辑指令,并可选上传参考图像来引导修改。每个版本都会保存到会话历史中,用户可随时重新加载任意先前输出,并从该处继续试验。

最终得到的是一个强调快速迭代、可靠图像编辑与历史驱动流程的交互式编辑器,其体验更接近真实产品,而非一次性模型调用。

我还建议您查看我们的 FLUX.2 基础教程以及我们的本地运行 FLUX.2 的指南。如果您也对其他图像生成模型感兴趣,请务必阅读我们关于 Google 的 Nano Banana 2 的指南。

什么是 FLUX.2 [klein] 4B?

FLUX.2 是一组针对不同取舍进行调优的图像模型家族,包含多个在质量、速度和灵活性方面各有优化的变体。FLUX.2 Klein 系列聚焦交互式视觉智能,这意味着模型被构建为快速响应、迅速迭代,并能在易获得的硬件上高效运行。 

FLUX.2 Klein 系列支持文生图、图像编辑以及多参考条件(multi-reference conditioning)。

FLUX.2 [klein] 在文本生成图像、单参考图像到图像、多参考任务中的 Elo 与延迟(上)及显存(下)对比

图: FLUX.2 [klein] 在文本生成图像、单参考图像到图像、多参考任务中的 Elo 与延迟(上)及显存(下)对比(Flux 2 博客)

Klein 4B 变体是 Klein 家族中对开发者最友好的选项,因为它在 Apache 2.0 许可下完全开源,且设计为可在约 13GB 显存的消费级 GPU 上运行。 

FLUX.2 Klein 关键能力

  • 实时迭代:Klein 面向先生成后快速微调的工作流。这非常适合交互式预览循环和以 UI 驱动的编辑体验。
  • 统一的生成与编辑:不同于将生成与编辑视作独立产品功能,Klein 模型家族自底向上支持二者。 
  • 多参考支持:Klein 支持多参考的生成与编辑,这对使用调色板图、风格参考或物体参考来约束编辑很有用。在官方 API 中,Klein 端点最多支持 4 张用于编辑的图像,非常适合“1 张基础图像 + 最多 3 张参考图像”的设置。

Klein 通常有两大类型:为速度优化的蒸馏模型,以及为灵活性优化的基础型基座模型(foundation models)。Klein 4B 蒸馏模型是构建交互式应用的务实之选;而当您希望获得最大微调灵活性与控制力(即便以更高延迟为代价)时,基础变体更为合适。

FLUX.2 Klein 教程:图像生成与编辑应用

本节我们将实现一个两阶段的图像工作流,使用 FLUX.2 Klein 并封装在一个Gradio 应用中。从高层来看,应用执行三项核心任务:

  • 根据文本提示生成基础图像
  • 使用第二条指令对生成的图像进行编辑,可选用参考图像进行引导。
  • 最后,维护会话历史以便探索。用户可从“历史”下拉菜单加载任何先前图像并继续编辑。

在幕后,该工作流由两类互补函数驱动:

  • local_generate() 函数使用 Diffusers 库在本地运行 FLUX.2 Klein 4B,以实现快速的 text-to-image 生成。
  • bfl_edit(edit_prompt, base_img, ref_imgs) 函数向 Black Forest Labs 托管的 FLUX.2 Klein 编辑 API 发送图像编辑请求,然后反复轮询返回的 polling_url,直到编辑结果就绪。

历史机制将生成的图像存为包含图像与简短标签的字典:{"img": PIL.Image, "label": str}。

在下方视频中,您可以看到该工作流的精简版。为演示效果,视频已加速:

步骤 1:环境配置

在构建图像生成与编辑应用之前,我们需要用最新的 Diffusers 库搭建一个干净环境。本教程中,我们将在配有 A100 GPU 的 Google Colab 实例上运行,以更快地生成图像。需要说明的是,相同设置也可在更入门的 GPU(如 T4)上运行,只是性能会略慢。

我们先卸载任何已存在的 Diffusers 版本以避免冲突,然后直接从 GitHub 安装最新开发版。同时安装用于模型加载、检查点处理和加速的配套库。

!pip -q uninstall -y diffusers
!pip -q install -U git+https://github.com/huggingface/diffusers.git
!pip -q install -U accelerate safetensors transformers huggingface_hub

安装完成后,我们检查已安装的 Diffusers 版本,并确认可以成功导入 Flux2KleinPipeline 类。

import diffusers
print("diffusers:", diffusers.__version__)
from diffusers import Flux2KleinPipeline
print("Flux2KleinPipeline import OK")

这表明我们的环境已正确配置。下一步,我们将把 FLUX.2 Klein 模型加载到 GPU,并为本地图像生成与基于 API 的编辑流程做好准备。

步骤 2:加载 FLUX.2 Klein 4B 

在加载模型之前,您需要 Black Forest Labs 的 API 密钥。首先登录您的Black Forest Labs 账户,在 Credits 选项卡下充值。最低充值约 10 美元,可获得约 1000 点积分。积分到账后,点击 Create Key,为其取一个易识别的名称,并妥善保存。

Black forest labs API Key

拿到 API 密钥后,下一步是加载 FLUX.2 Klein 4B 模型,并为两种不同的使用模式做好准备:本地图像生成和基于 API 的图像编辑。 

import os, time, base64, io, requests
import gradio as gr
import torch
import nest_asyncio
from PIL import Image
from diffusers import Flux2KleinPipeline
from google.colab import userdata
nest_asyncio.apply()
# =============================
# LOAD via API
# =============================
MODEL_ID = "black-forest-labs/FLUX.2-klein-4B"
BFL_API_KEY = userdata.get("BFL_API_KEY")
BFL_CREATE_URL = "https://api.bfl.ai/v1/flux-2-klein-4b"
print(f"Loading model to GPU: {torch.cuda.get_device_name(0)}")
# =============================
# LOAD via LOCAL HF PIPELINE
# =============================
dtype = torch.float16 
pipe = Flux2KleinPipeline.from_pretrained(MODEL_ID, torch_dtype=dtype).to("cuda")

我们首先将 API 密钥设置为 Colab 机密,这样它不会出现在笔记本代码中。还定义了模型 ID 和稍后将调用的 Klein 编辑端点。 

接下来,在启动 Gradio 界面前应用 nest_asyncio,以避免事件循环冲突。

最后,加载本地 FLUX.2 Klein 管线以进行基础图像生成。我们使用 Flux2KleinPipeline 并设为 float16 以获得广泛的 GPU 兼容性(A100 与 T4 均表现良好),随后将管线移动到 CUDA 以实现快速推理。此本地管线仅用于生成初始图像,而编辑通过 API 处理。

注意:您可能会好奇为何既本地加载模型,又配置 API 访问。原因在于能力分工:

  • 本地 Hugging Face 管线非常适合迭代式的文生图生成,并可完全掌控推理设置。
  • Black Forest Labs API 暴露了 FLUX.2 Klein 的高级图像编辑功能,这些功能目前在开源权重实现中尚不可用,如基于提示词的图像编辑及多参考图像编辑。

结合两种方式,我们即可兼得:本地生成的速度与完善的编辑支持。

步骤 3:辅助函数

在将一切接入 Gradio 界面之前,我们需要一组小型辅助函数。由于应用在本地生成图像,而通过 API 进行编辑,我们需要一种方式将图像编码为 base64 以用于 API 请求,需要一种方式下载以 URL 返回的编辑图像,以及一个本地生成函数来产生初始基础图像。

def pil_to_b64_png(img: Image.Image) -> str:
    buf = io.BytesIO()
    img.convert("RGB").save(buf, format="PNG")
    return base64.b64encode(buf.getvalue()).decode()
def url_to_pil(url: str) -> Image.Image:
    r = requests.get(url, timeout=60)
    r.raise_for_status()
    return Image.open(io.BytesIO(r.content)).convert("RGB")
def local_generate(prompt: str):
    if not prompt.strip():
        raise gr.Error("Prompt is empty.")
    out = pipe(
        prompt=prompt.strip(),
        width=1024,
        height=1024,
        num_inference_steps=4,
        guidance_scale=1.0,
        output_type="pil",
    )
    return out.images[0].convert("RGB")

上述代码定义了三个关键辅助函数:

  • pil_to_b64_png() 将 PIL 图像转换为 base64 编码的 PNG 字符串。之所以需要这样,是因为 FLUX.2 编辑 API 接受基础图像与参考图像的输入类型为 URL 或 base64 字符串。 
  • url_to_pil() 则负责反向处理。编辑 API 会返回生成结果的签名 URL,因此我们用 requests 获取该 URL,校验响应,并将下载的字节重新转换为 PIL 图像。这样编辑结果即可直接显示在 Gradio 界面中并保存到历史。
  • 最后,local_generate() 是我们的本地文生图入口。它先校验提示词不为空,然后以固定设置(如分辨率、步数与引导系数)调用本地 Flux2KleinPipeline。输出将以干净的 RGB PIL 图像返回,作为后续编辑的基础图像。

您可以随意尝试调整 FLUX 管线设置,如图像分辨率、扩散步数、引导系数等,以探索不同的质速权衡,并更好地理解各参数如何影响最终图像。 

Flux 2 图像编辑的文档是了解这些设置的好去处。

在下一步中,我们将使用这些工具,通过 FLUX.2 Klein 实现基于 API 的图像编辑。

步骤 4:通过 BFL API 进行编辑

这一步是纯 Hugging Face 工作流与本教程所用混合方法的核心差异。我们不尝试在本地运行图像编辑,而是将编辑委托给 API,由其可靠地处理基于提示词的编辑与可选的多参考条件。

从高层看,FLUX.2 Klein 编辑 API 需要描述编辑的文本提示词、要修改的基础图像,以及作为附加输入提供的可选参考图像。该 API 以异步方式运行,即我们提交请求,然后轮询返回的 URL,直到编辑结果就绪。

POLL_INTERVAL_S = 1.0
POLL_TIMEOUT_S = 120
def bfl_edit(edit_prompt, base_img, ref_imgs=None):
    if not BFL_API_KEY:
        raise gr.Error("Missing BFL_API_KEY in Colab Secrets.") 
    payload = {
        "prompt": edit_prompt.strip(),
        "input_image": pil_to_b64_png(base_img),
        "output_format": "png",
    }
    if ref_imgs:
        for i, ref in enumerate(ref_imgs[:3], start=2):
            payload[f"input_image_{i}"] = pil_to_b64_png(ref)
    create = requests.post(
        BFL_CREATE_URL,
        headers={"x-key": BFL_API_KEY, "accept": "application/json"},
        json=payload,
        timeout=60,
    )
    create.raise_for_status()
    job = create.json()
    polling_url = job.get("polling_url")
    if not polling_url:
        raise gr.Error(f"API Error: {job}")
    t0 = time.time()
    while True:
        if time.time() - t0 > POLL_TIMEOUT_S:
            raise gr.Error("BFL API polling timed out.")  
        time.sleep(POLL_INTERVAL_S)
        res = requests.get(polling_url, headers={"x-key": BFL_API_KEY})
        res.raise_for_status()
        j = res.json()
        if j["status"] == "Ready":
            return url_to_pil(j["result"]["sample"])
        if j["status"] in ("Error", "Failed"):
            raise gr.Error(f"Generation Failed: {j}")

bfl_edit() 函数完成三件重要的事:

  • 它接收三个输入:编辑提示词、我们要修改的基础图像,以及一个可选的参考图像列表。 
  • 我们构造包含编辑提示词与基础图像(编码为 base64 PNG)的 JSON 载荷。若提供了参考图像,则按 API 的索引格式附加它们。这样我们可以在基础图像之外最多提供三张参考图像,足以应对诸多实用工作流,如调色板迁移、风格锚定或物体对齐。
  • 接着,我们将请求提交到 API 端点并解析响应。API 返回一个需要反复查询的 polling_url,直到作业完成为止。我们通过一个简单的轮询循环来实现,并设置了两个护栏:轮询间隔以避免过度请求,以及超时以防笔记本无限等待。

当状态变为 Ready 时,API 响应会包含输出图像的签名 URL。我们使用先前的 url_to_pil() 辅助函数下载该图像,并以 PIL 图像返回。

下一步,我们将实现会话历史,使用户可重访任意生成或编辑的输出。

步骤 5:添加历史 

至此,在构建 UI 之前我们还需要补齐最后一块:一个轻量的会话记忆层,便于用户重访先前输出。我们使用两个 Gradio 状态对象来实现历史:

  • base_img_state 存储当前工作图像(未来的编辑将应用于此)。
  • history_state 将每个生成或编辑版本以列表形式存储,每项是包含图像及简短标签的字典。
def update_history(history):
    choices = [f"{i}: {h['label']}" for i, h in enumerate(history or [])]
    return gr.update(choices=choices, value=choices[-1] if choices else None)
def on_generate(prompt, history):
    img = local_generate(prompt)
    new_history = list(history or [])
    new_history.append({"img": img, "label": "Generated Base"})
    return img, img, new_history, update_history(new_history), "Base image generated."
def on_edit(edit_prompt, ref2, ref3, ref4, base_img, history):
    if base_img is None:
        raise gr.Error("Please generate a base image first.")
    refs = [r.convert("RGB") for r in (ref2, ref3, ref4) if r is not None]
    out = bfl_edit(edit_prompt, base_img, refs)
    new_history = list(history or [])
    new_history.append({"img": out, "label": f"Edit: {edit_prompt[:20]}..."})
    return out, out, new_history, update_history(new_history), "Edit successful."
def on_load(choice, history):
    if not history or not choice:
        return None, None, "No history to load."
    idx = int(choice.split(":")[0])
    img = history[idx]["img"]
    return img, img, f"Loaded entry #{idx}."
def on_clear():
    return None, None, [], gr.update(choices=[], value=None), "Cleared everything."

上述函数构成了我们演示中维护图像历史的核心逻辑:

  • update_history() 将会话历史格式化为下拉菜单。每个条目都带有索引与简短描述,且下拉菜单会默认选中新加入的项。这样无需手动刷新逻辑即可保持 UI 同步。
  • on_generate() 回调运行 local_generate() 以创建初始基础图像。随后将结果以 {"img": img, "label": "Generated Base"} 形式追加到历史中。该函数返回图像与更新后的历史状态,并更新下拉菜单以便新条目立刻可见。
  • on_edit() 回调类似,但调用的是 bfl_edit()。它取当前工作图像及可选参考图像,通过 API 应用编辑,并将输出追加到历史中。同时将编辑结果作为新的 base_img_state 返回,这意味着未来的编辑会应用于最新图像,除非用户显式加载旧版本。
  • on_load() 回调解析所选下拉条目,从 history_state 中取回对应图像,并再次将其设为当前工作图像。这样用户可以从任意先前的检查点分支进行编辑。
  • 最后,on_clear() 函数重置一切:清空输出、清空基础图像状态、重置历史列表并清空下拉菜单选项。

现在,让我们将这些函数接入一个包含两个文本框、可选参考上传以及历史工作流的 Gradio 界面。

步骤 6:Gradio 界面

本步骤将我们的生成、编辑与历史逻辑连接到一个简洁的 Gradio 界面。该界面使用 gr.Blocks 构建,从而对布局具有精细控制。 

with gr.Blocks() as demo:
    gr.Markdown("## FLUX.2 Klein: Generate & Edit Images")
    with gr.Row():
        with gr.Column(scale=1):
            gen_prompt = gr.Textbox(label="1. Generate Base Image", placeholder="A young boy riding a scooter...")
            gen_btn = gr.Button("Generate Base Image", variant="primary")
            edit_prompt = gr.Textbox(label="2. Describe Edits", placeholder="Add the red hat and yellow glasses from the references...")
            with gr.Accordion("Reference Images (Optional)", open=False):
                r2 = gr.Image(label="Ref 1", type="pil")
                r3 = gr.Image(label="Ref 2", type="pil")
                r4 = gr.Image(label="Ref 3", type="pil")
            edit_btn = gr.Button("Apply Edit", variant="secondary") 
            history_dd = gr.Dropdown(label="Session History", choices=[])
            with gr.Row():
                load_btn = gr.Button("Load Selected")
                clear_btn = gr.Button("Clear All")
            status = gr.Textbox(label="Status", interactive=False)
        with gr.Column(scale=1):
            output_view = gr.Image(label="Current Image", type="pil")
    base_img_state = gr.State(None)
    history_state = gr.State([])
    gen_btn.click(
        on_generate, 
        [gen_prompt, history_state], 
        [output_view, base_img_state, history_state, history_dd, status]
    ) 
    edit_btn.click(
        on_edit, 
        [edit_prompt, r2, r3, r4, base_img_state, history_state], 
        [output_view, base_img_state, history_state, history_dd, status]
    ) 
    load_btn.click(
        on_load, 
        [history_dd, history_state], 
        [output_view, base_img_state, status]
    )
    clear_btn.click(
        on_clear, 
        None, 
        [output_view, base_img_state, history_state, history_dd, status]
    )
demo.launch(share=True, inline=True, debug = True)

我们将界面分为两列:左侧用于输入和操作,右侧用于显示当前图像。具体构建如下:

  • 第一个文本框收集生成提示词,Generate Base Image 按钮触发 on_generate() 函数。该回调运行本地生成,将输出保存到历史中,并更新显示的图像与存储在 base_img_state 中的当前工作图像。
  • 第二个文本框收集编辑指令。当用户点击Apply Edit 时,我们调用 on_edit() 函数,它将当前工作图像作为基础图像并返回编辑结果。编辑结果也会追加到历史中,并成为下一次编辑的新工作图像。
  • 为了使工作流具备会话感知能力,我们还加入了Session History 下拉菜单,每个条目对应图像的一个已保存版本。点击Load Selected 按钮会触发 on_load(),从而恢复所选历史图像并再次将其设为当前工作图像。 
  • 最后,Clear All 按钮通过 on_clear() 函数重置一切,包括输出显示、基础图像状态和已存历史。

以下两个 Gradio State 对象保证整体一致性:

  • base_img_state 确保每次编辑都应用到正确的图像。
  • history_state 存储完整的输出时间线,便于回放与分支。

最后,demo.launch() 在 Colab 中启动应用,提供可分享链接、内联渲染,并启用调试日志以便在开发过程中定位问题。

FLUX.2 Klein 示例与观察

为了理解 FLUX.2 Klein 在实践中的表现,我使用最终的“生成与编辑”应用进行了小规模试验,测试了三种主要交互模式:基础图像生成、仅提示词编辑,以及使用一张或多张参考图像的参考引导编辑。我还验证了会话历史流程,确认可以恢复任意先前输出并从该点继续编辑。

图像生成

我先用一个简单的生成提示词创建了干净的基础图像。模型生成的主体连贯、光照一致、面部细节清晰且场景布局稳定。该基础图像质量较高,适合作为后续修改的起点,而无需重新生成。

image2.png

使用单张参考图像进行图像编辑

接着,我用一张参考图像作为颜色来源,并使用类似“将连衣裙颜色改为参考图像中的颜色”的编辑指令。编辑成功迁移了目标颜色,同时保留了大部分原始场景结构。

我的观察是,单参考编辑在提示词范围更窄、更聚焦时效果最佳。颜色迁移和服饰改色尤为可靠,因为它们不需要大的几何变化。当编辑指令聚焦于单一属性时,主体身份和背景细节往往保持稳定。

image6.png

使用单条提示词进行图像编辑

然后我尝试了仅提示词的语义编辑,例如“将背景替换为海滩”。模型干净地完成了背景替换,同时大致保持了主体一致性。 

在背景变化时,模型往往会调整色温和阴影强度以维持照片级真实感。这通常是优点,但也意味着编辑后的图像会更像一次不同的拍摄,而非简单的剪切粘贴。

image4.png

姿态引导

我测试了使用参考图像隐含身体姿态的编辑,并配合类似“匹配参考图像中的姿态”的提示词。生成结果改变了主体姿势,同时仍基本保持了相似的身份与风格。

以下是我用于姿态引导的参考图像:

Pose guidance - reference image

姿态编辑可行,但比颜色编辑更敏感。任何姿态变化都可能引发次要变化,如布料垂坠、四肢位置和相机构图。我做了几次试验才得到合适的姿态,腿部位置仍然有些欠佳。

image8.png

使用多张参考图像进行图像编辑

最后,我测试了多参考编辑,提供了多种输入,例如一张调色板参考图,并在编辑提示中明确指出“只使用参考图像的调色板改变背景颜色。不要用参考图像替换背景。” 

出乎我意料,多参考编

辑在每张参考图像各司其职时最有效。 

当提示中明确限定“仅调色板”且避免指示模型用参考内容进行替换时,调色板参考的效果很好。 

当参考含糊不清时,模型可能会借用纹理或布局线索,而非仅使用颜色。我的实验中注意到一些轻微伪影:类似参考图像的元素会微妙地渗入场景,但整体结果仍然稳健且视觉一致。

image1.png

本演示的一个实用优势在于,每张生成与编辑的图像都会存储在会话历史中。您可以加载任意先前条目,并从那个精确的检查点继续编辑。这大大加快了试验速度:您可以从较早版本分支出不同编辑方向、对比多个编辑路径,并避免丢失一个不错的中间结果。 

结语

在本教程中,我们使用 FLUX.2 Klein 4B 构建了完整的生成与图像编辑工作流。通过将 Diffusers 的本地文生图与 Black Forest Labs 托管的编辑 API 结合,我们得以创建一款交互式图像编辑器。

FLUX.2 Klein 低延迟、统一的生成与编辑能力,以及多参考支持,使其尤其适合此类交互式工作流。接下来,您可以扩展该应用,如批量编辑、参数控制、跨会话持久化,甚至加入一个可自动提出编辑建议的 LLM 驱动代理。 

FLUX.2 Klein 常见问题

我可以去掉 API,全部用 HF 实现吗?

可以,但编辑对库版本和后处理路径更敏感。如果您的目标是可靠的编辑工作流,那么使用 API 更稳妥。

我能让编辑应用到加载的历史图像上吗?

可以。在当前设计中,加载历史会设置基础图像状态。后续任何编辑都将应用于该基础图像。

我可以扩展历史以显示缩略图吗?

可以,您可以将下拉式历史改为图库(Gallery)。状态结构保持不变。

在本地运行 FLUX.2 Klein 4B 的系统要求是什么?

要高效运行该模型,您需要一块约 13GB 显存的消费级 GPU。尽管在 A100 等高端硬件上表现最佳,它也专门优化以便在更易获得的 GPU(如 NVIDIA T4)上运行,只是延迟略高,使其远比完整的 FLUX 基座模型轻量。

我可以将 FLUX.2 Klein 用于商业应用吗?

可以。FLUX.2 Klein 4B 变体在 Apache 2.0 许可证下发布。这是一种宽松的开源许可证,允许开发者将模型用于个人与商业软件,并进行修改与分发,而不受其他模型许可证中常见的严格限制。

“Klein” 与其他 FLUX.2 变体有何不同?

Klein 是一款蒸馏模型(40 亿参数),优先考虑速度与交互性,而非原始的灵活度。虽然更大的 FLUX.2 “基础”变体更适合深度微调,Klein 则面向“交互式视觉智能”而工程化,提供更快的推理与实时迭代能力,因此更适合构建响应迅速的用户界面与编辑工具。

Aashi Dutt

作者

Aashi Dutt

我是一名 Google Developers 机器学习(生成式 AI)领域的专家、Kaggle 三项专家,以及 Women Techmakers 大使,拥有 3 年以上的技术从业经验。2020 年我共同创办了一家健康科技初创公司,目前在佐治亚理工学院攻读计算机科学硕士,专攻机器学习。

主题