摘要:AI 图片/视频处理与文本 LLM 分属两种技术范式——前者处理"像素空间的生成与变换",后者处理"语义空间的推理与补全"。图片模型的额外复杂度不在于"是否本地部署",而在于推理过程是多个子模型协作的管线。


一、能力全景:AI 在图片与视频领域的边界

在深入技术细节之前,首先建立对 AI 图片/视频处理能力的整体认知——它能做什么、不能做什么,以及整个工具生态的分层结构。

1.1 六类图片任务

                    输入             输出              典型用例
─────────────────────────────────────────────────────────────
① 文生图         一段文字          一张新图           根据描述生成全新图片
② 图生图         一张图+文字      一张变体图          将照片转换为特定艺术风格
③ Inpainting     一张图+遮罩      一张修复图          移除画面中的多余物体
④ Outpainting    一张图          一张扩展图          扩展画幅,AI 补全新增区域
⑤ 超分放大       一张小图        一张大图            提升分辨率并保持清晰度
⑥ 图片理解       一张图          一段文字            识别内容、提取结构化信息

上述六类任务并非各自对应独立的模型——其底层共享同一套扩散模型架构,区别仅在于输入条件的不同组合。

1.2 视频任务

任务 输入 → 输出
文生视频 一段文字 → 一段数秒的视频
图生视频 一张静态图片 → 动态视频
视频编辑 一段视频 + 文字指令 → 修改后的视频
视频超分 低分辨率视频 → 高分辨率视频
风格迁移 视频 → 应用特定风格的视频

1.3 当前技术的局限性

  • ❌ 在保留真实背景的前提下移除大面积物体——生成式模型倾向于"虚构"背景内容,而非"延续"原有场景
  • ❌ 生成图片中的文字准确渲染(2026 年刚取得突破,仍未完全解决)
  • ❌ 长视频(超过 2 分钟)中保持角色外貌和场景风格的一致性
  • ❌ 精确控制人体细节部位(手指数量、牙齿形态等常见问题)
  • ❌ 在消费级显卡(8GB VRAM)上进行 4K 视频生成

1.4 五层生态结构

AI 图片/视频处理的工具链可划分为五个层次。理解这一分层结构,有助于在接触新工具时快速定位其所属层级和依赖关系。

┌─────────────────────────────────────────────────────────┐
│  第 5 层:用户界面                                       │
│  ComfyUI(节点式编排) / WebUI(图形界面) / 代码直接调用   │
├─────────────────────────────────────────────────────────┤
│  第 4 层:模型权重(.safetensors / .ckpt 文件)           │
│  FLUX.1 / SD 3.5 / Qwen-Image / Wan 2.7 / Kling ...    │
│  训练得到的参数文件,日常所说的"下载模型"即指此层           │
├─────────────────────────────────────────────────────────┤
│  第 3 层:工具库(Python 包)                             │
│  diffusers / transformers / opencv / onnxruntime        │
│  将第 2 层的底层操作封装为高层 API                        │
├─────────────────────────────────────────────────────────┤
│  第 2 层:深度学习框架                                    │
│  PyTorch / ONNX Runtime / llama.cpp                     │
│  AI 模型的运行时环境,类比 Java 程序的 JVM                │
│                                                        │
│  注:文本 LLM 还有另一条技术路线——llama.cpp(C++ 实现),  │
│  完全不依赖 PyTorch,直接被 Ollama 用作底层推理引擎。      │
│  图片模型当前以 PyTorch + diffusers 路线为主,暂无          │
│  llama.cpp 等价物(多组件管线难以用统一 C++ 引擎覆盖)。    │
├─────────────────────────────────────────────────────────┤
│  第 1 层:硬件与驱动                                      │
│  NVIDIA GPU + CUDA + cuDNN                              │
│  AI 推理的物理计算基础;无 GPU 时可使用 CPU,但速度大幅下降  │
└─────────────────────────────────────────────────────────┘

关键认知

  • 日常所说的"下载一个模型",实际下载的是第 4 层的 .safetensors 权重文件。但该文件必须依赖第 1~3 层的完整环境才能运行——下面三层缺一不可。
  • Ollama 的特殊性:Ollama 的管理层(Go 编写)负责模型下载和 API 暴露,但其底层推理引擎是 llama.cpp(C++),这个引擎绕过了 PyTorch(第 2 层)直接从 CUDA/CPU 执行推理。因此 Ollama 在技术栈上跨越了第 2-3 层——它用一个统一的可执行文件同时替代了"框架+库"的角色。图片模型领域暂无此类统一工具。

1.5 远程 API 与本地部署

一个常见误解是"图片模型必须下载到本地才能使用"。实际上,图片模型与文本 LLM 一样,同时支持远程 API 调用和本地部署两种模式。

# 文本 LLM 的 API 调用(常见模式)
from openai import OpenAI
client = OpenAI(base_url="https://api.deepseek.com", api_key="sk-xxx")
response = client.chat.completions.create(model="deepseek-chat", messages=[...])

# 图片模型的 API 调用——调用方式完全一致,仅端点不同
from openai import OpenAI
client = OpenAI(base_url="https://api.siliconflow.cn/v1", api_key="sk-xxx")
response = client.images.generate(model="FLUX.1-dev", prompt="一只猫")
                    文本 LLM                        图片模型
                    ────────                       ────────
远程 API 模式      调用 DeepSeek API               调用 Kling / SiliconFlow API
                   → 模型运行在服务端                → 模型运行在服务端
                   → 本地无需 GPU                    → 本地无需 GPU
                   → 按 Token 量计费                → 按张数或时长计费

本地部署模式       Ollama + Qwen 本地运行           diffusers + FLUX 本地运行
                   → 模型权重下载至本地              → 模型权重下载至本地
                   → 免费,需配备 GPU               → 免费,需配备 GPU
                   → 单一模型推理                    → 多子模型管线(额外复杂度来源)

远程 API 与本地部署并非互斥选择,而是两种按需切换的使用模式。 学习阶段建议走本地路线以理解管线原理,生产环境则按场景灵活选择。


二、环境地基:五层生态的逐层解析

第一章介绍了五层生态结构的全貌,本章自底向上逐层展开——说明每层是什么、为什么需要、以及如何验证安装正确。

2.1 第 1 层:CUDA — AI 推理的物理引擎

Python 代码
    ↓
PyTorch("执行矩阵乘法运算")
    ↓
CUDA(将运算指令翻译为 GPU 可执行的指令)
    ↓
GPU 硬件(数千计算核心并行处理)
    ↓
返回计算结果
  • 无 NVIDIA 显卡 → 只能使用 CPU 进行推理 → 速度下降 10-100 倍,但推理仍可进行
  • 有 NVIDIA 显卡但未安装 CUDA → PyTorch 检测不到 GPU → 自动回退至 CPU 模式
  • CUDA 版本与 PyTorch 版本必须匹配——约 80% 的环境问题源于此二者版本不兼容

2.2 第 2 层:PyTorch — AI 模型的运行时

PyTorch 是所有 AI 模型推理的运行时环境。类比:运行 Java 程序需要 JVM,运行 AI 模型需要 PyTorch。

提供的两项核心能力:

  1. 张量运算:numpy 的 GPU 加速等价物(torch.Tensor 的数据模型与 numpy.ndarray 类似,但可在 GPU 上执行)
  2. 自动求导:训练阶段使用,推理阶段不需要,但模型的网络结构定义依赖此机制

Python 3.14 无法安装 PyTorch 的原因:PyTorch 底层包含大量 C++ 扩展代码(CUDA 交互层、算子优化等),需针对各 Python 版本分别编译。每当新的 Python 版本发布,PyTorch 团队需要适配周期。截至 2026 年 7 月,PyTorch 最高支持 Python 3.12。

2.3 第 3 层:工具库 — 各 Python 包的功能定位

PyTorch 仅提供"张量运算"这一基础能力,不具备"加载模型并完成推理全流程"的高层功能:

# 仅使用 PyTorch 时,需手动实现以下全部步骤:
# - 从二进制文件解析模型权重
# - 按模型结构逐层构建网络(通常需要数百行代码定义 UNet)
# - 实现去噪循环(Scheduler 的完整数学公式)
# - 处理 Tokenizer 与 Text Encoder 的调用逻辑
# - VAE 编解码的前后处理
# 工作量约 500+ 行代码,且更换模型后大部分代码需重写

# 使用 diffusers 之后的等价代码:
from diffusers import StableDiffusionPipeline
pipe = StableDiffusionPipeline.from_pretrained("model-name")
image = pipe("a cat").images[0]
# 3 行代码。diffusers 封装了上述所有步骤。
库名 功能定位 若不使用的后果
torch GPU 加速的张量计算(GPU 上的 numpy) 无法运行任何 AI 模型
diffusers 扩散模型的管线工厂,一行加载、一行推理 需手写数百行管线编排代码
transformers 文本编码器的管线工厂(diffusers 内部依赖其处理 Prompt) diffusers 会自动安装
opencv-contrib-python-headless 经典图像处理操作 + DNN 模型推理 + 超分辨率模块 图片读写、缩放、超分均需另寻替代方案
accelerate 自动处理模型在 GPU/多 GPU/混合精度上的分配 需手动编写 .to("cuda") 及显存管理逻辑
Pillow (PIL) Python 最基础的图片读写库 无法打开/保存 jpg、png 等常见格式(同时也是 OpenCV 中文路径问题的兼容方案)
huggingface_hub 从 HuggingFace 自动下载模型权重文件 需手动从网页下载并指定本地路径
onnxruntime 使模型脱离 PyTorch 环境独立运行(用于部署场景) 部署包体积大、冷启动慢
ultralytics YOLO 目标检测(“识别图中所有人物位置”) 无法执行目标检测任务
xformers 优化注意力机制的计算效率与显存占用(可节省约 30% 显存) 同等条件下显存更紧张、推理更慢
bitsandbytes 8-bit/4-bit 量化推理(“将 23GB 模型压缩至 6GB”) 大模型在消费级显卡上无法运行

2.4 第 4 层:模型权重 — .safetensors 文件的本质

# FLUX.1-dev 模型权重文件的目录结构
~/.cache/huggingface/hub/models--black-forest-labs--FLUX.1-dev/
├── model_index.json       # 记录该模型由哪些子模型组件构成
├── transformer/           # 主去噪网络(UNet/DiT)的权重 → 约 23GB
│   └── diffusion_pytorch_model.safetensors
├── vae/                   # VAE 编解码器的权重 → 约 300MB
│   └── diffusion_pytorch_model.safetensors
├── text_encoder/          # 文本编码器的权重 → 约 3GB
│   └── model.safetensors
└── scheduler/             # 去噪策略的配置(不包含权重,仅数学公式参数)
    └── scheduler_config.json

模型权重文件不包含原始训练数据

常见误解:下载 FLUX 模型(约 12GB)= 下载了模型训练时见过的所有图片的压缩版本
实际情况:原始训练数据(数十亿张图片,PB 级存储规模)
          → 经过训练过程(GPU 集群运行数周至数月)
          → 产出一个 .safetensors 权重文件(约 12GB 的浮点参数矩阵)

类比:一个人看过几千张猫的照片后,大脑中并未存储这几千张照片的副本,
      但形成了"猫的外貌特征"这一抽象认知。
模型权重 = 训练得到的"识别与生成规律",而非训练数据的压缩存储。

远程 API 服务与本地部署使用的是同一份模型权重。 区别仅在于由谁的硬件执行推理——API 服务商的 A100 数据中心 GPU,还是个人的 RTX 4060 消费级显卡。在硬件性能相当的前提下,推理结果的质量没有差异。

2.5 第 5 层:用户界面 — 两种交互范式

范式 适用场景 代表工具
编程接口 开发者、需要自动化或批量处理的场景 diffusers Python API
可视化编排 设计师、不编写代码、需要直观调节参数 ComfyUI

ComfyUI 的底层同样调用 diffusers 库。用户在 ComfyUI 中通过拖拽节点构建的工作流,本质上等价于一段 diffusers Python 脚本。掌握 diffusers 编程接口后,ComfyUI 只是换了一种交互方式,核心概念完全相通。

2.6 环境安装验证清单

以下 4 条验证命令全部通过,即说明环境已就绪:

# ① GPU 是否可用
python -c "import torch; print(torch.cuda.is_available())"  # → True

# ② CUDA 版本与 PyTorch 版本是否匹配(示例为 CUDA 12.1)
python -c "import torch; print(torch.version.cuda)"          # → 12.1
python -c "import torch; print(torch.__version__)"           # → 2.x.x+cu121

# ③ diffusers 能否正常加载模型管线
python -c "from diffusers import DiffusionPipeline; print('OK')"  # → OK

# ④ OpenCV 扩展模块(dnn_superres)是否完整安装
python -c "import cv2; cv2.dnn_superres.DnnSuperResImpl_create()"  # → 对象创建成功

三、第一张 AI 图片:从代码到理解

环境就绪后,本章从一段最小可运行代码开始——先看到结果,再逐层拆解"一行推理调用背后发生了什么",最终建立对扩散模型推理全流程的结构化认知。

3.1 最小可运行示例

from diffusers import AutoPipelineForText2Image
import torch

# ① 加载管线(首次运行自动下载 FLUX.1-schnell 模型,约 12GB)
pipe = AutoPipelineForText2Image.from_pretrained(
    "black-forest-labs/FLUX.1-schnell",
    torch_dtype=torch.bfloat16
)
pipe.to("cuda")

# ② 生成图片
image = pipe(
    prompt="一只橘猫戴着墨镜坐在沙滩上,日落光线,电影质感"
).images[0]

# ③ 保存结果
image.save("my_first_ai_image.jpg")

以上共 6 行有效代码,运行后在当前目录生成一张 AI 创作的图片。

3.2 一行推理调用背后的六个步骤

pipe(prompt) 这一行看似简洁的调用,内部实际顺序执行了六个步骤:

用户输入:prompt="一只橘猫戴着墨镜坐在沙滩上..."
    │
    ▼
┌─────────────────────────────────────────────────────────┐
│ 步骤①:Text Encoder(文本编码)                           │
│                                                         │
│  "一只橘猫戴着墨镜..." → CLIP / T5 模型 → 语义向量        │
│  将自然语言描述转换为模型可理解的数值条件信号               │
│  输出:4096 维(FLUX)向量,编码了"猫""墨镜""沙滩"等语义   │
├─────────────────────────────────────────────────────────┤
│ 步骤②:随机种子 → 初始噪声                               │
│                                                         │
│  torch.randn(...) → 在潜空间中生成纯随机噪声              │
│  512×512 像素空间经 VAE 压缩后,在潜空间仅 64×64          │
│  随机种子固定 → 噪声固定 → 生成结果可精确复现              │
├─────────────────────────────────────────────────────────┤
│ 步骤③:Scheduler 初始化                                  │
│                                                         │
│  设定去噪总步数,建立从纯噪声到清晰图像的"调度计划"         │
│  FLUX-schnell(蒸馏版):仅需 1-4 步                      │
│  SD 3.5(标准版):需要 20-50 步                          │
├─────────────────────────────────────────────────────────┤
│ 步骤④:去噪循环(推理核心)——重复 N 次                    │
│                                                         │
│  循环每一步:当前噪声图 + 文本条件向量 + 当前步数编号        │
│  → UNet/DiT 预测"哪些像素更可能是噪声"                    │
│  → Scheduler 按预定公式减去预测的噪声分量                  │
│  → 噪声略微减少,图像略微清晰,进入下一步                  │
│                                                         │
│  第 1 步:完全随机噪点,无任何可辨识内容                    │
│  第 5 步:隐约出现色块轮廓(沙滩、天空区域)                │
│  第 10 步:可辨识出猫的色块与墨镜的大致形状                │
│  第 20 步:细节全部完成——毛发纹理、镜片反光、日落光感      │
├─────────────────────────────────────────────────────────┤
│ 步骤⑤:VAE Decoder(潜空间 → 像素空间)                   │
│                                                         │
│  潜空间中 64×64×16 的张量 → VAE 解码 → 512×512×3 像素图  │
│  类比:解压压缩文件——潜空间是压缩态,像素空间是展开态        │
├─────────────────────────────────────────────────────────┤
│ 步骤⑥:后处理                                           │
│                                                         │
│  值域裁剪([-1,1] → [0,255])→ 转换为 PIL Image 对象     │
│  → 返回给调用方                                         │
└─────────────────────────────────────────────────────────┘

3.3 四个核心组件的职责

组件 在管线中的职责 类比
Text Encoder 将 Prompt 自然语言编码为语义条件向量——告知模型"用户期望的内容" 需求翻译
UNet / DiT 去噪循环的核心计算模块——从含噪图像中预测噪声分量 雕刻:每次削去多余的石料
Scheduler 控制去噪的节奏——每一步移除多少噪声、采用何种算法策略 节拍器:控制每次下刀的深度与频率
VAE 像素空间与潜空间的双向转换——压缩与还原 打包与拆包:运输时压缩,使用前恢复

3.3.1 UNet 与 DiT:两代去噪架构

§3.3 的组件表中将去噪网络写为"UNet / DiT",两者是代际关系——当前旗舰模型(FLUX、SD 3.5)已全面转向 DiT。

UNet(2015-2023,以 Stable Diffusion 1.x/2.x/XL 为代表)

UNet 结构(概念示意):

  输入(含噪潜空间 64×64×4)
      ↓
  ┌──────────────────────┐
  │  卷积下采样(Encoder) │  ← 逐步压缩空间分辨率,提取特征
  │  64→32→16→8          │    基于 CNN 卷积层
  │      ↓ 跳跃连接       │    跳跃连接 = 将 Encoder 的中间结果
  │  8→16→32→64          │    直接传给 Decoder 同分辨率层
  │  卷积上采样(Decoder) │  ← 逐步恢复空间分辨率
  └──────────────────────┘
      ↓
  输出(噪声预测 64×64×4)

  文本条件注入方式:Cross-Attention(在特定层将文本向量与图像特征做交叉注意力)

DiT(Diffusion Transformer,2024-至今,以 FLUX/SD 3.5/SORA 为代表)

DiT 结构(概念示意):

  输入(含噪潜空间 latent)
      ↓
  ┌──────────────────────────────┐
  │  Patch Embedding             │  ← 将图像切成小块(类似 ViT),每块映射为 token
  │  (例如 2×2 像素 → 1 个 token)│
  ├──────────────────────────────┤
  │  Transformer Block × N 层    │  ← 无需卷积,纯 Transformer
  │  ┌────────────────────────┐  │
  │  │ Adaptive Layer Norm     │  │  ← 时间步条件注入:每个去噪步的"进度条"信号
  │  │   ↓                    │  │
  │  │ Multi-Head Self-Attention│ │  ← 图像各 patch 之间互相注意
  │  │   ↓                    │  │
  │  │ MLP(Feed-Forward)     │  │
  │  └────────────────────────┘  │
  │  文本条件注入方式:             │
  │    AdaLN(自适应归一化)或      │  ← 文本向量直接调制 Transformer 的归一化参数
  │    Joint Attention(联合注意力)│  ← 文本 token 和图像 token 一起做注意力
  └──────────────────────────────┘
      ↓
  输出(噪声预测)

为什么 DiT 取代了 UNet

维度 UNet(CNN 卷积) DiT(Transformer)
扩展性 加深加宽收益递减 随参数量增大持续提升(Scaling Law)
高分辨率 卷积感受野有限,需多层下采样 Self-Attention 天然全局视野
文本对齐 Cross-Attention 仅作用于特定层 AdaLN 逐层注入,条件信号贯穿始终
训练效率 CNN 在小规模上收敛快 Transformer 在大规模数据上收敛更好
代表模型 SD 1.5 / SDXL FLUX.1 / SD 3.5 / SORA / Wan 2.7

与文本 LLM 的殊途同归:DiT 的思路是将图片生成问题重新表述为"序列到序列"的 Transformer 任务——图像被切分成 token 序列,去噪过程就是对 token 序列的逐步变换。这与文本 LLM 的"Token → Token 自回归"在数学形式上趋于统一。理解这一点后,你会发现图片模型和文本 LLM 的差异在缩小——它们共享同一个架构范式(Transformer),区别在于输入输出的模态和生成策略(逐步去噪 vs 自回归)。

3.4 参数含义与调节方向

image = pipe(
    prompt="...",                                      # 正向提示词:描述期望内容
    negative_prompt="blurry, low quality",             # 负向提示词:描述不希望出现的内容
    num_inference_steps=4,                             # 去噪步数:schnell 版用 1-4,标准版用 20-50
    guidance_scale=3.5,                                # 引导强度:越高越贴合 Prompt,但可能过饱和
    height=1024, width=1024,                           # 输出尺寸(必须是 64 的整数倍)
    generator=torch.Generator("cuda").manual_seed(42), # 固定随机种子以复现结果
).images[0]
参数 增大 减小 推荐值
num_inference_steps 质量↑、速度↓ 速度↑、质量↓ schnell: 4 / 标准: 20-30
guidance_scale 更紧密贴合 Prompt 给模型更大自由度 FLUX: 3.5 / SD: 7.5
height × width 细节丰富度提升 推理速度加快 调试: 512² / 成品: 1024²

关于 guidance_scale 的机制:该参数背后是 Classifier-Free Guidance(CFG,无分类器引导) 算法。每一步去噪时,模型实际上执行两次预测——一次带上 Prompt 条件(“一只猫”),一次不带条件(空文本),然后按公式合成最终预测:

最终噪声预测 = 无条件预测 + guidance_scale × (有条件预测 − 无条件预测)

直观理解:有条件预测 − 无条件预测 是"Prompt 给模型额外注入了多少信息"。guidance_scale 控制这个额外信息的放大倍数——越大越贴合 Prompt,但也可能放大伪影和过饱和。FLUX 推荐 3.5、SD 推荐 7.5 是经验值,与各自的训练策略和 Text Encoder 强度有关。

3.5 远程调用:三家国内服务商

除本地部署外,文生图同样可以通过远程 API 调用实现,调用方式与文本 LLM 的 API 一致,仅端点和模型名不同:

# SiliconFlow(OpenAI 兼容接口,可用模型种类最多)
from openai import OpenAI
client = OpenAI(base_url="https://api.siliconflow.cn/v1", api_key="sk-xxx")
resp = client.images.generate(model="black-forest-labs/FLUX.1-dev",
                               prompt="一只猫", size="1024x1024")

# 智谱(CogView-4,支持对话式迭代编辑)
from zhipuai import ZhipuAI
client = ZhipuAI(api_key="xxx")
resp = client.images.generations(model="cogview-4", prompt="一只猫")

# 阿里百炼(Qwen-Image,中文字符渲染能力领先,同时提供编辑 API)
import dashscope
from dashscope import ImageSynthesis
resp = ImageSynthesis.call(api_key="sk-xxx", model="qwen-image-max",
                            prompt="一只猫", n=1, size="1024*1024")
SiliconFlow 智谱 阿里百炼
主要模型 FLUX.1, SD 3.5, SANA CogView-4 Qwen-Image 2.0, 通义万相
接口风格 OpenAI 兼容 自有 SDK + OpenAI 兼容 自有 SDK + OpenAI 兼容
差异化优势 可用模型种类最多 对话式迭代编辑 + 成本最低 中文文字渲染 + 图片编辑 API

四、噪声与遮罩:扩散模型的两个基础概念

在进入图生图、Inpainting 等具体操作之前,需要先理解两个贯穿全部图片编辑任务的基础概念——噪声和遮罩。它们是扩散模型实现"可控生成"的核心机制。

4.1 噪声的本质

噪声是一张与目标图片尺寸相同的纯随机像素矩阵,其中每个像素的值独立随机生成,不携带任何有意义的信息。

真实图片(256×256)                    纯噪声(256×256)
┌────────────────────┐                 ┌────────────────────┐
│  🐱 清晰可见的猫     │                 │  ░▒▓█▒░▓▒▓█░▓▒▓█  │
│  具有毛发、眼睛、轮廓 │                 │  ▓▒░▓█▓▒░▓█▒░▓█░  │
│  这是"信号"(有意义) │                 │  这是"噪声"(无意义)│
└────────────────────┘                 └────────────────────┘

扩散模型采用噪声作为起点的原因:噪声不包含任何预设信息,因此可以向任意方向演化。从同一份随机噪声出发,配合不同的 Prompt 条件,可以生成内容完全不同的图片——噪声是最公平的初始状态。

4.2 噪声对图片的影响

扩散模型的训练过程即为学习在不同噪声强度下如何恢复原始信号。噪声比例直接决定了图像的可见程度:

原图                    加 25% 噪声            加 50% 噪声            加 100% 噪声
┌────────────┐          ┌────────────┐          ┌────────────┐          ┌────────────┐
│ 🐱 清晰     │   →      │ 🐱 轻微模糊  │   →      │ 🐱 严重模糊  │   →      │ ░▒▓ 纯噪声  │
│ 细节完整    │          │ 细节开始丢失 │          │ 仅剩轮廓    │          │ 完全不可辨认 │
└────────────┘          └────────────┘          └────────────┘          └────────────┘

噪声 0%  — 原图,全部细节完好
噪声 30% — 轮廓与颜色仍可辨识,细节已丢失
噪声 50% — 仅能分辨大致的形状与色彩分布
噪声 100% — 完全随机的像素,不含任何原始图像信息

图生图中的 strength 参数即对应"加噪比例":strength=0 不加噪声,图片不变;strength=1 加 100% 噪声,等价于文生图。

4.3 遮罩的本质

遮罩是一张与原图尺寸相同的黑白二值图像,其每个像素的值仅表达一个含义:该位置是否允许模型修改。

原图                                遮罩
┌──────────────────────┐          ┌──────────────────────┐
│                      │          │████████    ████████   │
│  👤围观  🧑主角  👤围观│          │████████    ████████   │
│                      │   →      │████████    ████████   │
│  👤围观  👧主角  👤围观│          │████████    ████████   │
│                      │          │████████    ████████   │
└──────────────────────┘          └──────────────────────┘
                                   白色 = 允许模型自由修改
                                   黑色 = 保持原始像素不变

4.4 遮罩与噪声在 Inpainting 中的协作机制

Inpainting 的每一步去噪过程,都是遮罩引导下的"局部生成 + 全局锁定"操作:

去噪步骤 i(总步数 20,当前为第 5 步):

1. UNet 对整张图执行去噪预测(遮罩内外区域均参与计算)
2. 将遮罩外部区域强制覆盖为原图像素——"锁定不变区域"
3. 遮罩内部保留去噪结果——"AI 在此区域内自由生成"
4. 进入下一步迭代

每一步均需执行覆盖操作的原因:
→ UNet 的卷积运算会导致遮罩内外区域的信息相互渗透
→ 必须在每一步去噪后将遮罩外部重置为原图,确保只有遮罩内部发生修改

类比:装修施工中的成品保护
  整张图 = 整个房间,遮罩内部 = 施工区域
  遮罩 = 覆盖在不动区域上的保护层
  每一步施工结束后检查保护层是否完整,确保无污损扩散至非施工区域

噪声决定"修改的程度",遮罩决定"修改的位置"。 两者的组合构成了所有图片编辑任务的参数空间。


五、图片编辑的四种模式:同一管线,不同参数组合

核心认知:图生图、Inpainting、超分、Outpainting 并非四种独立技术——它们是同一套扩散管线,仅通过改变"起点噪声比例"和"遮罩范围"切换为不同模式。

5.1 四种模式的本质差异

                起点噪声比例    遮罩约束              本质操作
                ───────────    ────────              ────────
文生图          100%           无(整图参与生成)     从零创造
图生图          30%~80%        无(整图参与生成)     基于原图重新演绎
Inpainting      遮罩内 100%    遮罩外锁定为原图       局部重新生成
                遮罩外 0%
超分            10%~20%        无(整图微调)         细节增强

5.2 图生图 — 以 strength 控制修改幅度

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 参数效果对照

strength = 0.1   几乎无可见变化,仅轻微调整色调与纹理
strength = 0.3   保留原图构图与结构,风格开始转变
strength = 0.5   可辨认原图内容,但风格发生显著变化(如照片 → 水彩画)
strength = 0.8   仅保留大致空间布局,内容变化幅度大
strength = 1.0   等价于文生图,与原始图像再无关联

5.3 Inpainting — 以遮罩控制修改位置

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]

遮罩占比与成功率的关系

遮罩占比    <10%              10~30%               >30%
成功率     高                 中等                 低
原因分析   模型有充足的周边    模型勉强可推断       模型"虚构"的概率显著上升
           上下文作为参考      周边上下文           (实际使用中已验证的局限)

两种技术路线的适用场景

  • 移除路人、保持原始场景真实感 → 内容感知填充(Photoshop / OpenCV inpaint),利用原图纹理
  • 将门改为窗、需要创造全新内容 → 扩散模型 Inpainting,由模型生成新内容

5.4 超分辨率 — CNN 方法与扩散方法的对比

方案 A:CNN 超分(不编造内容,推荐用于保真场景)

import cv2
from PIL import Image
import numpy as np

sr = cv2.dnn_superres.DnnSuperResImpl_create()
sr.readModel("LapSRN_x4.pb")
sr.setModel("lapsrn", 4)

pil_img = Image.open("small.jpg")
cv_img = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
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:扩散超分(视觉效果更好,但可能引入虚构细节)

result = pipe_img2img(
    prompt="high resolution, sharp details, 4K quality",
    image=original,
    strength=0.15,        # 极低值,仅在纹理层面增强,不改变内容
).images[0]

选择依据:要求"严格保真"(证件照、文档扫描件)→ CNN 超分;追求"视觉观感"(风景照、艺术作品)→ 扩散超分。

5.5 Outpainting — 扩展画面边界

Outpainting 的核心逻辑是 Inpainting 的变体:将原图放置于更大画布的中央,四周空白区域作为遮罩,由模型在遮罩区域生成与原图风格一致的延续内容。

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 是"参考"(图像风格层面)。

6.1 纯 Prompt 控制的局限性

使用 Prompt"一只猫在沙发上"时,模型在每次推理中可能生成不同的猫的品种、观察角度、动作姿态、沙发款式和光线条件——每次结果均不相同。Prompt 本质上是"语义意图",而非精确的空间或身份指令。

三种控制技术在各自维度上解决了这一问题:

技术 控制维度 功能类比 文件大小
LoRA 内容 / 角色 / 风格 “教会模型识别特定人物” 10-200MB
ControlNet 空间结构 / 构图 “向模型提供构图骨架” ~1.4GB
IP-Adapter 图像风格 / 外观参考 “以参考图的风格进行创作” ~300MB

6.2 LoRA — 控制"外观特征"

LoRA 是一种轻量级权重补丁,使基础模型获得其原本不具备的特定概念——特定角色、特定面部特征或特定艺术风格。

无 LoRA:Prompt "一个女孩,动漫风格" → 每次生成的面部特征均不同
加载 LoRA:相同 Prompt + "角色A.safetensors" → 每次生成均保持角色一致

工作原理

基础 FLUX 模型(约 12GB)              LoRA 文件(约 20MB)
┌──────────────────────┐              ┌──────────────────────┐
│ 理解通用视觉概念:     │      +       │ 仅存储学习到的"偏差": │
│ "猫""狗""沙发""日落"   │              │ "该角色的眼型参数应   │
│ 无法辨识你的特定角色   │              │  向此方向偏移"       │
└──────────────────────┘              └──────────────────────┘
              ↘                          ↙
              推理时融合计算 → 基础模型通用能力 + LoRA 特化知识
pipe.load_lora_weights("character_lora.safetensors")
pipe.fuse_lora()  # 将 LoRA 权重融入基础模型,加速后续推理

image = pipe("my_character in a garden, cinematic lighting").images[0]

LoRA 权重的获取途径

  • 从 civitai.com 或 huggingface.co 下载社区训练的 LoRA(.safetensors 格式,10-200MB)
  • 自行训练:准备 10-30 张同一角色或风格的图片,在 8GB VRAM 的消费级显卡上训练 30 分钟至数小时

适用:固定角色外貌、特定艺术风格、特定物体外观。不适用:精确控制构图中的空间位置。

6.3 ControlNet — 控制"空间位置与姿态"

ControlNet 在标准扩散模型上附加一个可训练的控制分支,接收额外的条件输入(边缘检测图、深度图、骨骼姿态图等),使生成结果精确遵循指定的空间结构。

控制信号类型

控制信号 输入格式 约束内容 典型应用
Canny 边缘 线稿轮廓图 物体的边界与轮廓 将设计草图转换为照片级渲染
Depth 深度 灰度深度图 物体的前后关系与体积感 保持场景的空间层次
OpenPose 姿态 骨骼关节点图 人物的动作与姿势 指定人物的精确姿态
Scribble 涂鸦 手绘色块 构图的色彩分区 快速原型构图
Segmentation 语义分割色块 各类物体的空间分布 精确控制场景布局
# 以 Canny 边缘检测为例
from diffusers import StableDiffusion3ControlNetPipeline, ControlNetModel
import cv2, numpy as np

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]

6.4 IP-Adapter — 以图像替代文字 Prompt

IP-Adapter(Image Prompt Adapter)通过将参考图像的 CLIP 视觉特征注入去噪过程,实现"以图控图"——用图片描述期望的风格与外观,而非仅依赖文字 Prompt。

ControlNet 约束的是"结构"(线条走向、深度层次、关节位置)
IP-Adapter 约束的是"风格与质感"(色调氛围、纹理特征、物体类别)

输入水墨画参考图 → 生成结果的色彩与笔触呈现水墨质感
输入人物照片     → 生成结果具有该人物的面部特征
pipe.load_ip_adapter("h94/IP-Adapter", subfolder="sd3",
                      weight_name="ip-adapter_sd3.safetensors")

result = pipe(
    prompt="a cat sitting on a windowsill",
    ip_adapter_image=reference,     # 参考图像
    ip_adapter_scale=0.6,           # 影响力:0=不使用参考图,1=完全遵循
).images[0]

6.5 LoRA 与 ControlNet 的边界辨析

这是最容易混淆的一对概念,区别在于约束的维度不同:

LoRA 回答的问题:         "该物体长什么样?"(身份标识 / 外观特征)
ControlNet 回答的问题:    "该物体在画面中的哪里、呈现何种姿态?"(空间坐标 / 几何结构)

以角色生成为例:
LoRA → 眼睛为杏仁形,虹膜深褐色,睫毛具有特定卷曲形态("外观配方")
ControlNet → 眼部中心位于像素 (256,180),左右眼间距 64px("空间坐标")

同一角色在不同角度下:
  正面 / 侧面 / 俯视 → LoRA 保证五官的形态特征一致
                     → ControlNet 指定头部朝向与画面位置

决策标准

问题一:“关注的核心是该物体的外观特征吗?” → 是 → LoRA
问题二:“关注的核心是该物体的空间位置与姿态吗?” → 是 → ControlNet
两者均关注 → 联合使用。

6.6 三者的协同使用

任务:生成特定角色,在樱花树下,以指定姿势站立,并参考某张图片的色调氛围

Prompt:     "樱花树下,花瓣飘落"          ← 语义场景描述
LoRA:       角色A.safetensors            ← 固定角色外貌特征
ControlNet: OpenPose 骨骼关键点图         ← 指定身体姿态
IP-Adapter: 色调参考图                   ← 引导色彩与氛围

→ 模型同时接收四种条件信号,综合生成最终图像

七、视频处理与工程落地

7.1 视频生成的本质:在图片扩散模型上增加时间维度

图片生成:噪声 → 去噪循环 × N → 一张静态图像

视频生成:噪声 → 去噪循环 × N → 帧₁
                → 去噪循环 × N → 帧₂(须与帧₁ 保持物体位置与外观一致)
                → 去噪循环 × N → 帧₃(须与帧₂ 保持光影与背景连贯)
                ...
                → 去噪循环 × N → 帧₁₂₀(10 秒 × 24fps)

核心机制:每一帧的去噪过程均能访问相邻帧的信息——"时序注意力机制"

为实现上述机制,视频模型在图片模型架构的基础上加入了时间维度的建模:

  • 3D VAE:将图片 VAE 的 2D 卷积核(空间 H×W)升级为 3D 卷积核(空间 H×W + 时间 T),同时对多帧执行压缩和重建,保证帧间视觉一致性
  • 时空注意力(Spatio-Temporal Attention):在标准 Self-Attention(空间维度上各 patch 相互注意)之后增加一层 Temporal Attention(时间维度上同一空间位置的 patch 跨帧相互注意),使每一帧在去噪时能看到相邻帧的状态
  • RoPE 三维位置编码:将文本 LLM 中使用的旋转位置编码(RoPE)扩展到三维(空间 X/Y + 时间 T),帮助模型区分"同一帧内的相邻像素"和"相邻帧的同一位置"

Wan 2.7、CogVideoX 等主流视频模型均采用上述机制,而非简单地将 2D 卷积替换为 3D 卷积。

7.2 主流视频生成模型

远程 API(推荐入门) 本地部署(高显存需求)
顶级质量 Runway Gen-4.5, Veo 3.1 Wan 2.7(27B, Apache 2.0)
中文生态 Kling 3.0(最长 2 分钟), Jimeng Wan 2.7, CogVideoX
性价比 Kling 免费额度, Pika 2.5 SANA-1.5 视频版(8GB VRAM 可运行)
入门门槛 无硬件要求 8GB(SANA)→ 24GB+(Wan)

视频本地部署的硬件门槛远高于图片任务。入门阶段建议先通过远程 API 了解各类模型的效果与能力边界,再决定是否投入高显存硬件。

7.3 视频编辑的工程方法:逐帧处理

对于不具备专用视频编辑模型的场景,可将视频拆分为帧序列,逐帧应用图片处理管线,再合成为视频:

import cv2
from PIL import Image
import numpy as np

# 1. 拆帧
cap = cv2.VideoCapture("input.mp4")
frames = []
while True:
    ret, frame = cap.read()
    if not ret: break
    frames.append(frame)

# 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

7.4 FastAPI 封装:将图片处理能力服务化

将前述各章的能力封装为 HTTP API,使任意客户端均可通过网络请求调用:

from fastapi import FastAPI, UploadFile, File, Form
from fastapi.responses import Response
from diffusers import AutoPipelineForText2Image
from PIL import Image
import io, torch

app = FastAPI()

# 服务启动时一次性加载模型,常驻显存
@app.on_event("startup")
async def load_models():
    global pipe
    pipe = AutoPipelineForText2Image.from_pretrained(
        "black-forest-labs/FLUX.1-schnell",
        torch_dtype=torch.bfloat16
    ).to("cuda")

@app.post("/generate")
async def generate(prompt: str = Form(...)):
    image = pipe(prompt=prompt).images[0]
    buf = io.BytesIO()
    image.save(buf, format="JPEG", quality=95)
    return Response(content=buf.getvalue(), media_type="image/jpeg")

# 启动:uvicorn image_service:app --port 8000
# 调用:curl -X POST http://localhost:8000/generate -F "prompt=一只猫" -o cat.jpg

服务化前后的对比:

  • 每次编写独立脚本 → 统一 HTTP 接口调用
  • 模型重复加载(每次 5-30 秒冷启动) → 模型常驻显存,请求即时响应
  • 仅限本地使用 → 局域网内任意设备均可调用

7.5 全文回顾

第一章:能力全景   — AI 在图片与视频领域的边界、五层生态结构、远程与本地两种模式
第二章:环境地基   — 自底向上逐层解析,每层的功能定位与安装验证
第三章:第一张图片 — 扩散模型推理全流程的六步骤拆解与四个核心组件
第四章:噪声与遮罩 — 两个贯穿全部图片编辑任务的基础概念
第五章:四种模式   — 同一管线、不同参数组合(图生图/Inpainting/超分/Outpainting)
第六章:精确控制   — LoRA(外观)+ ControlNet(空间)+ IP-Adapter(风格)
第七章:视频与落地 — 视频 = 图片 × 时间维度 + FastAPI 服务化封装

配套阅读:《AI图片视频处理-工具使用手册.md》——各知识点的可执行代码、命令速查、实践经验记录与常见问题解决方案。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐