188 lines
6.2 KiB
Python
Executable File
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()
|