精确控制与视频生成:LoRA、ControlNet 与 IP-Adapter
想让 AI 画出特定姿势、特定风格的人物图,写一满屏 prompt 还是不对------问题不在模型,在你没用 LoRA + ControlNet + IP-Adapter 这三把精确控制刀。单独用 prompt 像跟画师口头描述,用这三把刀等于给画师发参考照片 + 线稿 + 色卡。这篇文章把噪声预测 vs 遮罩预测的二分法讲透,再教你三刀叠加工作流------LoRA 定角色 + ControlNet 定姿势 + IP-Adapter 定风格。
阅读约 20 分钟 | 系列第 6/14 篇
⚠️ 时效性提示:本文基于 2026 年 7 月的技术状态撰写。大模型版本迭代迅速(通常 3-6 个月一次大版本更新),文中涉及的模型名称、API 端点及性能基准数据请以各厂商最新公告为准。建议重点关注文章中的架构原理与设计决策------这些内容具有更长的时效性。
一、噪声与遮罩:扩散模型的两个基础概念
在进入图生图、Inpainting 等具体操作之前,需要先理解两个贯穿全部图片编辑任务的基础概念。
1.1 噪声的本质
噪声是一张与目标图片尺寸相同的纯随机像素矩阵,其中每个像素的值独立随机生成,不携带任何有意义的信息。
arduino
真实图片(256×256) 纯噪声(256×256)
┌────────────────────┐ ┌────────────────────┐
│ 🐱 清晰可见的猫 │ │ ░▒▓█▒░▓▒▓█░▓▒▓█ │
│ 具有毛发、眼睛、轮廓 │ │ 这是"噪声"(无意义)│
│ 这是"信号"(有意义) │ │ │
└────────────────────┘ └────────────────────┘
扩散模型采用噪声作为起点的原因:噪声不包含任何预设信息,因此可以向任意方向演化。从同一份随机噪声出发,配合不同的 Prompt 条件,可以生成内容完全不同的图片。
1.2 噪声对图片的影响
扩散模型的训练过程即为学习在不同噪声强度下如何恢复原始信号。噪声比例直接决定了图像的可见程度:
erlang
原图 加 25% 噪声 加 50% 噪声 加 100% 噪声
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
│ 🐱 清晰 │ → │ 🐱 轻微模糊 │ → │ 🐱 严重模糊 │ → │ ░▒▓ 纯噪声 │
│ 细节完整 │ │ 细节开始丢失 │ │ 仅剩轮廓 │ │ 完全不可辨认 │
└────────────┘ └────────────┘ └────────────┘ └────────────┘
噪声 0% --- 原图,全部细节完好
噪声 30% --- 轮廓与颜色仍可辨识,细节已丢失
噪声 50% --- 仅能分辨大致的形状与色彩分布
噪声 100% --- 完全随机的像素,不含任何原始图像信息
图生图中的 strength 参数即对应"加噪比例":strength=0 不加噪声,图片不变;strength=1 加 100% 噪声,等价于文生图。
1.3 遮罩的本质
遮罩是一张与原图尺寸相同的黑白二值图像,其每个像素的值仅表达一个含义:该位置是否允许模型修改。
markdown
原图 遮罩
┌──────────────────────┐ ┌──────────────────────┐
│ 👤围观 🧑主角 👤围观│ │████████ ████████ │
│ 👤围观 🧑主角 👤围观│ │████████ ████████ │
└──────────────────────┘ └──────────────────────┘
白色 = 允许模型自由修改
黑色 = 保持原始像素不变
1.4 遮罩与噪声在 Inpainting 中的协作机制
Inpainting 的每一步去噪过程,都是遮罩引导下的"局部生成 + 全局锁定"操作:
markdown
去噪步骤 i:
1. UNet 对整张图执行去噪预测(遮罩内外区域均参与计算)
2. 将遮罩外部区域强制覆盖为原图像素------"锁定不变区域"
3. 遮罩内部保留去噪结果------"AI 在此区域内自由生成"
4. 进入下一步迭代
每一步均需执行覆盖操作的原因:
→ UNet 的卷积运算会导致遮罩内外区域的信息相互渗透
→ 必须在每一步去噪后将遮罩外部重置为原图
噪声决定"修改的程度",遮罩决定"修改的位置"。 两者的组合构成了所有图片编辑任务的参数空间。
二、图片编辑的四种模式
核心认知:图生图、Inpainting、超分、Outpainting 并非四种独立技术------它们是同一套扩散管线,仅通过改变"起点噪声比例"和"遮罩范围"切换为不同模式。
erlang
起点噪声比例 遮罩约束 本质操作
─────────── ──────── ────────
文生图 100% 无(整图参与生成) 从零创造
图生图 30%~80% 无(整图参与生成) 基于原图重新演绎
Inpainting 遮罩内 100% 遮罩外锁定为原图 局部重新生成
超分 10%~20% 无(整图微调) 细节增强
2.1 图生图 --- 以 strength 控制修改幅度
python
from diffusers import AutoPipelineForImage2Image
import torch
from PIL import Image
pipe = AutoPipelineForImage2Image.from_pretrained(
"black-forest-labs/FLUX.1-schnell", torch_dtype=torch.bfloat16
).to("cuda")
original = Image.open("my_photo.jpg")
result = pipe(
prompt="水彩画风格,柔和的色彩",
image=original,
strength=0.5, # 0=保持原图,1=完全重新生成
num_inference_steps=4,
).images[0]
strength 参数效果对照:
ini
strength = 0.1 几乎无可见变化,仅轻微调整色调与纹理
strength = 0.3 保留原图构图与结构,风格开始转变
strength = 0.5 可辨认原图内容,但风格显著变化(如照片 → 水彩画)
strength = 0.8 仅保留大致空间布局,内容变化幅度大
strength = 1.0 等价于文生图,与原始图像再无关联
2.2 Inpainting --- 以遮罩控制修改位置
python
from diffusers import AutoPipelineForInpainting
pipe = AutoPipelineForInpainting.from_pretrained(
"black-forest-labs/FLUX.1-schnell", torch_dtype=torch.bfloat16
).to("cuda")
original = Image.open("my_photo.jpg")
mask = Image.open("mask.png") # 白色=允许修改,黑色=保持原样
result = pipe(
prompt="干净的街道背景,自然的建筑外墙",
image=original, mask_image=mask, num_inference_steps=4,
).images[0]
遮罩占比与成功率的关系:
erlang
遮罩占比 <10% 10~30% >30%
成功率 高 中等 低
原因分析 充足的周边上下文 模型勉强可推断 模型"虚构"的概率显著上升
两种技术路线的适用场景:
- 移除路人、保持真实感 → OpenCV 内容感知填充(利用原图纹理)
- 将门改为窗、创造全新内容 → 扩散模型 Inpainting(AI 生成新内容)(工具层面的具体操作见附录《AI 图片视频处理 --- 工具使用手册》§4.1)
2.3 超分辨率 --- CNN 方法与扩散方法的对比
方案 A:CNN 超分(不编造内容,推荐用于保真场景)
python
import cv2
sr = cv2.dnn_superres.DnnSuperResImpl_create()
sr.readModel("LapSRN_x4.pb")
sr.setModel("lapsrn", 4)
result = sr.upsample(cv_img)
| 模型 | 放大倍数 | 文件大小 | 速度 | 质量 |
|---|---|---|---|---|
| LapSRN | ×2,×4,×8 | 1.3MB | 快 | 良好 |
| EDSR | ×2,×3,×4 | 37MB | 慢 | 最优 |
| ESPCN | ×2,×3,×4 | 100KB | 最快 | 中等 |
| FSRCNN | ×2,×3,×4 | 40KB | 快 | 中等 |
方案 B:扩散超分(视觉效果更好,但可能引入虚构细节)
python
result = pipe_img2img(
prompt="high resolution, sharp details, 4K quality",
image=original, strength=0.15, # 极低值,仅在纹理层面增强
).images[0]
选择依据:要求"严格保真"(证件照)→ CNN 超分;追求"视觉观感"(风景照)→ 扩散超分。
2.4 Outpainting --- 扩展画面边界
Outpainting = Inpainting 变体:原图置于更大画布中央,四周空白区域作为遮罩,由模型生成延续内容。
python
canvas = Image.new("RGB", (w * 2, h))
canvas.paste(original, (w // 2, 0)) # 原图居中
mask = Image.new("L", canvas.size, 255) # 全白=允许生成
mask.paste(Image.new("L", original.size, 0), (w // 2, 0)) # 原图位置锁定
result = pipe_inpaint(prompt="继续延伸该场景,保持一致风格与光照",
image=canvas, mask_image=mask).images[0]
三、精确控制:LoRA、ControlNet 与 IP-Adapter
三者的定位:Prompt 是"建议"(语义层面),LoRA 是"训练"(身份/风格层面),ControlNet 是"命令"(空间/结构层面),IP-Adapter 是"参考"(图像风格层面)。
| 技术 | 控制维度 | 功能类比 | 文件大小 |
|---|---|---|---|
| LoRA | 内容 / 角色 / 风格 | "教会模型识别特定人物" | 10-200MB |
| ControlNet | 空间结构 / 构图 | "向模型提供构图骨架" | ~1.4GB |
| IP-Adapter | 图像风格 / 外观参考 | "以参考图的风格进行创作" | ~300MB |
3.1 LoRA --- 控制"外观特征"
LoRA 是一种轻量级权重补丁,使基础模型获得其原本不具备的特定概念。
arduino
无 LoRA:Prompt "一个女孩,动漫风格" → 每次生成的面部特征均不同
加载 LoRA:相同 Prompt + "角色A.safetensors" → 每次生成均保持角色一致
工作原理:
arduino
基础 FLUX 模型(约 12GB) LoRA 文件(约 20MB)
┌──────────────────────┐ ┌──────────────────────┐
│ 理解通用视觉概念 │ + │ 仅存储学习到的"偏差": │
│ 无法辨识特定角色 │ │ "该角色的眼型参数应 │
└──────────────────────┘ │ 向此方向偏移" │
└──────────────────────┘
↘ ↙
推理时融合计算 → 基础模型通用能力 + LoRA 特化知识
python
pipe.load_lora_weights("character_lora.safetensors")
pipe.fuse_lora()
image = pipe("my_character in a garden, cinematic lighting").images[0]
适用 :固定角色外貌、特定艺术风格、特定物体外观。不适用:精确控制构图中的空间位置。
3.2 ControlNet --- 控制"空间位置与姿态"
ControlNet 在标准扩散模型上附加一个可训练的控制分支,接收额外的条件输入(边缘检测图、深度图、骨骼姿态图等),使生成结果精确遵循指定的空间结构。
控制信号类型:
| 控制信号 | 输入格式 | 约束内容 | 典型应用 |
|---|---|---|---|
| Canny 边缘 | 线稿轮廓图 | 物体的边界与轮廓 | 将设计草图转换为照片级渲染 |
| Depth 深度 | 灰度深度图 | 物体的前后关系与体积感 | 保持场景的空间层次 |
| OpenPose 姿态 | 骨骼关节点图 | 人物的动作与姿势 | 指定人物的精确姿态 |
| Scribble 涂鸦 | 手绘色块 | 构图的色彩分区 | 快速原型构图 |
| Segmentation | 语义分割色块 | 各类物体的空间分布 | 精确控制场景布局 |
python
from diffusers import StableDiffusion3ControlNetPipeline, ControlNetModel
controlnet = ControlNetModel.from_pretrained(
"InstantX/SD3-Controlnet-Canny", torch_dtype=torch.float16
)
pipe = StableDiffusion3ControlNetPipeline.from_pretrained(
"stabilityai/stable-diffusion-3.5-medium",
controlnet=controlnet, torch_dtype=torch.float16
).to("cuda")
# 从参考图提取边缘作为控制图
edges = cv2.Canny(cv2.cvtColor(np.array(original), cv2.COLOR_RGB2BGR), 100, 200)
control_image = Image.fromarray(edges)
result = pipe(
prompt="a cat sitting on the sofa, photorealistic",
control_image=control_image,
controlnet_conditioning_scale=0.8, # 0=不约束,1=严格遵循
).images[0]
3.3 IP-Adapter --- 以图像替代文字 Prompt
IP-Adapter(Image Prompt Adapter)通过将参考图像的 CLIP 视觉特征注入去噪过程,实现"以图控图"。
arduino
ControlNet 约束的是"结构"(线条走向、深度层次、关节位置)
IP-Adapter 约束的是"风格与质感"(色调氛围、纹理特征、物体类别)
3.4 LoRA 与 ControlNet 的边界辨析
这是最容易混淆的一对概念,区别在于约束的维度不同:
arduino
LoRA 回答的问题: "该物体长什么样?"(身份标识 / 外观特征)
ControlNet 回答的问题: "该物体在画面中的哪里、呈现何种姿态?"(空间坐标 / 几何结构)
以角色生成为例:
LoRA → 眼睛为杏仁形,虹膜深褐色("外观配方")
ControlNet → 眼部中心位于像素 (256,180),左右眼间距 64px("空间坐标")
决策标准:
问题一:"关注的核心是该物体的外观特征吗?" → 是 → LoRA 问题二:"关注的核心是该物体的空间位置与姿态吗?" → 是 → ControlNet 两者均关注 → 联合使用。
3.5 三者的协同使用
makefile
任务:生成特定角色,在樱花树下,以指定姿势站立,并参考某张图片的色调氛围
Prompt: "樱花树下,花瓣飘落" ← 语义场景描述
LoRA: 角色A.safetensors ← 固定角色外貌特征
ControlNet: OpenPose 骨骼关键点图 ← 指定身体姿态
IP-Adapter: 色调参考图 ← 引导色彩与氛围
→ 模型同时接收四种条件信号,综合生成最终图像
四、视频生成
4.1 视频生成的本质:在图片扩散模型上增加时间维度
markdown
图片生成:噪声 → 去噪循环 × N → 一张静态图像
视频生成:噪声 → 去噪循环 × N → 帧₁
→ 去噪循环 × N → 帧₂(须与帧₁ 保持物体位置与外观一致)
...
→ 去噪循环 × N → 帧₁₂₀(10 秒 × 24fps)
核心机制:每一帧的去噪过程均能访问相邻帧的信息
视频模型在图片模型架构的基础上加入了时间维度的建模:
- 3D VAE:将 2D 卷积核升级为 3D 卷积核(空间 H×W + 时间 T),同时对多帧执行压缩和重建
- 时空注意力(Spatio-Temporal Attention):在 Self-Attention 之后增加 Temporal Attention,使每一帧在去噪时能看到相邻帧的状态
- RoPE 三维位置编码:扩展到三维(空间 X/Y + 时间 T),区分"同一帧内的相邻像素"和"相邻帧的同一位置"
深入理解:一次去噪步骤中发生了什么
以生成一段 T=16 帧、每帧 256×256 的视频为例,单步去噪的完整流程如下:
scss
输入:噪声潜变量 z_t,形状 (16, 32, 32, 4)
↑ 帧数 ↑ H/8 ↑ W/8 ↑ VAE通道数
(图片生成为 (1, 32, 32, 4),视频多了 16 倍帧维度)
步骤1 --- Spatial Self-Attention(帧内自注意力):
┌─────────────────────────────────────────────────────┐
│ 对每一帧独立执行: │
│ 帧 1 内,像素 (x₁,y₁) 关注同帧 (x₂,y₂) 的像素 │
│ 帧 2 内,像素 (x₁,y₁) 关注同帧 (x₂,y₂) 的像素 │
│ ... │
│ 帧 16 内,像素 (x₁,y₁) 关注同帧 (x₂,y₂) 的像素 │
│ │
│ → 输出:每一帧内部的空间关系已经理清 │
│ (猫的耳朵和眼睛在同一帧内建立了关联) │
└─────────────────────────────────────────────────────┘
↓
步骤2 --- Temporal Attention(跨帧时序注意力):
┌─────────────────────────────────────────────────────┐
│ 对每一个空间位置 (x,y),沿时间轴执行: │
│ 位置 (x₁,y₁) 处,帧 1 关注帧 2,3,...,16 的同一位置 │
│ 位置 (x₁,y₁) 处,帧 2 关注帧 1,3,...,16 的同一位置 │
│ ... │
│ │
│ → 输出:同一物体在不同帧之间建立了运动关联 │
│ (猫的耳朵在帧1到帧16之间平滑移动的轨迹被捕捉) │
└─────────────────────────────────────────────────────┘
↓
步骤3 --- Cross-Attention(文本条件注入):
与图片模型相同,从 Text Encoder 的 embedding 中提取语义引导
步骤4 --- Feed-Forward(逐位置非线性变换):
与标准 Transformer 一致
为什么需要 3D VAE?
scss
2D VAE(图片模型): 3D VAE(视频模型):
┌──────────────────────┐ ┌──────────────────────────────┐
│ 输入:1 × 256 × 256 × 3 │ 输入:16 × 256 × 256 × 3 │
│ 压缩空间:↓8× → 32 × 32 × 4 │ 压缩空间:↓8× → 32 × 32 × 4 │
│ │ 压缩时间:↓4× → 4 × 32 × 32 × 4│
│ 最终潜变量: │ 最终潜变量: │
│ (1, 32, 32, 4) │ (4, 32, 32, 4) │
│ 总元素:4,096 │ 总元素:16,384(4× 于图片) │
└──────────────────────┘ └──────────────────────────────┘
3D VAE 的卷积核形状:3×3×3(空间 3×3 + 时间深度 3)
→ 一次卷积同时跨越相邻 3 帧,将"帧间冗余信息"压缩掉
→ 相邻帧中静止的背景区域被大幅压缩,仅保留变化部分
架构对比:图片模型 vs 视频模型
scss
图片模型(以 FLUX 为例):
┌──────────┐ ┌─────────────────────────────────────────┐ ┌──────┐
│ Text │ │ Noise(1×H×W×C) × N 步去噪 │ │ VAE │
│ Encoder │────→│ ↓ ┌─ Self-Attn │────→│Decode│──→ 图片
│ (T5) │ │ DiT Block ───→ │ Cross-Attn │ │ │
└──────────┘ │ ↑ └─ Feed-Forward │ └──────┘
│ denoised │
└─────────────────────────────────────────┘
视频模型(以 Wan/CogVideoX 为例):
┌──────────┐ ┌──────────────────────────────────────────────────┐ ┌───────┐
│ Text │ │ Noise(T×H×W×C) × N 步去噪 │ │ 3D │
│ Encoder │────→│ ↓ ┌─ Spatial Self-Attn (帧内) │────→│ VAE │──→ 视频
│ (T5) │ │ DiT Block ───→ │ Temporal Attn (跨帧) │ │Decode │ (T帧)
└──────────┘ │ ↑ │ Cross-Attn │ └───────┘
│ denoised └─ Feed-Forward │
└──────────────────────────────────────────────────┘
关键差异:
• 输入维度从 4D (B, C, H, W) 升级为 5D (B, T, C, H, W)
• 每个 DiT Block 中多了一个 Temporal Attention 层
• VAE 从 2D 卷积升级为 3D 卷积,解码器直接输出多帧
4.2 为什么视频生成如此消耗显存?
从 §4.1 的去噪流程可以看出,视频模型处理的不是一个图像(H×W),而是一个帧序列(T×H×W)。计算量的增长远超直觉:
- Self-Attention 的空间维度:每个 Token(像素块)需要与所有 Token 计算注意力权重。图片的 Token 数是 O(H×W),视频的 Token 数膨胀为 O(T×H×W),而 Self-Attention 的复杂度是 Token 数的平方------即从 O((H×W)²) 跳变到 O((T×H×W)²)。
- Temporal Attention 的额外开销:每帧的每个空间位置需要沿时间轴计算注意力,引入额外的 O(T²) 复杂度。
- 具体数字 :81 帧 512×512 视频的注意力计算量 = 81²/1² × 512²/256² ≈ 6561 × 4 ≈ 图片的 6500 倍以上。
这是视频生成模型的根本瓶颈,也解释了为什么当前开源的视频生成模型通常只支持几秒到几十秒的输出。
显存开销与优化策略
视频模型的显存需求远高于图片模型的根本原因:
ini
图片模型(FLUX, 1 frame):
• 潜变量: 1 × 4096 × 64 = 262K tokens
• Self-Attn 复杂度:O(n²) ≈ 6.9 × 10¹⁰
• 典型显存:~16GB(bf16)
视频模型(Wan 2.7, 81 frames):
• 潜变量: 81 × 4096 × 64 = 21.2M tokens(81× 于图片)
• Self-Attn 复杂度:O(n²) ≈ 4.5 × 10¹⁴(6500× 于图片)
• 典型显存:~24GB(bf16,中等分辨率)
Temporal Attn 额外开销:
• 复杂度:O(T² × (H×W)) → 81² × 4096 ≈ 2.7 × 10⁷ 次交互
• 长视频(T=240)时此项成为主要瓶颈 → T²增长 → 57600×交互量
关键优化:时序分片 / 逐段处理
对于长视频(T > 模型最大帧数),采用时序分片策略:
markdown
策略:将长视频拆分为多个重叠的时间片段,逐段生成后拼接
片段 1 片段 2 片段 3
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 帧 1~81 │ + │ 帧 73~153│ + │ 帧 145~225│ → 拼接为 225 帧完整视频
└──────────┘ └──────────┘ └──────────┘
↑ 重叠 9 帧,保证过渡平滑
每段显存 ≈ 24GB(固定),总显存不再随 T 线性增长
代价:片段边界可能产生轻微的风格跳变,需后处理融合
一句话总结:视频生成 = 在图片扩散模型的每个 DiT Block 中插入一个 Temporal Attention 层,使各帧去噪时互相"看到"彼此,3D VAE 负责压缩时间冗余,时序分片负责控制显存。
4.3 主流视频生成模型
| 远程 API(推荐入门) | 本地部署(高显存需求) | |
|---|---|---|
| 顶级质量 | Runway Gen-4.5, Veo 3.1 | Wan 2.7(27B, Apache 2.0) |
| 中文生态 | Kling 3.0, Jimeng | Wan 2.7, CogVideoX |
| 性价比 | Kling 免费额度 | SANA-1.5 视频版(8GB VRAM) |
| 入门门槛 | 无硬件要求 | 8GB(SANA)→ 24GB+(Wan) |
视频本地部署的硬件门槛远高于图片任务。入门阶段建议先通过远程 API 了解各类模型的效果,再决定是否投入高显存硬件。
4.4 视频编辑的工程方法:逐帧处理
python
import cv2
from PIL import Image
import numpy as np
# 1. 拆帧
cap = cv2.VideoCapture("input.mp4")
frames = [frame for frame in iter(lambda: cap.read()[1] if cap.read()[0] else None, None)]
# 2. 逐帧应用图片处理管线(以风格迁移为例)
processed = []
for frame in frames:
pil_frame = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
result = pipe_img2img(prompt="油画风格", image=pil_frame, strength=0.4).images[0]
processed.append(cv2.cvtColor(np.array(result), cv2.COLOR_RGB2BGR))
# 3. 合成视频并合并原始音频
# ffmpeg -i output.mp4 -i input.mp4 -c:v copy -c:a aac -map 0:v:0 -map 1:a:0 final.mp4
核心要点回顾
- 噪声决定"改多少",遮罩决定"改哪里"------这两个参数的组合切换了文生图/图生图/Inpainting/超分四种模式
- strength 的调参范围:0.1(色调微调)→ 0.3(风格转变)→ 0.5(显著变化)→ 1.0(等价文生图)
- LoRA(外观配方)vs ControlNet(空间坐标)vs IP-Adapter(风格参考)------三个维度的精确切分是最核心的认知
- 决定标准:关注外观 → LoRA;关注空间位置 → ControlNet;关注风格参考 → IP-Adapter;都需要 → 组合
- 视频 = 图片 × 时间维度------3D VAE + 时空注意力 + RoPE 三维位置编码,不是简单的卷积升级
LoRA 微调角色一致性(几十 MB 小文件),ControlNet 精确控制姿势构图(线稿/骨骼/深度图),IP-Adapter 迁移风格保持统一(一张参考图)------三刀并用,AI 出图从"碰运气"变成"可控"。收藏这张三刀叠加工作流图,下次出图直接对照搭建。
上一篇 :《AI图片生成完全指南》 | 下一篇 :《大模型部署实战》 系列合集 :掘金AI合集