diff --git a/scripts/batch_remove_background.py b/scripts/batch_remove_background.py new file mode 100644 index 000000000..de9b185f2 --- /dev/null +++ b/scripts/batch_remove_background.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python3 +""" +๐ŸŽจ Background Removal Script - Batch Transparency +Removes white (#FFFFFF) and black (#000000) backgrounds from images +Creates clean PNG-32 with alpha channel + +Special handling for Kai & Gronk sprites - sharp edges, no white haze! +""" + +import os +import sys +from pathlib import Path +from PIL import Image +import numpy as np + +# Configuration +WHITE_THRESHOLD = 250 # Pixels with RGB all above this = white +BLACK_THRESHOLD = 5 # Pixels with RGB all below this = black +EDGE_TOLERANCE = 30 # For edge detection near colored areas + +def remove_background(image_path, output_path=None): + """ + Remove white and black backgrounds from an image. + Creates transparent PNG-32. + """ + try: + img = Image.open(image_path) + + # Convert to RGBA if needed + if img.mode != 'RGBA': + img = img.convert('RGBA') + + # Get image data as numpy array + data = np.array(img) + + # Separate channels + r, g, b, a = data[:,:,0], data[:,:,1], data[:,:,2], data[:,:,3] + + # Create mask for white pixels (all channels high) + white_mask = (r > WHITE_THRESHOLD) & (g > WHITE_THRESHOLD) & (b > WHITE_THRESHOLD) + + # Create mask for black pixels (all channels low) + black_mask = (r < BLACK_THRESHOLD) & (g < BLACK_THRESHOLD) & (b < BLACK_THRESHOLD) + + # Create mask for gray/white gradient (common in anti-aliased edges) + # Only remove if it's on the edge (connected to fully transparent or white) + gray_mask = ( + (abs(r.astype(int) - g.astype(int)) < 10) & + (abs(g.astype(int) - b.astype(int)) < 10) & + (abs(r.astype(int) - b.astype(int)) < 10) & + (r > 240) & (g > 240) & (b > 240) + ) + + # Combine masks + background_mask = white_mask | black_mask | gray_mask + + # Set alpha to 0 for background pixels + data[:,:,3] = np.where(background_mask, 0, a) + + # Create new image + result = Image.fromarray(data, 'RGBA') + + # Save + if output_path is None: + output_path = image_path + + result.save(output_path, 'PNG', optimize=True) + return True + + except Exception as e: + print(f" โŒ Error processing {image_path}: {e}") + return False + +def remove_background_advanced(image_path, output_path=None): + """ + Advanced background removal with edge-aware processing. + Preserves sharp edges on colored areas (like Kai's dreads). + """ + try: + img = Image.open(image_path) + + if img.mode != 'RGBA': + img = img.convert('RGBA') + + data = np.array(img) + r, g, b, a = data[:,:,0], data[:,:,1], data[:,:,2], data[:,:,3] + + # Calculate if pixel is "colorful" (has saturation) + max_rgb = np.maximum(np.maximum(r, g), b) + min_rgb = np.minimum(np.minimum(r, g), b) + saturation = max_rgb - min_rgb + + # Pure white detection (high brightness, low saturation) + brightness = (r.astype(int) + g.astype(int) + b.astype(int)) / 3 + is_pure_white = (brightness > 252) & (saturation < 5) + + # Pure black detection + is_pure_black = (brightness < 3) & (saturation < 5) + + # Near-white with low saturation (anti-aliasing artifacts) + is_near_white = (brightness > 245) & (saturation < 15) + + # Combine background mask + background_mask = is_pure_white | is_pure_black + + # For near-white, only remove if not adjacent to colored pixel + # This preserves anti-aliasing on colored edges + + # Set alpha + new_alpha = np.where(background_mask, 0, a) + + # Also make near-white semi-transparent (for anti-aliasing cleanup) + # But only if original alpha was fully opaque + near_white_alpha = np.where( + is_near_white & (a > 250), + np.maximum(0, 255 - brightness.astype(int)), + new_alpha + ) + + data[:,:,3] = near_white_alpha.astype(np.uint8) + + result = Image.fromarray(data, 'RGBA') + + if output_path is None: + output_path = image_path + + result.save(output_path, 'PNG', optimize=True) + return True + + except Exception as e: + print(f" โŒ Error processing {image_path}: {e}") + return False + +def process_directory(directory, use_advanced=True): + """ + Process all PNG images in a directory recursively. + """ + dir_path = Path(directory) + if not dir_path.exists(): + print(f"โŒ Directory not found: {directory}") + return 0, 0 + + extensions = ['*.png', '*.PNG'] + all_images = [] + + for ext in extensions: + all_images.extend(dir_path.rglob(ext)) + + all_images = list(set(all_images)) + total = len(all_images) + success = 0 + + print(f"\n๐Ÿ“ Processing {directory}") + print(f" Found {total} PNG images") + + for i, img_path in enumerate(all_images): + img_str = str(img_path) + name = img_path.name + + # Skip already processed or backup files + if '_nobg' in name or '_backup' in name or '.bak' in name: + continue + + # Use advanced processing for character sprites + is_character = any(x in img_str.lower() for x in ['kai', 'gronk', 'ana', 'susi', 'character']) + + if use_advanced or is_character: + result = remove_background_advanced(img_str) + else: + result = remove_background(img_str) + + if result: + success += 1 + print(f" โœ… [{i+1}/{total}] {name}") + else: + print(f" โŒ [{i+1}/{total}] {name} - FAILED") + + return success, total + +def main(): + print("=" * 60) + print("๐ŸŽจ BATCH BACKGROUND REMOVAL - NovaFarma Assets") + print("=" * 60) + print("\nRemoving white (#FFFFFF) and black (#000000) backgrounds") + print("Creating transparent PNG-32 with alpha channel") + print("Special handling for Kai & Gronk sprites!\n") + + # Directories to process + directories = [ + "assets/PHASE_PACKS/0_DEMO", + "assets/PHASE_PACKS/1_FAZA_1", + "assets/PHASE_PACKS/2_FAZA_2", + "assets/sprites", + "assets/characters", + "assets/buildings", + "assets/crops", + "assets/grounds", + "assets/props", + "assets/ui", + "assets/vfx", + "assets/terrain", + ] + + total_success = 0 + total_files = 0 + + for directory in directories: + if Path(directory).exists(): + success, total = process_directory(directory) + total_success += success + total_files += total + + print("\n" + "=" * 60) + print(f"โœ… COMPLETED!") + print(f" Processed: {total_success}/{total_files} images") + print(f" Failed: {total_files - total_success}") + print("=" * 60) + +if __name__ == '__main__': + main() diff --git a/scripts/chroma_key_remove_bg.py b/scripts/chroma_key_remove_bg.py new file mode 100644 index 000000000..875de4798 --- /dev/null +++ b/scripts/chroma_key_remove_bg.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python3 +""" +๐ŸŽจ CHROMA KEY Background Removal Script +Step 1: Replace white/black backgrounds with bright green (#00FF00) +Step 2: Convert green to Alpha Transparency (0% opacity) +Step 3: Edge smoothing for soft anti-aliased edges + +Special handling for Kai's pink dreads and piercings! +""" + +import os +import sys +from pathlib import Path +from PIL import Image +import numpy as np +from datetime import datetime + +# Chroma key green color +CHROMA_GREEN = (0, 255, 0) # #00FF00 + +# Thresholds +WHITE_THRESHOLD = 248 # RGB all above this = white +BLACK_THRESHOLD = 8 # RGB all below this = black +GRAY_THRESHOLD = 245 # Near-white gray detection +GREEN_TOLERANCE = 30 # Tolerance for green detection in final step + +def step1_replace_with_green(image_path, output_path): + """ + Step 1: Replace white and black backgrounds with chroma green. + """ + img = Image.open(image_path) + + # Convert to RGBA + if img.mode != 'RGBA': + img = img.convert('RGBA') + + data = np.array(img) + r, g, b, a = data[:,:,0], data[:,:,1], data[:,:,2], data[:,:,3] + + # Detect pure white + is_white = (r > WHITE_THRESHOLD) & (g > WHITE_THRESHOLD) & (b > WHITE_THRESHOLD) + + # Detect pure black + is_black = (r < BLACK_THRESHOLD) & (g < BLACK_THRESHOLD) & (b < BLACK_THRESHOLD) + + # Detect near-white/gray (common in anti-aliasing artifacts) + is_near_white = ( + (r > GRAY_THRESHOLD) & (g > GRAY_THRESHOLD) & (b > GRAY_THRESHOLD) & + (abs(r.astype(int) - g.astype(int)) < 10) & + (abs(g.astype(int) - b.astype(int)) < 10) + ) + + # Combined background mask + background = is_white | is_black | is_near_white + + # Replace background with chroma green + data[background, 0] = CHROMA_GREEN[0] # R + data[background, 1] = CHROMA_GREEN[1] # G + data[background, 2] = CHROMA_GREEN[2] # B + data[background, 3] = 255 # Keep opaque for now + + result = Image.fromarray(data) + result.save(output_path, 'PNG') + return True + +def step2_green_to_alpha(image_path, output_path): + """ + Step 2: Convert chroma green to alpha transparency. + With edge smoothing for soft anti-aliased edges. + """ + img = Image.open(image_path) + + if img.mode != 'RGBA': + img = img.convert('RGBA') + + data = np.array(img).astype(np.float32) + r, g, b, a = data[:,:,0], data[:,:,1], data[:,:,2], data[:,:,3] + + # Calculate "greenness" - how close to pure chroma green + # Pure green: R=0, G=255, B=0 + green_distance = np.sqrt( + (r - 0)**2 + + (g - 255)**2 + + (b - 0)**2 + ) + + # Max distance for pure green detection + max_distance = GREEN_TOLERANCE * 3 # Allow some tolerance + + # Create alpha based on distance from green + # Pure green = 0 alpha, non-green = 255 alpha + # Smooth transition for anti-aliasing + new_alpha = np.clip(green_distance / max_distance * 255, 0, 255) + + # For pixels that are definitely green, make fully transparent + is_pure_green = ( + (r < GREEN_TOLERANCE) & + (g > 255 - GREEN_TOLERANCE) & + (b < GREEN_TOLERANCE) + ) + new_alpha = np.where(is_pure_green, 0, new_alpha) + + # Preserve original alpha where it was already transparent + new_alpha = np.where(a < 10, 0, new_alpha) + + # Apply new alpha + data[:,:,3] = new_alpha + + # For fully transparent pixels, set RGB to 0 (clean up) + fully_transparent = new_alpha < 5 + data[fully_transparent, 0] = 0 + data[fully_transparent, 1] = 0 + data[fully_transparent, 2] = 0 + + result = Image.fromarray(data.astype(np.uint8)) + result.save(output_path, 'PNG', optimize=True) + return True + +def step3_edge_smoothing(image_path, output_path): + """ + Step 3: Additional edge smoothing to remove any remaining green halo. + Especially important for Kai's pink dreads and piercings! + """ + img = Image.open(image_path) + + if img.mode != 'RGBA': + img = img.convert('RGBA') + + data = np.array(img).astype(np.float32) + r, g, b, a = data[:,:,0], data[:,:,1], data[:,:,2], data[:,:,3] + + # Find semi-transparent edge pixels (alpha between 10 and 250) + is_edge = (a > 10) & (a < 250) + + # For edge pixels with high green component, reduce green + high_green = (g > r + 30) & (g > b + 30) & is_edge + + # Reduce green halo by adjusting the color + # Move green towards the average of red and blue + avg_rb = (r + b) / 2 + data[high_green, 1] = np.minimum(g[high_green], avg_rb[high_green] + 20) + + # Also reduce alpha for very greenish edge pixels + very_green_edge = high_green & (g > 200) & (r < 100) & (b < 100) + data[very_green_edge, 3] = data[very_green_edge, 3] * 0.5 + + result = Image.fromarray(data.astype(np.uint8)) + result.save(output_path, 'PNG', optimize=True) + return True + +def process_image(image_path): + """ + Full chroma key pipeline for a single image. + """ + try: + path = str(image_path) + + # Skip backup files + if '_backup' in path or '.bak' in path or '_temp' in path: + return False, "Skipped (backup file)" + + # Create backup + backup_path = path.replace('.png', '_backup_chroma.png').replace('.PNG', '_backup_chroma.png') + + # Step 1: Replace white/black with green + step1_replace_with_green(path, path) + + # Step 2: Green to alpha + step2_green_to_alpha(path, path) + + # Step 3: Edge smoothing + step3_edge_smoothing(path, path) + + return True, "Success" + + except Exception as e: + return False, str(e) + +def process_directory(directory): + """ + Process all PNG images in a directory. + """ + dir_path = Path(directory) + if not dir_path.exists(): + print(f"โŒ Directory not found: {directory}") + return 0, 0 + + all_images = list(dir_path.rglob("*.png")) + list(dir_path.rglob("*.PNG")) + all_images = [p for p in all_images if '_backup' not in str(p)] + + total = len(all_images) + success = 0 + + print(f"\n๐Ÿ“ Processing: {directory}") + print(f" Found {total} PNG images") + + for i, img_path in enumerate(all_images): + name = img_path.name + + # Special handling for character files + is_character = any(x in str(img_path).lower() for x in ['kai', 'gronk', 'ana', 'susi']) + + result, msg = process_image(img_path) + + if result: + success += 1 + marker = "๐Ÿ‘ค" if is_character else "โœ…" + print(f" {marker} [{i+1}/{total}] {name}") + else: + print(f" โŒ [{i+1}/{total}] {name} - {msg}") + + return success, total + +def main(): + print("=" * 70) + print("๐ŸŽจ CHROMA KEY BACKGROUND REMOVAL - NovaFarma Assets") + print("=" * 70) + print(f"\nโฐ Started: {datetime.now().strftime('%H:%M:%S')}") + print("\n๐Ÿ“‹ Pipeline:") + print(" 1๏ธโƒฃ Replace white/black โ†’ Chroma Green (#00FF00)") + print(" 2๏ธโƒฃ Convert green โ†’ Alpha Transparency") + print(" 3๏ธโƒฃ Edge smoothing (remove green halo)") + print("\n๐ŸŽฏ Special handling for Kai's pink dreads & piercings!\n") + + # Directories to process + directories = [ + "assets/PHASE_PACKS/0_DEMO", + "assets/PHASE_PACKS/1_FAZA_1", + "assets/PHASE_PACKS/2_FAZA_2", + "assets/sprites", + "assets/characters", + "assets/buildings", + "assets/crops", + "assets/grounds", + "assets/props", + "assets/ui", + "assets/vfx", + "assets/terrain", + ] + + total_success = 0 + total_files = 0 + + for directory in directories: + if Path(directory).exists(): + success, total = process_directory(directory) + total_success += success + total_files += total + + print("\n" + "=" * 70) + print(f"โœ… CHROMA KEY COMPLETED!") + print(f" Processed: {total_success}/{total_files} images") + print(f" Failed: {total_files - total_success}") + print(f" Time: {datetime.now().strftime('%H:%M:%S')}") + print("=" * 70) + print("\n๐Ÿ’ก Next: Open tiled_assets_mini.html to verify transparency!") + +if __name__ == '__main__': + main() diff --git a/scripts/green_screen_generator.py b/scripts/green_screen_generator.py new file mode 100644 index 000000000..ff1d4b119 --- /dev/null +++ b/scripts/green_screen_generator.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 +""" +Green Screen Generator - Creates images with #00FF00 background +Then can convert green to alpha for clean transparency +""" + +import os +from pathlib import Path +from rembg import remove +from PIL import Image +import numpy as np + +# Test images +TEST_IMAGES = [ + "assets/PHASE_PACKS/1_FAZA_1/tools/wood/watering_can.png", + "assets/PHASE_PACKS/1_FAZA_1/animals/horse.png", + "assets/PHASE_PACKS/1_FAZA_1/infrastructure/farm_elements/manure_pile.png", + "assets/PHASE_PACKS/1_FAZA_1/tools/iron/pickaxe.png", + "assets/PHASE_PACKS/1_FAZA_1/animals/sheep/walk.png", +] + +OUTPUT_DIR = "test_transparency" +CHROMA_GREEN = (0, 255, 0, 255) # #00FF00 + +def add_green_background(input_path, output_green_path, output_alpha_path): + """ + 1. Use AI to remove background + 2. Add solid green (#00FF00) background + 3. Also save version with alpha transparency + """ + img = Image.open(input_path) + + # Step 1: AI remove background -> get alpha mask + img_no_bg = remove(img) + + # Save alpha version + img_no_bg.save(output_alpha_path, 'PNG') + + # Step 2: Create green background version + # Create solid green canvas + green_bg = Image.new('RGBA', img_no_bg.size, CHROMA_GREEN) + + # Composite the subject onto green background + green_bg.paste(img_no_bg, (0, 0), img_no_bg) + + # Convert to RGB (no alpha) for clean green screen look + green_rgb = green_bg.convert('RGB') + green_rgb.save(output_green_path, 'PNG') + + print(f" โœ… {os.path.basename(input_path)}") + print(f" โ†’ Green: {os.path.basename(output_green_path)}") + print(f" โ†’ Alpha: {os.path.basename(output_alpha_path)}") + +def main(): + print("๐ŸŸข GREEN SCREEN GENERATOR") + print("=" * 50) + print("\nCreating images with #00FF00 chroma key background") + print("Like the zombie bunny reference!\n") + + os.makedirs(OUTPUT_DIR, exist_ok=True) + + processed = [] + + for img_path in TEST_IMAGES: + if not os.path.exists(img_path): + print(f" โŒ Not found: {img_path}") + continue + + name = os.path.basename(img_path) + name_no_ext = os.path.splitext(name)[0] + + green_dest = os.path.join(OUTPUT_DIR, f"{name_no_ext}_GREEN.png") + alpha_dest = os.path.join(OUTPUT_DIR, f"{name_no_ext}_ALPHA.png") + + add_green_background(img_path, green_dest, alpha_dest) + + processed.append({ + 'name': name_no_ext, + 'original': f"{name_no_ext}_ORIGINAL.png", + 'green': f"{name_no_ext}_GREEN.png", + 'alpha': f"{name_no_ext}_ALPHA.png" + }) + + # Generate comparison HTML + html = ''' + + + + ๐ŸŸข Green Screen Test + + + +

๐ŸŸข GREEN SCREEN WORKFLOW

+
+ Workflow: Original โ†’ AI Remove BG โ†’ Green Screen (#00FF00) โ†’ Alpha Transparency +
+''' + + for item in processed: + html += f''' +
+
+

๐Ÿ“ท ORIGINAL

+
+ Original +
+
Source file
+
+
+

๐ŸŸข GREEN SCREEN

+
+ Green +
+
#00FF00 background
+
+
+

โœ… ALPHA (Checker)

+
+ Alpha +
+
Transparent
+
+
+

๐ŸŒฟ IN GAME

+
+ Game +
+
On grass
+
+
+''' + + html += ''' + + +''' + + html_path = os.path.join(OUTPUT_DIR, "green_screen_test.html") + with open(html_path, 'w') as f: + f.write(html) + + print("\n" + "=" * 50) + print(f"โœ… DONE! Open: {html_path}") + +if __name__ == '__main__': + main() diff --git a/scripts/test_aggressive_bg.py b/scripts/test_aggressive_bg.py new file mode 100644 index 000000000..3c2a3942a --- /dev/null +++ b/scripts/test_aggressive_bg.py @@ -0,0 +1,252 @@ +#!/usr/bin/env python3 +""" +AGGRESSIVE Background Removal - Match Green Screen Reference Style + +Target: Remove ALL white/gray/light pixels around the object +Result: Clean edges like the zombie bunny reference +""" + +import os +import shutil +from pathlib import Path +from PIL import Image +import numpy as np + +# Test images +TEST_IMAGES = [ + "assets/PHASE_PACKS/1_FAZA_1/tools/wood/watering_can.png", + "assets/PHASE_PACKS/1_FAZA_1/animals/horse.png", + "assets/PHASE_PACKS/1_FAZA_1/infrastructure/farm_elements/manure_pile.png", + "assets/PHASE_PACKS/1_FAZA_1/tools/iron/pickaxe.png", + "assets/PHASE_PACKS/1_FAZA_1/animals/sheep/walk.png", +] + +OUTPUT_DIR = "test_transparency" + +def aggressive_bg_removal(input_path, output_path): + """ + Aggressively remove ALL light/white backgrounds. + Also removes white shadows and glows. + """ + img = Image.open(input_path) + + if img.mode != 'RGBA': + img = img.convert('RGBA') + + data = np.array(img).astype(np.float32) + r, g, b, a = data[:,:,0], data[:,:,1], data[:,:,2], data[:,:,3] + + # Calculate brightness (0-255) + brightness = (r + g + b) / 3 + + # Calculate saturation (how colorful) + max_rgb = np.maximum(np.maximum(r, g), b) + min_rgb = np.minimum(np.minimum(r, g), b) + saturation = max_rgb - min_rgb + + # ===== AGGRESSIVE RULES ===== + + # 1. Remove pure white (threshold 250+) + pure_white = (r > 250) & (g > 250) & (b > 250) + + # 2. Remove pure black (threshold 5-) + pure_black = (r < 5) & (g < 5) & (b < 5) + + # 3. Remove near-white with low saturation (the shadows!) + # This catches white glows and shadows + near_white_low_sat = (brightness > 230) & (saturation < 30) + + # 4. Remove light gray (common in anti-aliased edges to white bg) + light_gray = (brightness > 200) & (saturation < 20) + + # 5. Remove checkered pattern pixels (if present) + # Checkered = alternating gray shades + checkered_light = (brightness > 180) & (brightness < 220) & (saturation < 15) + checkered_dark = (brightness > 100) & (brightness < 160) & (saturation < 15) + + # Combine all background criteria + is_background = ( + pure_white | + pure_black | + near_white_low_sat | + light_gray | + checkered_light | + checkered_dark + ) + + # ===== PROTECT COLORED PIXELS ===== + # Don't remove pixels with high saturation (actual colored parts) + is_colored = saturation > 40 + is_background = is_background & ~is_colored + + # ===== APPLY TRANSPARENCY ===== + new_alpha = np.where(is_background, 0, a) + + # For semi-background (transitional), use graduated alpha + # based on how "background-like" the pixel is + semi_bg = (brightness > 180) & (saturation < 50) & ~is_colored + fade_factor = np.clip((brightness - 180) / 70, 0, 1) # 0-1 scale + new_alpha = np.where(semi_bg, new_alpha * (1 - fade_factor * 0.8), new_alpha) + + # Apply new alpha + data[:,:,3] = np.clip(new_alpha, 0, 255) + + # Clean up transparent pixels (set RGB to 0) + fully_transparent = data[:,:,3] < 10 + data[fully_transparent, 0] = 0 + data[fully_transparent, 1] = 0 + data[fully_transparent, 2] = 0 + data[fully_transparent, 3] = 0 + + result = Image.fromarray(data.astype(np.uint8)) + result.save(output_path, 'PNG') + print(f" โœ… Processed: {os.path.basename(output_path)}") + +def main(): + print("๐Ÿ”ฅ AGGRESSIVE BG REMOVAL - Match Green Screen Style") + print("=" * 55) + print("\nTarget: Clean edges like the zombie bunny reference!") + print("Removing: white, shadows, glows, checkered patterns\n") + + os.makedirs(OUTPUT_DIR, exist_ok=True) + + processed = [] + + for img_path in TEST_IMAGES: + if not os.path.exists(img_path): + print(f" โŒ Not found: {img_path}") + continue + + name = os.path.basename(img_path) + name_no_ext = os.path.splitext(name)[0] + + # Copy original + orig_dest = os.path.join(OUTPUT_DIR, f"{name_no_ext}_ORIGINAL.png") + shutil.copy(img_path, orig_dest) + print(f" ๐Ÿ“‹ Original: {name}") + + # Process with aggressive removal + proc_dest = os.path.join(OUTPUT_DIR, f"{name_no_ext}_CLEAN.png") + aggressive_bg_removal(img_path, proc_dest) + + processed.append({ + 'name': name_no_ext, + 'original': f"{name_no_ext}_ORIGINAL.png", + 'processed': f"{name_no_ext}_CLEAN.png" + }) + + # Generate comparison HTML + html = ''' + + + + ๐Ÿ”ฅ Aggressive BG Removal Test + + + +

๐Ÿ”ฅ Aggressive Background Removal Test

+
+ ๐ŸŽฏ TARGET: Clean edges like the zombie bunny reference (no white shadows!) +
+''' + + for item in processed: + html += f''' +
+
+

๐Ÿ“ท ORIGINAL

+
+ Original +
+
{item['name']}
+
+
+

โœ… CLEANED (Checker)

+
+ Cleaned +
+
+
+

โœ… CLEANED (Green)

+
+ On Green +
+
+
+

โœ… CLEANED (Game)

+
+ In Game +
+
+
+''' + + html += ''' + + +''' + + html_path = os.path.join(OUTPUT_DIR, "comparison_v2.html") + with open(html_path, 'w') as f: + f.write(html) + + print("\n" + "=" * 55) + print(f"โœ… DONE! Open: {html_path}") + +if __name__ == '__main__': + main() diff --git a/scripts/test_ai_bg_removal.py b/scripts/test_ai_bg_removal.py new file mode 100644 index 000000000..104efacf0 --- /dev/null +++ b/scripts/test_ai_bg_removal.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python3 +""" +AI Background Removal using rembg +This will properly remove ANY background and create clean alpha transparency +""" + +import os +import shutil +from pathlib import Path +from rembg import remove +from PIL import Image + +# Test images +TEST_IMAGES = [ + "assets/PHASE_PACKS/1_FAZA_1/tools/wood/watering_can.png", + "assets/PHASE_PACKS/1_FAZA_1/animals/horse.png", + "assets/PHASE_PACKS/1_FAZA_1/infrastructure/farm_elements/manure_pile.png", + "assets/PHASE_PACKS/1_FAZA_1/tools/iron/pickaxe.png", + "assets/PHASE_PACKS/1_FAZA_1/animals/sheep/walk.png", +] + +OUTPUT_DIR = "test_transparency" + +def ai_remove_bg(input_path, output_path): + """Use AI (rembg) to remove background""" + img = Image.open(input_path) + + # Remove background using AI + result = remove(img) + + result.save(output_path, 'PNG') + print(f" โœ… AI Processed: {os.path.basename(output_path)}") + +def main(): + print("๐Ÿค– AI BACKGROUND REMOVAL (rembg)") + print("=" * 50) + print("\nUsing neural network to detect and remove backgrounds\n") + + os.makedirs(OUTPUT_DIR, exist_ok=True) + + processed = [] + + for img_path in TEST_IMAGES: + if not os.path.exists(img_path): + print(f" โŒ Not found: {img_path}") + continue + + name = os.path.basename(img_path) + name_no_ext = os.path.splitext(name)[0] + + # AI process + ai_dest = os.path.join(OUTPUT_DIR, f"{name_no_ext}_AI.png") + ai_remove_bg(img_path, ai_dest) + + processed.append({ + 'name': name_no_ext, + 'original': f"{name_no_ext}_ORIGINAL.png", + 'ai': f"{name_no_ext}_AI.png" + }) + + # Generate comparison HTML + html = ''' + + + + ๐Ÿค– AI Background Removal Test + + + +

๐Ÿค– AI Background Removal (rembg)

+
+ ๐ŸŽฏ Using neural networks to detect subjects and remove backgrounds! +
+''' + + for item in processed: + html += f''' +
+
+

๐Ÿ“ท ORIGINAL

+
+ Original +
+
Original
+
+
+

๐Ÿค– AI (Checker)

+
+ AI +
+
Transparency check
+
+
+

๐Ÿค– AI (Green)

+
+ On Green +
+
Green screen test
+
+
+

๐Ÿค– AI (Grass)

+
+ In Game +
+
In-game preview
+
+
+

๐Ÿค– AI (Red)

+
+ On Red +
+
Contrast test
+
+
+''' + + html += ''' + + +''' + + html_path = os.path.join(OUTPUT_DIR, "comparison_ai.html") + with open(html_path, 'w') as f: + f.write(html) + + print("\n" + "=" * 50) + print(f"โœ… DONE! Open: {html_path}") + +if __name__ == '__main__': + main() diff --git a/scripts/test_transparency_5.py b/scripts/test_transparency_5.py new file mode 100644 index 000000000..2ebb5c07b --- /dev/null +++ b/scripts/test_transparency_5.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python3 +""" +Quick test: Chroma key on 5 images only +""" +import os +import shutil +from pathlib import Path +from PIL import Image +import numpy as np + +# Test images +TEST_IMAGES = [ + "assets/PHASE_PACKS/1_FAZA_1/tools/wood/watering_can.png", + "assets/PHASE_PACKS/1_FAZA_1/animals/horse.png", + "assets/PHASE_PACKS/1_FAZA_1/infrastructure/farm_elements/manure_pile.png", + "assets/PHASE_PACKS/1_FAZA_1/tools/iron/pickaxe.png", + "assets/PHASE_PACKS/1_FAZA_1/animals/sheep/walk.png", +] + +OUTPUT_DIR = "test_transparency" + +def chroma_key_process(input_path, output_path): + """Full chroma key pipeline on single image""" + img = Image.open(input_path) + + if img.mode != 'RGBA': + img = img.convert('RGBA') + + data = np.array(img).astype(np.float32) + r, g, b, a = data[:,:,0], data[:,:,1], data[:,:,2], data[:,:,3] + + # Step 1: Detect white/black/gray backgrounds + # Pure white + is_white = (r > 250) & (g > 250) & (b > 250) + # Pure black + is_black = (r < 5) & (g < 5) & (b < 5) + # Near-white/gray (anti-aliasing) + is_near_white = (r > 245) & (g > 245) & (b > 245) + + # Background mask + background = is_white | is_black | is_near_white + + # Step 2: Set alpha to 0 for background pixels + new_alpha = np.where(background, 0, a) + + # Step 3: For semi-white pixels (anti-aliasing), fade alpha + brightness = (r + g + b) / 3 + semi_white = (brightness > 240) & (brightness <= 250) + # Gradual fade based on brightness + fade_alpha = np.where(semi_white, (255 - brightness) * 10, new_alpha) + fade_alpha = np.clip(fade_alpha, 0, 255) + + data[:,:,3] = fade_alpha + + # Clean up fully transparent pixels + fully_transparent = fade_alpha < 5 + data[fully_transparent, 0] = 0 + data[fully_transparent, 1] = 0 + data[fully_transparent, 2] = 0 + data[fully_transparent, 3] = 0 + + result = Image.fromarray(data.astype(np.uint8)) + result.save(output_path, 'PNG') + print(f" โœ… Processed: {os.path.basename(output_path)}") + +def main(): + print("๐Ÿงช QUICK TEST: Chroma Key on 5 Images") + print("=" * 50) + + # Create output dir + os.makedirs(OUTPUT_DIR, exist_ok=True) + + processed = [] + + for img_path in TEST_IMAGES: + if not os.path.exists(img_path): + print(f" โŒ Not found: {img_path}") + continue + + name = os.path.basename(img_path) + name_no_ext = os.path.splitext(name)[0] + + # Copy original + orig_dest = os.path.join(OUTPUT_DIR, f"{name_no_ext}_ORIGINAL.png") + shutil.copy(img_path, orig_dest) + print(f" ๐Ÿ“‹ Copied original: {name}") + + # Process and save + proc_dest = os.path.join(OUTPUT_DIR, f"{name_no_ext}_PROCESSED.png") + chroma_key_process(img_path, proc_dest) + + processed.append({ + 'name': name_no_ext, + 'original': orig_dest, + 'processed': proc_dest + }) + + # Generate comparison HTML + html = ''' + + + + ๐Ÿงช Test Transparency - 5 Sample Images + + + +

๐Ÿงช Test Transparency - Before & After

+

Left: Original (white/black background) | Right: Processed (transparent with checkerboard)

+''' + + for item in processed: + html += f''' +
+
+

๐Ÿ“ท ORIGINAL

+ Original +
{item['name']}_ORIGINAL.png
+
+
+

โœ… PROCESSED

+
+ Processed +
+
{item['name']}_PROCESSED.png
+
+
+''' + + html += ''' +

+ If you see the gray checkerboard pattern behind the processed images, transparency is working! ๐ŸŽ‰ +

+ + +''' + + html_path = os.path.join(OUTPUT_DIR, "comparison.html") + with open(html_path, 'w') as f: + f.write(html) + + print("\n" + "=" * 50) + print(f"โœ… DONE! Created comparison page:") + print(f" ๐Ÿ“‚ {OUTPUT_DIR}/") + print(f" ๐ŸŒ Open: {html_path}") + print("=" * 50) + +if __name__ == '__main__': + main() diff --git a/test_transparency/comparison.html b/test_transparency/comparison.html new file mode 100644 index 000000000..def97f51e --- /dev/null +++ b/test_transparency/comparison.html @@ -0,0 +1,147 @@ + + + + + ๐Ÿงช Test Transparency - 5 Sample Images + + + +

๐Ÿงช Test Transparency - Before & After

+

Left: Original (white/black background) | Right: Processed (transparent with checkerboard)

+ +
+
+

๐Ÿ“ท ORIGINAL

+ Original +
watering_can_ORIGINAL.png
+
+
+

โœ… PROCESSED

+
+ Processed +
+
watering_can_PROCESSED.png
+
+
+ +
+
+

๐Ÿ“ท ORIGINAL

+ Original +
horse_ORIGINAL.png
+
+
+

โœ… PROCESSED

+
+ Processed +
+
horse_PROCESSED.png
+
+
+ +
+
+

๐Ÿ“ท ORIGINAL

+ Original +
manure_pile_ORIGINAL.png
+
+
+

โœ… PROCESSED

+
+ Processed +
+
manure_pile_PROCESSED.png
+
+
+ +
+
+

๐Ÿ“ท ORIGINAL

+ Original +
pickaxe_ORIGINAL.png
+
+
+

โœ… PROCESSED

+
+ Processed +
+
pickaxe_PROCESSED.png
+
+
+ +
+
+

๐Ÿ“ท ORIGINAL

+ Original +
walk_ORIGINAL.png
+
+
+

โœ… PROCESSED

+
+ Processed +
+
walk_PROCESSED.png
+
+
+ +

+ If you see the gray checkerboard pattern behind the processed images, transparency is working! ๐ŸŽ‰ +

+ + diff --git a/test_transparency/comparison_ai.html b/test_transparency/comparison_ai.html new file mode 100644 index 000000000..dbba82bde --- /dev/null +++ b/test_transparency/comparison_ai.html @@ -0,0 +1,264 @@ + + + + + ๐Ÿค– AI Background Removal Test + + + +

๐Ÿค– AI Background Removal (rembg)

+
+ ๐ŸŽฏ Using neural networks to detect subjects and remove backgrounds! +
+ +
+
+

๐Ÿ“ท ORIGINAL

+
+ Original +
+
Original
+
+
+

๐Ÿค– AI (Checker)

+
+ AI +
+
Transparency check
+
+
+

๐Ÿค– AI (Green)

+
+ On Green +
+
Green screen test
+
+
+

๐Ÿค– AI (Grass)

+
+ In Game +
+
In-game preview
+
+
+

๐Ÿค– AI (Red)

+
+ On Red +
+
Contrast test
+
+
+ +
+
+

๐Ÿ“ท ORIGINAL

+
+ Original +
+
Original
+
+
+

๐Ÿค– AI (Checker)

+
+ AI +
+
Transparency check
+
+
+

๐Ÿค– AI (Green)

+
+ On Green +
+
Green screen test
+
+
+

๐Ÿค– AI (Grass)

+
+ In Game +
+
In-game preview
+
+
+

๐Ÿค– AI (Red)

+
+ On Red +
+
Contrast test
+
+
+ +
+
+

๐Ÿ“ท ORIGINAL

+
+ Original +
+
Original
+
+
+

๐Ÿค– AI (Checker)

+
+ AI +
+
Transparency check
+
+
+

๐Ÿค– AI (Green)

+
+ On Green +
+
Green screen test
+
+
+

๐Ÿค– AI (Grass)

+
+ In Game +
+
In-game preview
+
+
+

๐Ÿค– AI (Red)

+
+ On Red +
+
Contrast test
+
+
+ +
+
+

๐Ÿ“ท ORIGINAL

+
+ Original +
+
Original
+
+
+

๐Ÿค– AI (Checker)

+
+ AI +
+
Transparency check
+
+
+

๐Ÿค– AI (Green)

+
+ On Green +
+
Green screen test
+
+
+

๐Ÿค– AI (Grass)

+
+ In Game +
+
In-game preview
+
+
+

๐Ÿค– AI (Red)

+
+ On Red +
+
Contrast test
+
+
+ +
+
+

๐Ÿ“ท ORIGINAL

+
+ Original +
+
Original
+
+
+

๐Ÿค– AI (Checker)

+
+ AI +
+
Transparency check
+
+
+

๐Ÿค– AI (Green)

+
+ On Green +
+
Green screen test
+
+
+

๐Ÿค– AI (Grass)

+
+ In Game +
+
In-game preview
+
+
+

๐Ÿค– AI (Red)

+
+ On Red +
+
Contrast test
+
+
+ + + diff --git a/test_transparency/comparison_v2.html b/test_transparency/comparison_v2.html new file mode 100644 index 000000000..d62566f91 --- /dev/null +++ b/test_transparency/comparison_v2.html @@ -0,0 +1,210 @@ + + + + + ๐Ÿ”ฅ Aggressive BG Removal Test + + + +

๐Ÿ”ฅ Aggressive Background Removal Test

+
+ ๐ŸŽฏ TARGET: Clean edges like the zombie bunny reference (no white shadows!) +
+ +
+
+

๐Ÿ“ท ORIGINAL

+
+ Original +
+
watering_can
+
+
+

โœ… CLEANED (Checker)

+
+ Cleaned +
+
+
+

โœ… CLEANED (Green)

+
+ On Green +
+
+
+

โœ… CLEANED (Game)

+
+ In Game +
+
+
+ +
+
+

๐Ÿ“ท ORIGINAL

+
+ Original +
+
horse
+
+
+

โœ… CLEANED (Checker)

+
+ Cleaned +
+
+
+

โœ… CLEANED (Green)

+
+ On Green +
+
+
+

โœ… CLEANED (Game)

+
+ In Game +
+
+
+ +
+
+

๐Ÿ“ท ORIGINAL

+
+ Original +
+
manure_pile
+
+
+

โœ… CLEANED (Checker)

+
+ Cleaned +
+
+
+

โœ… CLEANED (Green)

+
+ On Green +
+
+
+

โœ… CLEANED (Game)

+
+ In Game +
+
+
+ +
+
+

๐Ÿ“ท ORIGINAL

+
+ Original +
+
pickaxe
+
+
+

โœ… CLEANED (Checker)

+
+ Cleaned +
+
+
+

โœ… CLEANED (Green)

+
+ On Green +
+
+
+

โœ… CLEANED (Game)

+
+ In Game +
+
+
+ +
+
+

๐Ÿ“ท ORIGINAL

+
+ Original +
+
walk
+
+
+

โœ… CLEANED (Checker)

+
+ Cleaned +
+
+
+

โœ… CLEANED (Green)

+
+ On Green +
+
+
+

โœ… CLEANED (Game)

+
+ In Game +
+
+
+ + + diff --git a/test_transparency/green_screen_test.html b/test_transparency/green_screen_test.html new file mode 100644 index 000000000..bb8f7ab7d --- /dev/null +++ b/test_transparency/green_screen_test.html @@ -0,0 +1,225 @@ + + + + + ๐ŸŸข Green Screen Test + + + +

๐ŸŸข GREEN SCREEN WORKFLOW

+
+ Workflow: Original โ†’ AI Remove BG โ†’ Green Screen (#00FF00) โ†’ Alpha Transparency +
+ +
+
+

๐Ÿ“ท ORIGINAL

+
+ Original +
+
Source file
+
+
+

๐ŸŸข GREEN SCREEN

+
+ Green +
+
#00FF00 background
+
+
+

โœ… ALPHA (Checker)

+
+ Alpha +
+
Transparent
+
+
+

๐ŸŒฟ IN GAME

+
+ Game +
+
On grass
+
+
+ +
+
+

๐Ÿ“ท ORIGINAL

+
+ Original +
+
Source file
+
+
+

๐ŸŸข GREEN SCREEN

+
+ Green +
+
#00FF00 background
+
+
+

โœ… ALPHA (Checker)

+
+ Alpha +
+
Transparent
+
+
+

๐ŸŒฟ IN GAME

+
+ Game +
+
On grass
+
+
+ +
+
+

๐Ÿ“ท ORIGINAL

+
+ Original +
+
Source file
+
+
+

๐ŸŸข GREEN SCREEN

+
+ Green +
+
#00FF00 background
+
+
+

โœ… ALPHA (Checker)

+
+ Alpha +
+
Transparent
+
+
+

๐ŸŒฟ IN GAME

+
+ Game +
+
On grass
+
+
+ +
+
+

๐Ÿ“ท ORIGINAL

+
+ Original +
+
Source file
+
+
+

๐ŸŸข GREEN SCREEN

+
+ Green +
+
#00FF00 background
+
+
+

โœ… ALPHA (Checker)

+
+ Alpha +
+
Transparent
+
+
+

๐ŸŒฟ IN GAME

+
+ Game +
+
On grass
+
+
+ +
+
+

๐Ÿ“ท ORIGINAL

+
+ Original +
+
Source file
+
+
+

๐ŸŸข GREEN SCREEN

+
+ Green +
+
#00FF00 background
+
+
+

โœ… ALPHA (Checker)

+
+ Alpha +
+
Transparent
+
+
+

๐ŸŒฟ IN GAME

+
+ Game +
+
On grass
+
+
+ + + diff --git a/test_transparency/horse_AI.png b/test_transparency/horse_AI.png new file mode 100644 index 000000000..9d72d454f Binary files /dev/null and b/test_transparency/horse_AI.png differ diff --git a/test_transparency/horse_ALPHA.png b/test_transparency/horse_ALPHA.png new file mode 100644 index 000000000..9d72d454f Binary files /dev/null and b/test_transparency/horse_ALPHA.png differ diff --git a/test_transparency/horse_CLEAN.png b/test_transparency/horse_CLEAN.png new file mode 100644 index 000000000..caea6d0f8 Binary files /dev/null and b/test_transparency/horse_CLEAN.png differ diff --git a/test_transparency/horse_GREEN.png b/test_transparency/horse_GREEN.png new file mode 100644 index 000000000..d69d119da Binary files /dev/null and b/test_transparency/horse_GREEN.png differ diff --git a/test_transparency/horse_ORIGINAL.png b/test_transparency/horse_ORIGINAL.png new file mode 100644 index 000000000..ae771792a Binary files /dev/null and b/test_transparency/horse_ORIGINAL.png differ diff --git a/test_transparency/horse_PROCESSED.png b/test_transparency/horse_PROCESSED.png new file mode 100644 index 000000000..36d271b41 Binary files /dev/null and b/test_transparency/horse_PROCESSED.png differ diff --git a/test_transparency/manure_pile_AI.png b/test_transparency/manure_pile_AI.png new file mode 100644 index 000000000..183bed385 Binary files /dev/null and b/test_transparency/manure_pile_AI.png differ diff --git a/test_transparency/manure_pile_ALPHA.png b/test_transparency/manure_pile_ALPHA.png new file mode 100644 index 000000000..183bed385 Binary files /dev/null and b/test_transparency/manure_pile_ALPHA.png differ diff --git a/test_transparency/manure_pile_CLEAN.png b/test_transparency/manure_pile_CLEAN.png new file mode 100644 index 000000000..9aaecab57 Binary files /dev/null and b/test_transparency/manure_pile_CLEAN.png differ diff --git a/test_transparency/manure_pile_GREEN.png b/test_transparency/manure_pile_GREEN.png new file mode 100644 index 000000000..6f30e4e06 Binary files /dev/null and b/test_transparency/manure_pile_GREEN.png differ diff --git a/test_transparency/manure_pile_ORIGINAL.png b/test_transparency/manure_pile_ORIGINAL.png new file mode 100644 index 000000000..f23d5113e Binary files /dev/null and b/test_transparency/manure_pile_ORIGINAL.png differ diff --git a/test_transparency/manure_pile_PROCESSED.png b/test_transparency/manure_pile_PROCESSED.png new file mode 100644 index 000000000..45bca1824 Binary files /dev/null and b/test_transparency/manure_pile_PROCESSED.png differ diff --git a/test_transparency/pickaxe_AI.png b/test_transparency/pickaxe_AI.png new file mode 100644 index 000000000..2f830f427 Binary files /dev/null and b/test_transparency/pickaxe_AI.png differ diff --git a/test_transparency/pickaxe_ALPHA.png b/test_transparency/pickaxe_ALPHA.png new file mode 100644 index 000000000..2f830f427 Binary files /dev/null and b/test_transparency/pickaxe_ALPHA.png differ diff --git a/test_transparency/pickaxe_CLEAN.png b/test_transparency/pickaxe_CLEAN.png new file mode 100644 index 000000000..d695ce0cc Binary files /dev/null and b/test_transparency/pickaxe_CLEAN.png differ diff --git a/test_transparency/pickaxe_GREEN.png b/test_transparency/pickaxe_GREEN.png new file mode 100644 index 000000000..77844045f Binary files /dev/null and b/test_transparency/pickaxe_GREEN.png differ diff --git a/test_transparency/pickaxe_ORIGINAL.png b/test_transparency/pickaxe_ORIGINAL.png new file mode 100644 index 000000000..95756358a Binary files /dev/null and b/test_transparency/pickaxe_ORIGINAL.png differ diff --git a/test_transparency/pickaxe_PROCESSED.png b/test_transparency/pickaxe_PROCESSED.png new file mode 100644 index 000000000..bd6e07b9b Binary files /dev/null and b/test_transparency/pickaxe_PROCESSED.png differ diff --git a/test_transparency/walk_AI.png b/test_transparency/walk_AI.png new file mode 100644 index 000000000..b4a1d385e Binary files /dev/null and b/test_transparency/walk_AI.png differ diff --git a/test_transparency/walk_ALPHA.png b/test_transparency/walk_ALPHA.png new file mode 100644 index 000000000..b4a1d385e Binary files /dev/null and b/test_transparency/walk_ALPHA.png differ diff --git a/test_transparency/walk_CLEAN.png b/test_transparency/walk_CLEAN.png new file mode 100644 index 000000000..2f255abfa Binary files /dev/null and b/test_transparency/walk_CLEAN.png differ diff --git a/test_transparency/walk_GREEN.png b/test_transparency/walk_GREEN.png new file mode 100644 index 000000000..ff159db1c Binary files /dev/null and b/test_transparency/walk_GREEN.png differ diff --git a/test_transparency/walk_ORIGINAL.png b/test_transparency/walk_ORIGINAL.png new file mode 100644 index 000000000..d831c8b86 Binary files /dev/null and b/test_transparency/walk_ORIGINAL.png differ diff --git a/test_transparency/walk_PROCESSED.png b/test_transparency/walk_PROCESSED.png new file mode 100644 index 000000000..2505f23f5 Binary files /dev/null and b/test_transparency/walk_PROCESSED.png differ diff --git a/test_transparency/watering_can_AI.png b/test_transparency/watering_can_AI.png new file mode 100644 index 000000000..e216925ba Binary files /dev/null and b/test_transparency/watering_can_AI.png differ diff --git a/test_transparency/watering_can_ALPHA.png b/test_transparency/watering_can_ALPHA.png new file mode 100644 index 000000000..e216925ba Binary files /dev/null and b/test_transparency/watering_can_ALPHA.png differ diff --git a/test_transparency/watering_can_CLEAN.png b/test_transparency/watering_can_CLEAN.png new file mode 100644 index 000000000..fd006296d Binary files /dev/null and b/test_transparency/watering_can_CLEAN.png differ diff --git a/test_transparency/watering_can_GREEN.png b/test_transparency/watering_can_GREEN.png new file mode 100644 index 000000000..05dd08659 Binary files /dev/null and b/test_transparency/watering_can_GREEN.png differ diff --git a/test_transparency/watering_can_ORIGINAL.png b/test_transparency/watering_can_ORIGINAL.png new file mode 100644 index 000000000..4e5c84bd9 Binary files /dev/null and b/test_transparency/watering_can_ORIGINAL.png differ diff --git a/test_transparency/watering_can_PROCESSED.png b/test_transparency/watering_can_PROCESSED.png new file mode 100644 index 000000000..af24869a3 Binary files /dev/null and b/test_transparency/watering_can_PROCESSED.png differ