""" Lahr Carpet Cleaning — Service card image generator. Generates 12 unique images for residential and commercial service cards. Saves to: assets/images/services/ Run: python3 tools/gen-service-images.py """ import os import sys try: from google import genai from google.genai import types except ImportError: print("Installing google-genai...") os.system(f"{sys.executable} -m pip install google-genai --quiet") from google import genai from google.genai import types API_KEY = os.environ.get("GEMINI_API_KEY", "AIzaSyB_1p8KvaT_rdNJGPs8HKk8bKsvUlcL6Kg") OUT_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "assets", "images", "services") os.makedirs(OUT_DIR, exist_ok=True) client = genai.Client(api_key=API_KEY) IMAGES = [ # ── Residential ────────────────────────────────────────────────────────── { "name": "carpet-cleaning", "prompt": ( "Wide shot of a large industrial stand-up hot water extraction machine being pushed across " "a plush beige residential carpet. The machine is a heavy commercial-grade upright extractor " "on wheels — tall, wide cleaning head at the base, long upright handle. " "The carpet behind it transitions from dirty and matted to clean, bright, and fluffy. " "Completely dry machine exterior, no steam, no water spraying anywhere. " "Warm natural interior light. Ultra-realistic professional photography." ), }, { "name": "stairs-cleaning", "prompt": ( "Wide cinematic shot of a carpeted residential staircase. Each step has clean, " "bright plush carpet that looks freshly cleaned with visible extraction lines. " "Modern upstate New York home interior, natural light from above, " "warm wood banisters, no people, no equipment visible. " "Professional interior photography, ultra-realistic." ), }, { "name": "upholstery-cleaning", "prompt": ( "Close-up of a clean grey linen sofa cushion showing bright, lifted fabric texture " "after professional upholstery cleaning. Half the cushion shows the before " "(slightly soiled, flat fabric) and half shows the cleaned result (bright, fluffy, refreshed). " "Natural window light, residential living room in background, no people, no equipment. " "Ultra-realistic product photography." ), }, { "name": "floor-cleaning", "prompt": ( "Wide shot of a gleaming hardwood floor in a modern residential home after professional " "cleaning. The floor reflects soft natural window light, showing deep grain detail. " "Contemporary furniture in background, no people, no cleaning equipment visible. " "Professional interior photography, ultra-realistic." ), }, { "name": "area-rug-cleaning", "prompt": ( "Overhead flat-lay shot of a large vibrant Persian or oriental area rug " "with rich red, navy, and cream geometric patterns, looking freshly cleaned — " "colors vivid, fibers lifted and bright. Hardwood floor beneath. " "No people, no equipment, no water. Professional product photography, ultra-realistic." ), }, { "name": "add-ons", "prompt": ( "Close-up macro shot of clean carpet fibers being lifted by a professional " "grooming brush after hot water extraction cleaning. The fibers are bright, " "fluffy, and standing upright. Warm light catches the texture. " "No steam, no water, no people. Ultra-realistic macro photography." ), }, # ── Commercial ─────────────────────────────────────────────────────────── { "name": "vacation-rentals", "prompt": ( "Bright, airy vacation rental living room in the Finger Lakes region of upstate New York. " "Spotlessly clean cream carpet, contemporary furniture, large windows with lake views, " "warm natural afternoon light. Inviting and fresh. No people, no equipment. " "Professional real estate photography, ultra-realistic." ), }, { "name": "office-spaces", "prompt": ( "Wide shot of a clean modern corporate office with freshly cleaned dark charcoal carpet " "throughout. Open plan workspace, glass partitions, professional lighting. " "Carpet shows neat vacuum lines indicating recent professional cleaning. " "No people, no equipment. Professional architectural photography, ultra-realistic." ), }, { "name": "hotels-inns", "prompt": ( "Elegant hotel corridor in a boutique upstate New York inn. Clean, plush patterned " "carpet runner down the hallway with fresh vacuum lines. Warm sconce lighting, " "wood paneling, framed art on walls. No people, no equipment. " "Professional hospitality photography, ultra-realistic." ), }, { "name": "retail-showrooms", "prompt": ( "Wide shot of an upscale retail showroom or winery tasting room in the Finger Lakes. " "Clean, rich carpet throughout, warm lighting, product displays on shelves. " "Carpet looks freshly extracted — bright and spotless. " "No people, no equipment. Professional commercial interior photography, ultra-realistic." ), }, { "name": "property-management", "prompt": ( "View across three clean apartment living rooms in sequence, each showing " "spotlessly clean beige carpet with fresh vacuum lines after professional cleaning. " "Bright, neutral interiors ready for new tenants. Natural light, no furniture, " "no people, no equipment. Professional real estate photography, ultra-realistic." ), }, { "name": "commercial-overview", "prompt": ( "Professional carpet cleaning technician in a plain black shirt, shown from the side, " "pushing a large industrial stand-up hot water extraction machine through a bright commercial " "building lobby. The machine is a heavy commercial-grade upright extractor on wheels — " "tall, wide cleaning head, long handle. Clean carpet visible. No steam, no water spraying, " "no face visible. Professional editorial photography, ultra-realistic." ), }, ] def generate(): saved = [] total = len(IMAGES) for i, item in enumerate(IMAGES, 1): out_path = os.path.join(OUT_DIR, f"{item['name']}.jpg") print(f"[{i}/{total}] Generating {item['name']}...") try: resp = client.models.generate_images( model="imagen-4.0-generate-001", prompt=item["prompt"], config=types.GenerateImagesConfig( number_of_images=1, aspect_ratio="4:3", output_mime_type="image/jpeg", safety_filter_level="block_low_and_above", ), ) if resp.generated_images: img_bytes = resp.generated_images[0].image.image_bytes with open(out_path, "wb") as f: f.write(img_bytes) print(f" Saved {out_path} ({len(img_bytes)//1024}KB)") saved.append(item["name"]) else: print(f" No image returned for {item['name']}") except Exception as e: print(f" Error on {item['name']}: {e}") return saved if __name__ == "__main__": saved = generate() print(f"\nDone. {len(saved)}/{len(IMAGES)} images saved to assets/images/services/") if saved: print("Generated:", ", ".join(saved))