| """ |
| NumZoo training dataset generator. |
| |
| Generates LoRA training images matching the NumZoo aesthetic using Qwen-Image |
| via the HuggingFace Inference API (fal-ai provider, billed to your HF Pro credits). |
| (Qwen-Image beat FLUX.1-dev on multi-animal accuracy; FLUX.2-dev is edit-only.) |
| |
| Alignment with the live app is guaranteed by construction: |
| - The "A cute {animals} {places}" prefix is built with image_generator.build_subject |
| (the SAME function the app uses), from the app's exact ANIMAL_MAP / PLACE_MAP. |
| - The NUMZOO_STYLE suffix is imported from image_generator. |
| - Scenes deliberately mix 1β3 animals and 1β3 places, exactly like the app does |
| when the player selects multiple emojis. |
| So every caption looks like a real app prompt, plus a rich scene detail clause. |
| |
| Output: training/image_001.jpg + training/image_001.txt (caption) |
| |
| Requirements: |
| ~/miniforge3/bin/pip install huggingface_hub pillow python-dotenv |
| |
| Setup (HF Pro β just your existing token): |
| 1. Get an HF token (fine-grained, "Make calls to Inference Providers" permission) |
| https://huggingface.co/settings/tokens/new?ownUserPermissions=inference.serverless.write&tokenType=fineGrained |
| 2. Add to .env: HF_TOKEN=hf_... |
| |
| Usage: |
| ~/miniforge3/bin/python3 scripts/generate_dataset.py # all scenes |
| ~/miniforge3/bin/python3 scripts/generate_dataset.py --count 5 # first 5 (test run) |
| ~/miniforge3/bin/python3 scripts/generate_dataset.py --start 20 # resume from #20 |
| ~/miniforge3/bin/python3 scripts/generate_dataset.py --dry-run # preview prompts |
| """ |
|
|
| import os |
| import sys |
| import argparse |
| import time |
| from pathlib import Path |
|
|
| |
| try: |
| from dotenv import load_dotenv |
| load_dotenv(Path(__file__).parent.parent / ".env") |
| except ImportError: |
| pass |
|
|
| |
| |
| sys.path.insert(0, str(Path(__file__).parent.parent)) |
| from image_generator import ( |
| build_subject, |
| NUMZOO_STYLE as STYLE, |
| ANIMAL_MAP, |
| PLACE_MAP, |
| ) |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| SCENES: list[tuple[list[str], list[str], str]] = [ |
| |
| (["π°"], ["π"], "sitting on a polka-dot toadstool, fireflies and floating spores drifting around, soft lantern glow"), |
| (["π±"], ["π"], "building a tiny sandcastle with a bucket and shells, gentle waves lapping, warm sunset sky"), |
| (["πΆ"], ["π‘"], "napping in a flower-filled wheelbarrow, watering can and butterflies nearby, golden afternoon light"), |
| (["π¦"], ["β"], "curled up on a fluffy cloud cradling a tiny glowing star, glittering night sky"), |
| (["πΌ"], ["πΈ"], "nibbling a dango skewer as petals fall, paper lanterns strung above, soft pink light"), |
| (["π¨"], ["π΄"], "hugging a palm trunk with a coconut drink, striped hammock and a parrot, turquoise sea behind"), |
| (["π¦"], ["π"], "wearing a tiny crown at the end of a rainbow, pastel clouds and floating sparkles"), |
| (["π―"], ["ποΈ"], "bundled in a knitted scarf on a snowy peak planting a tiny flag, sparkling snow and faint aurora"), |
| (["πΈ"], ["πΊ"], "perched on a giant hibiscus bloom, dewdrops glistening, big tropical leaves and warm bokeh"), |
| (["π§"], ["π"], "sitting on the curve of a glowing crescent moon in a knitted hat, scattered twinkling stars"), |
| (["π¦"], ["πΈ"], "fluttering through cherry blossoms trailing sparkles, pastel petals swirling in the breeze"), |
| (["π¦"], ["β"], "galloping across a starry sky, rainbow mane glowing, a trail of sparkles behind"), |
| (["πΆ"], ["π"], "exploring beneath a giant mushroom with a tiny lantern, glowing toadstools and soft moss"), |
| (["π±"], ["π"], "curled asleep on a crescent moon wearing a nightcap, twinkling stars all around"), |
| (["π°"], ["π"], "splashing in shallow waves beside a starfish friend, beach pail and spade, pink sunset"), |
| (["πΌ"], ["π‘"], "tending a vegetable patch in a straw hat, bees and tall sunflowers, warm sun"), |
| (["π§"], ["ποΈ"], "sliding down a snowy slope on its belly, scarf flying, sparkling powder snow"), |
| (["π¦"], ["π΄"], "lounging in a hammock between two palms with sunglasses, coconuts and calm ocean"), |
| (["π¦"], ["πΊ"], "snoozing in a field of tropical flowers, a butterfly on its nose, dappled golden light"), |
| (["π―"], ["πΈ"], "chasing falling cherry petals, paper lanterns above, soft pink and lilac tones"), |
| (["πΈ"], ["π"], "playing a tiny flute on a lily pad among glowing mushrooms, fireflies and reeds"), |
| (["π¦"], ["π"], "standing proudly under a rainbow, flower garland around its neck, pastel clouds"), |
|
|
| |
| (["π°", "π±"], ["π"], "roasting marshmallows over a tiny campfire, fireflies and glowing mushrooms, cozy night"), |
| (["πΆ", "π¦"], ["π"], "building a sandcastle together with shell flags, gentle waves at golden hour"), |
| (["πΌ", "π¨"], ["πΈ"], "sharing tea under a blooming cherry tree, paper lanterns, drifting petals"), |
| (["π¦", "π―"], ["π‘"], "tumbling over a ball of yarn in a cottage garden, picket fence and butterflies"), |
| (["π§", "π°"], ["ποΈ"], "ice skating on a frozen pond atop a snowy mountain, fairy lights, gentle snowfall"), |
| (["πΈ", "π¦"], ["πΊ"], "resting together on lily pads among tropical flowers, dragonflies and warm bokeh"), |
| (["π±", "πΆ"], ["π"], "stargazing from a crescent moon with a tiny brass telescope, soft constellations"), |
| (["π¦", "π°"], ["π"], "trotting side by side under a rainbow, flower garlands and floating sparkles"), |
| (["π¦", "πΌ"], ["π"], "reading a glowing storybook under a toadstool, a lantern and curious fireflies"), |
| (["π¨", "π§"], ["π΄"], "sipping coconut drinks on a tropical island, beach umbrella and gentle surf"), |
| (["π°", "π¦"], ["β"], "swinging on a swing hung from the stars, sparkles raining down, deep blue night"), |
| (["π±", "πΈ"], ["π"], "collecting shells in tide pools at low tide, a little net and a pastel sunset"), |
|
|
| |
| (["π°", "π±", "πΆ"], ["π"], "having a picnic on a checkered blanket among glowing mushrooms, lanterns and fireflies"), |
| (["π¦", "πΌ", "π¨"], ["πΈ"], "a tea party under cherry blossoms with tiny cups, paper lanterns and drifting petals"), |
| (["π¦", "π―", "πΈ"], ["π‘"], "playing tag through flower beds in a cottage garden, butterflies and warm sun"), |
| (["π§", "π°", "π±"], ["ποΈ"], "building a snowman on a snowy peak in matching scarves, sparkling snow, aurora above"), |
| (["π¦", "π¦", "π°"], ["π"], "dancing under a rainbow amid sparkles and flower petals, pastel sky"), |
| (["πΆ", "π¦", "πΌ"], ["π"], "surfing tiny waves together with a beach ball, palm trees and sunset glow"), |
| (["π±", "π¨", "πΈ"], ["πΊ"], "weaving flower crowns in a field of tropical flowers, butterflies and golden bokeh"), |
| (["π¦", "π―", "π°"], ["β"], "huddled on a cloud counting sparkling stars under a shared blanket, soft glow"), |
|
|
| |
| (["π°"], ["π", "π"], "hopping from a mushroom grove toward a rainbow, sparkles bridging the two, pastel light"), |
| (["π±", "πΆ"], ["π", "π΄"], "a beach day between ocean waves and a tropical island, palm shade and scattered shells"), |
| (["π¦"], ["β", "π"], "soaring past sparkling stars toward a crescent moon, a glowing rainbow trail"), |
| (["πΌ"], ["πΈ", "π‘"], "wandering from cherry blossoms into a cosy cottage garden, petals and busy bees"), |
| (["π§", "π°"], ["ποΈ", "β"], "watching sparkling stars from a snowy mountain top, fairy lights and soft snow"), |
| (["π¦"], ["πΊ", "π΄"], "exploring tropical flowers along a tropical island shore, parrots and warm bokeh"), |
| (["πΈ", "π¦"], ["πΈ", "πΊ"], "drifting between cherry blossoms and tropical flowers, dewdrops and floating petals"), |
| (["π¦"], ["π‘", "π"], "lazing in a cottage garden as a rainbow arcs overhead, butterflies and golden light"), |
|
|
| |
| (["π°", "π±"], ["π", "π", "β"], "a dreamy journey through a mushroom forest, under a rainbow and beneath sparkling stars, a glowing trail"), |
| (["π¦"], ["π", "β", "π"], "flying past a crescent moon and sparkling stars toward a rainbow, sparkles everywhere"), |
| (["πΆ", "π¦", "πΌ"], ["π", "π΄", "πΊ"], "a tropical adventure across a sunny beach, a tropical island and fields of flowers, parrots and surf"), |
| (["π§", "π°", "π±"], ["ποΈ", "β", "π"], "a starry night on a snowy peak under a crescent moon and sparkling stars, fairy lights and aurora"), |
| ] |
|
|
|
|
| |
| |
| |
| |
| TRIGGER = "NUMZOO" |
|
|
|
|
| def _scene_name(idx: int, animals: list[str], places: list[str]) -> str: |
| """Short readable label for logs, e.g. '03_panda_1a1p'.""" |
| first = ANIMAL_MAP[animals[0]].replace("baby ", "") |
| return f"{idx:02d}_{first}_{len(animals)}a{len(places)}p" |
|
|
|
|
| def _gen_prompt(a: list[str], p: list[str], detail: str) -> str: |
| """Full STYLED prompt sent to the image model (needs explicit style cues).""" |
| return f"{build_subject(a, p)}, {detail}, {STYLE}" |
|
|
|
|
| def _caption(a: list[str], p: list[str], detail: str) -> str: |
| """Training caption: trigger word + content only, NO style words (BFL style-LoRA).""" |
| return f"{TRIGGER}. {build_subject(a, p)}, {detail}" |
|
|
|
|
| |
| PROMPTS: list[tuple[str, str, str]] = [ |
| (_scene_name(i + 1, a, p), _gen_prompt(a, p, detail), _caption(a, p, detail)) |
| for i, (a, p, detail) in enumerate(SCENES) |
| ] |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| MODEL = "Qwen/Qwen-Image" |
| PROVIDER = "fal-ai" |
| RETRIES = 4 |
|
|
|
|
| def generate_image(prompt: str) -> "PIL.Image.Image": |
| from huggingface_hub import InferenceClient |
|
|
| |
| |
| hf_token = os.environ.get("HF_TOKEN") |
| last_err = None |
| for attempt in range(1, RETRIES + 1): |
| try: |
| client = InferenceClient(provider=PROVIDER, api_key=hf_token) |
| return client.text_to_image(prompt, model=MODEL, width=1024, height=1024) |
| except Exception as e: |
| last_err = e |
| if attempt < RETRIES: |
| print(f" β οΈ attempt {attempt}/{RETRIES} failed ({str(e)[:60]}) β retrying") |
| time.sleep(4) |
| raise last_err |
|
|
|
|
| def main(): |
| parser = argparse.ArgumentParser() |
| parser.add_argument("--start", type=int, default=1, help="Resume from image N (1-based)") |
| parser.add_argument("--count", type=int, default=None, help="Generate at most N images then stop") |
| parser.add_argument("--dry-run", action="store_true", help="Print prompts without generating") |
| parser.add_argument("--captions-only", action="store_true", help="Rewrite .txt captions for existing images (no API calls)") |
| args = parser.parse_args() |
|
|
| if not args.dry_run and not args.captions_only: |
| if not os.environ.get("HF_TOKEN"): |
| print("β HF_TOKEN not found. Add it to .env") |
| print(" Get yours at https://huggingface.co/settings/tokens") |
| sys.exit(1) |
|
|
| out_dir = Path(__file__).parent.parent / "training" |
| out_dir.mkdir(exist_ok=True) |
|
|
| total = len(PROMPTS) |
| end_at = (args.start - 1 + args.count) if args.count else total |
|
|
| count_label = f"{args.count} images" if args.count else f"all {total} images" |
| print(f"NumZoo dataset generator β {count_label} β {out_dir}") |
| print(f"Range: {args.start}β{min(end_at, total)} of {total}") |
| print() |
|
|
| generated_this_run = 0 |
|
|
| for i, (name, gen_prompt, caption) in enumerate(PROMPTS): |
| n = i + 1 |
| if n < args.start: |
| continue |
| if n > end_at: |
| break |
|
|
| img_path = out_dir / f"image_{n:03d}.jpg" |
| txt_path = out_dir / f"image_{n:03d}.txt" |
|
|
| |
| if args.captions_only: |
| txt_path.write_text(caption) |
| print(f"[{n:02d}/{total}] π {name} β caption updated") |
| continue |
|
|
| if img_path.exists(): |
| print(f"[{n:02d}/{total}] β {name} β already exists, skipping") |
| continue |
|
|
| print(f"[{n:02d}/{total}] π¨ {name}") |
|
|
| if args.dry_run: |
| print(f" gen: {gen_prompt[:90]}β¦") |
| print(f" caption: {caption[:90]}β¦") |
| continue |
|
|
| try: |
| image = generate_image(gen_prompt) |
| image.save(img_path, "JPEG", quality=95) |
| txt_path.write_text(caption) |
| generated_this_run += 1 |
| print(f" β
saved {img_path.name}") |
| except Exception as e: |
| print(f" β failed: {e}") |
| time.sleep(5) |
|
|
| total_on_disk = len(list(out_dir.glob("*.jpg"))) |
| print(f"\nDone. {generated_this_run} generated this run Β· {total_on_disk}/{total} total in {out_dir}") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|