ok
This commit is contained in:
97
scripts/remove_checkerboard.py
Normal file
97
scripts/remove_checkerboard.py
Normal file
@@ -0,0 +1,97 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
|
||||
def remove_background(image_path, tolerance=30):
|
||||
try:
|
||||
img = Image.open(image_path).convert("RGBA")
|
||||
datas = img.getdata()
|
||||
|
||||
# Sample the corner pixel to guess the "background" theme
|
||||
# But since it's a checkerboard, precise color matching fails.
|
||||
# We'll use a seed-fill (flood fill) approach from the corners.
|
||||
|
||||
# Convert to numpy array for faster processing
|
||||
arr = np.array(img)
|
||||
|
||||
# Create a mask for visited pixels (background)
|
||||
h, w = arr.shape[:2]
|
||||
mask = np.zeros((h, w), dtype=bool)
|
||||
|
||||
# Stack for flood fill: (r, c)
|
||||
stack = [(0, 0), (0, w-1), (h-1, 0), (h-1, w-1)]
|
||||
|
||||
# We need to be careful not to delete the object.
|
||||
# Assumption: The checkerboard is grey-scale or near grey-scale.
|
||||
# And the object (tree) is colorful (green/brown).
|
||||
|
||||
# Helper to check if a pixel is "grey-ish" (background candidate)
|
||||
def is_background_candidate(pixel):
|
||||
r, g, b, a = pixel
|
||||
# Check for low saturation (grey/white/black)
|
||||
# max(r,g,b) - min(r,g,b) should be small for greys
|
||||
saturation = max(r, g, b) - min(r, g, b)
|
||||
return saturation < tolerance
|
||||
|
||||
# New approach: smart flood fill with color tolerance is hard on noisy checkerboard.
|
||||
# Let's try a simpler heuristic first:
|
||||
# Iterate all pixels. If a pixel is GREY-SCALE (within tolerance), make it transparent.
|
||||
# This risks deleting grey parts of the object (e.g. stones, bark).
|
||||
|
||||
newData = []
|
||||
for item in datas:
|
||||
# item is (r, g, b, a)
|
||||
r, g, b, a = item
|
||||
|
||||
# Check if it's a grey/white/black pixel
|
||||
# Checkerboard usually consists of light grey/white and dark grey blocks.
|
||||
# We enforce that R, G, and B are close to each other.
|
||||
if abs(r - g) < tolerance and abs(g - b) < tolerance and abs(r - b) < tolerance:
|
||||
# Also check brightness to avoid deleting dark black outlines if they are pure black
|
||||
# Let's say we only delete "light" greys/whites?
|
||||
# The "dark" squares in the checkerboard from the log were around (77, 77, 77).
|
||||
# This is quite dark.
|
||||
|
||||
# If we just delete all greys, we might lose outlines (usually (0,0,0)).
|
||||
# So we check if it's NOT pure black.
|
||||
if r > 20 and g > 20 and b > 20:
|
||||
newData.append((0, 0, 0, 0)) # Transparent
|
||||
else:
|
||||
newData.append(item)
|
||||
else:
|
||||
newData.append(item)
|
||||
|
||||
img.putdata(newData)
|
||||
|
||||
# Save back
|
||||
img.save(image_path, "PNG")
|
||||
print(f"Processed: {image_path}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error processing {image_path}: {e}")
|
||||
return False
|
||||
|
||||
# List of specific files to fix first (Trees)
|
||||
targets = [
|
||||
"assets/references/trees/apple/apple_tree.png"
|
||||
]
|
||||
|
||||
# Or scan directory
|
||||
target_dir = "assets/references"
|
||||
|
||||
if __name__ == "__main__":
|
||||
# If arguments provided, use them
|
||||
if len(sys.argv) > 1:
|
||||
files = sys.argv[1:]
|
||||
for f in files:
|
||||
remove_background(f)
|
||||
else:
|
||||
# Recursive scan for testing
|
||||
for root, dirs, files in os.walk(target_dir):
|
||||
for file in files:
|
||||
if file.endswith("apple_tree.png"): # Safety: only target the known bad one first
|
||||
full_path = os.path.join(root, file)
|
||||
remove_background(full_path)
|
||||
Reference in New Issue
Block a user