← back to Wallco Ai
drunk-animals: dedicated motif tagger w/ animal+pose+plant+style vocab
d5ea5adc572147ea03dbd0e79667f5556f771316 · 2026-05-12 19:35:39 -0700 · SteveStudio2
Generic tag_motifs.py prompt covers floral/damask/geometric vocab.
Drunk-animal designs would only get 'tropical / palm / dense' tags
from that pool. New script feeds llava a category-specific prompt
listing animal species (frog/elephant/giraffe/orangutan/...),
poses (drunk/dancing/sleeping/...), props (cocktail/martini/...),
plants (monstera/fern/...), and styles (warhol/silkscreen/...)
so /designs?motif=frog etc. actually returns frogs.
Background tag run kicked at start of this tick — 43 designs.
Files touched
A scripts/tag_drunk_animal_motifs.py
Diff
commit d5ea5adc572147ea03dbd0e79667f5556f771316
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Tue May 12 19:35:39 2026 -0700
drunk-animals: dedicated motif tagger w/ animal+pose+plant+style vocab
Generic tag_motifs.py prompt covers floral/damask/geometric vocab.
Drunk-animal designs would only get 'tropical / palm / dense' tags
from that pool. New script feeds llava a category-specific prompt
listing animal species (frog/elephant/giraffe/orangutan/...),
poses (drunk/dancing/sleeping/...), props (cocktail/martini/...),
plants (monstera/fern/...), and styles (warhol/silkscreen/...)
so /designs?motif=frog etc. actually returns frogs.
Background tag run kicked at start of this tick — 43 designs.
---
scripts/tag_drunk_animal_motifs.py | 107 +++++++++++++++++++++++++++++++++++++
1 file changed, 107 insertions(+)
diff --git a/scripts/tag_drunk_animal_motifs.py b/scripts/tag_drunk_animal_motifs.py
new file mode 100644
index 0000000..7d967b4
--- /dev/null
+++ b/scripts/tag_drunk_animal_motifs.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+"""
+Drunk-animal motif tagger — runs llava on local Ollama against every
+spoon_all_designs row where category='drunk-animals' and motifs IS NULL.
+
+Uses a drunk-animal-aware prompt vocabulary (frog/elephant/giraffe/orangutan/
+jungle/cocktail/etc.) so the tags actually match the prompt aesthetic, rather
+than the generic floral/damask vocabulary in tag_motifs.py.
+
+Free (local Ollama). ~10-15s per image. Idempotent — skips already-tagged rows.
+
+Usage: python3 scripts/tag_drunk_animal_motifs.py [limit]
+"""
+import base64, json, subprocess, time, sys
+from pathlib import Path
+import urllib.request
+
+OLLAMA = 'http://127.0.0.1:11434'
+MODEL = 'llava:latest'
+
+PROMPT = """Look at this wallpaper pattern image. It probably shows a drunk
+animal in a forest, in a Warhol-style botanical-pop aesthetic. Extract 3 to 6
+concrete tags describing what you see.
+
+Pick from this vocabulary (or invent similar single-word tags):
+ ANIMALS: frog, elephant, giraffe, orangutan, capybara, lemur, sloth,
+ macaw, panda, peacock, parrot, monkey, snake
+ POSE: drunk, tipsy, dancing, sleeping, hiccup, swaying, dancing,
+ passed-out, smiling, drinking, holding-glass
+ PROPS: cocktail, martini, wine, champagne, umbrella, glass, bottle, bubbles
+ PLANTS: monstera, fern, banana-leaf, palm, philodendron, vine, bromeliad,
+ orchid, lily, flower, jungle, forest, leaves
+ STYLE: warhol, pop-art, silkscreen, halftone, neon, bright, primary-colors,
+ 4-panel, color-swap, screen-print, bold, flat, outlined
+
+Return ONLY a JSON array of 3-6 lowercase tag strings. No commentary. No code fences.
+"""
+
+def psql(sql):
+ r = subprocess.run(['psql','dw_unified','-At','-q'], input=sql, capture_output=True, text=True)
+ if r.returncode != 0: raise RuntimeError(r.stderr)
+ return r.stdout.strip()
+
+def esc(s):
+ if s is None: return 'NULL'
+ return "'" + str(s).replace("'", "''") + "'"
+
+def tag_one(image_path):
+ img_b64 = base64.b64encode(Path(image_path).read_bytes()).decode()
+ body = json.dumps({
+ 'model': MODEL,
+ 'prompt': PROMPT,
+ 'images': [img_b64],
+ 'stream': False,
+ 'options': { 'temperature': 0.2 }
+ }).encode()
+ req = urllib.request.Request(f'{OLLAMA}/api/generate',
+ data=body, headers={'Content-Type': 'application/json'})
+ with urllib.request.urlopen(req, timeout=120) as r:
+ out = json.loads(r.read())
+ text = (out.get('response') or '').strip()
+ import re
+ m = re.search(r'\[[^\]]+\]', text)
+ if not m: return []
+ try:
+ tags = json.loads(m.group(0))
+ if not isinstance(tags, list): return []
+ return [str(t).lower().strip()[:30] for t in tags if t][:6]
+ except Exception:
+ return []
+
+def main():
+ limit = int(sys.argv[1]) if len(sys.argv) > 1 else 50
+ rows = json.loads(psql(
+ f"SELECT COALESCE(json_agg(t),'[]'::json) FROM (SELECT id, local_path "
+ f"FROM spoon_all_designs "
+ f"WHERE category='drunk-animals' AND local_path IS NOT NULL "
+ f"AND (motifs IS NULL OR array_length(motifs,1) IS NULL) "
+ f"ORDER BY id LIMIT {limit}) t;") or '[]')
+ print(f'{len(rows)} drunk-animal designs pending motif tags')
+ sql_lines = ['BEGIN;']
+ ok = 0
+ for i, r in enumerate(rows, 1):
+ p = Path(r['local_path'])
+ if not p.exists():
+ print(f' [{i}/{len(rows)}] missing file: {r["local_path"]}')
+ continue
+ try:
+ t0 = time.time()
+ tags = tag_one(p)
+ took = time.time() - t0
+ if tags:
+ arr = "ARRAY[" + ",".join(esc(x) for x in tags) + "]::text[]"
+ sql_lines.append(f"UPDATE spoon_all_designs SET motifs={arr} WHERE id={r['id']};")
+ print(f' [{i:>3}/{len(rows)}] #{r["id"]:<4} ({took:.1f}s) → {tags}')
+ ok += 1
+ else:
+ print(f' [{i:>3}/{len(rows)}] #{r["id"]:<4} no tags returned')
+ except Exception as e:
+ print(f' [{i:>3}/{len(rows)}] #{r["id"]:<4} err: {e}')
+ sql_lines.append('COMMIT;')
+ Path('/tmp/dad_motifs.sql').write_text('\n'.join(sql_lines))
+ subprocess.run(['psql','dw_unified','-q','-f','/tmp/dad_motifs.sql'], check=True)
+ print(f'\nTagged {ok}/{len(rows)}')
+
+if __name__ == '__main__':
+ main()
← a5f9d55 drunk-animals: category-aware titler (Hiccup/Tipple/Tableau/
·
back to Wallco Ai
·
drunk-animals: /drunk-animals friendly URL → 302 to /designs 1370963 →