pixelizer / main.py
odinhg's picture
upload code and palettes
53b9670 verified
raw
history blame
3.37 kB
import streamlit as st
from PIL import Image, ImageEnhance
from io import BytesIO
from pathlib import Path
def load_palette_file(path: Path):
"""Load a .hex palette file into a list of RGB tuples."""
colors = []
with open(path, "r") as f:
for line in f:
hexcode = line.strip().lstrip("#")
if len(hexcode) == 6: # valid hex
r = int(hexcode[0:2], 16)
g = int(hexcode[2:4], 16)
b = int(hexcode[4:6], 16)
colors.append((r, g, b))
else:
st.warning(f"Invalid hex code in palette file: {line}")
return colors
def apply_custom_palette(image: Image.Image, colors, dithering: bool):
"""Quantize an image to a custom palette with optional dithering."""
# Build palette (zero pad to 256 colors if needed)
palette_img = Image.new("P", (1, 1))
palette_data = []
for r, g, b in colors[:256]:
palette_data.extend([r, g, b])
palette_data.extend([0] * (768 - len(palette_data)))
palette_img.putpalette(palette_data)
# Quantize with the custom palette
dither_mode = Image.FLOYDSTEINBERG if dithering else Image.NONE
image = image.quantize(palette=palette_img, dither=dither_mode, method=Image.MEDIANCUT)
return image.convert("RGB")
st.set_page_config(page_title="Pixelizer", layout="wide")
st.sidebar.title("Pixelizer")
st.sidebar.header("Image Controls")
uploaded_file = st.sidebar.file_uploader("Upload image", type=["png", "jpg", "jpeg"])
show_original = st.sidebar.toggle("Show original", value=False)
dithering = st.sidebar.toggle("Enable dithering", value=True)
pixel_size = st.sidebar.number_input("Pixel size", min_value=1, max_value=100, value=4, step=1)
palette_dir = Path("palettes")
palette_files = sorted(palette_dir.glob("*.hex"))
palette_names = [f.stem for f in palette_files]
palette = st.sidebar.selectbox("Palette", palette_names)
contrast = st.sidebar.slider("Contrast", 0.5, 2.0, 1.0, 0.1)
brightness = st.sidebar.slider("Brightness", 0.5, 2.0, 1.0, 0.1)
if uploaded_file:
original_image = Image.open(uploaded_file).convert("RGB")
# Contrast and brightness adjustment
image = original_image.copy()
enhancer_contrast = ImageEnhance.Contrast(image)
image = enhancer_contrast.enhance(contrast)
enhancer_brightness = ImageEnhance.Brightness(image)
image = enhancer_brightness.enhance(brightness)
# Downscale for pixelization
processed_image = image.copy()
w, h = processed_image.size
processed_image = processed_image.resize(
(max(1, w // pixel_size), max(1, h // pixel_size)),
resample=Image.NEAREST
)
# Apply palette
palette_file = palette_dir / f"{palette}.hex"
processed_image = apply_custom_palette(processed_image, load_palette_file(palette_file), dithering)
upscaled_processed_image = processed_image.resize((w, h), Image.NEAREST)
# Display
if show_original:
st.image(original_image, caption="Original", width="content")
else:
st.image(upscaled_processed_image, caption="Processed", width="content")
# Save
buf = BytesIO()
processed_image.save(buf, format="PNG")
byte_im = buf.getvalue()
st.sidebar.download_button(
"Save image",
data=byte_im,
file_name="pixelized.png",
mime="image/png"
)