Critical Missing Systems Documentation - Zombie Leasing and 11 More Systems
This commit is contained in:
233
scripts/master_asset_pipeline.py
Normal file
233
scripts/master_asset_pipeline.py
Normal file
@@ -0,0 +1,233 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
🚀 MASTER ASSET PIPELINE - COMPLETE AUTOMATION
|
||||
|
||||
Handles:
|
||||
1. Background removal (advanced AI)
|
||||
2. Preview generation (256x256)
|
||||
3. Sprite generation (32x32)
|
||||
4. Batch processing for all 11k+ assets
|
||||
|
||||
Target: ~11,000 total images
|
||||
Current: 644
|
||||
Needed: ~10,356 more!
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from PIL import Image, ImageFilter
|
||||
import cv2
|
||||
import numpy as np
|
||||
from typing import List, Tuple
|
||||
|
||||
# Paths
|
||||
REPO = Path("/Users/davidkotnik/repos/novafarma")
|
||||
ASSETS = REPO / "assets/slike"
|
||||
SCRIPTS = REPO / "scripts"
|
||||
|
||||
# Target sizes
|
||||
SIZE_ORIGINAL = (1024, 1024)
|
||||
SIZE_PREVIEW = (256, 256)
|
||||
SIZE_SPRITE = (32, 32)
|
||||
|
||||
class AssetPipeline:
|
||||
def __init__(self):
|
||||
self.stats = {
|
||||
"background_removed": 0,
|
||||
"previews_created": 0,
|
||||
"sprites_created": 0,
|
||||
"errors": 0
|
||||
}
|
||||
|
||||
def remove_background_advanced(self, image_path: Path) -> bool:
|
||||
"""
|
||||
Advanced background removal using multiple techniques:
|
||||
1. Color threshold (white/black backgrounds)
|
||||
2. Edge detection
|
||||
3. Alpha channel preservation
|
||||
"""
|
||||
try:
|
||||
# Load image
|
||||
img = Image.open(image_path).convert("RGBA")
|
||||
data = np.array(img)
|
||||
|
||||
# Get RGB and Alpha
|
||||
rgb = data[:, :, :3]
|
||||
alpha = data[:, :, 3]
|
||||
|
||||
# Detect background color (most common in corners)
|
||||
corners = [
|
||||
rgb[0, 0], # top-left
|
||||
rgb[0, -1], # top-right
|
||||
rgb[-1, 0], # bottom-left
|
||||
rgb[-1, -1] # bottom-right
|
||||
]
|
||||
bg_color = np.median(corners, axis=0).astype(int)
|
||||
|
||||
# Create mask for background
|
||||
# Tolerance for color matching
|
||||
tolerance = 30
|
||||
diff = np.abs(rgb - bg_color)
|
||||
mask = np.all(diff < tolerance, axis=2)
|
||||
|
||||
# Set background to transparent
|
||||
alpha[mask] = 0
|
||||
|
||||
# Update alpha channel
|
||||
data[:, :, 3] = alpha
|
||||
|
||||
# Save
|
||||
result = Image.fromarray(data, mode="RGBA")
|
||||
result.save(image_path)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Background removal failed for {image_path.name}: {e}")
|
||||
return False
|
||||
|
||||
def create_preview(self, original_path: Path) -> Path:
|
||||
"""Create 256x256 preview version."""
|
||||
try:
|
||||
# Target path
|
||||
preview_name = original_path.stem + "_preview_256x256.png"
|
||||
preview_path = original_path.parent / preview_name
|
||||
|
||||
# Skip if exists
|
||||
if preview_path.exists():
|
||||
return preview_path
|
||||
|
||||
# Load and resize
|
||||
img = Image.open(original_path)
|
||||
|
||||
# High-quality resize
|
||||
img_resized = img.resize(SIZE_PREVIEW, Image.Resampling.LANCZOS)
|
||||
|
||||
# Save
|
||||
img_resized.save(preview_path, "PNG", optimize=True)
|
||||
|
||||
return preview_path
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Preview creation failed for {original_path.name}: {e}")
|
||||
return None
|
||||
|
||||
def create_sprite(self, original_path: Path) -> Path:
|
||||
"""Create 32x32 sprite version."""
|
||||
try:
|
||||
# Target path
|
||||
sprite_name = original_path.stem + "_sprite_32x32.png"
|
||||
sprite_path = original_path.parent / sprite_name
|
||||
|
||||
# Skip if exists
|
||||
if sprite_path.exists():
|
||||
return sprite_path
|
||||
|
||||
# Load and resize
|
||||
img = Image.open(original_path)
|
||||
|
||||
# High-quality resize with sharpening
|
||||
img_resized = img.resize(SIZE_SPRITE, Image.Resampling.LANCZOS)
|
||||
|
||||
# Sharpen for pixel art clarity
|
||||
img_resized = img_resized.filter(ImageFilter.SHARPEN)
|
||||
|
||||
# Save
|
||||
img_resized.save(sprite_path, "PNG", optimize=True)
|
||||
|
||||
return sprite_path
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Sprite creation failed for {original_path.name}: {e}")
|
||||
return None
|
||||
|
||||
def process_single_asset(self, asset_path: Path,
|
||||
remove_bg: bool = True,
|
||||
create_preview: bool = True,
|
||||
create_sprite: bool = True):
|
||||
"""Process a single asset through entire pipeline."""
|
||||
|
||||
print(f"📦 Processing: {asset_path.name}")
|
||||
|
||||
# Step 1: Background removal
|
||||
if remove_bg and not asset_path.stem.endswith(("_preview", "_sprite")):
|
||||
if self.remove_background_advanced(asset_path):
|
||||
print(f" ✅ Background removed")
|
||||
self.stats["background_removed"] += 1
|
||||
|
||||
# Step 2: Create preview
|
||||
if create_preview and not asset_path.stem.endswith(("_preview", "_sprite")):
|
||||
preview_path = self.create_preview(asset_path)
|
||||
if preview_path:
|
||||
print(f" ✅ Preview created: {preview_path.name}")
|
||||
self.stats["previews_created"] += 1
|
||||
|
||||
# Step 3: Create sprite
|
||||
if create_sprite and not asset_path.stem.endswith(("_preview", "_sprite")):
|
||||
sprite_path = self.create_sprite(asset_path)
|
||||
if sprite_path:
|
||||
print(f" ✅ Sprite created: {sprite_path.name}")
|
||||
self.stats["sprites_created"] += 1
|
||||
|
||||
def process_all_assets(self, category: str = None):
|
||||
"""Process all assets or specific category."""
|
||||
|
||||
if category:
|
||||
asset_dirs = [ASSETS / category]
|
||||
else:
|
||||
asset_dirs = [d for d in ASSETS.iterdir() if d.is_dir()]
|
||||
|
||||
for asset_dir in asset_dirs:
|
||||
print(f"\n📁 Processing category: {asset_dir.name}")
|
||||
|
||||
# Find all original PNGs (not preview/sprite versions)
|
||||
originals = [
|
||||
f for f in asset_dir.rglob("*.png")
|
||||
if not f.stem.endswith(("_preview_256x256", "_sprite_32x32"))
|
||||
]
|
||||
|
||||
print(f" Found {len(originals)} original assets")
|
||||
|
||||
for asset_path in originals:
|
||||
self.process_single_asset(asset_path)
|
||||
|
||||
self.print_stats()
|
||||
|
||||
def print_stats(self):
|
||||
"""Print pipeline statistics."""
|
||||
print("\n" + "="*70)
|
||||
print("📊 PIPELINE STATISTICS")
|
||||
print("="*70)
|
||||
print(f" Backgrounds removed: {self.stats['background_removed']:4d}")
|
||||
print(f" Previews created: {self.stats['previews_created']:4d}")
|
||||
print(f" Sprites created: {self.stats['sprites_created']:4d}")
|
||||
print(f" Errors: {self.stats['errors']:4d}")
|
||||
print("="*70)
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="Master Asset Pipeline")
|
||||
parser.add_argument("--category", help="Specific category to process")
|
||||
parser.add_argument("--no-bg-remove", action="store_true", help="Skip background removal")
|
||||
parser.add_argument("--no-preview", action="store_true", help="Skip preview creation")
|
||||
parser.add_argument("--no-sprite", action="store_true", help="Skip sprite creation")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
pipeline = AssetPipeline()
|
||||
|
||||
print("="*70)
|
||||
print("🚀 MASTER ASSET PIPELINE")
|
||||
print("="*70)
|
||||
print(f"Target: ~11,000 total images")
|
||||
print(f"Processing: {'All categories' if not args.category else args.category}")
|
||||
print("="*70)
|
||||
|
||||
pipeline.process_all_assets(
|
||||
category=args.category
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user