Pix2 / app.py
Besimplestudio's picture
Update app.py
8400955 verified
Raw History Blame Contribute Delete
3.75 kB
import gradio as gr
from PIL import Image, ImageDraw, ImageFont
import numpy as np
# Define color ranges using RGB bounds
COLOR_RANGES = {
"7": {"name": "Red", "min": (150, 0, 0), "max": (255, 100, 100)}, # Red
"B": {"name": "Yellow", "min": (200, 200, 0), "max": (255, 255, 100)}, # Yellow
"H": {"name": "Blue", "min": (0, 0, 150), "max": (100, 100, 255)}, # Blue
"9": {"name": "Orange", "min": (200, 100, 0), "max": (255, 180, 50)}, # Orange
"A": {"name": "Yellow-Orange", "min": (200, 150, 0), "max": (255, 255, 100)}, # Light orange/yellow
"D": {"name": "Green", "min": (0, 150, 0), "max": (100, 255, 100)}, # Green
"F": {"name": "Cyan", "min": (0, 150, 150), "max": (100, 255, 255)}, # Cyan
"I": {"name": "Dark Blue", "min": (0, 0, 100), "max": (50, 50, 255)}, # Dark blue
"M": {"name": "Magenta", "min": (150, 0, 150), "max": (255, 100, 255)}, # Magenta
"L": {"name": "Purple", "min": (100, 0, 150), "max": (180, 50, 255)}, # Purple
"0": {"name": "Black", "min": (0, 0, 0), "max": (50, 50, 50)}, # Black
"W": {"name": "White", "min": (200, 200, 200), "max": (255, 255, 255)}, # White
"6": {"name": "Brown", "min": (100, 50, 0), "max": (180, 100, 50)}, # Brown
}
BLOCK_SIZE = 20
def rgb_in_range(rgb, min_rgb, max_rgb):
"""Check if RGB value is within range (inclusive)."""
r, g, b = rgb
mr, mg, mb = min_rgb
Mr, Mg, Mb = max_rgb
return mr <= r <= Mr and mg <= g <= Mg and mb <= b <= Mb
def find_nearest_color_key(rgb):
"""Find the best matching color key based on ranges."""
for key, data in COLOR_RANGES.items():
if rgb_in_range(rgb, data["min"], data["max"]):
return key
# If no match, use black as fallback
return "0"
def process_image(img):
# Convert to RGB
img = img.convert("RGB")
w, h = img.size
# Resize to max 50x50 for performance
MAX_DIM = 50
if max(w, h) > MAX_DIM:
scale = MAX_DIM / max(w, h)
img = img.resize((int(w * scale), int(h * scale)))
w, h = img.size
# Create output image
output_img = Image.new("RGB", (w * BLOCK_SIZE, h * BLOCK_SIZE), (255, 255, 255))
draw = ImageDraw.Draw(output_img)
font = ImageFont.load_default()
for y in range(h):
for x in range(w):
pixel = img.getpixel((x, y))
key = find_nearest_color_key(pixel)
color_data = COLOR_RANGES[key]
# Use average of min/max as representative color
color = (
(color_data["max"][0] + color_data["min"][0]) // 2,
(color_data["max"][1] + color_data["min"][1]) // 2,
(color_data["max"][2] + color_data["min"][2]) // 2
)
# Draw block
x0 = x * BLOCK_SIZE
y0 = y * BLOCK_SIZE
x1 = x0 + BLOCK_SIZE
y1 = y0 + BLOCK_SIZE
draw.rectangle([x0, y0, x1, y1], fill=color, outline=(0, 0, 0))
# Center text
bbox = draw.textbbox((0, 0), key, font=font)
w_text = bbox[2] - bbox[0]
h_text = bbox[3] - bbox[1]
draw.text(
(x0 + (BLOCK_SIZE - w_text) / 2, y0 + (BLOCK_SIZE - h_text) / 2),
key,
fill=(0, 0, 0),
font=font
)
return output_img
# Interface β€” NO EXAMPLES, just clean upload
iface = gr.Interface(
fn=process_image,
inputs=gr.Image(type="pil"),
outputs=gr.Image(type="pil"),
title="🎨 Color By Number Pixel Art",
description="Upload any image β†’ Get a printable color-by-number pixel art with symbols. Colors are grouped by range (e.g., all reds β†’ '7')."
)
if __name__ == "__main__":
iface.launch()