diff --git a/docs/krea2-pov-pose-atlas.md b/docs/krea2-pov-pose-atlas.md index f12fec2..2910dbd 100644 --- a/docs/krea2-pov-pose-atlas.md +++ b/docs/krea2-pov-pose-atlas.md @@ -23,6 +23,10 @@ the existing Position Pool / Action Filter / Insta-OF chain. Pair it with `SxCP Krea2 Variant Evidence` to display the fixed-seed eval entry, image paths, and generator decision behind that variant. +For command-line planning, `python tools/krea2_tuning_report.py` shows which +catalog variants are proven or pending and which atlas pose folders are still +unmapped by the catalog. + ## Inventory | Family | Pose images | Control images | First sample | diff --git a/krea2_tuning_report.py b/krea2_tuning_report.py index 7a0d639..6f309b7 100644 --- a/krea2_tuning_report.py +++ b/krea2_tuning_report.py @@ -1,6 +1,7 @@ from __future__ import annotations from collections import Counter +from pathlib import Path from typing import Any try: @@ -66,6 +67,76 @@ def coverage_summary() -> dict[str, Any]: } +def _catalog_atlas_root() -> Path: + catalog = krea2_pose_variant_catalog.load_catalog() + return Path(str(catalog.get("atlas_root") or "")) + + +def _mapped_atlas_folders() -> dict[str, list[str]]: + mapped: dict[str, list[str]] = {} + for variant in krea2_pose_variant_catalog.variants(): + key = str(variant.get("key") or "") + for folder in variant.get("atlas_folders") or []: + folder_name = str(folder) + if not folder_name: + continue + mapped.setdefault(folder_name, []).append(key) + return mapped + + +def _is_background_or_control_folder(folder_name: str) -> bool: + lower = folder_name.lower() + return ( + lower == "bg" + or lower == "woman" + or lower.endswith("_control") + or lower.endswith("_bg") + or lower.endswith("_control_bg") + ) + + +def atlas_folder_rows(atlas_root: str | Path | None = None) -> list[dict[str, Any]]: + root = Path(atlas_root) if atlas_root is not None else _catalog_atlas_root() + if not root.is_dir(): + return [] + mapped = _mapped_atlas_folders() + rows: list[dict[str, Any]] = [] + for folder in sorted(root.iterdir(), key=lambda path: path.name.lower()): + if not folder.is_dir(): + continue + folder_name = folder.name + if _is_background_or_control_folder(folder_name): + continue + image_count = sum(1 for _ in folder.glob("*.png")) + if image_count <= 0: + continue + control_folder = root / f"{folder_name}_control" + variant_keys = mapped.get(folder_name, []) + if not variant_keys and not control_folder.is_dir(): + continue + rows.append( + { + "folder": folder_name, + "image_count": image_count, + "mapped": bool(variant_keys), + "variant_keys": list(variant_keys), + "control_folder": str(control_folder) if control_folder.is_dir() else "", + } + ) + return rows + + +def atlas_coverage_summary(atlas_root: str | Path | None = None) -> dict[str, Any]: + rows = atlas_folder_rows(atlas_root=atlas_root) + unmapped = [str(row.get("folder")) for row in rows if not row.get("mapped")] + return { + "pose_folder_count": len(rows), + "mapped_folder_count": len(rows) - len(unmapped), + "unmapped_folder_count": len(unmapped), + "unmapped_folders": unmapped, + } + + def next_test_plans() -> list[dict[str, Any]]: rows_by_key = {str(row.get("key")): row for row in coverage_rows()} plans: list[dict[str, Any]] = [] @@ -94,7 +165,7 @@ def next_test_plans() -> list[dict[str, Any]]: return plans -def markdown_report() -> str: +def markdown_report(atlas_root: str | Path | None = None) -> str: lines = [ "# Krea2 Pose Variant Coverage", "", @@ -132,4 +203,19 @@ def markdown_report() -> str: *[f" - {cue}" for cue in plan["avoid_cues"]], ] ) + atlas_summary = atlas_coverage_summary(atlas_root=atlas_root) + if atlas_summary["pose_folder_count"]: + unmapped = atlas_summary["unmapped_folders"] + lines.extend( + [ + "", + "## Atlas Folder Coverage", + "", + f"- Pose folders: {atlas_summary['pose_folder_count']}", + f"- Mapped folders: {atlas_summary['mapped_folder_count']}", + f"- Unmapped folders: {atlas_summary['unmapped_folder_count']}", + ] + ) + if unmapped: + lines.extend(["", "Unmapped atlas folders:", *[f"- {folder}" for folder in unmapped]]) return "\n".join(lines) diff --git a/tools/prompt_smoke.py b/tools/prompt_smoke.py index 0fb4c2b..13a98e5 100644 --- a/tools/prompt_smoke.py +++ b/tools/prompt_smoke.py @@ -6878,6 +6878,27 @@ def smoke_krea2_tuning_report_policy() -> None: any(str(path).endswith("ballsucking/101_ballsucking.png") for path in ballsucking_plan.get("reference_paths") or []), "Ballsucking test plan lost atlas reference path", ) + with tempfile.TemporaryDirectory() as tmpdir: + atlas_root = Path(tmpdir) + for folder in ("doggy", "doggy_control", "custom_pose", "custom_pose_control", "bg", "woman", "doggy_bg"): + folder_path = atlas_root / folder + folder_path.mkdir() + (folder_path / f"{folder}_sample.png").write_bytes(b"") + atlas_rows = krea2_tuning_report.atlas_folder_rows(atlas_root=atlas_root) + atlas_by_folder = {row.get("folder"): row for row in atlas_rows} + _expect(atlas_by_folder.get("doggy", {}).get("mapped") is True, "Atlas report should mark catalog folders as mapped") + _expect(atlas_by_folder.get("custom_pose", {}).get("mapped") is False, "Atlas report should expose unmapped pose folders") + _expect("doggy_control" not in atlas_by_folder, "Atlas report should exclude control folders") + _expect("doggy_bg" not in atlas_by_folder, "Atlas report should exclude background folders") + _expect("bg" not in atlas_by_folder, "Atlas report should exclude shared bg folder") + _expect("woman" not in atlas_by_folder, "Atlas report should exclude non-pose woman folder") + atlas_summary = krea2_tuning_report.atlas_coverage_summary(atlas_root=atlas_root) + _expect(atlas_summary.get("pose_folder_count") == 2, "Atlas report should count only pose folders") + _expect(atlas_summary.get("mapped_folder_count") == 1, "Atlas report should count mapped pose folders") + _expect(atlas_summary.get("unmapped_folders") == ["custom_pose"], "Atlas report should identify unmapped pose folders") + atlas_markdown = krea2_tuning_report.markdown_report(atlas_root=atlas_root) + _expect("Atlas Folder Coverage" in atlas_markdown, "Krea2 tuning report markdown lost atlas coverage section") + _expect("custom_pose" in atlas_markdown, "Krea2 tuning report markdown lost unmapped atlas folder") markdown = krea2_tuning_report.markdown_report() _expect("pov_ballsucking_low_head" in markdown, "Krea2 tuning report markdown lost candidate variant") _expect("needs_fixed_seed_tests" in markdown, "Krea2 tuning report markdown lost coverage state")