Text-to-Image
English
numpy
machine-learning
deep-learning
generative-ai
text-2-image
image-generation
open-weights
model-weights
ai-art
pixel-art
game-development
gamedev
game-assets
asset-generator
sprite-generator
offline
tiny-model
numpy-runtime
int8-quantization
self-supervised
procedural-data
gpt
english-prompts
awesome-ai
| #!/usr/bin/env python3 | |
| """PXG-Tiny CLI (offline, NumPy-only). | |
| python3 cli.py "a golden sword" -o sword.png | |
| python3 cli.py "a golden sword" --variations 4 --sheet row.png | |
| python3 cli.py --ask "can you draw a photo of a cat" | |
| python3 cli.py --sheet-from-prompts prompts.txt --outdir sprites/ | |
| """ | |
| import argparse | |
| import json | |
| import sys | |
| from pathlib import Path | |
| ROOT = Path(__file__).resolve().parents[0] | |
| sys.path.insert(0, str(ROOT / "src")) | |
| from PIL import Image # noqa: E402 | |
| from pxg_tiny import config as C # noqa: E402 | |
| from pxg_tiny.pipeline import PXGPipeline # noqa: E402 | |
| def sheet(grids, path, scale=8): | |
| n = len(grids) | |
| im = Image.new("RGBA", (n * (16 * scale + 4) + 4, 16 * scale + 8), | |
| (24, 24, 30, 255)) | |
| from pxg_tiny.render import grid_to_rgba | |
| for i, g in enumerate(grids): | |
| tile = Image.fromarray(grid_to_rgba(g), "RGBA").resize( | |
| (16 * scale, 16 * scale), Image.NEAREST) | |
| im.paste(tile, (4 + i * (16 * scale + 4), 4), tile) | |
| im.save(path) | |
| def main(): | |
| ap = argparse.ArgumentParser(prog="pxg-tiny") | |
| ap.add_argument("prompt", nargs="?") | |
| ap.add_argument("-o", "--out", default=None) | |
| ap.add_argument("--seed", type=int, default=0) | |
| ap.add_argument("--temperature", type=float, default=None) | |
| ap.add_argument("--top-k", type=int, default=None) | |
| ap.add_argument("--variations", type=int, default=1) | |
| ap.add_argument("--sheet", action="store_true", | |
| help="save a variations sheet next to the png") | |
| ap.add_argument("--sheet-from-prompts", default=None, | |
| help="path to a text file with one prompt per line; " | |
| "renders every prompt into --outdir") | |
| ap.add_argument("--outdir", default=None, | |
| help="output directory for --sheet-from-prompts") | |
| ap.add_argument("--ask", action="store_true", | |
| help="ask-first mode: clarify/refuse instead of drawing") | |
| ap.add_argument("--bundle", default=str(ROOT / "weights")) | |
| ap.add_argument("--no-quality", action="store_true", | |
| help="disable the grounding auto-retry gate") | |
| args = ap.parse_args() | |
| if args.sheet_from_prompts: | |
| outdir = Path(args.outdir or "pxg_batch_out") | |
| outdir.mkdir(parents=True, exist_ok=True) | |
| lines = [l.strip() for l in Path(args.sheet_from_prompts) | |
| .read_text().splitlines() if l.strip()] | |
| pipe = PXGPipeline(args.bundle) | |
| grids = [] | |
| results = [] | |
| for i, p in enumerate(lines): | |
| g, m = pipe.generate_pixels(p, seed=args.seed, | |
| enforce_quality=not args.no_quality, | |
| temperature=args.temperature, | |
| top_k=args.top_k) | |
| results.append({"prompt": p, "gate": m.get("gate")}) | |
| if g is None: | |
| print(json.dumps({"i": i, "prompt": p, **m}, indent=2)) | |
| continue | |
| from pxg_tiny.render import save_png | |
| f = outdir / f"{i+1:02d}_{p.replace(' ', '_')[:24]}.png" | |
| save_png(g, f, scale=8) | |
| grids.append(g) | |
| results[-1]["file"] = str(f) | |
| if grids: | |
| sheet(grids, outdir / "sheet.png") | |
| print(json.dumps({"n_prompts": len(lines), "n_rendered": len(grids), | |
| "outdir": str(outdir), "results": results}, indent=2)) | |
| return | |
| if not args.prompt: | |
| ap.error("give me a prompt, e.g. pxg-tiny \"a gold sword\"") | |
| pipe = PXGPipeline(args.bundle) | |
| if args.ask: | |
| label, msg = pipe.should_ask(args.prompt) | |
| print(json.dumps({"label": label, "message": msg}, indent=2)) | |
| return | |
| out = Path(args.out) if args.out else Path( | |
| args.prompt.replace(" ", "_")[:24].replace("/", "_") + ".png") | |
| out.parent.mkdir(parents=True, exist_ok=True) | |
| grids, metas = [], [] | |
| if args.variations > 1: | |
| res = pipe.variations(args.prompt, k=args.variations, | |
| start_seed=args.seed, | |
| enforce_quality=not args.no_quality, | |
| temperature=args.temperature, top_k=args.top_k) | |
| grids = [g for g, _ in res if g is not None] | |
| metas = [m for _, m in res] | |
| else: | |
| g, m = pipe.generate_pixels(args.prompt, seed=args.seed, | |
| temperature=args.temperature, | |
| top_k=args.top_k, | |
| enforce_quality=not args.no_quality) | |
| if g is None: | |
| print(json.dumps(m, indent=2)) | |
| return | |
| grids, metas = [g], [m] | |
| sheet(grids, out) | |
| print(json.dumps({"prompt": args.prompt, "out": str(out), | |
| "n": len(grids), "meta": metas[0]}, indent=2)) | |
| if __name__ == "__main__": | |
| main() | |