Diffusers 中的 Flux.1 ControlNet 管线:用 Canny / Depth / Union 条件精确控制图像生成
【免费下载链接】diffusers🤗 Diffusers: State-of-the-art diffusion models for image, video, and audio generation in PyTorch.项目地址: https://gitcode.com/GitHub_Trending/di/diffusers
导读
本文围绕 Flux.1 ControlNet 官方文档,系统讲解 🤗 Diffusers 中FluxControlNetPipeline的完整用法:从 InstantX 与 XLabs 两套预训练 ControlNet 的选型,到文本到图像、图像到图像、局部重绘三种调用方式,再到多 ControlNet 组合、ControlNet-Union 模式与 LoRA 融合等进阶能力。读完本文,你将掌握如何加载 Flux.1-dev 基座模型与 ControlNet 权重、如何调优controlnet_conditioning_scale、control_guidance_start/end等核心参数,并理解其底层实现原理(对应源码 pipeline_flux_controlnet.py 与模型定义 controlnet_flux.py)。
一、背景:ControlNet 为 Flux.1 带来的条件控制
ControlNet 最初由 Lvmin Zhang、Anyi Rao 与 Maneesh Agrawala 在论文Adding Conditional Control to Text-to-Image Diffusion Models中提出。其核心思想是:在冻结已训练好的大规模文生图扩散模型(如 Flux.1)权重的前提下,额外训练一套可插拔的条件控制网络,用“零卷积”(zero-initialized convolution layers)从零逐步增长参数,从而在不破坏基座模型的前提下注入边缘、深度、分割、人体姿态等空间条件。
本文涉及的是 Flux.1 架构上的 ControlNet 实现,与 Stable Diffusion 的 ControlNet 在使用 API 上相似,但底层架构(MMDiT、2×2 打包等)完全不同。
在 Diffusers 中,这条管线即为FluxControlNetPipeline,位于 src/diffusers/pipelines/flux/pipeline_flux_controlnet.py。官方文档明确指出该代码由The InstantX Team实现,同时XLabs Team贡献的另一套 ControlNet 权重同样受支持。
二、选择预训练 ControlNet 权重
2.1 InstantX 系列(官方文档推荐)
| ControlNet 类型 | 开发者 | 权重仓库 |
|---|---|---|
| Canny(边缘) | The InstantX Team | InstantX/FLUX.1-dev-Controlnet-Canny |
| Depth(深度) | The InstantX Team | Shakker-Labs/FLUX.1-dev-ControlNet-Depth |
| Union(多条件统一) | The InstantX Team | InstantX/FLUX.1-dev-Controlnet-Union |
2.2 XLabs 系列(同样受支持)
| ControlNet 类型 | 开发者 | 权重仓库 |
|---|---|---|
| Canny | The XLabs Team | XLabs-AI/flux-controlnet-canny-diffusers |
| Depth | The XLabs Team | XLabs-AI/flux-controlnet-depth-diffusers |
| HED(软边缘) | The XLabs Team | XLabs-AI/flux-controlnet-hed-diffusers |
使用提示:两套权重的结构存在差异,详见下文"两种架构分支"一节——InstantX 的模型没有
input_hint_block,需要先把条件图编码进 VAE 潜在空间;XLabs 的模型自带input_hint_block,直接消费像素级提示图。因此在加载管线时,务必保证 ControlNet 权重与基座模型(black-forest-labs/FLUX.1-dev)配套使用,并统一使用torch_dtype=torch.bfloat16以匹配 Flux.1 的训练精度。
三、快速上手:文本到图像(Text-to-Image)
官方文档(同时被写入 pipeline_flux_controlnet.py 的 docstring 作为示例)给出的最小可用代码如下:
import torch from diffusers.utils import load_image from diffusers import FluxControlNetPipeline from diffusers import FluxControlNetModel base_model = "black-forest-labs/FLUX.1-dev" controlnet_model = "InstantX/FLUX.1-dev-controlnet-canny" controlnet = FluxControlNetModel.from_pretrained(controlnet_model, torch_dtype=torch.bfloat16) pipe = FluxControlNetPipeline.from_pretrained( base_model, controlnet=controlnet, torch_dtype=torch.bfloat16 ) pipe.to("cuda") control_image = load_image("https://huggingface.co/InstantX/SD3-Controlnet-Canny/resolve/main/canny.jpg") prompt = "A girl in city, 25 years old, cool, futuristic" image = pipe( prompt, control_image=control_image, control_guidance_start=0.2, control_guidance_end=0.8, controlnet_conditioning_scale=1.0, num_inference_steps=28, guidance_scale=3.5, ).images[0] image.save("flux.png")关键参数解析(依据__call__签名,见 pipeline_flux_controlnet.py)
| 参数 | 默认值 | 说明 |
|---|---|---|
control_image | 必填 | 条件控制图,支持PIL.Image.Image、np.ndarray、torch.Tensor及对应 list;多 ControlNet 时必须按序传入 list |
controlnet_conditioning_scale | 1.0 | ControlNet 输出注入主网络残差前的乘性权重;多 ControlNet 时可传 list 分别指定 |
control_guidance_start | 0.0 | ControlNet 开始生效的步数百分比(0~1) |
control_guidance_end | 1.0 | ControlNet 停止生效的步数百分比(0~1) |
control_mode | None | 仅 ControlNet-Union 使用,指定条件类型(见下文 Union 小节) |
guidance_scale | 7.0 | 无分类器引导强度,越高越贴近 prompt,但过高会降低图像质量 |
true_cfg_scale | 1.0 | 大于 1 且传入negative_prompt时启用真正的无分类器引导(Flux.1 默认不适用传统 CFG) |
num_inference_steps | 28 | 去噪步数,越多质量越高但更慢 |
height/width | 1024(由default_sample_size=128与 VAE 缩放因子推导) | 生成尺寸,须能被vae_scale_factor * 2(即 16)整除 |
max_sequence_length | 512 | T5 文本编码器最大序列长度,上限 512(超限会在check_inputs中报错) |
generator | None | 随机数生成器,传入后可复现结果 |
latents | None | 预先生成的噪声潜在向量,用于固定构图/种子 |
joint_attention_kwargs | None | 透传给注意力处理器的 kwargs,可用于 LoRA scale 等 |
control_guidance_start/end的范围语义:以0.2 ~ 0.8为例,表示前 20% 与后 20% 的采样步中 ControlNet 不参与,只在中间 60% 的步数施加条件——这在"先用 prompt 自由构图、再让条件约束细节"的工作流中非常实用。若传入多 ControlNet,这两个参数会自动扩展为与 ControlNet 数量等长的 list(见 pipeline_flux_controlnet.py 的归一化逻辑)。
四、底层原理:ControlNet 是如何接入 Flux 的
4.1 模型定义与两种架构分支
FluxControlNetModel定义于 src/diffusers/models/controlnets/controlnet_flux.py。其__init__中有一个关键开关:
- 当构造时传入
conditioning_embedding_channels时,会创建ControlNetConditioningEmbedding(block_out_channels=(16, 16, 16, 16))作为input_hint_block——这是XLabs 架构,条件图直接走提示编码块进入网络; - 当该值为
None时input_hint_block = None——这是InstantX 架构,条件图必须先经 VAE 编码并完成 2×2 打包。
对应地,管线在 pipeline_flux_controlnet.py 中用self.controlnet.input_hint_block is None判断分支:
# xlab controlnet has a input_hint_block and instantx controlnet does not controlnet_blocks_repeat = False if self.controlnet.input_hint_block is None else True if self.controlnet.input_hint_block is None: # vae encode control_image = retrieve_latents(self.vae.encode(control_image), generator=generator) control_image = (control_image - self.vae.config.shift_factor) * self.vae.config.scaling_factor # pack:将 latent 切分为 2×2 patch control_image = self._pack_latents(...)4.2 Flux 特有的 latent 打包(packing)
Flux 的 MMDiT 架构不直接处理B, C, H, W的潜在张量,而是将其拆成 2×2 的 patch 并展平为序列。因此管线中的_pack_latents(pipeline_flux_controlnet.py)会把(batch, channels, H, W)重排为(batch, (H/2)*(W/2), channels*4);_unpack_latents则在解码前反向还原。这也是为什么height/width必须能被 16 整除(VAE 8 倍压缩 × 2×2 打包),check_inputs会对不满足的尺寸发出告警。
4.3 前向推理流程(__call__主循环)
__call__的执行顺序可归纳为:
- 输入校验:
check_inputs检查prompt与prompt_embeds二选一、negative_prompt配对、尺寸可整除等; - 文本编码:
encode_prompt分别调用 CLIP(得到 pooled 输出)与 T5(序列最长 512)两套编码器,得到prompt_embeds、pooled_prompt_embeds与text_ids; - 条件图准备:
prepare_image完成预处理、resize 到目标尺寸并按num_images_per_prompt重复;随后按上节所述进入 VAE 编码 + 打包分支; - 步进去噪:每步用
FlowMatchEulerDiscreteScheduler计算时间步,将control_image、text_ids、latent_image_ids一并送入transformer,ControlNet 输出乘以controlnet_conditioning_scale后叠加进主网络; - 解码输出:
_unpack_latents还原潜在张量,VAE 解码为图像,默认返回FluxPipelineOutput(return_dict=True)或 tuple。
4.4 多 ControlNet:FluxMultiControlNetModel
传入 list/tuple 形式的 ControlNet 时,__init__会自动包装为FluxMultiControlNetModel(controlnet_flux.py),并在前向中逐条读取controlnet_cond、controlnet_mode与conditioning_scale,把多个条件网络的结果累加进主网络(controlnet_flux.py)。此时control_image、controlnet_conditioning_scale、control_guidance_start/end、control_mode都应传等长 list,实现"Canny 边缘 + Depth 深度"之类的组合控制。
五、进阶用法一:图像到图像(Image-to-Image)
仓库还提供FluxControlNetImg2ImgPipeline(pipeline_flux_controlnet_image_to_image.py),支持在条件控制的同时以一张源图初始化 latent,实现"参考原图结构 + 受 ControlNet 约束"的重绘。
import torch from diffusers import FluxControlNetImg2ImgPipeline, FluxControlNetModel from diffusers.utils import load_image controlnet = FluxControlNetModel.from_pretrained( "InstantX/FLUX.1-dev-controlnet-canny", torch_dtype=torch.bfloat16 ) pipe = FluxControlNetImg2ImgPipeline.from_pretrained( "black-forest-labs/FLUX.1-dev", controlnet=controlnet, torch_dtype=torch.bfloat16, ) pipe.to("cuda") init_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/img2img-init.jpg") control_image = load_image("https://huggingface.co/InstantX/SD3-Controlnet-Canny/resolve/main/canny.jpg") image = pipe( "a cat wearing sunglasses", image=init_image, control_image=control_image, strength=0.6, # 保留原图结构的程度 controlnet_conditioning_scale=0.8, num_inference_steps=28, guidance_scale=3.5, ).images[0]
strength控制重绘程度:值越大越偏离原图。该参数与control_image的 VAE 编码逻辑共同作用,实际效果建议在 0.4~0.8 之间调试。
六、进阶用法二:局部重绘(Inpainting)
FluxControlNetInpaintPipeline(pipeline_flux_controlnet_inpainting.py)把 ControlNet 条件与掩码重绘结合起来:掩码区域被重新生成,而 ControlNet 约束确保生成结果贴合指定的空间结构(如边缘、深度)。
import torch from diffusers import FluxControlNetInpaintPipeline, FluxControlNetModel from diffusers.utils import load_image controlnet = FluxControlNetModel.from_pretrained( "InstantX/FLUX.1-dev-controlnet-canny", torch_dtype=torch.bfloat16 ) pipe = FluxControlNetInpaintPipeline.from_pretrained( "black-forest-labs/FLUX.1-dev", controlnet=controlnet, torch_dtype=torch.bfloat16, ) pipe.to("cuda") init_image = load_image(".../dog.png") mask_image = load_image(".../dog_mask.png") control_image = load_image(".../canny.png") image = pipe( "a majestic lion", image=init_image, mask_image=mask_image, control_image=control_image, strength=0.8, guidance_scale=3.5, controlnet_conditioning_scale=1.0, num_inference_steps=28, ).images[0]掩码(白色区域为待重绘)会通过 VAE 编码进 latent 空间,并与控制条件一起在去噪循环中发挥作用;两种进阶管线共享本文第一节的所有 ControlNet 参数语义,可平滑迁移。
七、进阶用法三:ControlNet-Union 与多条件组合
7.1 Union 模式
InstantX/FLUX.1-dev-Controlnet-Union允许单个 ControlNet 处理多种条件类型。通过control_mode指定条件类型(管线要求其为int或None,多 ControlNet 时传等长 list,见 pipeline_flux_controlnet.py):
image = pipe( prompt, control_image=control_image, control_mode=0, # 具体取值映射见 InstantX 权重仓库说明(如 Canny/Depth/…) controlnet_conditioning_scale=1.0, num_inference_steps=28, guidance_scale=3.5, ).images[0]control_mode会在进入多 ControlNet 前向时被展开为与条件图等长的张量(FluxMultiControlNetModel.forward中逐一对应,未指定时以-1兜底,见 controlnet_flux.py)。
7.2 多 ControlNet 组合(以 Canny + Depth 为例)
controlnet_canny = FluxControlNetModel.from_pretrained( "InstantX/FLUX.1-dev-controlnet-canny", torch_dtype=torch.bfloat16 ) controlnet_depth = FluxControlNetModel.from_pretrained( "Shakker-Labs/FLUX.1-dev-ControlNet-Depth", torch_dtype=torch.bfloat16 ) pipe = FluxControlNetPipeline.from_pretrained( "black-forest-labs/FLUX.1-dev", controlnet=[controlnet_canny, controlnet_depth], # list 自动包装为 FluxMultiControlNetModel torch_dtype=torch.bfloat16, ) pipe.to("cuda") image = pipe( prompt, control_image=[canny_image, depth_image], controlnet_conditioning_scale=[0.8, 0.7], # 逐 ControlNet 权重 control_guidance_start=[0.0, 0.1], control_guidance_end=[1.0, 0.9], num_inference_steps=28, guidance_scale=3.5, ).images[0]八、进阶用法四:与 LoRA 协同使用
FluxControlNetPipeline同时继承FluxLoraLoaderMixin、FromSingleFileMixin与FluxIPAdapterMixin(pipeline_flux_controlnet.py),因此可:
- 通过
pipe.load_lora_weights(...)加载风格/概念 LoRA,并通过joint_attention_kwargs={"scale": 0.8}调节 LoRA 强度(源码中encode_prompt会根据lora_scale动态scale_lora_layers/unscale_lora_layers); - 通过
pipe.load_ip_adapter(...)与ip_adapter_image参数叠加 IP-Adapter 图像语义引导,实现"文本 + 条件图 + 参考图"三重控制; - 通过
FromSingleFileMixin从单文件权重加载整条管线。
提示:
_callback_tensor_inputs = ["latents", "prompt_embeds", "control_image"],这意味着步进回调(callback_on_step_end)可拿到这三个张量,用于中间过程可视化或提前中断(pipe.interrupt)。
九、参考实现与自测
仓库为上述能力提供了完整的单测覆盖,可作为行为基准(torch_dtype建议按 CI 环境使用torch.bfloat16):
- 文本到图像: tests/pipelines/controlnet_flux/test_controlnet_flux.py
- 图像到图像: tests/pipelines/controlnet_flux/test_controlnet_flux_img2img.py
- 局部重绘: tests/pipelines/controlnet_flux/test_controlnet_flux_inpaint.py
测试中覆盖了control_guidance_start/end边界、多 ControlNet 传参、FluxPipelineOutput结构等关键行为,阅读这些用例可帮助你校准自己的调用参数。若需要为 Flux.1 训练自定义 ControlNet,仓库还提供了官方训练脚本 examples/flux-control/train_control_flux.py(支持--dataset_name/--jsonl_for_train数据源、--image_column/--conditioning_image_column列名配置、--resolution(需能被 8 整除,以保证 VAE 与 Transformer 尺寸一致)、--validation_prompt/--validation_image周期验证等参数),以及 LoRA 版 train_control_lora_flux.py。
十、常见问题与调参建议
- 生成尺寸异常:
height/width必须能被 16 整除,否则check_inputs会告警并自动调整尺寸(pipeline_flux_controlnet.py)。 - 条件约束过强/过弱:优先调节
controlnet_conditioning_scale(0.5~1.0 常见区间);当希望"先自由生成、后受约束"时,使用control_guidance_start=0.2, control_guidance_end=0.8这类区间化设置。 - InstantX 与 XLabs 权重混用:两者架构分支不同(
input_hint_block有无),混用可能导致维度不匹配,请保持同一团队权重。 - 内存占用过高:可启用 CPU offload(
pipe.enable_model_cpu_offload(),类属性model_cpu_offload_seq = "text_encoder->text_encoder_2->image_encoder->transformer->vae"已按依赖顺序定义)或enable_sequential_cpu_offload()。 - 关于调度器:管线默认使用
FlowMatchEulerDiscreteScheduler,可参考 Schedulers 指南 在速度与质量之间权衡;若需在多条管线间复用 VAE/文本编码器等组件,参见 Reusing models across pipelines。
小结
FluxControlNetPipeline及其衍生管线(Img2Img、Inpaint)把 ControlNet 的空间条件控制能力完整地带到了 Flux.1 生态:通过 controlnet_flux.py 中的FluxControlNetModel/FluxMultiControlNetModel与管线侧 pipeline_flux_controlnet.py 的打包-去噪-解包流程,配合 InstantX / XLabs 的预训练权重,即可用 Canny 边缘、深度图、Union 多模式乃至多个 ControlNet 的组合,精确约束 Flux.1 的图像生成结果,同时保留 LoRA、IP-Adapter 等生态能力的叠加空间。
【免费下载链接】diffusers🤗 Diffusers: State-of-the-art diffusion models for image, video, and audio generation in PyTorch.项目地址: https://gitcode.com/GitHub_Trending/di/diffusers
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考