upload code and palettes
Browse files- main.py +91 -0
- palettes/antiquity16.hex +16 -0
- palettes/blk-nx64.hex +64 -0
- palettes/cc-29.hex +29 -0
- palettes/crystal-flames.hex +24 -0
- palettes/dustbyte.hex +4 -0
- palettes/midnight-ablaze.hex +7 -0
- palettes/oil-6.hex +6 -0
- palettes/retro-115.hex +7 -0
- palettes/snail-village.hex +6 -0
main.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from PIL import Image, ImageEnhance
|
| 3 |
+
from io import BytesIO
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
+
def load_palette_file(path: Path):
|
| 7 |
+
"""Load a .hex palette file into a list of RGB tuples."""
|
| 8 |
+
colors = []
|
| 9 |
+
with open(path, "r") as f:
|
| 10 |
+
for line in f:
|
| 11 |
+
hexcode = line.strip().lstrip("#")
|
| 12 |
+
if len(hexcode) == 6: # valid hex
|
| 13 |
+
r = int(hexcode[0:2], 16)
|
| 14 |
+
g = int(hexcode[2:4], 16)
|
| 15 |
+
b = int(hexcode[4:6], 16)
|
| 16 |
+
colors.append((r, g, b))
|
| 17 |
+
else:
|
| 18 |
+
st.warning(f"Invalid hex code in palette file: {line}")
|
| 19 |
+
return colors
|
| 20 |
+
|
| 21 |
+
def apply_custom_palette(image: Image.Image, colors, dithering: bool):
|
| 22 |
+
"""Quantize an image to a custom palette with optional dithering."""
|
| 23 |
+
# Build palette (zero pad to 256 colors if needed)
|
| 24 |
+
palette_img = Image.new("P", (1, 1))
|
| 25 |
+
palette_data = []
|
| 26 |
+
for r, g, b in colors[:256]:
|
| 27 |
+
palette_data.extend([r, g, b])
|
| 28 |
+
palette_data.extend([0] * (768 - len(palette_data)))
|
| 29 |
+
palette_img.putpalette(palette_data)
|
| 30 |
+
|
| 31 |
+
# Quantize with the custom palette
|
| 32 |
+
dither_mode = Image.FLOYDSTEINBERG if dithering else Image.NONE
|
| 33 |
+
image = image.quantize(palette=palette_img, dither=dither_mode, method=Image.MEDIANCUT)
|
| 34 |
+
return image.convert("RGB")
|
| 35 |
+
|
| 36 |
+
st.set_page_config(page_title="Pixelizer", layout="wide")
|
| 37 |
+
st.sidebar.title("Pixelizer")
|
| 38 |
+
st.sidebar.header("Image Controls")
|
| 39 |
+
uploaded_file = st.sidebar.file_uploader("Upload image", type=["png", "jpg", "jpeg"])
|
| 40 |
+
show_original = st.sidebar.toggle("Show original", value=False)
|
| 41 |
+
dithering = st.sidebar.toggle("Enable dithering", value=True)
|
| 42 |
+
pixel_size = st.sidebar.number_input("Pixel size", min_value=1, max_value=100, value=4, step=1)
|
| 43 |
+
palette_dir = Path("palettes")
|
| 44 |
+
palette_files = sorted(palette_dir.glob("*.hex"))
|
| 45 |
+
palette_names = [f.stem for f in palette_files]
|
| 46 |
+
palette = st.sidebar.selectbox("Palette", palette_names)
|
| 47 |
+
|
| 48 |
+
contrast = st.sidebar.slider("Contrast", 0.5, 2.0, 1.0, 0.1)
|
| 49 |
+
brightness = st.sidebar.slider("Brightness", 0.5, 2.0, 1.0, 0.1)
|
| 50 |
+
|
| 51 |
+
if uploaded_file:
|
| 52 |
+
original_image = Image.open(uploaded_file).convert("RGB")
|
| 53 |
+
|
| 54 |
+
# Contrast and brightness adjustment
|
| 55 |
+
image = original_image.copy()
|
| 56 |
+
enhancer_contrast = ImageEnhance.Contrast(image)
|
| 57 |
+
image = enhancer_contrast.enhance(contrast)
|
| 58 |
+
enhancer_brightness = ImageEnhance.Brightness(image)
|
| 59 |
+
image = enhancer_brightness.enhance(brightness)
|
| 60 |
+
|
| 61 |
+
# Downscale for pixelization
|
| 62 |
+
processed_image = image.copy()
|
| 63 |
+
w, h = processed_image.size
|
| 64 |
+
processed_image = processed_image.resize(
|
| 65 |
+
(max(1, w // pixel_size), max(1, h // pixel_size)),
|
| 66 |
+
resample=Image.NEAREST
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
# Apply palette
|
| 70 |
+
palette_file = palette_dir / f"{palette}.hex"
|
| 71 |
+
processed_image = apply_custom_palette(processed_image, load_palette_file(palette_file), dithering)
|
| 72 |
+
upscaled_processed_image = processed_image.resize((w, h), Image.NEAREST)
|
| 73 |
+
|
| 74 |
+
# Display
|
| 75 |
+
if show_original:
|
| 76 |
+
st.image(original_image, caption="Original", width="content")
|
| 77 |
+
else:
|
| 78 |
+
st.image(upscaled_processed_image, caption="Processed", width="content")
|
| 79 |
+
|
| 80 |
+
# Save
|
| 81 |
+
buf = BytesIO()
|
| 82 |
+
processed_image.save(buf, format="PNG")
|
| 83 |
+
byte_im = buf.getvalue()
|
| 84 |
+
|
| 85 |
+
st.sidebar.download_button(
|
| 86 |
+
"Save image",
|
| 87 |
+
data=byte_im,
|
| 88 |
+
file_name="pixelized.png",
|
| 89 |
+
mime="image/png"
|
| 90 |
+
)
|
| 91 |
+
|
palettes/antiquity16.hex
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
202020
|
| 2 |
+
2d211e
|
| 3 |
+
452923
|
| 4 |
+
6d3d29
|
| 5 |
+
b16b4a
|
| 6 |
+
e89f6e
|
| 7 |
+
e8be82
|
| 8 |
+
5d7557
|
| 9 |
+
8e9257
|
| 10 |
+
707b88
|
| 11 |
+
8aa7ac
|
| 12 |
+
e55d4d
|
| 13 |
+
f1866c
|
| 14 |
+
d26730
|
| 15 |
+
de9a28
|
| 16 |
+
e8d8a5
|
palettes/blk-nx64.hex
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
000000
|
| 2 |
+
12173d
|
| 3 |
+
293268
|
| 4 |
+
464b8c
|
| 5 |
+
6b74b2
|
| 6 |
+
909edd
|
| 7 |
+
c1d9f2
|
| 8 |
+
ffffff
|
| 9 |
+
a293c4
|
| 10 |
+
7b6aa5
|
| 11 |
+
53427f
|
| 12 |
+
3c2c68
|
| 13 |
+
431e66
|
| 14 |
+
5d2f8c
|
| 15 |
+
854cbf
|
| 16 |
+
b483ef
|
| 17 |
+
8cff9b
|
| 18 |
+
42bc7f
|
| 19 |
+
22896e
|
| 20 |
+
14665b
|
| 21 |
+
0f4a4c
|
| 22 |
+
0a2a33
|
| 23 |
+
1d1a59
|
| 24 |
+
322d89
|
| 25 |
+
354ab2
|
| 26 |
+
3e83d1
|
| 27 |
+
50b9eb
|
| 28 |
+
8cdaff
|
| 29 |
+
53a1ad
|
| 30 |
+
3b768f
|
| 31 |
+
21526b
|
| 32 |
+
163755
|
| 33 |
+
008782
|
| 34 |
+
00aaa5
|
| 35 |
+
27d3cb
|
| 36 |
+
78fae6
|
| 37 |
+
cdc599
|
| 38 |
+
988f64
|
| 39 |
+
5c5d41
|
| 40 |
+
353f23
|
| 41 |
+
919b45
|
| 42 |
+
afd370
|
| 43 |
+
ffe091
|
| 44 |
+
ffaa6e
|
| 45 |
+
ff695a
|
| 46 |
+
b23c40
|
| 47 |
+
ff6675
|
| 48 |
+
dd3745
|
| 49 |
+
a52639
|
| 50 |
+
721c2f
|
| 51 |
+
b22e69
|
| 52 |
+
e54286
|
| 53 |
+
ff6eaf
|
| 54 |
+
ffa5d5
|
| 55 |
+
ffd3ad
|
| 56 |
+
cc817a
|
| 57 |
+
895654
|
| 58 |
+
61393b
|
| 59 |
+
3f1f3c
|
| 60 |
+
723352
|
| 61 |
+
994c69
|
| 62 |
+
c37289
|
| 63 |
+
f29faa
|
| 64 |
+
ffccd0
|
palettes/cc-29.hex
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
f2f0e5
|
| 2 |
+
b8b5b9
|
| 3 |
+
868188
|
| 4 |
+
646365
|
| 5 |
+
45444f
|
| 6 |
+
3a3858
|
| 7 |
+
212123
|
| 8 |
+
352b42
|
| 9 |
+
43436a
|
| 10 |
+
4b80ca
|
| 11 |
+
68c2d3
|
| 12 |
+
a2dcc7
|
| 13 |
+
ede19e
|
| 14 |
+
d3a068
|
| 15 |
+
b45252
|
| 16 |
+
6a536e
|
| 17 |
+
4b4158
|
| 18 |
+
80493a
|
| 19 |
+
a77b5b
|
| 20 |
+
e5ceb4
|
| 21 |
+
c2d368
|
| 22 |
+
8ab060
|
| 23 |
+
567b79
|
| 24 |
+
4e584a
|
| 25 |
+
7b7243
|
| 26 |
+
b2b47e
|
| 27 |
+
edc8c4
|
| 28 |
+
cf8acb
|
| 29 |
+
5f556a
|
palettes/crystal-flames.hex
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
913a46
|
| 2 |
+
b36067
|
| 3 |
+
ffaba1
|
| 4 |
+
ffbaa1
|
| 5 |
+
db7684
|
| 6 |
+
eb91a2
|
| 7 |
+
f7c3a3
|
| 8 |
+
ffb259
|
| 9 |
+
f0c060
|
| 10 |
+
ffc773
|
| 11 |
+
ff9354
|
| 12 |
+
fa7550
|
| 13 |
+
dea55b
|
| 14 |
+
f7b068
|
| 15 |
+
e8534d
|
| 16 |
+
c93b36
|
| 17 |
+
cc7349
|
| 18 |
+
b8553d
|
| 19 |
+
d92b25
|
| 20 |
+
ad1025
|
| 21 |
+
a64116
|
| 22 |
+
822333
|
| 23 |
+
99222c
|
| 24 |
+
82012a
|
palettes/dustbyte.hex
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
788374
|
| 2 |
+
f5e9bf
|
| 3 |
+
aa644d
|
| 4 |
+
372a39
|
palettes/midnight-ablaze.hex
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ff8274
|
| 2 |
+
d53c6a
|
| 3 |
+
7c183c
|
| 4 |
+
460e2b
|
| 5 |
+
31051e
|
| 6 |
+
1f0510
|
| 7 |
+
130208
|
palettes/oil-6.hex
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fbf5ef
|
| 2 |
+
f2d3ab
|
| 3 |
+
c69fa5
|
| 4 |
+
8b6d9c
|
| 5 |
+
494d7e
|
| 6 |
+
272744
|
palettes/retro-115.hex
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
040026
|
| 2 |
+
004bc0
|
| 3 |
+
0097ec
|
| 4 |
+
00f3fc
|
| 5 |
+
ffbcff
|
| 6 |
+
d569f6
|
| 7 |
+
6c1fd3
|
palettes/snail-village.hex
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
323c39
|
| 2 |
+
524b24
|
| 3 |
+
7d5563
|
| 4 |
+
917a46
|
| 5 |
+
a3ad54
|
| 6 |
+
99e550
|