第 12/60 天
引言
AI 漫画创作中最令人头疼的问题之一,就是如何让生成的画面”听你的话”。你希望角色摆出特定的姿势、做出特定的表情,但 AI 往往自由发挥——角色手部变形、动作不符合分镜要求、构图偏离预期。
ControlNet 的出现彻底改变了这一局面。作为 Stable Diffusion 生态中最强大的控制插件,ControlNet 允许你通过参考图(骨骼姿态、深度图、边缘检测、涂鸦等)精确控制 AI 的生成结果。在漫画创作中,ControlNet 的姿态控制(OpenPose)和构图控制(Canny/Depth)是两大核心应用。
本文将深入讲解 ControlNet 在 AI 漫画中的应用,从安装配置到实战工作流,帮助你用 AI 画出精准的漫画分镜。
核心概念
什么是 ControlNet?
ControlNet 是 2023 年由张吕敏(Lvmin Zhang)团队提出的神经网络架构,通过对预训练扩散模型添加可训练的条件控制模块,实现对生成图像的精确控制。它不会改变原有的 Stable Diffusion 模型,而是在其基础上增加一个”控制通道”。
漫画创作中常用的 ControlNet 模型
| ControlNet 模型 | 用途 | 漫画应用场景 |
|---|---|---|
| OpenPose (姿态) | 识别人物骨骼关键点,控制姿势 | 角色动作、战斗场景、表情控制 |
| Canny (边缘) | 提取图像的边缘轮廓 | 线稿上色、构图参考、保持角色轮廓 |
| Depth (深度) | 提取图像的深度信息 | 场景透视、镜头景深、背景层次 |
| SoftEdge (软边缘) | 提取柔和的边缘线条 | 风格迁移、保持草稿风格 |
| Lineart (线稿) | 提取精细线稿 | 漫画线稿自动上色、线稿细化 |
| Scribble (涂鸦) | 识别手绘涂鸦 | 快速草图转成品、分镜设计 |
| IP-Adapter | 图像风格/内容参考 | 角色一致性、风格统一 |
OpenPose 姿态控制的核心原理
OpenPose 深度学习模型能检测图像中的人物关键点,包括:
– 身体关键点(25 个):头、肩、肘、腕、髋、膝、踝
– 手部关键点(21 个/手):每根手指的关节
– 面部关键点(70 个):眼、眉、嘴、鼻的轮廓
你在漫画中使用 OpenPose 时,只需提供一张参考姿势图(可以是真实照片、3D 模型截图或手动标注的骨骼图),ControlNet 就能让 AI 生成的角色严格遵循这个姿势。
实战步骤
步骤 1:安装 ControlNet(WebUI 环境)
在 Stable Diffusion WebUI 中安装 ControlNet 扩展:
# 进入 SD WebUI 目录
cd /workspace/stable-diffusion-webui
# 通过 Git 安装 ControlNet 扩展
git clone https://github.com/Mikubill/sd-webui-controlnet.git extensions/sd-webui-controlnet
# 下载 ControlNet 模型文件(以 OpenPose 为例)
mkdir -p models/ControlNet
cd models/ControlNet
# 使用 huggingface-cli 下载模型
huggingface-cli download lllyasviel/ControlNet-v1-1 control_v11p_sd15_openpose.pth
--local-dir ./models/ControlNet
# 下载 OpenPose 预处理器
huggingface-cli download lllyasviel/ControlNet-v1-1
annotator/ckpts/body_pose_model.pth
--local-dir ./models/ControlNet
步骤 2:OpenPose 姿态获取
获取漫画角色的参考姿势有三种方式:
方式一:手动绘制骨骼图(推荐漫画分镜)
{
"pose_keypoints": [
{"x": 300, "y": 100, "part": "nose"},
{"x": 280, "y": 80, "part": "left_eye"},
{"x": 320, "y": 80, "part": "right_eye"},
{"x": 300, "y": 140, "part": "neck"},
{"x": 250, "y": 180, "part": "left_shoulder"},
{"x": 350, "y": 180, "part": "right_shoulder"},
{"x": 220, "y": 250, "part": "left_elbow"},
{"x": 380, "y": 240, "part": "right_elbow"},
{"x": 180, "y": 320, "part": "left_wrist"},
{"x": 400, "y": 300, "part": "right_wrist"},
{"x": 300, "y": 240, "part": "hip"},
{"x": 270, "y": 340, "part": "left_knee"},
{"x": 330, "y": 340, "part": "right_knee"},
{"x": 260, "y": 420, "part": "left_ankle"},
{"x": 340, "y": 420, "part": "right_ankle"}
]
}
方式二:从参考图片提取(使用 Python 脚本)
import cv2
import numpy as np
from controlnet_aux import OpenposeDetector
# 初始化 OpenPose 检测器
openpose = OpenposeDetector.from_pretrained("lllyasviel/ControlNet")
# 读取参考图片
ref_image = cv2.imread("reference_pose.jpg")
ref_image = cv2.cvtColor(ref_image, cv2.COLOR_BGR2RGB)
# 提取姿势骨骼图
# include_hand=True 打开手部检测, include_face=True 打开面部检测
pose_image = openpose(
ref_image,
hand_and_face=True,
detect_resolution=512,
image_resolution=512
)
# 保存骨骼图
pose_image.save("openpose_output.png")
print("OpenPose 骨骼图已保存到 openpose_output.png")
print(f"参考图尺寸: {ref_image.shape}")
print(f"输出图尺寸: {pose_image.size}")
方式三:使用 ComfyUI 节点管道
# ComfyUI 工作流配置(JSON 格式片段)
workflow:
nodes:
- id: 1
type: "LoadImage"
params:
image: "ref_pose.png"
- id: 2
type: "ControlNetLoader"
params:
control_net_name: "control_v11p_sd15_openpose.pth"
- id: 3
type: "OpenPosePreprocessor"
params:
detect_resolution: 512
image_resolution: 512
hand_and_face: true
- id: 4
type: "ApplyControlNet"
params:
strength: 0.8
start_percent: 0
end_percent: 0.8
- id: 5
type: "KSampler"
params:
seed: 42
steps: 30
cfg: 7.5
sampler_name: "dpmpp_2m_sde"
scheduler: "karras"
denoise: 1.0
步骤 3:构建漫画分镜控制管道
在漫画创作中,你需要同时控制多个画面元素。以下是组合使用多个 ControlNet 的实战配置:
"""
漫画分镜控制管道:同时使用 OpenPose + Canny + Depth 三个 ControlNet
"""
import torch
from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
from diffusers.utils import load_image
from PIL import Image
# 加载三个 ControlNet 模型
openpose_cn = ControlNetModel.from_pretrained(
"lllyasviel/control_v11p_sd15_openpose",
torch_dtype=torch.float16
)
canny_cn = ControlNetModel.from_pretrained(
"lllyasviel/control_v11f1p_sd15_canny",
torch_dtype=torch.float16
)
depth_cn = ControlNetModel.from_pretrained(
"lllyasviel/control_v11f1p_sd15_depth",
torch_dtype=torch.float16
)
# 构建管道
pipe = StableDiffusionControlNetPipeline.from_pretrained(
"runwayml/stable-diffusion-v1-5",
controlnet=[openpose_cn, canny_cn, depth_cn],
torch_dtype=torch.float16
).to("cuda")
# 漫画分镜提示词
prompt = """
manga panel, warrior raising sword, dynamic action pose,
detailed armor, battle scene, cinematic lighting,
black and white lineart style, manga screentone texture,
high quality, detailed background, intense expression
""".strip()
# 负面提示词
negative_prompt = """
bad anatomy, distorted hands, missing fingers, extra limbs,
low quality, watermark, text, signature, blurry, bad proportions
""".strip()
# 每个 ControlNet 的权重配置
controlnet_conditioning_scale = [0.9, 0.6, 0.4] # OpenPose/Canny/Depth
control_guidance_start = [0.0, 0.0, 0.0]
control_guidance_end = [0.7, 0.8, 0.9]
# 生成
image = pipe(
prompt=prompt,
negative_prompt=negative_prompt,
num_inference_steps=30,
guidance_scale=7.5,
controlnet_conditioning_scale=controlnet_conditioning_scale,
control_guidance_start=control_guidance_start,
control_guidance_end=control_guidance_end,
generator=torch.Generator("cuda").manual_seed(42)
).images[0]
image.save("manga_panel_controlnet.png")
print("漫画分镜已生成,保存为 manga_panel_controlnet.png")
步骤 4:漫画分镜脚本自动化
将姿态控制集成到漫画分镜脚本中,实现批量生成:
"""
漫画分镜自动化脚本:从分镜脚本批量生成漫画页面
"""
import json
import os
from pathlib import Path
# 漫画分镜脚本(JSON 格式)
storyboard = {
"page": 1,
"title": "觉醒",
"panels": [
{
"id": "p1-1",
"layout": "full_page",
"description": "主角站在山巅,仰望天空,手握长剑",
"pose": "standing_hero.png",
"camera": "低角度仰视",
"prompt": "anime style, young hero standing on cliff, looking up at sky, holding sword, dramatic lighting, wind blowing cape, detailed background clouds, cinematic composition",
"controlnet_config": {
"openpose": 0.9,
"depth": 0.6
}
},
{
"id": "p1-2",
"layout": "split_top",
"description": "敌人从阴影中现身,双手握拳",
"pose": "villain_appear.png",
"camera": "平视",
"prompt": "manga style, villain emerging from shadows, clenched fists, menacing look, dark aura, low key lighting, intense atmosphere",
"controlnet_config": {
"openpose": 0.85,
"softedge": 0.5
}
},
{
"id": "p1-3",
"layout": "split_bottom",
"description": "两人对峙,剑拔弩张",
"pose": "showdown_pose.png",
"camera": "侧面全景",
"prompt": "face off scene, hero and villain facing each other, tense atmosphere, detailed background dojo, perspective lines, manga screentone",
"controlnet_config": {
"openpose": 0.8,
"depth": 0.5,
"canny": 0.4
}
}
]
}
# 批量生成函数
def generate_manga_panels(storyboard, output_dir="output"):
"""根据分镜脚本批量生成漫画面板"""
os.makedirs(output_dir, exist_ok=True)
for panel in storyboard["panels"]:
print(f"生成面板 {panel['id']}: {panel['description']}")
# 构建完整提示词
style_prompt = "masterpiece, best quality, manga panel, " + panel["prompt"]
# 这里集成实际调用 ControlNet 管道的代码
# 生成结果保存到 {output_dir}/{panel["id"]}.png
print(f" 提示词: {style_prompt[:80]}...")
print(f" 姿态参考: {panel['pose']}")
print(f" 镜头: {panel['camera']}")
print(f" 完成: {output_dir}/{panel['id']}.png")
print(f"n共生成 {len(storyboard['panels'])} 个面板")
print(f"输出目录: {output_dir}")
# 执行生成
generate_manga_panels(storyboard)
# 输出页面布局 HTML(用于预览)
def export_layout_html(storyboard, output_dir="output"):
"""生成漫画页面布局预览 HTML"""
html = """
<!DOCTYPE html>
<html>
<head>
<style>
body { background: #333; display: flex; justify-content: center; }
.page { width: 800px; background: #fff; padding: 20px; margin: 20px; }
.panel { border: 2px solid #000; margin: 5px; }
img { width: 100%; display: block; }
</style>
</head>
<body>
<div class="page">
<h2>""" + storyboard["title"] + """ - 第 """ + str(storyboard["page"]) + """页</h2>
"""
for panel in storyboard["panels"]:
html += f"""
<div class="panel" id="{panel['id']}">
<img src="{output_dir}/{panel['id']}.png" alt="{panel['description']}">
<p>{panel['description']} | 镜头: {panel['camera']}</p>
</div>
"""
html += """
</div>
</body>
</html>
"""
with open(f"{output_dir}/page_preview.html", "w", encoding="utf-8") as f:
f.write(html)
print(f"布局预览已生成: {output_dir}/page_preview.html")
export_layout_html(storyboard)
步骤 5:ControlNet 参数调优指南
| 参数 | 推荐值范围 | 说明 |
|---|---|---|
| Control Weight | 0.5 – 1.0 | 值越大对生成结果的控制越强。漫画角色姿态建议 0.8-0.9 |
| Starting Control Step | 0.0 – 0.2 | 从哪一步开始生效。默认 0.0 即从一开始就受到控制 |
| Ending Control Step | 0.6 – 1.0 | 在哪一步停止控制。0.6-0.8 让后半段有自由发挥空间 |
| Preprocessor Resolution | 512 – 1024 | 预处理器分析分辨率,越高越精细但越慢 |
| Guidance Scale | 7.0 – 9.0 | 提示词遵循度,漫画推荐 7.5 |
调优口诀: 姿态不行升权重,构图太乱降结束,细节不够加分辨率,风格太强降引导。
常见问题
Q1:ControlNet 生成的角色姿势正确但面部崩坏怎么办?
这是漫画创作中的常见问题。解决方法是分层控制:先用 OpenPose 控制整体姿势,但在 ControlNet 的 Ending Control Step 设置 0.7-0.8,让后续步骤能自由修正面部细节。同时,搭配面部修复插件(如 ADetailer)效果更佳。
Q2:多个 ControlNet 叠加使用时,权重应该如何分配?
遵循”一文一主”原则:选择一个主要的 ControlNet(通常是 OpenPose,权重 0.8-0.9)作为核心控制,其他辅助 ControlNet 权重降低到 0.3-0.6。例如,姿态控制用 0.9,背景深度用 0.4,边缘检测用 0.3。
Q3:OpenPose 无法识别复杂的战斗姿势怎么办?
战斗姿势涉及身体扭曲、透视缩短,OpenPose 可能识别不准。解决方案:
1. 使用 3D 建模软件(如 Blender)摆出姿势后截图
2. 使用 PoseMyArt 等专门姿势参考应用
3. 手动在 OpenPose 编辑器(如 OpenPose Editor 扩展)中调整骨骼点
4. 考虑使用 DWPose(更精确的全身姿态检测器)
Q4:ControlNet 和 LoRA 同时使用时冲突怎么办?
两者控制的是不同层面:ControlNet 控制姿势和构图,LoRA 控制角色外貌和风格。建议:
– 先单独测试:确认 LoRA 和 ControlNet 各自能正常工作
– 降低 LoRA 权重(0.5-0.7)和 ControlNet 权重(0.7-0.8)
– 如果仍冲突,提高 ControlNet 的 Ending Control Step(如 0.9)让 LoRA 只在最后阶段修正
Q5:生成漫画线稿时,如何用 ControlNet 保持黑白漫风格?
使用 Canny ControlNet 配合线稿风格的 LoRA 模型。Canny 权重设为 0.6-0.8,同时在提示词中加入 “black and white, lineart, manga style, no color, halftone dots, screentone” 等关键词。也可以使用 Lineart ControlNet 替代 Canny,对线稿的识别更精准。
总结
ControlNet 是 AI 漫画创作中不可或缺的控制工具,它让 AI 从”自由创作”变成”听你指挥”:
- OpenPose 姿态控制是漫画角色动作设计的核心工具,通过骨骼关键点精确控制每个角色的姿势
- 多 ControlNet 组合使用(OpenPose + Canny/Depth/SoftEdge)可以实现对漫画画面全方位的控制
- 分镜脚本自动化,将 ControlNet 参数集成到 JSON 脚本中,实现批量生成和团队协作
- 参数调优是掌握 ControlNet 的关键,理解 Control Weight、Starting/Ending Step 等参数的含义
- 分层控制策略,不同 ControlNet 模型各司其职,配合 LoRA 和提示词,共同构建高质量漫画画面
下一期我们将深入探讨 LoRA 训练:打造专属漫画角色,敬请期待!















暂无评论内容