冰朔 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

141 lines
5.3 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
"""QC: PROP-002牌匾 + PROP-003广告牌 R2候选图 · 闸门1"""
import sys, os, json, base64, time, subprocess
from pathlib import Path
sys.path.insert(0, os.path.dirname(__file__))
from secrets_loader import secret, endpoint
AK = secret("SC-004")
URL = endpoint("EPT-002")
ROOT = Path(__file__).resolve().parents[1]
# 候选图路径
CANDIDATES = {
"PROP-002": {
"name": "天道宗无字牌匾",
"spec": "深棕色木质无字牌匾长方形横版边缘古朴雕花装饰3D动漫卡通渲染风格纯白背景无文字",
"reject": ["写实", "照片感", "真实木纹", "产品摄影", "文字", "汉字", "字母"],
"dir": ROOT / "assets/candidates/D157-PROP-002-R2/PROP-002-Tiandao-Plaque",
"files": [f"PROP-002-R2-candidate-{i:02d}-1080x1920.jpg" for i in range(1, 5)]
},
"PROP-003": {
"name": "免费招徒广告牌",
"spec": "木制立式支架+米黄色空白宣纸底板原木色支架朴素稳固宣纸干净无字墨迹修仙世界摊位宣传木板3D动漫卡通渲染纯白背景",
"reject": ["写实", "照片感", "画架", "easel", "美术画架", "真实木纹", "产品图", "文字", "墨迹"],
"dir": ROOT / "assets/candidates/D157-PROP-003-R2/PROP-003-Recruit-Board",
"files": [f"PROP-003-R2-candidate-{i:02d}-1080x1920.jpg" for i in range(1, 5)]
}
}
def encode_image(path):
with open(path, "rb") as f:
return base64.b64encode(f.read()).decode()
def qwen_vl_qc(img_b64, asset_name, spec, reject_words):
"""调用Qwen-VL质检单张图"""
prompt = f"""你是一个动态漫资产生成质检员。请严格检查这张图片。
资产名称:{asset_name}
规范要求:{spec}
请逐项检查并打分0-100
1. 风格匹配度是否为3D动漫卡通渲染风格非写实/照片感)
2. 内容准确性(是否包含规范要求的所有元素)
3. 一票否决项检查(以下任意一项出现直接判不合格):
- 是否有文字/汉字/字母?
- 是否是写实/照片感风格?
- 是否像产品摄影图?
- 对于PROP-003是否是画架(easel)样式而不是摊位木板?
输出格式严格JSON
{{"score": <0-100>, "style_ok": <bool>, "content_ok": <bool>, "has_text": <bool>, "is_photorealistic": <bool>, "is_easel": <bool>, "issues": ["问题列表"], "verdict": "PASS/FAIL"}}"""
payload = {
"model": "qwen-vl-max-latest",
"input": {
"messages": [
{"role": "user", "content": [
{"image": f"data:image/jpeg;base64,{img_b64}"},
{"text": prompt}
]}
]
},
"parameters": {"enable_thinking": False}
}
r = subprocess.run(
["curl", "-s", "-m", "60", "--noproxy", "*", "-X", "POST", URL,
"-H", f"Authorization: Bearer {AK}",
"-H", "Content-Type: application/json",
"-d", json.dumps(payload)],
capture_output=True, text=True, timeout=65
)
try:
d = json.loads(r.stdout)
content = d["output"]["choices"][0]["message"]["content"]
# 提取JSON
import re
m = re.search(r'\{[^{}]*\}', content)
if m:
return json.loads(m.group())
return {"score": 0, "verdict": "FAIL", "issues": ["无法解析VL响应"], "raw": content[:200]}
except Exception as e:
return {"score": 0, "verdict": "FAIL", "issues": [f"API错误: {e}"]}
def main():
if not AK:
print("✗ SC-004密钥未找到"); return 1
results = {}
for aid, cfg in CANDIDATES.items():
print(f"\n{'='*50}")
print(f"QC: {aid} · {cfg['name']}")
print(f"{'='*50}")
results[aid] = []
for fname in cfg["files"]:
fpath = cfg["dir"] / fname
if not fpath.exists():
print(f" ✗ 文件不存在: {fname}")
continue
print(f"\n 检查: {fname} ...", end=" ", flush=True)
img_b64 = encode_image(str(fpath))
qc = qwen_vl_qc(img_b64, cfg["name"], cfg["spec"], cfg["reject"])
v = qc.get("verdict", "?")
s = qc.get("score", "?")
issues = qc.get("issues", [])
print(f"[{v}] score={s}")
for iss in issues:
print(f" ⚠️ {iss}")
qc["_file"] = fname
results[aid].append(qc)
time.sleep(1)
# 输出汇总
print(f"\n\n{'='*60}")
print("QC 汇总报告")
print(f"{'='*60}")
for aid, qlist in results.items():
print(f"\n{aid}")
for q in qlist:
fn = q.get("_file", "?")
v = q.get("verdict", "?")
s = q.get("score", "?")
ph = q.get("is_photorealistic", False)
easel = q.get("is_easel", False)
has_txt = q.get("has_text", False)
flag = ""
if ph: flag += " 写实"
if easel: flag += " 画架"
if has_txt: flag += " 有字"
print(f" {fn}: {v} (score={s}){flag}")
# 写入QC报告
report_path = ROOT / "assets/candidates/D157-PROP-QC-report.json"
with open(report_path, "w") as f:
json.dump(results, f, ensure_ascii=False, indent=2)
print(f"\n📋 QC报告已写入: {report_path}")
return 0
if __name__ == "__main__":
sys.exit(main())