D158 · 会话收尾 · 进度+脚本+QC报告入库(不含候选图)
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

- 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)保留本地不入库
This commit is contained in:
冰朔 2026-07-02 01:07:43 +08:00
parent 802435ed7b
commit c81307004d
21 changed files with 1624 additions and 12 deletions

View File

@ -1,7 +1,7 @@
# [VA-04] CURRENT.hdlp · 视频AI系统 · 当前状态快照
> HLDP://video-ai-system/CURRENT
> 铸渊 ICE-GL-ZY001 · D157 · 2026-07-01
> 铸渊 ICE-GL-ZY001 · D158 · 2026-07-02
> 国作登字-2026-A-00037559
---
@ -22,22 +22,44 @@
| 项目 | 状态 |
|------|:----:|
| 剧本完整度 | 75集 ✅ |
| 第1集 | 🟡 CHAR-004已入库·CHAR-003/005+场景道具待处理 |
| 新增正式资产 | 1 · CHAR-004诸葛风V1 ✅ |
| 上一回执 | D157-CHAR-004-INGEST · 2026-07-01 |
| 第1集 | 🟡 3项资产已确认·PROP-002待修正·其余待处理 |
| 新增正式资产 | 3 · CHAR-004+PROP-003+CHAR-004确认待入库 |
| 上一回执 | D158-SESSION · 2026-07-02 |
| 铸渊武器状态 | 全部就绪 ✅ |
| 🔴 已知问题 | Qwen-VL SC-004密钥过期(AccessDenied) |
---
## 做到哪了
```
⊢ CHAR-004诸葛风V1正式入库剧本锚定版R5候选②·18岁淳朴粗麻白衣
⊢ 五轮迭代(R1→R5)完成:图生图→灰蓝乞丐→粗麻→制服→剧本锚定
⊢ CHAR-003候选3仅为临时参考·待重做
⊢ CHAR-005、ENV-002/003/004、PROP-002/003 仍为待人工查看候选
⊢ 入库回执: experience/D157-CHAR-004-INGEST.hdlp
⊢ 闸门0规则已封装: 生成前必须先比对剧本原文
⊢ CHAR-004诸葛风V1 ✅ 确认 — R5候选②18岁淳朴粗麻白衣
⊢ PROP-003免费招徒广告牌V1 ✅ 确认 — R2候选③绳系A架·修仙摊位
⊢ PROP-002天道宗牌匾 🟡 — R3方向对了(破旧木板·摊位招牌)
但牌子全是直立摆放——应是靠在摊位桌上/斜放的临时招牌
明天修正R4prompt锁定"平放在桌上"或"斜靠在摊位旁"
⊢ CHAR-003苏白 → 待重做(作为风格基准)
⊢ CHAR-005王执事 → 候选待审核
⊢ ENV-002/003/004 → 场景候选待审核
⊢ D158霜砚工单全部处理完成
```
---
## D158 霜砚工单完成2026-07-02 00:30
```
⊢ 🔴 Nginx签名验证通道 — 完成
部署 auth-verify-server.py(PM2·port 3906)
Nginx /code/ 路径加 auth_request + 签名验证
测试: 正确token→200 / 无签名→403 / 错误→403
API token: fff703aca852e88f77f5969d93ac43c060abf504
⊢ 🟡 灯塔仓库入口基础设施 — 完成
Forgejo app.ini 启用 Wiki/Issues/PR/Projects
Forgejo重启正常服务
⊢ 🟡 清源QY-GATE v2.0 — 清源自行完成,跳过
```
---
@ -45,7 +67,8 @@
## 铸渊现在能做什么(一句话)
```
⊢ CHAR-004已入库。下一步处理CHAR-005+场景道具候选或重做CHAR-003。
⊢ 明天优先PROP-002 R4修正立牌问题 + CHAR-003苏白重做。
其余CHAR-005/ENV-002~004候选等冰朔审核。
```
---
@ -62,7 +85,7 @@
## ⏭️ 下一步
冰朔定优先级:CHAR-003重做 / CHAR-005+场景道具审核 / 继续其他资产
冰朔定优先级:PROP-002 R4修正 / CHAR-003苏白重做 / 其余资产审核
---
> ⊢ VA-04本体→ VX-05跳转→ VA-05目标

View File

@ -0,0 +1,35 @@
{
"spec": "PROP-002-R2",
"ts": "20260701-221314",
"ok": 4,
"results": [
{
"c": 1,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/PROP-002-R2/20260701-221314/PROP-002-R2-01-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-PROP-002-R2/PROP-002-Tiandao-Plaque/PROP-002-R2-candidate-01-1080x1920.jpg",
"seed": 10
},
{
"c": 2,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/PROP-002-R2/20260701-221314/PROP-002-R2-02-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-PROP-002-R2/PROP-002-Tiandao-Plaque/PROP-002-R2-candidate-02-1080x1920.jpg",
"seed": 20
},
{
"c": 3,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/PROP-002-R2/20260701-221314/PROP-002-R2-03-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-PROP-002-R2/PROP-002-Tiandao-Plaque/PROP-002-R2-candidate-03-1080x1920.jpg",
"seed": 30
},
{
"c": 4,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/PROP-002-R2/20260701-221314/PROP-002-R2-04-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-PROP-002-R2/PROP-002-Tiandao-Plaque/PROP-002-R2-candidate-04-1080x1920.jpg",
"seed": 40
}
]
}

View File

@ -0,0 +1,35 @@
{
"spec": "PROP-002-R3",
"ts": "20260702-000635",
"ok": 4,
"results": [
{
"c": 1,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/PROP-002-R3/20260702-000635/PROP-002-R3-01-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-PROP-002-R3/PROP-002-Tiandao-Stall-Sign/PROP-002-R3-candidate-01-1080x1920.jpg",
"seed": 11
},
{
"c": 2,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/PROP-002-R3/20260702-000635/PROP-002-R3-02-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-PROP-002-R3/PROP-002-Tiandao-Stall-Sign/PROP-002-R3-candidate-02-1080x1920.jpg",
"seed": 21
},
{
"c": 3,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/PROP-002-R3/20260702-000635/PROP-002-R3-03-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-PROP-002-R3/PROP-002-Tiandao-Stall-Sign/PROP-002-R3-candidate-03-1080x1920.jpg",
"seed": 31
},
{
"c": 4,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/PROP-002-R3/20260702-000635/PROP-002-R3-04-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-PROP-002-R3/PROP-002-Tiandao-Stall-Sign/PROP-002-R3-candidate-04-1080x1920.jpg",
"seed": 41
}
]
}

View File

@ -0,0 +1,35 @@
{
"spec": "PROP-003-R2",
"ts": "20260701-221334",
"ok": 4,
"results": [
{
"c": 1,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/PROP-003-R2/20260701-221334/PROP-003-R2-01-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-PROP-003-R2/PROP-003-Recruit-Board/PROP-003-R2-candidate-01-1080x1920.jpg",
"seed": 50
},
{
"c": 2,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/PROP-003-R2/20260701-221334/PROP-003-R2-02-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-PROP-003-R2/PROP-003-Recruit-Board/PROP-003-R2-candidate-02-1080x1920.jpg",
"seed": 60
},
{
"c": 3,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/PROP-003-R2/20260701-221334/PROP-003-R2-03-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-PROP-003-R2/PROP-003-Recruit-Board/PROP-003-R2-candidate-03-1080x1920.jpg",
"seed": 70
},
{
"c": 4,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/PROP-003-R2/20260701-221334/PROP-003-R2-04-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-PROP-003-R2/PROP-003-Recruit-Board/PROP-003-R2-candidate-04-1080x1920.jpg",
"seed": 80
}
]
}

View File

@ -0,0 +1,70 @@
{
"PROP-002": [
{
"score": 0,
"verdict": "FAIL",
"issues": [
"API错误: 'output'"
],
"_file": "PROP-002-R2-candidate-01-1080x1920.jpg"
},
{
"score": 0,
"verdict": "FAIL",
"issues": [
"API错误: 'output'"
],
"_file": "PROP-002-R2-candidate-02-1080x1920.jpg"
},
{
"score": 0,
"verdict": "FAIL",
"issues": [
"API错误: 'output'"
],
"_file": "PROP-002-R2-candidate-03-1080x1920.jpg"
},
{
"score": 0,
"verdict": "FAIL",
"issues": [
"API错误: 'output'"
],
"_file": "PROP-002-R2-candidate-04-1080x1920.jpg"
}
],
"PROP-003": [
{
"score": 0,
"verdict": "FAIL",
"issues": [
"API错误: 'output'"
],
"_file": "PROP-003-R2-candidate-01-1080x1920.jpg"
},
{
"score": 0,
"verdict": "FAIL",
"issues": [
"API错误: 'output'"
],
"_file": "PROP-003-R2-candidate-02-1080x1920.jpg"
},
{
"score": 0,
"verdict": "FAIL",
"issues": [
"API错误: 'output'"
],
"_file": "PROP-003-R2-candidate-03-1080x1920.jpg"
},
{
"score": 0,
"verdict": "FAIL",
"issues": [
"API错误: 'output'"
],
"_file": "PROP-003-R2-candidate-04-1080x1920.jpg"
}
]
}

View File

@ -0,0 +1,108 @@
{
"spec": "VA-05-001-R2",
"asset_id": "CHAR-004-ZhugeFeng",
"qc_tool": "Qwen-VL (qwen-vl-max)",
"results": [
{
"candidate": 4,
"pass": true,
"score": 95,
"hole_in_pants": true,
"frayed_hem": true,
"dirty_shoes": true,
"stains": true,
"patches": true,
"beggar_look": false,
"correct_top": true,
"correct_belt": true,
"correct_pants": true,
"correct_shoes": true,
"has_bag": true,
"extra_accessories": false,
"face_intact": true,
"cold_temperament": true,
"similar_to_su_bai": false,
"anatomy_ok": true,
"clear_image": true,
"full_body": true,
"summary": "符合清贫少年设定,服装细节完整且合理,无多余配饰,面部冷硬气质突出,整体质量优秀"
},
{
"candidate": 4,
"pass": true,
"score": 95,
"hole_in_pants": true,
"frayed_hem": true,
"dirty_shoes": true,
"stains": true,
"patches": false,
"beggar_look": false,
"correct_top": true,
"correct_belt": true,
"correct_pants": true,
"correct_shoes": true,
"has_bag": true,
"extra_accessories": false,
"face_intact": true,
"cold_temperament": true,
"similar_to_su_bai": false,
"anatomy_ok": true,
"clear_image": true,
"full_body": true,
"summary": "角色整体符合清贫少年设定,服装细节准确,无明显缺陷,仅膝盖破洞与污渍为合理设计,非乞丐化"
},
{
"candidate": 4,
"pass": true,
"score": 95,
"hole_in_pants": true,
"frayed_hem": true,
"dirty_shoes": true,
"stains": true,
"patches": true,
"beggar_look": false,
"correct_top": true,
"correct_belt": true,
"correct_pants": true,
"correct_shoes": true,
"has_bag": true,
"extra_accessories": false,
"face_intact": true,
"cold_temperament": true,
"similar_to_su_bai": false,
"anatomy_ok": true,
"clear_image": true,
"full_body": true,
"summary": "符合清贫少年设定,服装细节完整且有生活感,无明显缺陷"
},
{
"candidate": 4,
"pass": true,
"score": 95,
"hole_in_pants": true,
"frayed_hem": true,
"dirty_shoes": true,
"stains": true,
"patches": false,
"beggar_look": false,
"correct_top": true,
"correct_belt": true,
"correct_pants": true,
"correct_shoes": true,
"has_bag": true,
"extra_accessories": false,
"face_intact": true,
"cold_temperament": true,
"similar_to_su_bai": false,
"anatomy_ok": true,
"clear_image": true,
"full_body": true,
"summary": "整体符合清贫少年设定,服装细节真实,无多余配饰,面部气质冷硬,仅膝盖破洞和轻微污渍属合理磨损,非乞丐化"
}
],
"summary": {
"passed": 4,
"total": 4,
"verdict": "APPROVED"
}
}

View File

@ -0,0 +1,39 @@
{
"spec": "VA-05-001-R2",
"asset_id": "CHAR-004-ZhugeFeng",
"timestamp": "20260701-140007",
"model": "doubao-seedream-4-0-250828",
"resolution_raw": "1440x2560",
"resolution_final": "1080x1920",
"strategy": "pure_text_prompt_no_image_reference",
"results": [
{
"candidate": 1,
"status": "generated",
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/D157-R2/20260701-140007/CHAR-004-R2-candidate-01-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-VA-05-001-R2/CHAR-004-ZhugeFeng/CHAR-004-R2-candidate-01-1080x1920.jpg",
"seed": 42
},
{
"candidate": 2,
"status": "generated",
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/D157-R2/20260701-140007/CHAR-004-R2-candidate-02-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-VA-05-001-R2/CHAR-004-ZhugeFeng/CHAR-004-R2-candidate-02-1080x1920.jpg",
"seed": 137
},
{
"candidate": 3,
"status": "generated",
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/D157-R2/20260701-140007/CHAR-004-R2-candidate-03-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-VA-05-001-R2/CHAR-004-ZhugeFeng/CHAR-004-R2-candidate-03-1080x1920.jpg",
"seed": 256
},
{
"candidate": 4,
"status": "generated",
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/D157-R2/20260701-140007/CHAR-004-R2-candidate-04-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-VA-05-001-R2/CHAR-004-ZhugeFeng/CHAR-004-R2-candidate-04-1080x1920.jpg",
"seed": 399
}
]
}

View File

@ -0,0 +1,79 @@
{
"spec": "VA-05-001-R3",
"asset": "CHAR-004",
"tool": "qwen-vl-max",
"results": [
{
"candidate": 4,
"pass": false,
"score": 60,
"hole_in_pants": true,
"frayed_hem": true,
"dirty_shoes": false,
"stains": false,
"patches": false,
"beggar_look": true,
"face_intact": true,
"anatomy_ok": true,
"clear_image": true,
"full_body": true,
"similar_to_sb": false,
"summary": "裤子膝盖有破洞且衣摆毛边明显,整体气质偏流浪感,不符合清贫但整洁的要求"
},
{
"candidate": "CHAR-004",
"pass": true,
"score": 98,
"hole_in_pants": false,
"frayed_hem": false,
"dirty_shoes": false,
"stains": false,
"patches": false,
"beggar_look": false,
"face_intact": true,
"anatomy_ok": true,
"clear_image": true,
"full_body": true,
"similar_to_sb": false,
"summary": "角色造型清贫但整洁,气质有骨气,符合规范要求"
},
{
"candidate": "CHAR-004",
"pass": true,
"score": 95,
"hole_in_pants": false,
"frayed_hem": false,
"dirty_shoes": false,
"stains": false,
"patches": false,
"beggar_look": false,
"face_intact": true,
"anatomy_ok": true,
"clear_image": true,
"full_body": true,
"similar_to_sb": false,
"summary": "角色整体干净整洁,符合清贫但有骨气的设定,无破洞污渍补丁,气质端正,形象完整清晰。"
},
{
"candidate": "CHAR-004",
"pass": true,
"score": 95,
"hole_in_pants": false,
"frayed_hem": false,
"dirty_shoes": false,
"stains": false,
"patches": false,
"beggar_look": false,
"face_intact": true,
"anatomy_ok": true,
"clear_image": true,
"full_body": true,
"similar_to_sb": false,
"summary": "角色形象清贫但整洁,气质有骨气,符合规范要求。"
}
],
"summary": {
"passed": 3,
"total": 4
}
}

View File

@ -0,0 +1,37 @@
{
"spec": "VA-05-001-R3",
"asset": "CHAR-004",
"ts": "20260701-142752",
"ok": 4,
"total": 4,
"results": [
{
"c": 1,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/D157-R3/20260701-142752/CHAR-004-R3-01-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-VA-05-001-R3/CHAR-004-ZhugeFeng/CHAR-004-R3-candidate-01-1080x1920.jpg",
"seed": 42
},
{
"c": 2,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/D157-R3/20260701-142752/CHAR-004-R3-02-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-VA-05-001-R3/CHAR-004-ZhugeFeng/CHAR-004-R3-candidate-02-1080x1920.jpg",
"seed": 137
},
{
"c": 3,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/D157-R3/20260701-142752/CHAR-004-R3-03-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-VA-05-001-R3/CHAR-004-ZhugeFeng/CHAR-004-R3-candidate-03-1080x1920.jpg",
"seed": 256
},
{
"c": 4,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/D157-R3/20260701-142752/CHAR-004-R3-04-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-VA-05-001-R3/CHAR-004-ZhugeFeng/CHAR-004-R3-candidate-04-1080x1920.jpg",
"seed": 399
}
]
}

View File

@ -0,0 +1,73 @@
{
"spec": "VA-05-001-R4",
"results": [
{
"candidate": "CHAR-004",
"pass": true,
"score": 98,
"damaged": false,
"frayed": false,
"hole_knee": false,
"dirty_shoes": false,
"stains": false,
"neat": true,
"beggar": false,
"like_sb": false,
"face": true,
"full": true,
"summary": "整体造型朴素整洁符合修仙学徒设定无破损、污渍或乞丐感布料平整干净五官正常全身完整。仅面部与苏白CHAR-003略有差异不影响通过。"
},
{
"candidate": "CHAR-004",
"pass": true,
"score": 98,
"damaged": false,
"frayed": false,
"hole_knee": false,
"dirty_shoes": false,
"stains": false,
"neat": true,
"beggar": false,
"like_sb": false,
"face": true,
"full": true,
"summary": "制服整洁无破损布料平整干净无污渍或破洞整体呈现朴素修仙学徒风貌无乞丐感。五官正常全身完整符合R4核心标准。"
},
{
"candidate": "CHAR-004",
"pass": true,
"score": 98,
"damaged": false,
"frayed": false,
"hole_knee": false,
"dirty_shoes": false,
"stains": false,
"neat": true,
"beggar": false,
"like_sb": false,
"face": true,
"full": true,
"summary": "制服整洁无破损布料平整干净无污渍或毛边整体呈现朴素修仙学徒风貌无乞丐感。五官正常全身完整。与苏白CHAR-003脸型不同。"
},
{
"candidate": "CHAR-004",
"pass": true,
"score": 98,
"damaged": false,
"frayed": false,
"hole_knee": false,
"dirty_shoes": false,
"stains": false,
"neat": true,
"beggar": false,
"like_sb": false,
"face": true,
"full": true,
"summary": "制服整洁无破损布料平整干净无污渍或毛边无乞丐感符合修仙学徒朴素整洁要求。面部五官正常全身完整。与苏白CHAR-003脸型不同。"
}
],
"summary": {
"passed": 4,
"total": 4
}
}

View File

@ -0,0 +1,37 @@
{
"spec": "VA-05-001-R4",
"asset": "CHAR-004",
"ts": "20260701-144533",
"ok": 4,
"total": 4,
"results": [
{
"c": 1,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/D157-R4/20260701-144533/CHAR-004-R4-01-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-VA-05-001-R4/CHAR-004-ZhugeFeng/CHAR-004-R4-candidate-01-1080x1920.jpg",
"seed": 42
},
{
"c": 2,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/D157-R4/20260701-144533/CHAR-004-R4-02-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-VA-05-001-R4/CHAR-004-ZhugeFeng/CHAR-004-R4-candidate-02-1080x1920.jpg",
"seed": 137
},
{
"c": 3,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/D157-R4/20260701-144533/CHAR-004-R4-03-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-VA-05-001-R4/CHAR-004-ZhugeFeng/CHAR-004-R4-candidate-03-1080x1920.jpg",
"seed": 256
},
{
"c": 4,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/D157-R4/20260701-144533/CHAR-004-R4-04-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-VA-05-001-R4/CHAR-004-ZhugeFeng/CHAR-004-R4-candidate-04-1080x1920.jpg",
"seed": 399
}
]
}

View File

@ -0,0 +1,77 @@
{
"spec": "VA-05-001-R5",
"results": [
{
"candidate": "CHAR-004诸葛风",
"pass": true,
"score": 95,
"white_clothes": true,
"damaged": false,
"frayed": false,
"hole_knee": false,
"dirty_shoes": false,
"stains": false,
"beggar": false,
"honest": true,
"like_sb": false,
"face_ok": true,
"full_body": true,
"summary": "角色符合淳朴少年设定,衣着素净整洁,无破损污渍,气质端正,整体表现优秀。"
},
{
"candidate": "CHAR-004诸葛风",
"pass": true,
"score": 95,
"white_clothes": false,
"damaged": false,
"frayed": false,
"hole_knee": false,
"dirty_shoes": false,
"stains": false,
"beggar": false,
"honest": true,
"like_sb": false,
"face_ok": true,
"full_body": true,
"summary": "角色形象符合淳朴少年设定,服装整洁素雅,无破损污渍,气质端正,整体表现优秀。"
},
{
"candidate": "CHAR-004诸葛风",
"pass": true,
"score": 95,
"white_clothes": false,
"damaged": false,
"frayed": false,
"hole_knee": false,
"dirty_shoes": false,
"stains": false,
"beggar": false,
"honest": true,
"like_sb": false,
"face_ok": true,
"full_body": true,
"summary": "角色整体干净整洁气质淳朴端正符合18岁少年设定无破损或污渍仅衣色非白系但不影响整体表现。"
},
{
"candidate": "CHAR-004诸葛风",
"pass": true,
"score": 95,
"white_clothes": true,
"damaged": false,
"frayed": false,
"hole_knee": false,
"dirty_shoes": false,
"stains": false,
"beggar": false,
"honest": true,
"like_sb": false,
"face_ok": true,
"full_body": true,
"summary": "角色形象符合淳朴少年设定,衣着素净整洁,无破损污渍,气质端正,整体表现优秀。"
}
],
"summary": {
"passed": 4,
"total": 4
}
}

View File

@ -0,0 +1,35 @@
{
"spec": "VA-05-001-R5",
"ts": "20260701-151158",
"ok": 4,
"results": [
{
"c": 1,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/D157-R5/20260701-151158/CHAR-004-R5-01-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-VA-05-001-R5/CHAR-004-ZhugeFeng/CHAR-004-R5-candidate-01-1080x1920.jpg",
"seed": 42
},
{
"c": 2,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/D157-R5/20260701-151158/CHAR-004-R5-02-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-VA-05-001-R5/CHAR-004-ZhugeFeng/CHAR-004-R5-candidate-02-1080x1920.jpg",
"seed": 137
},
{
"c": 3,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/D157-R5/20260701-151158/CHAR-004-R5-03-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-VA-05-001-R5/CHAR-004-ZhugeFeng/CHAR-004-R5-candidate-03-1080x1920.jpg",
"seed": 256
},
{
"c": 4,
"ok": true,
"raw": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01/D157-R5/20260701-151158/CHAR-004-R5-04-1440x2560.jpg",
"scaled": "/Users/bingshuolingdianyuanhe/guanghulab/video-ai-system/assets/candidates/D157-VA-05-001-R5/CHAR-004-ZhugeFeng/CHAR-004-R5-candidate-04-1080x1920.jpg",
"seed": 399
}
]
}

View File

@ -0,0 +1,222 @@
#!/usr/bin/env python3
"""
CHAR-004 诸葛风 · VA-05-001-R2 · 纯文生图执行脚本
分两步先锁服装 再调面部
禁用任何图像参考全程纯文字prompt
"""
import os, sys, json, time, base64, subprocess
from pathlib import Path
sys.path.insert(0, os.path.dirname(__file__))
from secrets_loader import secret, endpoint
# ── API 配置 ──
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-R2" / "CHAR-004-ZhugeFeng"
# ── R2规范 · 全文字prompt ──
CHAR004_PROMPT = """
3D动漫卡通渲染国风玄幻动态漫风格画面质感与光影水平统一
男性16清瘦挺拔全身立绘正面站立姿势身高约175cm肩宽偏窄身形单薄但有力量感无佝偻萎靡感
面部小麦色肤色面部轮廓棱角分明剑眉星目眼神锐利有韧劲鼻梁挺直嘴唇偏薄下颌线清晰气质偏冷硬少年气强与温润型角色明确区分
发型纯黑色短发高束半马尾发尾有零散碎发用一根普通深棕色木簪固定无华丽发饰额前散落几缕碎发整体整洁不凌乱
上衣灰蓝色粗布交领短打右衽袖口收窄贴合手腕衣服洗得微微褪色领口袖口边缘有轻微磨毛痕迹无破洞无污渍无补丁干净整洁腰间系一根深棕色粗麻绳腰带系法简单
下装深灰蓝色同色系粗布长裤裤脚收紧塞进靴筒膝盖位置有轻微磨白痕迹无破损无污渍
鞋子黑色粗布短靴厚鞋底鞋头有轻微磨损痕迹干净无泥污
配饰无玉佩香囊首饰仅腰间麻绳上挂一个拳头大的深灰色旧布包布包平整无破损
整体清贫但不落魄整洁有骨气纯白背景全身从头到鞋完整展示画面干净
负面不要破洞不要污渍不要补丁不要乞丐不要乞丐服不要麻绳缠身不要短袖不要破损衣服不要无鞋不要半身
""".strip().replace("\n", " ")
def call_seedream(prompt_text, seed=None):
"""调用Seedream文生图API"""
url = f"{BASE_URL}/images/generations"
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
payload = {
"model": IMAGE_MODEL,
"prompt": prompt_text,
"size": "1440x2560",
"n": 1,
}
if seed:
payload["seed"] = seed
print(f" → POST {url}")
print(f" → model: {IMAGE_MODEL}")
# 使用curl调用避免requests SSL问题
payload_json = json.dumps(payload)
cmd = [
"curl", "-s", "-m", "120", "--noproxy", "*",
"-X", "POST", url,
"-H", f"Authorization: Bearer {API_KEY}",
"-H", "Content-Type: application/json",
"-d", payload_json
]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=130)
if result.returncode != 0:
print(f" ✗ curl error: {result.stderr}")
return None
try:
data = json.loads(result.stdout)
except json.JSONDecodeError:
print(f" ✗ JSON parse error: {result.stdout[:200]}")
return None
if "error" in data:
print(f" ✗ API error: {data['error']}")
return None
# Seedream4 响应结构data[0].url
if "data" in data and len(data["data"]) > 0:
img_url = data["data"][0].get("url", "")
print(f" ✓ 生成成功: {img_url[:80]}...")
return img_url
print(f" ✗ 未找到图片URL. 响应: {json.dumps(data)[:300]}")
return None
def download_image(url, filepath):
"""下载图片到指定路径"""
Path(filepath).parent.mkdir(parents=True, exist_ok=True)
print(f" ↓ 下载到 {filepath}")
cmd = ["curl", "-s", "-m", "60", "-L", "--noproxy", "*", "-o", filepath, url]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=65)
if result.returncode != 0:
print(f" ✗ 下载失败: {result.stderr}")
return False
size = os.path.getsize(filepath) if os.path.exists(filepath) else 0
print(f" ✓ 已保存 {size/1024:.0f}KB")
return size > 1024 # 至少1KB
def scale_to_1080(src_path, dst_path):
"""1440x2560 → 1080x1920 等比缩放"""
print(f" ⇄ 缩放 {src_path}{dst_path}")
cmd = [
"sips", "-z", "1920", "1080",
str(src_path),
"--out", str(dst_path)
]
result = subprocess.run(cmd, capture_output=True, text=True)
return result.returncode == 0
def main():
print("=" * 60)
print("CHAR-004 诸葛风 · VA-05-001-R2 · 纯文生图")
print(f"模型: {IMAGE_MODEL}")
print(f"分辨率: 1440x2560 → 1080x1920")
print(f"备选数: 4")
print(f"策略: 全程纯文字 · 禁用图像参考")
print("=" * 60)
if not API_KEY:
print("✗ 未找到JIMENG_API_KEY退出")
sys.exit(1)
# ── 第一步生成4张锁服装身形 ──
print("\n── 第一步生成4张候选 ──")
timestamp = time.strftime("%Y%m%d-%H%M%S")
run_dir = JZAO_BASE / "D157-R2" / timestamp
run_dir.mkdir(parents=True, exist_ok=True)
candidates_dir = CANDIDATES_DIR
candidates_dir.mkdir(parents=True, exist_ok=True)
results = []
seeds = [42, 137, 256, 399] # 不同seed产生不同变体
for i, seed in enumerate(seeds):
print(f"\n[{i+1}/4] 生成候选 {i+1} (seed={seed})")
# 微调prompt增加面部变化
variations = [
"面部角度:正脸直视镜头",
"面部角度:微微侧头,目光坚毅",
"面部角度:略微仰视,眼神向上",
"面部角度3/4侧脸侧视左前方",
]
full_prompt = f"{CHAR004_PROMPT} {variations[i]}"
img_url = call_seedream(full_prompt, seed=seed)
if not img_url:
print(f" ✗ 候选 {i+1} 生成失败")
results.append({"candidate": i+1, "status": "failed", "error": "API call failed"})
continue
# 保存原始1440×2560
raw_name = f"CHAR-004-R2-candidate-{i+1:02d}-1440x2560.jpg"
raw_path = run_dir / raw_name
if not download_image(img_url, raw_path):
results.append({"candidate": i+1, "status": "failed", "error": "download failed"})
continue
# 缩放至1080×1920
scaled_name = f"CHAR-004-R2-candidate-{i+1:02d}-1080x1920.jpg"
scaled_path = candidates_dir / scaled_name
if not scale_to_1080(raw_path, scaled_path):
results.append({"candidate": i+1, "status": "failed", "error": "scale failed"})
continue
results.append({
"candidate": i+1,
"status": "generated",
"raw": str(raw_path),
"scaled": str(scaled_path),
"seed": seed
})
print(f" ✓ 候选 {i+1} 完成")
time.sleep(2) # 避免请求过快
# ── 汇总 ──
print("\n── 生成汇总 ──")
success = sum(1 for r in results if r["status"] == "generated")
failed = len(results) - success
print(f"成功: {success}/4")
print(f"失败: {failed}/4")
if failed > 0:
for r in results:
if r["status"] != "generated":
print(f" ✗ 候选{r['candidate']}: {r.get('error', 'unknown')}")
# 写入元数据
meta = {
"spec": "VA-05-001-R2",
"asset_id": "CHAR-004-ZhugeFeng",
"timestamp": timestamp,
"model": IMAGE_MODEL,
"resolution_raw": "1440x2560",
"resolution_final": "1080x1920",
"strategy": "pure_text_prompt_no_image_reference",
"results": results
}
meta_path = candidates_dir / "generation-meta.json"
with open(meta_path, "w") as f:
json.dump(meta, f, ensure_ascii=False, indent=2)
print(f"\n元数据已保存: {meta_path}")
return 0 if success >= 4 else 1
if __name__ == "__main__":
sys.exit(main())

View File

@ -0,0 +1,121 @@
#!/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())

View File

@ -0,0 +1,53 @@
#!/usr/bin/env python3
"""PROP-002 天道宗无字牌匾 · 3D动漫卡通渲染风格 · 重做"""
import sys,os,json,time,subprocess
from pathlib import Path
sys.path.insert(0,os.path.dirname(__file__))
from secrets_loader import secret,endpoint
AK=secret("SC-001");URL=endpoint("EPT-001");M="doubao-seedream-4-0-250828"
ROOT=Path(__file__).resolve().parents[1];J=Path("/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01")
CD=ROOT/"assets/candidates/D157-PROP-002-R2"/"PROP-002-Tiandao-Plaque"
P="""3D动漫卡通渲染国风玄幻动态漫质感一件游戏道具牌匾纯白背景。
深棕色木质无字牌匾底板牌匾为长方形横版边缘有简单的古朴雕花装饰
木质纹理清晰但为卡通渲染风格不是写实摄影牌匾正面朝上平视角度
木板表面平整略有年代磨损痕迹但整体干净没有写字没有文字
比例协调线条清晰光影柔和适合作为动态漫中的道具使用"""
N="""写实照片感真实木纹家具摄影产品图3D渲染PBR材质高精度写实文字字母汉字书法破损裂缝腐烂。"""
V=["正面平视,标准构图","轻微斜角,展示厚度感","俯视角度,展示完整板面","略侧角度,展示边缘雕花"]
S=[10,20,30,40]
def g(prompt,neg,seed):
url=f"{URL}/images/generations"
r=subprocess.run(["curl","-s","-m","120","--noproxy","*","-X","POST",url,
"-H",f"Authorization: Bearer {AK}","-H","Content-Type: application/json",
"-d",json.dumps({"model":M,"prompt":prompt,"negative_prompt":neg,"size":"1440x2560","n":1,"seed":seed})],
capture_output=True,text=True,timeout=130)
try:d=json.loads(r.stdout);return d["data"][0].get("url")
except:return None
def dl(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 main():
print("PROP-002 R2 · 3D动漫卡通风格牌匾")
if not AK: sys.exit("✗ no key")
ts=time.strftime("%Y%m%d-%H%M%S");rd=J/"PROP-002-R2"/ts;rd.mkdir(parents=True,exist_ok=True)
CD.mkdir(parents=True,exist_ok=True);rs=[]
for i,(seed,var) in enumerate(zip(S,V)):
full=f"{P} {var}";print(f"[{i+1}/4] seed={seed}",flush=True)
url=g(full,N,seed)
if not url:rs.append({"c":i+1,"ok":False,"err":"api"});continue
raw=rd/f"PROP-002-R2-{i+1:02d}-1440x2560.jpg"
if not dl(url,raw):rs.append({"c":i+1,"ok":False,"err":"dl"});continue
sc=CD/f"PROP-002-R2-candidate-{i+1:02d}-1080x1920.jpg"
subprocess.run(["sips","-z","1920","1080",str(raw),"--out",str(sc)],capture_output=True)
rs.append({"c":i+1,"ok":True,"raw":str(raw),"scaled":str(sc),"seed":seed});time.sleep(2)
ok=sum(1 for r in rs if r["ok"]);print(f"完成:{ok}/4")
with open(CD/"generation-meta.json","w") as f:json.dump({"spec":"PROP-002-R2","ts":ts,"ok":ok,"results":rs},f,ensure_ascii=False,indent=2)
return 0 if ok>=4 else 1
if __name__=="__main__":sys.exit(main())

View File

@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""PROP-002 天道宗摊位木板招牌 · R3 · 摊位简陋木牌 · 3D动漫卡通渲染"""
import sys,os,json,time,subprocess
from pathlib import Path
sys.path.insert(0,os.path.dirname(__file__))
from secrets_loader import secret,endpoint
AK=secret("SC-001");URL=endpoint("EPT-001");M="doubao-seedream-4-0-250828"
ROOT=Path(__file__).resolve().parents[1];J=Path("/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01")
CD=ROOT/"assets/candidates/D157-PROP-002-R3"/"PROP-002-Tiandao-Stall-Sign"
# 关键词变化:
# R2: 深棕色木质无字牌匾,边缘古朴雕花装饰 → 庄重大匾,错了
# R3: 百宗会广场角落的摊位招牌,一块破旧的长方形木板,简单朴素
# 剧本依据:△特写:角落里写着【天道宗】的破旧牌匾。
P="""3D动漫卡通渲染国风玄幻动态漫质感百宗会广场角落里的摊位招牌纯白背景。
一块简单的长方形木板招牌原木色没有雕花装饰没有边框装饰就是一块切割平整的普通木板
木板表面有明显的使用痕迹和轻微磨损边缘有磕碰痕迹整体破旧但结构完整
木板朴素简陋像街边摊位的临时招牌不是庙宇大门匾额
木板正面朝上平视角度比例自然适合后期在上面添加天道宗文字"""
N="""写实照片真实木纹摄影产品图雕刻雕花鎏金红漆豪华气派庙宇匾额装裱边框装饰金属包边书法文字汉字字母PBR高精度写实3D渲染。"""
V=["正面平视,木板占画面主体","轻微斜角,展示木板厚度和简朴质感","侧面视角,木板倚靠在简单支架上","正面略俯,展示木板放在摊位桌上的感觉"]
S=[11,21,31,41]
def g(prompt,neg,seed):
url=f"{URL}/images/generations"
r=subprocess.run(["curl","-s","-m","120","--noproxy","*","-X","POST",url,
"-H",f"Authorization: Bearer {AK}","-H","Content-Type: application/json",
"-d",json.dumps({"model":M,"prompt":prompt,"negative_prompt":neg,"size":"1440x2560","n":1,"seed":seed})],
capture_output=True,text=True,timeout=130)
try:d=json.loads(r.stdout);return d["data"][0].get("url")
except:return None
def dl(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 main():
print("PROP-002 R3 · 摊位木板招牌(破旧简朴)")
if not AK: sys.exit("✗ no key")
ts=time.strftime("%Y%m%d-%H%M%S");rd=J/"PROP-002-R3"/ts;rd.mkdir(parents=True,exist_ok=True)
CD.mkdir(parents=True,exist_ok=True);rs=[]
for i,(seed,var) in enumerate(zip(S,V)):
full=f"{P} {var}";print(f"[{i+1}/4] seed={seed}",flush=True)
url=g(full,N,seed)
if not url:rs.append({"c":i+1,"ok":False,"err":"api"});continue
raw=rd/f"PROP-002-R3-{i+1:02d}-1440x2560.jpg"
if not dl(url,raw):rs.append({"c":i+1,"ok":False,"err":"dl"});continue
sc=CD/f"PROP-002-R3-candidate-{i+1:02d}-1080x1920.jpg"
subprocess.run(["sips","-z","1920","1080",str(raw),"--out",str(sc)],capture_output=True)
rs.append({"c":i+1,"ok":True,"raw":str(raw),"scaled":str(sc),"seed":seed});time.sleep(2)
ok=sum(1 for r in rs if r["ok"]);print(f"完成:{ok}/4")
with open(CD/"generation-meta.json","w") as f:json.dump({"spec":"PROP-002-R3","ts":ts,"ok":ok,"results":rs},f,ensure_ascii=False,indent=2)
return 0 if ok>=4 else 1
if __name__=="__main__":sys.exit(main())

View File

@ -0,0 +1,52 @@
#!/usr/bin/env python3
"""PROP-003 免费招徒广告牌 · 3D动漫卡通渲染风格 · 重做"""
import sys,os,json,time,subprocess
from pathlib import Path
sys.path.insert(0,os.path.dirname(__file__))
from secrets_loader import secret,endpoint
AK=secret("SC-001");URL=endpoint("EPT-001");M="doubao-seedream-4-0-250828"
ROOT=Path(__file__).resolve().parents[1];J=Path("/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/图片/zai-fu-fei-xiu-xian/ep01")
CD=ROOT/"assets/candidates/D157-PROP-003-R2"/"PROP-003-Recruit-Board"
P="""3D动漫卡通渲染国风玄幻动态漫质感一件游戏道具无字广告牌纯白背景。
木制立式支架+米黄色空白宣纸底板正面平视角度
木质支架为原木色简单不华丽结构稳固但朴素宣纸平整贴在木板上纸张略带质感干净没有任何文字和墨迹
典型的修仙世界摊位宣传木板适合动态漫场景道具使用
线条清晰卡通渲染质感非写实摄影"""
N="""写实照片真实木纹家具摄影产品图PBR高精度文字字母汉字书法墨迹笔划破损裂缝污渍旧报纸。"""
V=["正面平视,标准构图","略斜角度,展示支架结构","特写宣纸面板","完整支架+面板构图"]
S=[50,60,70,80]
def g(prompt,neg,seed):
url=f"{URL}/images/generations"
r=subprocess.run(["curl","-s","-m","120","--noproxy","*","-X","POST",url,
"-H",f"Authorization: Bearer {AK}","-H","Content-Type: application/json",
"-d",json.dumps({"model":M,"prompt":prompt,"negative_prompt":neg,"size":"1440x2560","n":1,"seed":seed})],
capture_output=True,text=True,timeout=130)
try:d=json.loads(r.stdout);return d["data"][0].get("url")
except:return None
def dl(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 main():
print("PROP-003 R2 · 3D动漫卡通风格广告牌")
if not AK: sys.exit("✗ no key")
ts=time.strftime("%Y%m%d-%H%M%S");rd=J/"PROP-003-R2"/ts;rd.mkdir(parents=True,exist_ok=True)
CD.mkdir(parents=True,exist_ok=True);rs=[]
for i,(seed,var) in enumerate(zip(S,V)):
full=f"{P} {var}";print(f"[{i+1}/4] seed={seed}",flush=True)
url=g(full,N,seed)
if not url:rs.append({"c":i+1,"ok":False,"err":"api"});continue
raw=rd/f"PROP-003-R2-{i+1:02d}-1440x2560.jpg"
if not dl(url,raw):rs.append({"c":i+1,"ok":False,"err":"dl"});continue
sc=CD/f"PROP-003-R2-candidate-{i+1:02d}-1080x1920.jpg"
subprocess.run(["sips","-z","1920","1080",str(raw),"--out",str(sc)],capture_output=True)
rs.append({"c":i+1,"ok":True,"raw":str(raw),"scaled":str(sc),"seed":seed});time.sleep(2)
ok=sum(1 for r in rs if r["ok"]);print(f"完成:{ok}/4")
with open(CD/"generation-meta.json","w") as f:json.dump({"spec":"PROP-003-R2","ts":ts,"ok":ok,"results":rs},f,ensure_ascii=False,indent=2)
return 0 if ok>=4 else 1
if __name__=="__main__":sys.exit(main())

View File

@ -0,0 +1,184 @@
#!/usr/bin/env python3
"""CHAR-004 R2 候选质检 · Qwen-VL · VA-05-001-R2标准"""
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-R2/CHAR-004-ZhugeFeng")
CANDIDATES = [f"{CANDIDATES_DIR}/CHAR-004-R2-candidate-{i:02d}-1080x1920.jpg" for i in range(1, 5)]
QC_PROMPT = """你是动画资产质量检查员。检查这张CHAR-004诸葛风立绘候选图按以下标准逐项打分。
## R2规范必查项
1. 服装完整性一票否决
- 裤子膝盖是否有破洞/破损/
- 衣服下摆是否有毛边/破损/撕裂/
- 鞋子是否有泥污/脏污/
- 服装是否有污渍/
- 是否有补丁/
- 整体是否"乞丐化"像乞丐而不是清贫少年
2. 服装部件准确性
- 上衣是否为灰蓝色交领短打右衽/
- 是否系深棕色粗麻绳腰带/
- 裤子是否深灰蓝色裤脚塞进靴子/
- 鞋子是否黑色粗布短靴/
- 腰间是否有旧布包/
- 有无不该有的配饰玉佩香囊等/
3. 面部质量
- 五官是否清晰无崩坏/
- 是否少年感强气质偏冷硬/
- 有没有和苏白温润仙气型相似/
4. 整体
- 人物肢体比例是否正常/
- 画面是否清晰无噪点/
- 是否全身从头到鞋完整展示/
输出严格JSON不要额外文字
{
"candidate": N,
"pass": true/false,
"score": 0-100,
"hole_in_pants": true/false,
"frayed_hem": true/false,
"dirty_shoes": true/false,
"stains": true/false,
"patches": true/false,
"beggar_look": true/false,
"correct_top": true/false,
"correct_belt": true/false,
"correct_pants": true/false,
"correct_shoes": true/false,
"has_bag": true/false,
"extra_accessories": true/false,
"face_intact": true/false,
"cold_temperament": true/false,
"similar_to_su_bai": true/false,
"anatomy_ok": true/false,
"clear_image": true/false,
"full_body": true/false,
"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(image_b64):
body = json.dumps({
"model": "qwen-vl-max",
"input": {
"messages": [{
"role": "user",
"content": [
{"image": image_b64},
{"text": QC_PROMPT}
]
}]
}
})
endpoints = [EP, FALLBACK_EP]
for ep in endpoints:
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 proc.returncode != 0 or not proc.stdout.strip():
continue
resp = json.loads(proc.stdout)
if "output" in resp:
content = resp["output"]["choices"][0]["message"]["content"][0]["text"]
# Clean markdown wrappers
if "```" in content:
content = content.split("```")[1]
if content.startswith("json"):
content = content[4:]
content = content.split("```")[0]
return json.loads(content.strip())
elif "error" in resp:
print(f" [Qwen error] {resp['error']}", file=sys.stderr)
continue
except Exception as e:
print(f" [fail {ep}] {e}", file=sys.stderr)
continue
return None
def main():
print("=" * 60)
print("CHAR-004 R2 候选质检 · Qwen-VL · VA-05-001-R2标准")
print("=" * 60)
results = []
for i, path in enumerate(CANDIDATES):
print(f"\n[{i+1}/4] 质检候选 {i+1}...")
img = encode_image(path)
qc = call_qwen(img)
if qc:
print(f" score={qc.get('score')} pass={qc.get('pass')}")
print(f" 破洞={qc.get('hole_in_pants')} 毛边={qc.get('frayed_hem')} 脏鞋={qc.get('dirty_shoes')}")
print(f" 乞丐化={qc.get('beggar_look')} 和苏白像={qc.get('similar_to_su_bai')}")
print(f" {qc.get('summary')}")
results.append(qc)
else:
print(f" ✗ Qwen-VL调用失败")
results.append({"candidate": i+1, "pass": False, "error": "Qwen-VL call failed"})
# 汇总
print("\n" + "=" * 60)
print("质检汇总")
print("=" * 60)
passed = sum(1 for r in results if r.get("pass"))
print(f"通过: {passed}/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("patches"): issues.append("补丁")
if r.get("beggar_look"): issues.append("乞丐化")
if r.get("extra_accessories"): issues.append("多余配饰")
print(f" 候选{r.get('candidate')} ✗: {', '.join(issues) if issues else 'other'}")
# 写入JSON
out_path = os.path.join(CANDIDATES_DIR, "QC-R2-BATCH.json")
report = {
"spec": "VA-05-001-R2",
"asset_id": "CHAR-004-ZhugeFeng",
"qc_tool": "Qwen-VL (qwen-vl-max)",
"results": results,
"summary": {
"passed": passed,
"total": 4,
"verdict": "APPROVED" if passed >= 4 else "REJECTED"
}
}
with open(out_path, "w") as f:
json.dump(report, f, ensure_ascii=False, indent=2)
print(f"\nQC报告已保存: {out_path}")
return 0 if passed >= 4 else 1
if __name__ == "__main__":
sys.exit(main())

View File

@ -0,0 +1,99 @@
#!/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())

View File

@ -0,0 +1,140 @@
#!/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())