MJ 与 SD 的协同不是简单接力,而是一次能力解耦:MJ 作为"视觉语义引擎"提供高审美参考,SD 作为"可控渲染引擎"完成像素级交付。两者之间的桥梁,是 IP-Adapter、ControlNet 与 LoRA 三大适配器。理解它们的数学角色,才能构建稳定的混合管线。
MJ 的输出是一张高审美图像,本质是"隐空间采样的一个样本";SD 要复用它的价值,有三条路径:
strength 微调。三者可叠加:ControlNet 锁结构,IP-Adapter 锁风格,img2img 保底图,形成"三重约束"。
MJ 无官方 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 做角色一致性。探索阶段一次出 4 张,快速筛方向。
IP-Adapter 是 MJ→SD 协同的关键。它把参考图通过 CLIP 图像编码器转成 image embedding,拼接到文本 embedding 后送入 UNet,实现"图像即提示词"。
import torch
from PIL import Image
from diffusers import StableDiffusionXLPipeline, DDIMScheduler
from transformers import CLIPVisionModelWithProjection, CLIPImageProcessor
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.scheduler = DDIMScheduler.from_config(pipe.scheduler.config)
# 加载 IP-Adapter 权重
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, # MJ 图作为风格参考
num_inference_steps=30, guidance_scale=7.5,
generator=torch.Generator("cuda").manual_seed(42),
).images[0]
out.save("sd_styled.png")ip_adapter_scale 是核心旋钮:过高会照抄参考图(包括不想要的元素),过低则风格漂移。0.6 是经验起点。
风格有了,构图仍需精确控制。ControlNet 从 MJ 图提取 Canny 边缘或深度图,约束 SD 的空间结构。
import cv2, numpy as np
from diffusers import StableDiffusionXLControlNetPipeline, ControlNetModel
from diffusers.utils import load_image
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)
# 用 MiDaS 或 Depth-Anything 生成深度图,这里以 Canny 简化演示
ref = np.array(load_image("mj_cache/style_0.png"))
edges = cv2.Canny(ref, 100, 200)
cond = np.stack([edges] * 3, axis=-1)
final = pipe2(
prompt="premium product shot, brand style, soft light",
negative_prompt="cluttered, lowres",
image=cond,
ip_adapter_image=Image.fromarray(ref),
controlnet_conditioning_scale=0.7,
num_inference_steps=30, guidance_scale=7.5,
generator=torch.Generator("cuda").manual_seed(7),
).images[0]
final.save("sd_final.png")双适配器叠加:IP-Adapter 管风格,ControlNet 管结构,两者权重需平衡——通常风格 0.5、结构 0.7,避免互相压制。
import hashlib
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]
def pipeline(jobs, out="output"):
pathlib.Path(out).mkdir(exist_ok=True)
for j in jobs:
k = jid(j)
if os.path.exists(f"{out}/{k}.png"):
continue # 幂等:已产出跳过
urls = mj_run(j.mj_prompt)
ref_path = cache_prompt(j.mj_prompt, urls)
img = sd_pipeline(ref_path, j.sd_prompt, j.ip_scale, j.cn_scale, j.seed)
img.save(f"{out}/{k}.png")
json.dump(asdict(j), open(f"{out}/{k}.json", "w"), ensure_ascii=False)幂等 + 元数据是底线:任务可重跑不重复,参数可追溯可回滚。
--cref/--sref,SD 用 IP-Adapter + 固定种子。enable_model_cpu_offload()、vae_slicing 降显存。MJ 与 SD 的深度协同,本质是把"审美"与"控制"解耦再重组:MJ 提供高维视觉语义,IP-Adapter 迁移风格,ControlNet 锁定结构,img2img 保底起点。当这套管线具备幂等、可追溯、可评估的工程属性,AI 绘画才真正从灵感工具升级为可交付的生产系统。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。