Bestiary portraits in the high-res style: a painted pixel-art bust for every creature
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a24fc51605
commit
8d2362fc11
41 files changed
+308
-1
No files matched your search
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python3
|
||||
# Generate bestiary portraits with fal-ai/nano-banana-pro/edit; the reference image is the
|
||||
# front and front three-quarter view of the creature's 64 px idle sheet, scaled up 6x.
|
||||
import base64, io, json, os, sys, urllib.request, concurrent.futures as cf
|
||||
from PIL import Image
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from portraits import CREATURES, prompt_for, sheet
|
||||
|
||||
OUT = "/home/zink/Projects/dungeoncraft/art/generated/portraits"
|
||||
KEY = open(os.path.expanduser("~/.config/dungeoncraft/fal-key")).read().strip()
|
||||
EXTRA = os.environ.get("EXTRA", "")
|
||||
|
||||
|
||||
def ref_uri(cid):
|
||||
im = Image.open(sheet(cid)).convert("RGBA")
|
||||
h = im.height
|
||||
two = im.crop((0, 0, 2 * h, h)).resize((12 * h, 6 * h), Image.NEAREST)
|
||||
bg = Image.new("RGBA", two.size, (40, 36, 44, 255))
|
||||
bg.alpha_composite(two)
|
||||
buf = io.BytesIO()
|
||||
bg.convert("RGB").save(buf, "PNG")
|
||||
return "data:image/png;base64," + base64.b64encode(buf.getvalue()).decode()
|
||||
|
||||
|
||||
def gen(cid, suffix=""):
|
||||
body = {"prompt": prompt_for(cid, EXTRA), "image_urls": [ref_uri(cid)], "aspect_ratio": "1:1",
|
||||
"resolution": "1K", "num_images": 1, "output_format": "png"}
|
||||
req = urllib.request.Request("https://fal.run/fal-ai/nano-banana-pro/edit", data=json.dumps(body).encode(),
|
||||
headers={"Authorization": "Key " + KEY, "Content-Type": "application/json"})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=600) as r:
|
||||
res = json.load(r)
|
||||
with urllib.request.urlopen(res["images"][0]["url"], timeout=300) as r:
|
||||
data = r.read()
|
||||
path = f"{OUT}/{cid}-raw{suffix}.png"
|
||||
open(path, "wb").write(data)
|
||||
return cid, "ok " + path
|
||||
except Exception as e:
|
||||
msg = str(e)
|
||||
if hasattr(e, "read"):
|
||||
try:
|
||||
msg += " " + e.read().decode()[:300]
|
||||
except Exception:
|
||||
pass
|
||||
return cid, "ERROR " + msg
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
ids = sys.argv[1:] or [c["id"] for c in CREATURES]
|
||||
suffix = os.environ.get("SUFFIX", "")
|
||||
with cf.ThreadPoolExecutor(4) as ex:
|
||||
for cid, st in ex.map(lambda i: gen(i, suffix), ids):
|
||||
print(cid, st, flush=True)
|
||||
Reference in new issue
Block a user