guanghulab/video-ai-system/tools/qc_char003_r6.py

104 lines
4.9 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""CHAR-003 V2 R6 QC · Seedream · R3-02风格 + 17-18岁"""
import sys, os, json, base64, subprocess
from pathlib import Path
# 读 .env
ROOT = Path('D:/WorkBuddy/guanghulab/video-ai-system')
ENV_FILE = ROOT / ".env"
env = {}
if ENV_FILE.exists():
for line in open(ENV_FILE):
line = line.strip()
if line and not line.startswith("#") and "=" in line:
k, v = line.split("=", 1)
env[k.strip()] = v.strip()
AK = env.get("ALIYUN_QWEN_VL_KEY", "")
EP = env.get("ALIYUN_QWEN_VL_ENDPOINT", "https://ws-umd6xwlovzmshuat.cn-beijing.maas.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation")
FE = "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation"
DIR = ROOT / "assets/candidates/D158-VA-05-001-V2/CHAR-003-SuBai/R6"
CS = [DIR / f"CHAR-003-V2-R6-candidate-{i:02d}-1080x1920.png" for i in range(1, 5)]
PROMPT = """检查CHAR-003苏白立绘。剧本设定18岁男性白色长衫青色腰带黑色长发半束半披双手叉腰自信开朗气质俊秀略带痞气穷但不自卑。
逐项判定
1. 是否为男性(male: bool)
2. 是否白色长衫(white_robe: bool)
3. 是否有青色/深色腰带(cyan_sash: bool)
4. 是否为黑色长发半束半披(hair_half_tied: bool)
5. 是否双手叉腰(hands_on_hips: bool)
6. 服装是否完整无破损(intact: bool)
7. 是否有乞丐感/寒酸感(beggar: bool)
8. 是否十七八岁年轻少年(young: bool)
9. 是否俊秀自信(handsome_confident: bool)
10. 是否与诸葛风长相相似(like_zhuge: bool)
11. 五官是否清晰无崩坏(face_ok: bool)
12. 全身是否完整(full_body: bool)
13. 袖子是否完整非无袖/背心(sleeves_ok: bool)
评分规则
- male/white_robe/cyan_sash/hair_half_tied/hands_on_hips/intact/young/handsome_confident/face_ok/full_body/sleeves_ok 任一不满足 pass=false
- beggar=true like_zhuge=true pass=false
输出JSON: {"candidate": N, "pass": bool, "score": 0-100, "male": bool, "white_robe": bool, "cyan_sash": bool, "hair_half_tied": bool, "hands_on_hips": bool, "intact": bool, "beggar": bool, "young": bool, "handsome_confident": bool, "like_zhuge": bool, "face_ok": bool, "full_body": bool, "sleeves_ok": bool, "summary": "一句话"}"""
def encode(p):
with open(p, "rb") as f:
b64 = base64.b64encode(f.read()).decode()
return f"data:image/png;base64,{b64}"
def q(img):
b = json.dumps({"model": "qwen-vl-max", "input": {"messages": [{"role": "user", "content": [{"image": img}, {"text": PROMPT}]}]}})
for ep in [EP, FE]:
try:
r = subprocess.run(["curl", "-s", "-m", "90", "--noproxy", "*", "-X", "POST", ep,
"-H", f"Authorization: Bearer {AK}", "-H", "Content-Type: application/json", "--data-binary", "@-"],
input=b, capture_output=True, text=True, timeout=95)
if not r.stdout.strip():
continue
d = json.loads(r.stdout)
if "output" in d:
c = d["output"]["choices"][0]["message"]["content"][0]["text"]
if "```" in c:
c = c.split("```")[1]
c = c[4:] if c.startswith("json") else c
c = c.split("```")[0]
return json.loads(c.strip())
except Exception as e:
continue
return None
rs = []
for i, p in enumerate(CS):
print(f"[{i+1}/4]", end=" ", flush=True)
qc = q(encode(p))
if qc:
print(f"s={qc.get('score', '?')} pass={qc.get('pass', '?')}")
rs.append(qc)
else:
print("")
rs.append({"candidate": i+1, "pass": False, "error": "api"})
ok = sum(1 for r in rs if r.get("pass"))
print(f"\n通过: {ok}/4")
for r in rs:
if not r.get("pass") and "error" not in r:
iss = []
for k, v in r.items():
if isinstance(v, bool) and v:
if k in ["male", "white_robe", "cyan_sash", "hair_half_tied", "hands_on_hips", "intact", "young", "handsome_confident", "face_ok", "full_body", "sleeves_ok"]:
pass
elif k == "beggar": iss.append("乞丐感")
elif k == "like_zhuge": iss.append("像诸葛风")
else: iss.append(k)
elif isinstance(v, bool) and not v and k in ["male", "white_robe", "cyan_sash", "hair_half_tied", "hands_on_hips", "intact", "young", "handsome_confident", "face_ok", "full_body", "sleeves_ok"]:
iss.append(f"{k}")
print(f"{r.get('candidate')} ✗: {','.join(iss) if iss else '其他'} s={r.get('score')}")
rep = {"spec": "VA-05-001-V2-R6", "method": "qwen-vl-auto", "results": rs, "summary": {"passed": ok, "total": 4}}
with open(DIR / "QC-R6-BATCH.json", "w", encoding="utf-8") as f:
json.dump(rep, f, ensure_ascii=False, indent=2)
print(f"\n报告: QC-R6-BATCH.json")