122 lines
5.6 KiB
Python
122 lines
5.6 KiB
Python
#!/usr/bin/env python3
|
||
"""CHAR-004 R3 · 反乞丐滑坡版 · 纯文生图"""
|
||
import sys, os, json, time, subprocess
|
||
from pathlib import Path
|
||
|
||
sys.path.insert(0, os.path.dirname(__file__))
|
||
from secrets_loader import secret, endpoint
|
||
|
||
API_KEY = secret("SC-001")
|
||
BASE_URL = endpoint("EPT-001")
|
||
IMAGE_MODEL = "doubao-seedream-4-0-250828"
|
||
|
||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||
JZAO_BASE = Path("/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01")
|
||
CANDIDATES_DIR = REPO_ROOT / "assets/candidates/D157-VA-05-001-R3" / "CHAR-004-ZhugeFeng"
|
||
|
||
POSITIVE = """3D动漫卡通渲染,国风玄幻动态漫质感,单人男性角色全身立绘,纯白背景。
|
||
|
||
角色为16岁少年,清瘦挺拔,身高约175cm,肩宽略窄,身形单薄但精神利落,不佝偻,不颓废。小麦色肤色,面部轮廓棱角分明,剑眉星目,眼神锐利有韧劲,鼻梁挺直,嘴唇偏薄,下颌线清晰,少年气强,气质冷硬。黑色短发,高束半马尾,发尾有少量碎发,用普通深棕色木簪固定,额前散落几缕碎发,整体整洁。
|
||
|
||
服装为朴素修仙学徒服,灰蓝色棉麻粗布交领上衣,右衽,袖口收窄,内搭浅色中衣;衣服整体干净整洁,只在领口、袖口边缘有非常轻微的磨毛质感,没有破洞,没有补丁,没有污渍。腰间系深棕色粗麻绳腰带,系法简单。下装为深灰蓝色同色系棉麻长裤,裤脚收进黑色短靴,膝盖和裤脚仅有轻微磨白,没有破损,没有泥污。黑色粗布短靴,厚鞋底,干净整洁。
|
||
|
||
腰间麻绳上挂一个拳头大小的深灰色旧布包,布包完整平整,布料略有年代感,但没有破口,没有补丁,没有脏污。
|
||
|
||
整体气质是清贫但整洁、有骨气、一心求仙的少年,不是乞丐,不是流民,不是流浪汉。
|
||
|
||
人物完整居中露出,比例协调,线条清晰,色彩柔和,卡通渲染,3D动漫角色质感。"""
|
||
|
||
NEGATIVE = """乞丐,流民,流浪汉,破损服装,破洞,补丁,污渍,泥污,脏鞋,破裤腿,烂袖口,脏布包,佝偻,颓废,萎靡,肮脏,凌乱,衣衫褴褛,夸张破旧,CHAR-003同款脸,CHAR-003同款五官,CHAR-003同款发型,与苏白长相相似,真人照片,写实,畸形肢体,五官崩坏,模糊,水印。"""
|
||
|
||
VARIATIONS = [
|
||
"正面直视镜头,表情沉稳有韧劲",
|
||
"微微侧头,目光坚毅看向前方",
|
||
"略微仰视,下颌抬起,眼神向上有向往",
|
||
"3/4侧脸,侧视左前方,少年英气",
|
||
]
|
||
|
||
def call_api(prompt_text, neg_text, seed):
|
||
url = f"{BASE_URL}/images/generations"
|
||
payload = json.dumps({
|
||
"model": IMAGE_MODEL,
|
||
"prompt": prompt_text,
|
||
"negative_prompt": neg_text,
|
||
"size": "1440x2560",
|
||
"n": 1,
|
||
"seed": seed,
|
||
})
|
||
proc = subprocess.run([
|
||
"curl", "-s", "-m", "120", "--noproxy", "*",
|
||
"-X", "POST", url,
|
||
"-H", f"Authorization: Bearer {API_KEY}",
|
||
"-H", "Content-Type: application/json",
|
||
"-d", payload
|
||
], capture_output=True, text=True, timeout=130)
|
||
try:
|
||
data = json.loads(proc.stdout)
|
||
if "data" in data and len(data["data"]) > 0:
|
||
return data["data"][0].get("url")
|
||
except:
|
||
pass
|
||
return None
|
||
|
||
def download(url, dst):
|
||
Path(dst).parent.mkdir(parents=True, exist_ok=True)
|
||
subprocess.run(["curl", "-s", "-m", "60", "-L", "--noproxy", "*", "-o", dst, url],
|
||
capture_output=True, timeout=65)
|
||
return os.path.exists(dst) and os.path.getsize(dst) > 1024
|
||
|
||
def scale(src, dst):
|
||
subprocess.run(["sips", "-z", "1920", "1080", str(src), "--out", str(dst)],
|
||
capture_output=True)
|
||
return os.path.exists(dst)
|
||
|
||
def main():
|
||
print("CHAR-004 R3 · 反乞丐滑坡")
|
||
print(f"策略: 整洁优先+负面词锁死+{len(VARIATIONS)}角度变体")
|
||
|
||
if not API_KEY:
|
||
print("✗ 无API Key"); sys.exit(1)
|
||
|
||
ts = time.strftime("%Y%m%d-%H%M%S")
|
||
run_dir = JZAO_BASE / "D157-R3" / ts
|
||
run_dir.mkdir(parents=True, exist_ok=True)
|
||
CANDIDATES_DIR.mkdir(parents=True, exist_ok=True)
|
||
|
||
results = []
|
||
seeds = [42, 137, 256, 399]
|
||
|
||
for i, (seed, var) in enumerate(zip(seeds, VARIATIONS)):
|
||
full = f"{POSITIVE} {var}"
|
||
print(f"\n[{i+1}/4] seed={seed} 角度={var[:10]}...")
|
||
url = call_api(full, NEGATIVE, seed)
|
||
if not url:
|
||
print(" ✗ API失败"); results.append({"c": i+1, "ok": False, "err": "api"})
|
||
continue
|
||
|
||
raw = run_dir / f"CHAR-004-R3-{i+1:02d}-1440x2560.jpg"
|
||
if not download(url, raw):
|
||
print(" ✗ 下载失败"); results.append({"c": i+1, "ok": False, "err": "download"})
|
||
continue
|
||
print(f" ✓ raw {os.path.getsize(raw)/1024:.0f}KB")
|
||
|
||
scaled = CANDIDATES_DIR / f"CHAR-004-R3-candidate-{i+1:02d}-1080x1920.jpg"
|
||
if not scale(raw, scaled):
|
||
print(" ✗ 缩放失败"); results.append({"c": i+1, "ok": False, "err": "scale"})
|
||
continue
|
||
print(f" ✓ scaled {os.path.getsize(scaled)/1024:.0f}KB")
|
||
results.append({"c": i+1, "ok": True, "raw": str(raw), "scaled": str(scaled), "seed": seed})
|
||
time.sleep(2)
|
||
|
||
ok = sum(1 for r in results if r["ok"])
|
||
print(f"\n{'='*40}\n完成: {ok}/4")
|
||
|
||
meta = {"spec": "VA-05-001-R3", "asset": "CHAR-004", "ts": ts, "ok": ok, "total": 4, "results": results}
|
||
with open(CANDIDATES_DIR / "generation-meta.json", "w") as f:
|
||
json.dump(meta, f, ensure_ascii=False, indent=2)
|
||
|
||
return 0 if ok >= 4 else 1
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|