guanghulab/video-ai-system/tools/qc_char004_r3.py
冰朔 c81307004d
Some checks failed
自动更新代码和重启 / update-and-restart (push) Has been cancelled
CI检查 + 自动部署 / check (push) Has been cancelled
CI检查 + 自动部署 / deploy (push) Has been cancelled
铸渊自检 / health-check (push) Has been cancelled
D158 · 会话收尾 · 进度+脚本+QC报告入库(不含候选图)
- CURRENT-STATE 更新至D158
- 生成脚本: char004/char004_r3/prop002/prop002_r3/prop003 全量
- QC脚本: qc_char004_r2/r3 + qc_prop_r2
- QC报告: CHAR-004 R2~R5 + PROP全量
- generation-meta: 所有候选批次元数据
- 候选图(JPG)保留本地不入库
2026-07-02 01:07:43 +08:00

100 lines
4.7 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-004 R3 候选质检 · Qwen-VL · 反乞丐滑坡版标准"""
import sys, os, json, base64, subprocess
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from secrets_loader import secret, endpoint
API_KEY = secret("SC-004")
EP = endpoint("EPT-002")
FALLBACK_EP = "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation"
CANDIDATES_DIR = os.path.expanduser("~/guanghulab/video-ai-system/assets/candidates/D157-VA-05-001-R3/CHAR-004-ZhugeFeng")
CANDIDATES = [f"{CANDIDATES_DIR}/CHAR-004-R3-candidate-{i:02d}-1080x1920.jpg" for i in range(1, 5)]
QC_PROMPT = """你是动画资产质量检查员。检查这张CHAR-004诸葛风立绘候选图。规范要求清贫但整洁、有骨气、不是乞丐不是流民。服装干净无破洞无补丁无污渍。
逐项检查并输出严格JSON
1. 裤子膝盖有没有破洞/破损?(hole_in_pants: true/false)
2. 衣服下摆有没有毛边/撕裂?(frayed_hem: true/false)
3. 鞋子干不干净?有泥污没?(dirty_shoes: true/false)
4. 衣服有没有污渍?(stains: true/false)
5. 有没有补丁?(patches: true/false)
6. 整体气质像不像乞丐/流浪汉?(beggar_look: true/false)
7. 五官清晰无崩坏吗?(face_intact: true/false)
8. 肢体比例正常吗?(anatomy_ok: true/false)
9. 画面清晰吗?(clear_image: true/false)
10. 全身从头到鞋完整吗?(full_body: true/false)
11. 和苏白CHAR-003的脸像不像(similar_to_sb: true/false)
注意:
- "磨白"不等于"破洞"——裤子膝盖只有颜色变浅、没有破损开口hole_in_pants就是false
- 鞋子没有明显泥污就是dirty_shoes=false
- 衣领轻微磨毛不等于毛边撕裂——frayed_hem特指明显破损/散开/撕裂
输出严格JSON不要额外文字
{"candidate":N,"pass":true/false,"score":0-100,"hole_in_pants":bool,"frayed_hem":bool,"dirty_shoes":bool,"stains":bool,"patches":bool,"beggar_look":bool,"face_intact":bool,"anatomy_ok":bool,"clear_image":bool,"full_body":bool,"similar_to_sb":bool,"summary":"一句话"}"""
def encode_image(path):
with open(path, "rb") as f:
b64 = base64.b64encode(f.read()).decode()
ext = path.rsplit(".", 1)[-1].lower()
mime = {"jpg": "jpeg", "jpeg": "jpeg", "png": "png"}.get(ext, "jpeg")
return f"data:image/{mime};base64,{b64}"
def call_qwen(img):
body = json.dumps({"model":"qwen-vl-max","input":{"messages":[{"role":"user","content":[{"image":img},{"text":QC_PROMPT}]}]}})
for ep in [EP, FALLBACK_EP]:
try:
proc = subprocess.run(["curl","-s","-m","60","--noproxy","*","-X","POST",ep,"-H",f"Authorization: Bearer {API_KEY}","-H","Content-Type: application/json","--data-binary","@-"],input=body,capture_output=True,text=True,timeout=65)
if not proc.stdout.strip(): continue
resp = json.loads(proc.stdout)
if "output" in resp:
content = resp["output"]["choices"][0]["message"]["content"][0]["text"]
if "```" in content:
content = content.split("```")[1]
if content.startswith("json"): content = content[4:]
content = content.split("```")[0]
return json.loads(content.strip())
except: continue
return None
def main():
print("CHAR-004 R3 QC · Qwen-VL")
results = []
for i, p in enumerate(CANDIDATES):
print(f"[{i+1}/4]", end=" ", flush=True)
qc = call_qwen(encode_image(p))
if qc:
s = qc.get("score","?")
ok = qc.get("pass","?")
h = qc.get("hole_in_pants","?")
print(f"score={s} pass={ok} 破洞={h}")
results.append(qc)
else:
print("")
results.append({"candidate":i+1,"pass":False,"error":"api"})
ok = sum(1 for r in results if r.get("pass"))
print(f"\n通过: {ok}/4")
for r in results:
if not r.get("pass"):
issues = []
if r.get("hole_in_pants"): issues.append("破洞")
if r.get("frayed_hem"): issues.append("毛边")
if r.get("dirty_shoes"): issues.append("脏鞋")
if r.get("stains"): issues.append("污渍")
if r.get("beggar_look"): issues.append("乞丐化")
print(f" 候选{r.get('candidate')} ✗: {', '.join(issues) if issues else '评分不足'} (score={r.get('score')})")
report = {"spec":"VA-05-001-R3","asset":"CHAR-004","tool":"qwen-vl-max","results":results,"summary":{"passed":ok,"total":4}}
with open(os.path.join(CANDIDATES_DIR,"QC-R3-BATCH.json"),"w") as f:
json.dump(report,f,ensure_ascii=False,indent=2)
print(f"\n报告: QC-R3-BATCH.json")
return 0
if __name__=="__main__":
sys.exit(main())