Midjourney 和 Stable Diffusion 常被放在一起比较,但专业团队从不二选一。MJ 强在审美先验、构图直觉和出图速度;SD 强在可控性、局部编辑和批量自动化。真正的生产力,是把两者放进同一条管线:MJ 做创意探索与氛围定调,SD 做精修、一致性与规模化输出。
环节 | 推荐工具 | 原因 |
|---|---|---|
概念探索 | Midjourney | 风格多样、构图大胆、迭代快 |
角色/风格锁定 | SD + LoRA | 可训练、可复现、一致性强 |
构图控制 | SD + ControlNet | 姿态、线稿、深度精确控制 |
局部修改 | SD Inpainting | 蒙版级重绘,不破坏整体 |
批量放大 | SD + ESRGAN | 本地免费、可脚本化 |
核心原则:MJ 产出参考图,SD 产出可交付资产。
Midjourney 官方无公开 API,专业团队通常通过第三方网关调用。生产管线的核心是任务提交 → 轮询 → 结果落盘,并保留提示词与种子以便复现:
import os, time, requests, json, hashlib, pathlib
BASE = "https://api.goapi.ai/mj/v2"
H = {"X-API-Secret": os.getenv("MJ_KEY"), "Content-Type": "application/json"}
def mj_run(prompt, aspect="1:1", version="6.1", mode="relax"):
r = requests.post(f"{BASE}/imagine", headers=H, json={
"prompt": prompt, "aspect_ratio": aspect,
"version": version, "process_mode": mode,
}).json()
tid = r["task_id"]
while True:
s = requests.get(f"{BASE}/fetch", headers=H,
params={"task_id": tid}).json()
if s["status"] == "finished":
return s["task_result"]["image_urls"]
if s["status"] == "failed":
raise RuntimeError(s.get("message"))
time.sleep(5)
def cache_prompt(prompt, urls, out="mj_cache"):
pathlib.Path(out).mkdir(exist_ok=True)
key = hashlib.md5(prompt.encode()).hexdigest()[:10]
for i, u in enumerate(urls):
p = f"{out}/{key}_{i}.png"
if not os.path.exists(p):
open(p, "wb").write(requests.get(u).content)
json.dump({"prompt": prompt, "urls": urls},
open(f"{out}/{key}.json", "w"), ensure_ascii=False)
return f"{out}/{key}_0.png"要点:--style raw 降低默认美化,--sref 做风格迁移,--cref 做角色一致性。V8.2 在参数面上与 V8.1 完全一致,迁移只需替换版本号。
确定方向后,用 SD 锁定风格。LoRA 的价值在于:同一个触发词,跨批次产出高度一致的视觉语言,这是 MJ 难以稳定做到的:
import requests, base64
API = "http://127.0.0.1:7860"
def txt2img(prompt, negative, lora, seed=42, steps=30):
payload = {
"prompt": f"{prompt}, <lora:{lora}:0.8>",
"negative_prompt": negative,
"seed": seed, "steps": steps,
"width": 1024, "height": 576,
"cfg_scale": 6.5,
"sampler_name": "DPM++ 2M Karras",
}
r = requests.post(f"{API}/sdapi/v1/txt2img",
json=payload, timeout=300)
r.raise_for_status()
img = r.json()["images"][0]
with open("sd_output.png", "wb") as f:
f.write(base64.b64decode(img.split(",", 1)[-1]))MJ 与 SD 的协同不是简单接力,而是一次能力解耦:MJ 作为“视觉语义引擎”提供高审美参考,SD 作为“可控渲染引擎”完成像素级交付。
IP-Adapter 负责风格注入——把 MJ 参考图编码为 image embedding,拼接到文本 embedding 后送入 UNet:
import torch
from PIL import Image
from diffusers import StableDiffusionXLPipeline, DDIMScheduler
from transformers import CLIPVisionModelWithProjection
pipe = StableDiffusionXLPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16,
image_encoder=CLIPVisionModelWithProjection.from_pretrained(
"h94/IP-Adapter", subfolder="models/image_encoder",
torch_dtype=torch.float16),
).to("cuda")
pipe.load_ip_adapter("h94/IP-Adapter", subfolder="sdxl_models",
weight_name="ip-adapter_sdxl.bin")
pipe.set_ip_adapter_scale(0.6) # 0.4~0.7 为常用区间
ref = Image.open("mj_cache/style_0.png").convert("RGB")
out = pipe(
prompt="luxury perfume bottle, marble, studio lighting, 8k",
negative_prompt="text, watermark, deformed",
ip_adapter_image=ref,
num_inference_steps=30, guidance_scale=7.5,
).images[0]ControlNet 负责结构约束——从 MJ 图提取 Canny 边缘或深度图,锁定空间结构:
import cv2, numpy as np
from diffusers import StableDiffusionXLControlNetPipeline, ControlNetModel
cn = ControlNetModel.from_pretrained(
"diffusers/controlnet-depth-sdxl-1.0", torch_dtype=torch.float16)
pipe2 = StableDiffusionXLControlNetPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
controlnet=cn, torch_dtype=torch.float16).to("cuda")
pipe2.load_ip_adapter("h94/IP-Adapter", subfolder="sdxl_models",
weight_name="ip-adapter_sdxl.bin")
pipe2.set_ip_adapter_scale(0.5)
edges = cv2.Canny(np.array(ref), 100, 200)
cond = np.stack([edges] * 3, axis=-1)
final = pipe2(
prompt="premium product shot, brand style, soft light",
image=cond, ip_adapter_image=Image.fromarray(np.array(ref)),
controlnet_conditioning_scale=0.7,
num_inference_steps=30, guidance_scale=7.5,
).images[0]ip_adapter_scale 过高会照抄参考图,过低则风格漂移,0.6 是经验起点。双适配器叠加时,通常风格 0.5、结构 0.7,避免互相压制。
把 MJ prompt、SD prompt、LoRA、ControlNet 权重、种子全部纳入版本管理:
import hashlib, json
from dataclasses import dataclass, asdict
@dataclass
class Job:
mj_prompt: str
sd_prompt: str
ip_scale: float
cn_scale: float
seed: int
def jid(j):
return hashlib.md5(json.dumps(asdict(j), sort_keys=True)
.encode()).hexdigest()[:12]每一次生成都记录 job id,任何一张输出都能回放。MJ 负责“想到”,SD 负责“做到”,把两者纳入同一条可复现的管线,AI 绘画才能从灵感游戏变成可规模化的生产力。
确认模型许可证与训练数据来源,商用前核查;不得模仿受保护 IP 与真人肖像;AI 生成内容按平台要求标注;客户素材需获得授权,禁止未授权图生图。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。