""" Generate unique hero images for every page via ComfyUI SDXL. No people. No machines. Residential = warm home scenes. Commercial = professional spaces. Run in tmux: python3 tools/gen-hero-images.py """ import json, time, urllib.request, os, random COMFY = "http://localhost:8188" OUT_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "assets", "images", "hero") CKPT = "sd_xl_base_1.0.safetensors" NEG = ( "people, person, human, hands, feet, boots, shoes, worker, cleaner, technician, " "machine, vacuum cleaner, equipment, steam, city skyline, apartment building exterior, " "urban, dirty, stain, text, watermark, logo, blurry, low quality, cartoon, anime, " "illustration, render, CGI" ) IMAGES = [ { "filename": "hero-carpet-cleaning.jpg", "positive": ( "wide shot of a bright residential living room in an upstate New York home, " "thick plush beige carpet throughout, warm afternoon sunlight through large windows, " "comfortable couch and coffee table, Finger Lakes farmhouse style interior, " "no people, ultra-realistic architectural photography, 16:9 wide" ), }, { "filename": "hero-stairs.jpg", "positive": ( "wide shot of a clean carpeted staircase inside a suburban American home, " "light grey carpet runner on dark wood stairs, white painted banister, " "bright natural light from above, no people, no equipment, " "ultra-realistic interior photography, 16:9" ), }, { "filename": "hero-upholstery.jpg", "positive": ( "bright cozy living room in a residential home, large comfortable fabric sofa " "in warm neutral tones, clean armchair beside it, plush carpet beneath, " "afternoon light, Finger Lakes countryside visible through window, " "no people, ultra-realistic interior photography, 16:9" ), }, { "filename": "hero-floors.jpg", "positive": ( "wide shot of a clean hardwood floor hallway in a spacious suburban American home, " "light oak floors gleaming, white walls, natural light streaming through windows, " "no people, no equipment, ultra-realistic interior photography, 16:9" ), }, { "filename": "hero-area-rugs.jpg", "positive": ( "beautiful oriental area rug centered in a bright residential living room, " "rich warm tones of deep red and gold, hardwood floor surrounding the rug, " "cozy farmhouse interior, natural light, Finger Lakes region home decor, " "no people, ultra-realistic interior photography, 16:9" ), }, { "filename": "hero-add-ons.jpg", "positive": ( "bright clean residential bedroom interior, freshly cleaned light beige carpet, " "white walls, large window with sheer curtains, simple wooden bed frame, " "crisp natural morning light, Finger Lakes home style, " "no people, no machines, ultra-realistic interior photography, 16:9" ), }, { "filename": "hero-commercial.jpg", "positive": ( "wide shot of a modern commercial office building lobby interior, " "clean dark grey commercial carpet throughout, professional corporate space, " "glass entrance doors, white walls, recessed lighting, " "no people, no equipment, architectural photography, ultra-realistic, 16:9" ), }, { "filename": "hero-offices.jpg", "positive": ( "modern open-plan corporate office interior, clean grey carpet tiles, " "rows of empty desks, glass partitions, professional overhead lighting, " "large windows with daylight, no people, no equipment, " "architectural photography, ultra-realistic, 16:9" ), }, { "filename": "hero-vacation-rentals.jpg", "positive": ( "bright cozy vacation rental cottage living room interior, Finger Lakes region style, " "clean beige carpet, wooden ceiling beams, stone fireplace, comfortable furniture, " "large window with lake view in distance, warm inviting atmosphere, " "no people, ultra-realistic interior photography, 16:9" ), }, { "filename": "hero-hotels.jpg", "positive": ( "long elegant hotel corridor interior, clean patterned burgundy carpet runner, " "warm wall sconce lighting along white walls, numbered wooden room doors, " "soft warm glow, upscale hospitality interior, no people, no equipment, " "professional architectural photography, ultra-realistic, 16:9" ), }, { "filename": "hero-retail.jpg", "positive": ( "upscale retail showroom interior, clean light grey carpet flooring, " "modern minimalist display fixtures, bright track lighting overhead, " "white walls, large storefront windows with natural light, " "no people, no equipment, architectural photography, ultra-realistic, 16:9" ), }, { "filename": "hero-property-management.jpg", "positive": ( "clean move-in ready apartment unit living room, fresh neutral carpet, " "white walls, bright windows, empty space ready for tenants, " "no furniture, no people, no equipment, " "real estate photography style, ultra-realistic, 16:9" ), }, { "filename": "hero-about.jpg", "positive": ( "warm exterior view of a classic upstate New York suburban home, " "green lawn, mature trees, clear blue sky, Finger Lakes region, " "inviting residential property, no people, no vehicles, " "professional real estate photography, ultra-realistic, 16:9" ), }, { "filename": "hero-service-area.jpg", "positive": ( "scenic Finger Lakes region landscape, rolling green hills, vineyards in distance, " "calm lake water reflecting sky, late afternoon golden hour light, " "upstate New York countryside, no people, no vehicles, " "professional landscape photography, ultra-realistic, 16:9" ), }, { "filename": "hero-living-room.jpg", "positive": ( "spacious bright residential living room in a Finger Lakes area home, " "plush clean light grey carpet, white walls, large sectional sofa, " "afternoon sunlight through bay windows, warm cozy family atmosphere, " "no people, ultra-realistic interior photography, 16:9" ), }, { "filename": "hero-clean-result.jpg", "positive": ( "close-up wide angle of immaculate freshly cleaned residential carpet, " "uniform plush beige pile, bright natural light raking across the surface, " "showing deep clean texture, no people, no machines, " "ultra-realistic macro photography, 16:9" ), }, ] def build_workflow(positive, seed=None): if seed is None: seed = random.randint(0, 2**32) return { "3": {"class_type": "KSampler", "inputs": { "cfg": 7.5, "denoise": 1.0, "latent_image": ["5", 0], "model": ["4", 0], "negative": ["7", 0], "positive": ["6", 0], "sampler_name": "dpmpp_2m", "scheduler": "karras", "seed": seed, "steps": 30, }}, "4": {"class_type": "CheckpointLoaderSimple", "inputs": {"ckpt_name": CKPT}}, "5": {"class_type": "EmptyLatentImage", "inputs": {"batch_size": 1, "height": 576, "width": 1024}}, "6": {"class_type": "CLIPTextEncode", "inputs": {"clip": ["4", 1], "text": positive}}, "7": {"class_type": "CLIPTextEncode", "inputs": {"clip": ["4", 1], "text": NEG}}, "8": {"class_type": "VAEDecode", "inputs": {"samples": ["3", 0], "vae": ["4", 2]}}, "9": {"class_type": "SaveImage", "inputs": {"filename_prefix": "hero_gen", "images": ["8", 0]}}, } def queue_prompt(workflow): data = json.dumps({"prompt": workflow}).encode() req = urllib.request.Request(f"{COMFY}/prompt", data=data, headers={"Content-Type": "application/json"}) with urllib.request.urlopen(req) as resp: return json.loads(resp.read())["prompt_id"] def wait_for_result(prompt_id, timeout=900): start = time.time() while time.time() - start < timeout: try: with urllib.request.urlopen(f"{COMFY}/history/{prompt_id}") as resp: hist = json.loads(resp.read()) if prompt_id in hist: outputs = hist[prompt_id].get("outputs", {}) for node_out in outputs.values(): if "images" in node_out: return node_out["images"] except Exception: pass time.sleep(5) return None def download_image(img_info, out_path): fname = img_info["filename"] subfolder = img_info.get("subfolder", "") img_type = img_info.get("type", "output") url = f"{COMFY}/view?filename={fname}&subfolder={subfolder}&type={img_type}" with urllib.request.urlopen(url) as resp: data = resp.read() try: from PIL import Image import io img = Image.open(io.BytesIO(data)).convert("RGB") img.save(out_path, "JPEG", quality=92) print(f" OK: {os.path.basename(out_path)} ({os.path.getsize(out_path)//1024}KB)", flush=True) except ImportError: png_path = out_path.replace(".jpg", ".png") with open(png_path, "wb") as f: f.write(data) print(f" OK (PNG): {png_path}", flush=True) total = len(IMAGES) for i, spec in enumerate(IMAGES): out_path = os.path.join(OUT_DIR, spec["filename"]) print(f"\n[{i+1}/{total}] {spec['filename']}", flush=True) workflow = build_workflow(spec["positive"]) prompt_id = queue_prompt(workflow) print(f" queued {prompt_id[:8]}...", flush=True) images = wait_for_result(prompt_id) if images: download_image(images[0], out_path) else: print(f" FAILED (timeout)", flush=True) print("\nAll done.", flush=True)