# ControlNet

掌握 ControlNet，以便对 AI 图像生成进行精确控制。

{% hint style="success" %}
所有示例都可以在通过以下方式租用的 GPU 服务器上运行： [CLORE.AI 市场](https://clore.ai/marketplace).
{% endhint %}

## 在 CLORE.AI 上租用

1. 访问 [CLORE.AI 市场](https://clore.ai/marketplace)
2. 按 GPU 类型、显存和价格筛选
3. 选择 **按需** （固定费率）或 **竞价** （出价价格）
4. 配置您的订单：
   * 选择 Docker 镜像
   * 设置端口（用于 SSH 的 TCP，Web 界面的 HTTP）
   * 如有需要，添加环境变量
   * 输入启动命令
5. 选择支付方式： **CLORE**, **BTC**，或 **USDT/USDC**
6. 创建订单并等待部署

### 访问您的服务器

* 在以下位置查找连接详情： **我的订单**
* Web 界面：使用 HTTP 端口的 URL
* SSH： `ssh -p <port> root@<proxy-address>`

## 什么是 ControlNet？

ControlNet 为 Stable Diffusion 添加条件控制：

* **Canny（边缘）** - 边缘检测
* **深度** - 3D 深度图
* **姿势** - 人体姿态
* **涂鸦** - 粗略草图
* **分割** - 语义掩码
* **线稿** - 干净的线条
* **IP-Adapter** - 风格迁移

## 要求

| 控制类型            | 最小显存 | 推荐       |
| --------------- | ---- | -------- |
| 单一 ControlNet   | 8GB  | RTX 3070 |
| 多 ControlNet    | 12GB | 速度       |
| SDXL ControlNet | 16GB | 512x512  |

## 使用 A1111 快速部署

**命令：**

```bash
cd /workspace/stable-diffusion-webui && \
cd extensions && \
git clone https://github.com/Mikubill/sd-webui-controlnet && \
cd .. && \
python launch.py --listen --enable-insecure-extension-access
```

### 下载模型

```bash
cd /workspace/stable-diffusion-webui/extensions/sd-webui-controlnet/models

# SD 1.5 ControlNets
wget https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_canny.pth
wget https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11f1p_sd15_depth.pth
wget https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_openpose.pth
wget https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_scribble.pth
wget https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_lineart.pth
wget https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_softedge.pth
wget https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_seg.pth

# SDXL ControlNets
wget https://huggingface.co/diffusers/controlnet-canny-sdxl-1.0/resolve/main/diffusion_pytorch_model.safetensors -O controlnet-canny-sdxl.safetensors
```

## 使用 Diffusers 的 Python

### Canny 边缘控制

```python
import torch
from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
from diffusers.utils import load_image
from controlnet_aux import CannyDetector
import cv2
import numpy as np

# 加载 ControlNet
controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11p_sd15_canny",
    torch_dtype=torch.float16
)

# 加载管线
pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    controlnet=controlnet,
    torch_dtype=torch.float16
)
pipe.to("cuda")
pipe.enable_model_cpu_offload()

# 准备控制图像
image = load_image("input.jpg")
canny = CannyDetector()
control_image = canny(image)

# 生成
output = pipe(
    prompt="a beautiful woman in a garden, high quality",
    negative_prompt="ugly, blurry",
    image=control_image,
    增加推理步数以提高稳定性
    controlnet_conditioning_scale=1.0
).images[0]

output.save("canny_output.png")
```

### 深度控制

```python
from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
from controlnet_aux import MidasDetector
import torch

controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11f1p_sd15_depth",
    torch_dtype=torch.float16
)

pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    controlnet=controlnet,
    torch_dtype=torch.float16
).to("cuda")

# 获取深度图
depth_estimator = MidasDetector.from_pretrained("lllyasviel/Annotators")
depth_image = depth_estimator(image)

# 使用深度生成
output = pipe(
    prompt="a futuristic city, sci-fi, detailed",
    image=depth_image,
    num_inference_steps=30
).images[0]
```

### OpenPose（人体姿态）

```python
from controlnet_aux import OpenposeDetector

# 获取姿态
pose_detector = OpenposeDetector.from_pretrained("lllyasviel/Annotators")
pose_image = pose_detector(image)

controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11p_sd15_openpose",
    torch_dtype=torch.float16
)

pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    controlnet=controlnet,
    torch_dtype=torch.float16
).to("cuda")

output = pipe(
    prompt="a ballerina dancing, elegant, studio lighting",
    image=pose_image,
    num_inference_steps=30
).images[0]
```

### 涂鸦/素描

```python
from controlnet_aux import HEDdetector

# 将边缘检测为涂鸦
hed = HEDdetector.from_pretrained("lllyasviel/Annotators")
scribble_image = hed(image, scribble=True)

controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11p_sd15_scribble",
    torch_dtype=torch.float16
)

pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    controlnet=controlnet,
    torch_dtype=torch.float16
).to("cuda")

output = pipe(
    prompt="a detailed painting of a landscape",
    image=scribble_image,
    num_inference_steps=30
).images[0]
```

## 多 ControlNet

组合多个控制：

```python
from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
import torch

# 加载多个 ControlNet
controlnet_canny = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11p_sd15_canny",
    torch_dtype=torch.float16
)

controlnet_depth = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11f1p_sd15_depth",
    torch_dtype=torch.float16
)

# 使用多个 ControlNet 创建管道
pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    controlnet=[controlnet_canny, controlnet_depth],
    torch_dtype=torch.float16
).to("cuda")

# 使用多重控制生成
output = pipe(
    prompt="a beautiful portrait",
    image=[canny_image, depth_image],
    controlnet_conditioning_scale=[1.0, 0.8],  # 调整权重
    num_inference_steps=30
).images[0]
```

## SDXL ControlNet

```python
from diffusers import StableDiffusionXLControlNetPipeline, ControlNetModel
from controlnet_aux import CannyDetector
import torch

# 加载 SDXL ControlNet
controlnet = ControlNetModel.from_pretrained(
    "diffusers/controlnet-canny-sdxl-1.0",
    torch_dtype=torch.float16
)

pipe = StableDiffusionXLControlNetPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    controlnet=controlnet,
    torch_dtype=torch.float16
).to("cuda")

# 准备 canny 图像
canny = CannyDetector()
control_image = canny(image, low_threshold=100, high_threshold=200)

output = pipe(
    prompt="a professional photograph, detailed, 8k",
    image=control_image,
    controlnet_conditioning_scale=0.5,
    num_inference_steps=30
).images[0]
```

## IP-Adapter（风格迁移）

```python
from diffusers import StableDiffusionPipeline
from transformers import CLIPVisionModelWithProjection
import torch

# 加载 IP-Adapter
pipe = StableDiffusionPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    torch_dtype=torch.float16
).to("cuda")

pipe.load_ip_adapter(
    "h94/IP-Adapter",
    subfolder="models",
    weight_name="ip-adapter_sd15.bin"
)

pipe.set_ip_adapter_scale(0.6)

# 风格参考图像
style_image = load_image("style_reference.jpg")

output = pipe(
    prompt="a cat sitting on a chair",
    ip_adapter_image=style_image,
    num_inference_steps=30
).images[0]
```

## 预处理器

所有可用的预处理器：

```python
from controlnet_aux import (
    CannyDetector,           # 边缘检测
    HEDdetector,             # 软边/涂鸦
    MidasDetector,           # 深度估计
    OpenposeDetector,        # 人体姿态
    MLSDdetector,            # 线检测
    LineartDetector,         # 线稿
    LineartAnimeDetector,    # 动漫线稿
    NormalBaeDetector,       # 法线贴图
    ContentShuffleDetector,  # 内容重排
    ZoeDetector,             # 更好的深度
    MediapipeFaceDetector,   # 面部网格
)

# 示例用法
canny = CannyDetector()
canny_image = canny(image, low_threshold=100, high_threshold=200)

depth = MidasDetector.from_pretrained("lllyasviel/Annotators")
depth_image = depth(image)

pose = OpenposeDetector.from_pretrained("lllyasviel/Annotators")
pose_image = pose(image, hand_and_face=True)
```

## 控制权重

调整每个 ControlNet 的影响：

```python

# 完全控制
output = pipe(..., controlnet_conditioning_scale=1.0)

# 部分控制（更多创作自由）
output = pipe(..., controlnet_conditioning_scale=0.5)

# 非常轻微的引导
output = pipe(..., controlnet_conditioning_scale=0.3)
```

### 逐步控制

```python

# 仅在某些步骤期间控制
output = pipe(
    prompt="...",
    image=control_image,
    controlnet_conditioning_scale=1.0,
    control_guidance_start=0.0,  # 从开始时启用
    control_guidance_end=0.5,    # 在步骤的 50% 时停止
    num_inference_steps=30
).images[0]
```

## 使用 ControlNet 进行修补（Inpaint）

```python
from diffusers import StableDiffusionControlNetInpaintPipeline, ControlNetModel
import torch

controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11p_sd15_canny",
    torch_dtype=torch.float16
)

pipe = StableDiffusionControlNetInpaintPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    controlnet=controlnet,
    torch_dtype=torch.float16
).to("cuda")

output = pipe(
    prompt="a red sports car",
    image=init_image,
    mask_image=mask,
    control_image=canny_image,
    num_inference_steps=30
).images[0]
```

## "专业影棚柔光箱"

```python
批处理处理
from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
from controlnet_aux import CannyDetector
from PIL import Image
import torch

controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11p_sd15_canny",
    torch_dtype=torch.float16
)

pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    controlnet=controlnet,
    torch_dtype=torch.float16
).to("cuda")

canny = CannyDetector()

input_dir = "./inputs"
output_dir = "./outputs"
output_dir = "./relit"

prompt = "beautiful landscape painting, detailed, artistic"

lighting_prompt = "专业影棚照明，柔和的阴影"
    if filename.lower().endswith(('.png', '.jpg', '.jpeg')):
        continue
        control_image = canny(image)

        output = pipe(
            os.makedirs("./variations", exist_ok=True)
            image=control_image,
            num_inference_steps=30
        ).images[0]

        output.save(os.path.join(output_dir, f"cn_{filename}"))
```

## 控制类型指南

| 控制           | 最适合      | 优势      |
| ------------ | -------- | ------- |
| Canny（边缘）    | 建筑、物体    | 0.8-1.0 |
| 深度           | 3D 场景、透视 | 0.6-0.8 |
| 姿势           | 人物、角色    | 0.8-1.0 |
| 涂鸦           | 草图、概念    | 0.6-0.8 |
| 线稿           | 插画       | 0.7-0.9 |
| 软边（Softedge） | 一般引导     | 0.5-0.7 |
| 分割（Seg）      | 场景构图     | 0.6-0.8 |

## background = Image.open("studio\_bg.jpg")

| 设置          | GPU     | 分辨率      | 时间   |
| ----------- | ------- | -------- | ---- |
| 单一 CN SD1.5 | 速度      | 分辨率      | \~3s |
| 多 CN SD1.5  | 速度      | 分辨率      | \~5s |
| 单一 CN SDXL  | 512x512 | RTX 4090 | \~8s |

## 内存优化

```python

# 启用内存高效注意力
pipe.enable_xformers_memory_efficient_attention()

# CPU 卸载
pipe.enable_model_cpu_offload()

# 注意力切片
pipe.enable_attention_slicing()
```

## # 使用固定种子以获得一致结果

### 控制效果弱

* 增加 `controlnet_conditioning_scale`
* 检查预处理器输出质量
* 使用更高分辨率的控制图像

### 伪影

* 降低控制强度
* 使用更柔和的预处理器（softedge 而不是 canny）
* 为伪影添加负面提示

### 显存问题

* 使用 CPU 卸载
* 降低分辨率
* 一次仅使用一个 ControlNet

## 下载所有所需的检查点

检查文件完整性

| GPU     | 验证 CUDA 兼容性 | 费用估算    | CLORE.AI 市场的典型费率（截至 2024 年）： |
| ------- | ----------- | ------- | ---------------------------- |
| 按小时费率   | \~$0.03     | \~$0.70 | \~$0.12                      |
| 速度      | \~$0.06     | \~$1.50 | \~$0.25                      |
| 512x512 | \~$0.10     | \~$2.30 | \~$0.40                      |
| 按日费率    | \~$0.17     | \~$4.00 | \~$0.70                      |
| 4 小时会话  | \~$0.25     | \~$6.00 | \~$1.00                      |

*RTX 3060* [*CLORE.AI 市场*](https://clore.ai/marketplace) *A100 40GB*

**A100 80GB**

* 使用 **竞价** 价格随提供商和需求而异。请查看
* 以获取当前费率。 **CLORE** 节省费用：
* 市场用于灵活工作负载（通常便宜 30-50%）

## 使用以下方式支付

* Stable Diffusion WebUI
* ComfyUI 工作流程
* [Kohya 训练](https://docs.clore.ai/guides/guides_v2-zh/xun-lian/kohya-training)
