🎨 Add 54 PNG assets with transparent backgrounds + generation scripts
Assets (54 PNG files with transparent backgrounds): - npcs/: 13 (Gronk, Kai, Ana, trader, blacksmith, healer, hunter, farmer, etc.) - zivali/: 12 (Susi, husky, cow, pig, sheep, horse, wolf, bear, phoenix, unicorn) - bosses/: 8 (T-Rex, Kraken, Dragon, Zombie King, Golem, Spider Queen, etc.) - mutanti/: 5 (zombies, slimes) - items/: 7 (axe, sword, wands, potions, ana_bracelet, gronk_vape) - environment/: 5 (tree, campfire, chest, rock, bush) Scripts added: - generate_v7_final.py: Google Imagen API generator - generate_comfyui_transparent.py: ComfyUI with auto transparency - generate_local_final.py: ComfyUI + rembg background removal - generate_background.py: Asset registry and queue manager Style: 2D Indie Cartoon Vector with clean lines, full body, tight crop All backgrounds removed with rembg for transparent PNG output
This commit is contained in:
349
scripts/generate_comfyui_transparent.py
Normal file
349
scripts/generate_comfyui_transparent.py
Normal file
@@ -0,0 +1,349 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
🎮 DOLINA SMRTI - ComfyUI Local Generator
|
||||
Transparent Background (Alpha Channel)
|
||||
2D Indie Cartoon Style - Approved Gronk/Kai look
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import uuid
|
||||
import requests
|
||||
from pathlib import Path
|
||||
from PIL import Image
|
||||
import io
|
||||
|
||||
# Configuration
|
||||
COMFYUI_URL = "http://127.0.0.1:8000"
|
||||
OUTPUT_DIR = Path("/Users/davidkotnik/repos/novafarma/assets/images")
|
||||
|
||||
# ============================================================================
|
||||
# STYLE DEFINITIONS - Approved 2D Indie Cartoon
|
||||
# ============================================================================
|
||||
|
||||
STYLE_PREFIX = """2D indie game sprite, cartoon vector style, clean smooth lines,
|
||||
full body visible from head to feet standing on ground,
|
||||
tight crop max 5 pixel padding around subject, """
|
||||
|
||||
STYLE_SUFFIX = """, isolated on pure white background #FFFFFF, game asset"""
|
||||
|
||||
NEGATIVE_PROMPT = """pixel art, pixelated, voxel, 3d render, realistic photo,
|
||||
anime, manga, chibi, floating, cut off, cropped, blurry, low quality,
|
||||
watermark, text, signature, green background, colored background"""
|
||||
|
||||
# ============================================================================
|
||||
# MASTER REGISTRY
|
||||
# ============================================================================
|
||||
|
||||
def generate_registry():
|
||||
"""Generate complete asset registry"""
|
||||
assets = []
|
||||
|
||||
# Main Characters
|
||||
gronk = """Gronk the troll, massive green-grey skinned troll, PINK dreadlocks,
|
||||
stretched earlobes with large gauges, facial piercings, holding colorful vape device with pink smoke,
|
||||
wearing LOOSE BAGGY black t-shirt with band logo, LOOSE WIDE black pants, pink sneakers,
|
||||
peaceful zen expression"""
|
||||
|
||||
kai = """Kai teenage boy survivor, post-apocalyptic nomad style, GREEN natural dreadlocks hair,
|
||||
stretched earlobes with plugs, facial piercings on nose and lip, wearing dirty worn blue jacket,
|
||||
torn jeans, combat boots, survival backpack, determined serious expression, athletic build"""
|
||||
|
||||
ana = """Ana teenage girl explorer, PINK dreadlocks hair, stretched earlobes with small plugs,
|
||||
wearing brown adventure vest over green shirt, blue jeans, brown hiking boots,
|
||||
leather backpack with map, kind curious expression"""
|
||||
|
||||
main_chars = [
|
||||
("gronk_troll", gronk),
|
||||
("kai_survivor", kai),
|
||||
("ana_explorer", ana),
|
||||
]
|
||||
|
||||
for name, desc in main_chars:
|
||||
assets.append({"cat": "npcs", "file": f"{name}.png", "prompt": desc})
|
||||
for d in ["front", "back", "left", "right"]:
|
||||
for a in ["idle", "walk1", "walk2"]:
|
||||
assets.append({
|
||||
"cat": "npcs",
|
||||
"file": f"{name}_{d}_{a}.png",
|
||||
"prompt": f"{desc}, {d} facing view, {a} animation frame"
|
||||
})
|
||||
|
||||
# NPCs
|
||||
npcs = [
|
||||
("npc_trader", "wasteland trader, nomadic merchant with cart, weathered face, hooded cloak"),
|
||||
("npc_blacksmith", "nomadic blacksmith, leather apron, strong arms, hammer"),
|
||||
("npc_healer", "herbalist healer woman, herb pouch, kind elderly, flower crown"),
|
||||
("npc_hunter", "wasteland hunter, crossbow, animal pelts, face paint"),
|
||||
("npc_farmer", "apocalypse farmer, patched overalls, straw hat, hoe"),
|
||||
]
|
||||
|
||||
for name, desc in npcs:
|
||||
assets.append({"cat": "npcs", "file": f"{name}.png", "prompt": desc})
|
||||
|
||||
# Animals
|
||||
animals = [
|
||||
("susi_dachshund", "Susi dachshund dog, brown and black, long body, floppy ears, pink collar"),
|
||||
("dog_husky", "husky dog, fluffy grey white, blue eyes, friendly"),
|
||||
("cat_tabby", "orange tabby cat, striped, lazy curious"),
|
||||
("cow_spotted", "dairy cow, black white spots, friendly"),
|
||||
("chicken_white", "white chicken, red comb, pecking"),
|
||||
("phoenix_chicken", "Phoenix Chicken, fire feathers, glowing orange-red, legendary"),
|
||||
("unicorn", "magical Unicorn, white horse, rainbow mane, golden spiral horn"),
|
||||
]
|
||||
|
||||
for name, desc in animals:
|
||||
assets.append({"cat": "zivali", "file": f"{name}.png", "prompt": desc})
|
||||
|
||||
# Mutants
|
||||
mutants = [
|
||||
("zombie_basic", "basic zombie, shambling, torn clothes, grey-green skin, undead"),
|
||||
("zombie_runner", "fast zombie, sprinting, aggressive, rage face"),
|
||||
("zombie_dreadlocks", "zombie with dreadlocks, brown shirt, farmer style"),
|
||||
("slime_green", "green slime monster, bouncy blob, cute-creepy, small eyes"),
|
||||
("slime_blue", "blue slime, water element, wobbly, transparent"),
|
||||
]
|
||||
|
||||
for name, desc in mutants:
|
||||
assets.append({"cat": "mutanti", "file": f"{name}.png", "prompt": desc})
|
||||
|
||||
# Bosses
|
||||
bosses = [
|
||||
("boss_trex", "T-Rex dinosaur boss, massive predator, huge jaws, fierce"),
|
||||
("boss_kraken", "Kraken sea monster, giant octopus, many tentacles"),
|
||||
("boss_zombie_king", "Zombie King, massive undead, rotting crown"),
|
||||
("boss_slime_emperor", "Slime Emperor, gigantic rainbow slime, crown"),
|
||||
("boss_dragon_fire", "Fire Dragon, red scales, breathing flames, wings"),
|
||||
]
|
||||
|
||||
for name, desc in bosses:
|
||||
assets.append({"cat": "bosses", "file": f"{name}.png", "prompt": desc})
|
||||
|
||||
# Items
|
||||
items = [
|
||||
("tool_axe_iron", "iron axe, chopping tool, wooden handle"),
|
||||
("tool_pickaxe_iron", "iron pickaxe, mining tool"),
|
||||
("weapon_sword_iron", "iron sword, combat blade"),
|
||||
("weapon_bow_wood", "wooden bow with arrows"),
|
||||
("wand_fire", "fire magic wand, red crystal tip, flames"),
|
||||
("wand_ice", "ice magic wand, blue crystal, frost"),
|
||||
("ana_bracelet", "Ana's magical bracelet, glowing, family heirloom"),
|
||||
("gronk_vape", "Gronk's golden vape device, colorful, smoking"),
|
||||
("potion_health", "health potion, red liquid bottle"),
|
||||
("food_bread", "bread loaf, fresh baked"),
|
||||
]
|
||||
|
||||
for name, desc in items:
|
||||
assets.append({"cat": "items", "file": f"{name}.png", "prompt": desc})
|
||||
|
||||
# Environment
|
||||
tiles = [
|
||||
("tile_grass_green", "green grass ground tile, 32x32 seamless"),
|
||||
("tile_dirt_brown", "brown dirt path tile, 32x32 seamless"),
|
||||
("tile_water_shallow", "shallow blue water tile, 32x32 seamless"),
|
||||
("tile_stone_grey", "grey stone floor tile, 32x32 seamless"),
|
||||
]
|
||||
|
||||
objects = [
|
||||
("tree_oak", "oak tree, green leaves, brown trunk"),
|
||||
("tree_dead", "dead tree, no leaves, gnarled branches"),
|
||||
("rock_large", "large boulder, moss covered"),
|
||||
("bush_green", "green bush, leafy"),
|
||||
("campfire", "campfire, flames, logs"),
|
||||
("chest_wooden", "wooden treasure chest, closed"),
|
||||
]
|
||||
|
||||
for name, desc in tiles + objects:
|
||||
assets.append({"cat": "environment", "file": f"{name}.png", "prompt": desc})
|
||||
|
||||
return assets
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# COMFYUI WORKFLOW - With Background Removal
|
||||
# ============================================================================
|
||||
|
||||
def create_workflow(prompt_text: str, output_name: str, size: int = 512) -> dict:
|
||||
"""Creates ComfyUI workflow with white background for easy removal"""
|
||||
seed = int(time.time() * 1000) % 2147483647
|
||||
full_prompt = STYLE_PREFIX + prompt_text + STYLE_SUFFIX
|
||||
|
||||
return {
|
||||
"1": {
|
||||
"class_type": "CheckpointLoaderSimple",
|
||||
"inputs": {"ckpt_name": "dreamshaper_8.safetensors"}
|
||||
},
|
||||
"2": {
|
||||
"class_type": "EmptyLatentImage",
|
||||
"inputs": {"width": size, "height": size, "batch_size": 1}
|
||||
},
|
||||
"3": {
|
||||
"class_type": "CLIPTextEncode",
|
||||
"inputs": {"text": full_prompt, "clip": ["1", 1]}
|
||||
},
|
||||
"4": {
|
||||
"class_type": "CLIPTextEncode",
|
||||
"inputs": {"text": NEGATIVE_PROMPT, "clip": ["1", 1]}
|
||||
},
|
||||
"5": {
|
||||
"class_type": "KSampler",
|
||||
"inputs": {
|
||||
"seed": seed,
|
||||
"steps": 30,
|
||||
"cfg": 7.5,
|
||||
"sampler_name": "euler_ancestral",
|
||||
"scheduler": "karras",
|
||||
"denoise": 1.0,
|
||||
"model": ["1", 0],
|
||||
"positive": ["3", 0],
|
||||
"negative": ["4", 0],
|
||||
"latent_image": ["2", 0]
|
||||
}
|
||||
},
|
||||
"6": {
|
||||
"class_type": "VAEDecode",
|
||||
"inputs": {"samples": ["5", 0], "vae": ["1", 2]}
|
||||
},
|
||||
"7": {
|
||||
"class_type": "SaveImage",
|
||||
"inputs": {"filename_prefix": output_name, "images": ["6", 0]}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def queue_prompt(workflow: dict) -> str:
|
||||
try:
|
||||
r = requests.post(
|
||||
f"{COMFYUI_URL}/prompt",
|
||||
json={"prompt": workflow, "client_id": f"ds_{uuid.uuid4().hex[:8]}"}
|
||||
)
|
||||
return r.json().get("prompt_id")
|
||||
except Exception as e:
|
||||
print(f"Queue error: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def wait_completion(prompt_id: str, timeout: int = 300) -> bool:
|
||||
start = time.time()
|
||||
while time.time() - start < timeout:
|
||||
try:
|
||||
r = requests.get(f"{COMFYUI_URL}/history/{prompt_id}")
|
||||
data = r.json()
|
||||
if prompt_id in data and data[prompt_id].get("outputs"):
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
time.sleep(2)
|
||||
return False
|
||||
|
||||
|
||||
def download_image(prompt_id: str, output_path: Path) -> bool:
|
||||
try:
|
||||
h = requests.get(f"{COMFYUI_URL}/history/{prompt_id}").json()
|
||||
for out in h.get(prompt_id, {}).get("outputs", {}).values():
|
||||
for img in out.get("images", []):
|
||||
r = requests.get(f"{COMFYUI_URL}/view", params={
|
||||
"filename": img["filename"],
|
||||
"subfolder": img.get("subfolder", ""),
|
||||
"type": "output"
|
||||
})
|
||||
if r.status_code == 200:
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Load image and convert white background to transparent
|
||||
img_data = Image.open(io.BytesIO(r.content)).convert("RGBA")
|
||||
datas = img_data.getdata()
|
||||
|
||||
new_data = []
|
||||
for item in datas:
|
||||
# If pixel is white (or near white), make transparent
|
||||
if item[0] > 240 and item[1] > 240 and item[2] > 240:
|
||||
new_data.append((255, 255, 255, 0)) # Transparent
|
||||
else:
|
||||
new_data.append(item)
|
||||
|
||||
img_data.putdata(new_data)
|
||||
img_data.save(str(output_path), "PNG")
|
||||
return True
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"Download error: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 70)
|
||||
print("🎮 DOLINA SMRTI - ComfyUI Local Generator")
|
||||
print(" 2D Indie Cartoon Style | Transparent Background")
|
||||
print("=" * 70)
|
||||
|
||||
# Check ComfyUI
|
||||
try:
|
||||
r = requests.get(f"{COMFYUI_URL}/system_stats", timeout=5)
|
||||
v = r.json()["system"]["comfyui_version"]
|
||||
print(f"✅ ComfyUI v{v} running on port 8000")
|
||||
except:
|
||||
print("❌ ComfyUI not running!")
|
||||
print(" Start with: open /Applications/ComfyUI.app")
|
||||
return
|
||||
|
||||
# Build registry
|
||||
print("\n📋 Building asset registry...")
|
||||
ASSETS = generate_registry()
|
||||
print(f"📊 Total: {len(ASSETS)} assets")
|
||||
|
||||
cats = {}
|
||||
for a in ASSETS:
|
||||
cats[a["cat"]] = cats.get(a["cat"], 0) + 1
|
||||
print("\nBreakdown:")
|
||||
for cat, count in sorted(cats.items(), key=lambda x: -x[1]):
|
||||
print(f" {cat}: {count}")
|
||||
|
||||
existing = sum(1 for a in ASSETS if (OUTPUT_DIR / a["cat"] / a["file"]).exists())
|
||||
print(f"\n✅ Exist: {existing} | 🎯 Generate: {len(ASSETS) - existing}")
|
||||
|
||||
print("\n🎮 STARTING GENERATION...\n")
|
||||
|
||||
success, skip, fail = 0, 0, 0
|
||||
|
||||
for i, asset in enumerate(ASSETS, 1):
|
||||
path = OUTPUT_DIR / asset["cat"] / asset["file"]
|
||||
|
||||
if path.exists():
|
||||
skip += 1
|
||||
continue
|
||||
|
||||
print(f"[{i}/{len(ASSETS)}] 🎨 {asset['file'][:40]}...")
|
||||
|
||||
# Determine size
|
||||
size = 512
|
||||
if "tile_" in asset["file"]:
|
||||
size = 256 # Tiles are smaller
|
||||
elif "boss_" in asset["file"]:
|
||||
size = 768 # Bosses are larger
|
||||
|
||||
wf = create_workflow(asset["prompt"], asset["file"].replace(".png", ""), size)
|
||||
pid = queue_prompt(wf)
|
||||
|
||||
if pid and wait_completion(pid) and download_image(pid, path):
|
||||
print(f" ✅ SAVED (transparent)")
|
||||
success += 1
|
||||
else:
|
||||
print(f" ❌ FAILED")
|
||||
fail += 1
|
||||
|
||||
if i % 10 == 0:
|
||||
print(f"\n📊 [{i}/{len(ASSETS)}] ✅{success} ⏭️{skip} ❌{fail}\n")
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print("🎮 GENERATION COMPLETE!")
|
||||
print(f"✅ Success: {success} | ⏭️ Skip: {skip} | ❌ Fail: {fail}")
|
||||
print(f"📁 Output: {OUTPUT_DIR}")
|
||||
print("\nAll images have TRANSPARENT backgrounds (alpha channel)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user