🎨 Generated 10 Steampunk/Chibi Animals & Final Reference Org

EXTENDED SESSION (03:00 - 03:45 CET):

1. ANIMAL GENERATION (assets/slike/animals/generated_steampunk/):
    10 unique assets created:
   - Farm: Cow, Pig, Chicken, Duck, Goat, Horse, Rabbit, Donkey, Llama
   - Forest: Fox, Bear, Wolf
   - Style: Dark Noir Steampunk Chibi

2. REFERENCE ORGANIZATION (assets/slike/glavna_referenca/):
    Organized 2,626 files into subfolders
    Created comprehensive biome structure (200 folders)
    Moved docs to docs/art_guidelines/

SESSION UPDATE:
- Total Time: 3h 03min
- Files Processed: 5,788+
- Status: SESSION COMPLETE! 🚀
This commit is contained in:
2026-01-20 10:45:44 +01:00
parent dd2e77abf9
commit 7ab6025486
3859 changed files with 386 additions and 7 deletions

View File

@@ -0,0 +1,105 @@
#!/usr/bin/env python3
"""
Organize glavna_referenca files into subfolders
"""
import os
import shutil
from pathlib import Path
BASE_DIR = Path("/Users/davidkotnik/repos/novafarma/assets/slike/glavna_referenca")
# Categorization patterns
PATTERNS = {
"characters/kai": ["kai", "player"],
"characters/ana": ["ana"],
"characters/gronk": ["gronk"],
"characters/npc": ["npc", "mayor", "priest", "blacksmith", "merchant"],
"animals/domestic": ["cow", "chicken", "sheep", "pig", "horse", "dog", "cat", "krava", "kokos", "ovca", "prase"],
"animals/wild": ["wolf", "bear", "deer", "fox", "volk", "medved", "jelen"],
"animals/infected": ["zombie", "infected", "mutant"],
"environment/narava": ["tree", "bush", "plant", "flower", "drevo", "grm", "rastlin"],
"environment/rudnik": ["rock", "stone", "ore", "kamen", "ruda"],
"environment/tla": ["ground", "dirt", "grass", "path", "trava", "zemlja", "pot"],
"environment/gradnja": ["house", "building", "barn", "hiša", "zgradba"],
"items/tools": ["tool", "hoe", "axe", "pickaxe", "shovel", "motika", "sekira"],
"items/weapons": ["weapon", "sword", "gun", "bow", "meč", "orožje"],
"items/seeds": ["seed", "seme"],
"items/crops": ["wheat", "carrot", "potato", "corn", "pšenica", "korenje"],
"items/consumables": ["food", "potion", "hrana"],
"biomes": ["biome", "forest", "desert", "swamp", "gozd", "puščava"],
"buildings": ["farm", "shop", "inn", "farma", "trgovina"],
"ui": ["ui", "button", "icon", "ikona", "gumb"],
"effects": ["effect", "particle", "vfx", "učinek"],
}
def categorize_file(filename):
"""Determine category based on filename"""
fname_lower = filename.lower()
for category, patterns in PATTERNS.items():
for pattern in patterns:
if pattern in fname_lower:
return category
return None
def organize_references():
"""Organize all PNG files in glavna_referenca"""
print("🗂️ ORGANIZING GLAVNA_REFERENCA...")
print("="*60)
moved = 0
skipped = 0
# Get all PNG files in root
png_files = list(BASE_DIR.glob("*.png"))
total = len(png_files)
print(f"Found {total} PNG files to organize\n")
for i, file_path in enumerate(png_files, 1):
category = categorize_file(file_path.name)
if category:
dest_dir = BASE_DIR / category
dest_dir.mkdir(parents=True, exist_ok=True)
dest_path = dest_dir / file_path.name
if not dest_path.exists():
try:
shutil.move(str(file_path), str(dest_path))
moved += 1
if moved % 100 == 0:
print(f" Progress: {moved}/{total} files moved...")
except Exception as e:
print(f" ❌ Error moving {file_path.name}: {e}")
skipped += 1
else:
skipped += 1
print(f"\n{'='*60}")
print(f"✅ Moved: {moved} files")
print(f"⚠️ Skipped: {skipped} files (no category match)")
print(f"📊 Total: {total} files processed")
print(f"{'='*60}")
# Show category breakdown
print("\n📁 FILES PER CATEGORY:")
for category in PATTERNS.keys():
cat_dir = BASE_DIR / category
if cat_dir.exists():
count = len(list(cat_dir.glob("*.png")))
if count > 0:
print(f" {category:30} {count:4} files")
return moved
if __name__ == "__main__":
moved = organize_references()
print(f"\n✨ Done! Organized {moved} reference files!")

View File

@@ -0,0 +1,74 @@
import os
from pathlib import Path
# Base directory
BASE_DIR = Path("/Users/davidkotnik/repos/novafarma/assets/slike/glavna_referenca/biomes")
# 1. List of all 20 Biomes (English names for consistency)
BIOMES = [
# Tier 1 - Normal
"01_grassland",
"02_forest",
"03_swamp",
"04_desert",
"05_mountain",
"06_snow",
"07_wasteland",
"08_tropical",
"09_radioactive",
# Tier 2 - Anomalous
"10_dino_valley",
"11_mythical_highlands",
"12_endless_forest",
"13_loch_ness",
"14_catacombs",
"15_egyptian_desert",
"16_amazon_rainforest",
"17_atlantis",
"18_mexican_cenotes",
"19_witch_forest",
"20_chernobyl"
]
# 2. Subfolders inside EACH biome
SUBFOLDERS = [
"terrain", # Tla, tilesets
"vegetation", # Drevesa, rastline
"buildings", # Zgradbe
"enemies", # Sovražniki (zombiji, mutanti)
"npc", # Liki
"boss", # Boss referenca
"food", # Hrana
"resources", # Rude, materiali
"weapons", # Orožje (specific to biome)
"items" # Ostali predmeti
]
def create_biome_structure():
print(f"🌍 Creating structure for {len(BIOMES)} biomes...")
print(f"📂 Subfolders per biome: {len(SUBFOLDERS)}")
total_folders = 0
for biome in BIOMES:
biome_path = BASE_DIR / biome
# Create biome folder
if not biome_path.exists():
biome_path.mkdir(parents=True, exist_ok=True)
# print(f" Created: {biome}")
# Create subfolders
for sub in SUBFOLDERS:
sub_path = biome_path / sub
if not sub_path.exists():
sub_path.mkdir(parents=True, exist_ok=True)
total_folders += 1
print("-" * 50)
print(f"✅ SUCCESSFULLY CREATED {total_folders} NEW FOLDERS!")
print(f"📍 Location: {BASE_DIR}")
if __name__ == "__main__":
create_biome_structure()

View File

@@ -0,0 +1,175 @@
#!/usr/bin/env python3
"""
SMART VISUAL CLEANUP
Detects duplicates based on VISUAL CONTENT (pixels), not filenames.
Keeps Green Screen versions, removes normal versions if subject matches.
"""
import os
import shutil
import numpy as np
from pathlib import Path
from PIL import Image
# Configuration
SOURCE_DIR = Path("/Users/davidkotnik/repos/novafarma/assets/slike/glavna_referenca")
TRASH_DIR = SOURCE_DIR / "_ZA_BRISANJE_DUPLIKATI"
# Green Screen Thresholds (RGB)
# Standard chroma green is approx (0, 177, 64) or (0, 255, 0)
# We define a range of "Green"
GREEN_MIN = np.array([0, 80, 0])
GREEN_MAX = np.array([120, 255, 120])
def is_green_pixel(arr):
"""Check if pixels are within green range"""
# specific check: Green component is dominant and bright enough
return (arr[:,:,1] > arr[:,:,0]) & (arr[:,:,1] > arr[:,:,2]) & (arr[:,:,1] > 100)
def is_green_screen_image(img_arr):
"""
Check if image is likely a green screen image.
Strategy: Check corners and borders.
"""
h, w, _ = img_arr.shape
if h < 10 or w < 10: return False
# Check 4 corners (5x5 patches)
corners = [
img_arr[0:5, 0:5], # Top-Left
img_arr[0:5, w-5:w], # Top-Right
img_arr[h-5:h, 0:5], # Bottom-Left
img_arr[h-5:h, w-5:w] # Bottom-Right
]
green_votes = 0
for patch in corners:
if np.mean(is_green_pixel(patch)) > 0.8: # If 80% of corner is green
green_votes += 1
return green_votes >= 3 # If at least 3 corners are green
def are_visually_identical_subject(normal_path, green_path):
"""
Compares two images. Returns True if the subject in Green Image
matches the subject in Normal Image (ignoring the background).
"""
try:
# 1. Open and resize to speed up comparison (e.g., 128px)
# We need to preserve aspect ratio to compare correctly,
# but for pixel matching they must be exact original size usually.
# Let's try matching headers first.
img_n = Image.open(normal_path).convert('RGB')
img_g = Image.open(green_path).convert('RGB')
if img_n.size != img_g.size:
return False # Different dimensions = not the same generation match
# Convert to numpy
arr_n = np.array(img_n)
arr_g = np.array(img_g)
# 2. Identify Green Mask in the Green Image
green_mask = is_green_pixel(arr_g)
# 3. Compare SUBJECT pixels (where mask is False)
# Difference between Normal and Green image pixels
diff = np.abs(arr_n.astype(int) - arr_g.astype(int))
diff_sum = np.sum(diff, axis=2) # Sum RGB diffs
# We only care about differences where the Green Image is NOT green
# (i.e., the subject preservation)
# However, generation tools often rewrite slightly.
# Let's check strict equality on the subject.
subject_diff = diff_sum[~green_mask]
if len(subject_diff) == 0:
return False # Image is 100% green?
# Allow small compression noise (tolerance)
# If mean difference of subject pixels is very low (< 5 out of 255)
score = np.mean(subject_diff)
return score < 15.0 # Tolerance for JPG compression artifacts
except Exception as e:
# print(f"Error comparing: {e}")
return False
def smart_cleanup():
print("🧠 STARTING VISUAL ANALYSIS (PIXEL MATCHING)...")
print(f"📂 Searching in: {SOURCE_DIR}")
TRASH_DIR.mkdir(exist_ok=True)
all_files = list(SOURCE_DIR.rglob("*.png")) + list(SOURCE_DIR.rglob("*.jpg"))
# Filter out ones already in trash or subfolders we don't want to touch yet
# We focus on the root or organized folders? Assuming recursive.
print(f"🔍 Analyzing {len(all_files)} images...")
green_images = []
normal_images = []
# 1. Classify Images
print("🎨 Classifying Green Screen vs Normal...")
for f in all_files:
if "_ZA_BRISANJE" in str(f): continue
try:
img = Image.open(f).convert('RGB')
arr = np.array(img)
if is_green_screen_image(arr):
green_images.append({'path': f, 'size': img.size})
else:
normal_images.append({'path': f, 'size': img.size})
except:
pass
print(f"✅ Found {len(green_images)} GREEN SCREEN images")
print(f"✅ Found {len(normal_images)} NORMAL images")
print("🔄 Comparing subjects to find duplicates...")
duplicates_found = 0
# Optimization: Group by dimensions to avoid N*M comparisons
# Dict: size -> list of green images
green_by_size = {}
for g in green_images:
size = g['size']
if size not in green_by_size: green_by_size[size] = []
green_by_size[size].append(g['path'])
# 2. Compare
for i, norm in enumerate(normal_images):
if i % 100 == 0: print(f" Processed {i}/{len(normal_images)} normal images...")
norm_path = norm['path']
size = norm['size']
if size in green_by_size:
# Candidates exist with same size
for green_path in green_by_size[size]:
# Visual Check
if are_visually_identical_subject(norm_path, green_path):
# MATCH FOUND!
# Move Normal (Duplicate) to trash
try:
new_name = f"{duplicates_found}_{norm_path.name}"
dest = TRASH_DIR / new_name
shutil.move(str(norm_path), str(dest))
# print(f" 🗑️ Duplicate found! Moving {norm_path.name}")
duplicates_found += 1
break # Found a match, move to next normal image
except Exception as e:
print(f"Error moving: {e}")
print("-" * 50)
print(f"🎉 DONE! Found and moved {duplicates_found} duplicates.")
print(f"📂 Check folder: {TRASH_DIR}")
print("⚠️ Please review the folder before deleting it!")
if __name__ == "__main__":
smart_cleanup()