Files
EPEEAIKit/art-agent/backend/scripts/test_frame_extract.py
2026-04-20 21:52:35 +08:00

108 lines
3.8 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.
"""Phase 2 验收脚本:独立验证 combined_video 抽帧 + 左右切分逻辑。
不需要再跑一次 Trellis省钱直接用 generated/ 下已有的
`trellis_combined_*.mp4` 做验证。
用法(在 backend 目录激活 venv 之后):
python scripts/test_frame_extract.py
python scripts/test_frame_extract.py generated/trellis_combined_211176d3940b.mp4
成功标准:
- 退出码 0
- 打印的 color_png / normal_png 均为 /generated/view_xxx.png
- 到 backend/generated/ 能看到 6 对 color + normal 文件12 张)且尺寸接近正方形
- 目视检查:每组 color 图按 0/60/120/180/240/300° 依次环绕建筑一圈
"""
import asyncio
import json
import sys
from pathlib import Path
_BACKEND_ROOT = Path(__file__).resolve().parent.parent
if str(_BACKEND_ROOT) not in sys.path:
sys.path.insert(0, str(_BACKEND_ROOT))
from dotenv import load_dotenv # noqa: E402
load_dotenv(_BACKEND_ROOT / ".env", override=True)
from app.services import video_frame_extractor # noqa: E402
from app.services.image_gen import GENERATED_DIR # noqa: E402
def _pick_default_video() -> str:
candidates = sorted(
GENERATED_DIR.glob("trellis_combined_*.mp4"),
key=lambda p: p.stat().st_mtime,
reverse=True,
)
if not candidates:
raise FileNotFoundError(
"generated/ 下找不到 trellis_combined_*.mp4请先跑一次 Phase 1 "
"scripts/test_trellis.py或显式传入视频路径"
)
return f"/generated/{candidates[0].name}"
async def main() -> int:
video_arg = sys.argv[1] if len(sys.argv) > 1 else _pick_default_video()
# 允许传相对路径或纯文件名
if not video_arg.startswith("/generated/"):
if video_arg.startswith("generated/"):
video_arg = "/" + video_arg.replace("\\", "/")
elif not video_arg.startswith("/") and not Path(video_arg).is_absolute():
# 如果只给了个文件名,假定在 generated/ 下
if (GENERATED_DIR / video_arg).exists():
video_arg = f"/generated/{video_arg}"
print(f"[test_frame_extract] 输入视频:{video_arg}")
print("[test_frame_extract] 抽取 6 视角az=[0,60,120,180,240,300]el 全 0")
azimuths = [0, 60, 120, 180, 240, 300]
elevations = [0] * len(azimuths)
frames = await video_frame_extractor.extract_by_azimuth(
combined_video_path=video_arg,
azimuths=azimuths,
elevations=elevations,
total_rotation_deg=360,
)
print("[test_frame_extract] 抽帧结果:")
print(json.dumps(frames, ensure_ascii=False, indent=2))
# 简单完整性校验
all_exist = True
for f in frames:
for key in ("color_png", "normal_png"):
rel = f.get(key)
if not rel:
print(f"[test_frame_extract] 警告:帧 az={f['az']} 缺少 {key}")
all_exist = False
continue
local = GENERATED_DIR / Path(rel).name
size = local.stat().st_size if local.exists() else -1
status = "OK" if size > 0 else "MISSING"
print(f" [{status}] az={f['az']:3d} {key}: {rel} ({size} bytes)")
if size <= 0:
all_exist = False
if not all_exist:
print("[test_frame_extract] 部分文件缺失,判定失败")
return 1
print(
f"[test_frame_extract] 成功:共 {len(frames)} 个视角,"
f"color+normal 合计 {len(frames) * 2} 张已落盘到 {GENERATED_DIR}"
)
print(
"[test_frame_extract] 请目视检查:打开 generated/ 下任意 view_*_color.png"
"确认 az=0 正面、az=180 背面、其余按顺序环绕normal 帧应为蓝紫调法线图。"
)
return 0
if __name__ == "__main__":
sys.exit(asyncio.run(main()))