#!/usr/bin/env python3 """Generate multiple images from a kq4-decompile visual image with different seeds.""" import random import subprocess import sys import os import urllib.request def check_server(server_address: str = "127.0.0.1:8188", timeout: int = 5) -> bool: """Check if ComfyUI server is running and accessible.""" try: req = urllib.request.Request( f"http://{server_address}/system_stats", method="GET", ) with urllib.request.urlopen(req, timeout=timeout) as response: return response.status == 200 except Exception: return False def count_existing_variations(image_path: str) -> int: """Count existing image variations for a visual image. Args: image_path: Path to the kq4-decompile visual image Returns: Number of existing generated images matching the room name """ image_dir = os.path.dirname(os.path.abspath(image_path)) or "." klein_outputs_dir = os.path.join(image_dir, "klein_outputs") if not os.path.exists(klein_outputs_dir): return 0 image_basename = os.path.basename(image_path) room_name = ( image_basename.replace("pic_", "").replace("_visual.png", "").replace("_", "-") ) count = 0 for item in os.listdir(klein_outputs_dir): if item.startswith(f"{room_name}_") and item.endswith("_generated.png"): count += 1 return count def generate_multiple_klein( image_path: str, count: int = 2, server: str = "127.0.0.1:8188", dry_run: bool = False, lora_disney: float = 0.8, lora_kq5hoyos: float = 0.7, lora_sylvain: float = 0.5, caption_prompt: str = None, ) -> tuple[list[str], int]: """Generate multiple images with different random seeds. Args: image_path: Path to kq4-decompile visual image count: Total number of variations to have (default: 2) server: ComfyUI server address dry_run: If True, validate only without generating Returns: Tuple of (list of created image paths, number of existing variations) """ if not os.path.exists(image_path): print(f"Error: Image file not found: {image_path}") sys.exit(1) image_basename = os.path.basename(image_path) room_name = ( image_basename.replace("pic_", "").replace("_visual.png", "").replace("_", "-") ) if dry_run: print(f" Image: {image_basename}") return [], count_existing_variations(image_path) existing = count_existing_variations(image_path) print(f"Found {existing} existing variations") print(f"Requested total: {count}") needed = count - existing if needed <= 0: print(f"\nAlready have {existing} variations (requested {count})") print("No new images needed.") return [], existing print(f"Will generate {needed} new variation(s)") output_paths = [] script_dir = os.path.dirname(os.path.abspath(__file__)) generator_script = os.path.join(script_dir, "generate_from_caption_klein.py") for i in range(needed): seed = random.randint(1, 2**32 - 1) print(f"\n{'=' * 60}") print(f"Generation {i + 1} of {needed} (seed: {seed})") print(f"Progress: {existing + i + 1} of {count} total") print(f"{'=' * 60}") cmd = [ sys.executable, generator_script, image_path, str(seed), "--server", server, "--lora-disney", str(lora_disney), "--lora-kq5hoyos", str(lora_kq5hoyos), "--lora-sylvain", str(lora_sylvain), ] if caption_prompt: cmd.extend(["--caption-prompt", caption_prompt]) result = subprocess.run(cmd, capture_output=False) if result.returncode != 0: print( f"Warning: Generation {i + 1} of {needed} failed with exit code {result.returncode}" ) else: image_dir = os.path.dirname(os.path.abspath(image_path)) or "." klein_outputs_dir = os.path.join(image_dir, "klein_outputs") output_path = os.path.join( klein_outputs_dir, f"{room_name}_{seed}_generated.png" ) output_paths.append(output_path) return output_paths, existing def main(): import argparse parser = argparse.ArgumentParser( description="Generate multiple images from kq4-decompile visual with different seeds" ) parser.add_argument( "image_path", help="Path to kq4-decompile visual image (e.g., kq4-sierra-decompile/rooms/kq4-057-witch-cave/pic_057_visual.png)", ) parser.add_argument( "--count", "-n", type=int, default=2, help="Number of variations to generate (default: 2)", ) parser.add_argument( "--server", default="127.0.0.1:8188", help="ComfyUI server address (default: 127.0.0.1:8188)", ) parser.add_argument( "--dry-run", action="store_true", help="Test mode: validate image file and server connection without generating", ) parser.add_argument( "--lora-disney", type=float, default=0.8, help="Disney LoRA strength (default: 0.8)", ) parser.add_argument( "--lora-kq5hoyos", type=float, default=0.7, help="KQ5Hoyos LoRA strength (default: 0.7)", ) parser.add_argument( "--lora-sylvain", type=float, default=0.5, help="Sylvain LoRA strength (default: 0.5)", ) parser.add_argument( "--caption-prompt", default=None, help="Custom caption prompt for scene description", ) args = parser.parse_args() if args.dry_run: print("\n" + "=" * 60) print("DRY RUN MODE - Validating without generating") print("=" * 60) print(f"\nChecking ComfyUI server at {args.server}...") if check_server(args.server): print("✓ Server is accessible") else: print(f"✗ Server is NOT accessible at {args.server}") print(" Please ensure ComfyUI is running before generating images.") sys.exit(1) print(f"\nValidating: {args.image_path}") output_paths, existing = generate_multiple_klein( args.image_path, args.count, args.server, dry_run=True, lora_disney=args.lora_disney, lora_kq5hoyos=args.lora_kq5hoyos, lora_sylvain=args.lora_sylvain, caption_prompt=args.caption_prompt, ) print(f"✓ Image file is valid (found {existing} existing variations)") print("\n" + "=" * 60) print("✓ Dry run successful! All checks passed.") print("=" * 60) sys.exit(0) print(f"\nChecking ComfyUI server at {args.server}...") if not check_server(args.server): print(f"Error: ComfyUI server is not running at {args.server}") print("Please start ComfyUI first or check the server address.") print(f"\nTo test without generating, use: --dry-run") sys.exit(1) print("✓ Server is running\n") output_paths, existing = generate_multiple_klein( args.image_path, args.count, args.server, lora_disney=args.lora_disney, lora_kq5hoyos=args.lora_kq5hoyos, lora_sylvain=args.lora_sylvain, caption_prompt=args.caption_prompt, ) print(f"\n{'=' * 60}") print("All generations complete!") print(f"{'=' * 60}") if output_paths: print(f"\nCreated {len(output_paths)} new image(s):") for output_path in output_paths: print(f" - {output_path}") print(f"\nTotal variations now: {existing + len(output_paths)}") if __name__ == "__main__": main()