Files
ai-game-2/asset-work/generate_all_captions_combo.py
2026-03-27 08:14:33 -07:00

188 lines
6.2 KiB
Python
Executable File

#!/usr/bin/env python3
"""Generate combo images from all kq4-decompile visual images that have captions."""
import subprocess
import sys
import os
import re
import argparse
from pathlib import Path
DEFAULT_CAPTION_TEXT = "oil painting style"
def find_captions_for_image(image_path: Path) -> list[tuple[Path, str, int]]:
"""Find all caption files for a visual image.
Looks for caption files in asset-work/kq4_XXX_room_name/ directory.
Returns list of (caption_path, caption_text, caption_number) tuples.
Uses a set to avoid duplicates when multiple room folders match.
Args:
image_path: Path to the visual image
Returns:
List of tuples with (caption_file_path, caption_text, caption_number)
"""
image_basename = os.path.basename(image_path)
room_number = image_basename.replace("pic_", "").split("_")[0]
room_folder_pattern = f"kq4_{room_number}_*"
script_dir = Path(__file__).parent.resolve()
captions_set = set()
captions = []
for room_dir in script_dir.glob(room_folder_pattern):
if room_dir.is_dir():
for caption_file in sorted(room_dir.glob("caption_*.txt")):
match = re.match(r"caption_(\d+)\.txt", caption_file.name)
if match:
caption_num = int(match.group(1))
caption_key = (caption_num, caption_file.name)
if caption_key not in captions_set:
captions_set.add(caption_key)
with open(caption_file, "r") as f:
caption_text = f.read().strip()
captions.append((caption_file, caption_text, caption_num))
return sorted(captions, key=lambda x: x[2]) if captions else [(None, DEFAULT_CAPTION_TEXT, 0)]
def find_caption_for_image(image_path: Path) -> tuple[Path | None, str]:
"""Find first caption file for a visual image.
Args:
image_path: Path to the visual image
Returns:
Tuple of (caption_file_path or None, caption_text or default)
"""
captions = find_captions_for_image(image_path)
if captions and captions[0][0]:
return captions[0][0], captions[0][1]
return None, DEFAULT_CAPTION_TEXT
def main():
parser = argparse.ArgumentParser(
description="Generate combo images from all kq4-decompile visual images with captions"
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Dry run mode - validate without generating",
)
args = parser.parse_args()
script_dir = Path(__file__).parent.resolve()
generator = script_dir / "generate_multiple_combo.py"
dry_run = args.dry_run
if not generator.exists():
print(f"Error: generate_multiple_combo.py not found in {script_dir}")
sys.exit(1)
print("Finding all kq4-decompile visual images...")
visual_images = list(Path("kq4-sierra-decompile/rooms").rglob("pic_*_visual.png"))
total = len(visual_images)
if total == 0:
print("No kq4-decompile visual images found!")
sys.exit(0)
print(f"Found {total} visual image(s)")
all_images_to_process = []
for image_path in visual_images:
captions = find_captions_for_image(image_path)
for caption_path, caption_text, caption_num in captions:
all_images_to_process.append((image_path, caption_text, caption_path, caption_num))
total_captions = len(all_images_to_process)
print(f"Found {total_captions} captions to process ({len(visual_images)} rooms)")
if dry_run:
print("\nDRY RUN MODE - Validating all visual images")
counter = 0
failed = 0
total_existing = 0
total_created = 0
total_needed = 0
variations_per_image = 1
for image_path, caption_text, caption_path, caption_num in all_images_to_process:
counter += 1
caption_info = f"[caption_{caption_num}]" if caption_path else "[default]"
cmd = [
"python3",
str(generator),
str(image_path),
caption_text,
"--count",
str(variations_per_image),
"--caption-num",
str(caption_num),
]
if dry_run:
cmd.append("--dry-run")
try:
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
print(result.stdout)
existing_match = re.search(
r"[Ff]ound (\d+) existing variations?", result.stdout
)
created_match = re.search(r"Created (\d+) new image", result.stdout)
if existing_match:
existing = int(existing_match.group(1))
total_existing += existing
needed = max(0, variations_per_image - existing)
total_needed += needed
if created_match:
total_created += int(created_match.group(1))
status = "" if result.returncode == 0 else ""
print(f"[{counter}/{total_captions}] {status} {image_path.name} {caption_info}")
except subprocess.CalledProcessError as e:
failed += 1
print(f"[{counter}/{total_captions}] ✗ {image_path.name} {caption_info} - FAILED")
print()
print("=" * 50)
if dry_run:
print("DRY RUN COMPLETE")
print("=" * 50)
print(f"Rooms found: {total}")
print(f"Captions to process: {total_captions}")
print(f"Images already existing: {total_existing}")
print(f"Images would be created: {total_needed}")
print(f"Total when complete: {total_existing + total_needed}")
if failed == 0:
print(f"✓ All captions validated successfully!")
else:
print(f"{failed} of {total_captions} captions failed validation")
sys.exit(1)
else:
print("GENERATION COMPLETE")
print("=" * 50)
print(f"Rooms processed: {total}")
print(f"Captions processed: {total_captions}")
print(f"Images already existing: {total_existing}")
print(f"Images newly created: {total_created}")
print(f"Total images now: {total_existing + total_created}")
if failed > 0:
print(f"Failed: {failed}")
if __name__ == "__main__":
main()