guanghulab/video-ai-system/tools/qc_char003_r9.py
bingshuo 6d5fcdfb21
Some checks failed
自动更新代码和重启 / update-and-restart (push) Has been cancelled
CI检查 + 自动部署 / check (push) Has been cancelled
CI检查 + 自动部署 / deploy (push) Has been cancelled
ICE-GL-ZY001 · CHAR-003苏白V2 R9批准入库 · 风格基准锁定 · D161
2026-07-02 16:18:26 +08:00

88 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""CHAR-003 V2 R9 QC · 去麻子 · Qwen-VL"""
import sys, os, json, base64, subprocess
from pathlib import Path
ROOT = Path('D:/WorkBuddy/guanghulab/video-ai-system')
env = {}
for line in open(ROOT/".env"):
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", "")
FE = "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation"
DIR = ROOT / "assets/candidates/D158-VA-05-001-V2/CHAR-003-SuBai/R9"
CS = [DIR / f"CHAR-003-V2-R9-candidate-{i:02d}-1080x1920.png" for i in range(1, 5)]
PROMPT = """检查CHAR-003苏白立绘。设定18岁男性白色长衫青色腰带黑色长发半束半披双手叉腰自信开朗俊秀略带痞气穷但不自卑。
判定JSON布尔小写
1. male: 是否为男性?
2. young: 是否看起来17-18岁年轻少年
3. white_robe: 是否白色长衫?
4. cyan_sash: 是否有青色/深色腰带?
5. hair_half_tied: 是否黑色长发半束半披(头顶束发带)?
6. hands_on_hips: 是否双手叉腰?
7. intact: 服装是否完整干净无破损?
8. beggar: 是否有乞丐感/寒酸感true=有,一票否决)
9. handsome_confident: 是否俊秀自信?
10. like_zhuge: 是否与诸葛风长相相似true=相似,一票否决)
11. face_ok: 五官是否清晰无崩坏?
12. full_body: 是否全身完整?
13. freckles: 脸上是否有雀斑/麻子/斑点true=有,一票否决)
14. sleeves_ok: 袖子是否完整(非无袖背心)?
pass = 男性且年轻且白衫且腰带且半束发且叉腰且完整且俊秀且五官清晰且全身完整且无麻子且无袖正常,且 beggar=false 且 like_zhuge=false。
输出格式:{{"candidate": N, "pass": bool, "score": 0-100, "male": bool, "young": bool, ...}} 以及 "summary": "一句话"""
def enc(p):
with open(p, "rb") as f:
return f"data:image/png;base64,{base64.b64encode(f.read()).decode()}"
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)
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.replace("```", "").strip()
return json.loads(c)
except Exception as e:
print(e)
continue
return None
rs = []
for i, p in enumerate(CS):
print(f"[{i+1}/4]", end=" ", flush=True)
qc = q(enc(p))
if qc:
qc["candidate"] = i + 1
print(f"pass={qc.get('pass')} s={qc.get('score')}")
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 = [k for k, v in r.items() if isinstance(v, bool) and v and k in ["beggar", "like_zhuge", "freckles"]]
iss += [k for k, v in r.items() if isinstance(v, bool) and not v and k in ["male", "young", "white_robe", "cyan_sash", "hair_half_tied", "hands_on_hips", "intact", "handsome_confident", "face_ok", "full_body", "sleeves_ok"]]
print(f"{r['candidate']} ✗: {','.join(iss) if iss else '其他'} s={r.get('score')}")
with open(DIR / "QC-R9-BATCH.json", "w", encoding="utf-8") as f:
json.dump({"spec": "VA-05-001-V2-R9", "method": "qwen-vl-auto", "results": rs, "summary": {"passed": ok, "total": 4}}, f, ensure_ascii=False, indent=2)
print("报告: QC-R9-BATCH.json")