摘要: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 调用和本地部署两种模式。
python
# 文本 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。
提供的两项核心能力:
- 张量运算 :numpy 的 GPU 加速等价物(
torch.Tensor的数据模型与numpy.ndarray类似,但可在 GPU 上执行) - 自动求导:训练阶段使用,推理阶段不需要,但模型的网络结构定义依赖此机制
Python 3.14 无法安装 PyTorch 的原因:PyTorch 底层包含大量 C++ 扩展代码(CUDA 交互层、算子优化等),需针对各 Python 版本分别编译。每当新的 Python 版本发布,PyTorch 团队需要适配周期。截至 2026 年 7 月,PyTorch 最高支持 Python 3.12。
2.3 第 3 层:工具库 --- 各 Python 包的功能定位
PyTorch 仅提供"张量运算"这一基础能力,不具备"加载模型并完成推理全流程"的高层功能:
python
# 仅使用 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 文件的本质
bash
# 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 条验证命令全部通过,即说明环境已就绪:
bash
# ① 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 最小可运行示例
python
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 参数含义与调节方向
python
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 一致,仅端点和模型名不同:
python
# 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 控制修改幅度
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 参数效果对照:
strength = 0.1 几乎无可见变化,仅轻微调整色调与纹理
strength = 0.3 保留原图构图与结构,风格开始转变
strength = 0.5 可辨认原图内容,但风格发生显著变化(如照片 → 水彩画)
strength = 0.8 仅保留大致空间布局,内容变化幅度大
strength = 1.0 等价于文生图,与原始图像再无关联
5.3 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]
遮罩占比与成功率的关系:
遮罩占比 <10% 10~30% >30%
成功率 高 中等 低
原因分析 模型有充足的周边 模型勉强可推断 模型"虚构"的概率显著上升
上下文作为参考 周边上下文 (实际使用中已验证的局限)
两种技术路线的适用场景:
- 移除路人、保持原始场景真实感 → 内容感知填充(Photoshop / OpenCV inpaint),利用原图纹理
- 将门改为窗、需要创造全新内容 → 扩散模型 Inpainting,由模型生成新内容
5.4 超分辨率 --- CNN 方法与扩散方法的对比
方案 A:CNN 超分(不编造内容,推荐用于保真场景)
python
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:扩散超分(视觉效果更好,但可能引入虚构细节)
python
result = pipe_img2img(
prompt="high resolution, sharp details, 4K quality",
image=original,
strength=0.15, # 极低值,仅在纹理层面增强,不改变内容
).images[0]
选择依据:要求"严格保真"(证件照、文档扫描件)→ CNN 超分;追求"视觉观感"(风景照、艺术作品)→ 扩散超分。
5.5 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 是"参考"(图像风格层面)。
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 特化知识
python
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 | 语义分割色块 | 各类物体的空间分布 | 精确控制场景布局 |
python
# 以 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 约束的是"风格与质感"(色调氛围、纹理特征、物体类别)
输入水墨画参考图 → 生成结果的色彩与笔触呈现水墨质感
输入人物照片 → 生成结果具有该人物的面部特征
python
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 视频编辑的工程方法:逐帧处理
对于不具备专用视频编辑模型的场景,可将视频拆分为帧序列,逐帧应用图片处理管线,再合成为视频:
python
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,使任意客户端均可通过网络请求调用:
python
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》------各知识点的可执行代码、命令速查、实践经验记录与常见问题解决方案。