Politrees's picture
Update app.py
aa112fe verified
import gradio as gr
from PIL import Image
# ─── Доступные размеры для даунскейла ───
SIZES = ["8×8", "16×16", "32×32", "64×64", "128×128"]
def parse_size(label: str) -> tuple[int, int]:
"""'16×16' → (16, 16)"""
w, h = label.split("×")
return int(w), int(h)
def downscale(
image: Image.Image,
target_size: str,
preview_scale: int,
) -> tuple[Image.Image, Image.Image, str]:
"""
1) Даунскейлит входное изображение до target_size методом NEAREST.
2) Апскейлит результат обратно (preview) чтобы пиксели были видны.
Возвращает (маленькое, превью, инфо-строку).
"""
if image is None:
raise gr.Error("Загрузи изображение!")
# Принудительно RGBA (текстуры Minecraft часто с альфой)
image = image.convert("RGBA")
tw, th = parse_size(target_size)
# ── Nearest-Neighbor даунскейл ──
small = image.resize((tw, th), resample=Image.NEAREST)
# ── Превью: масштабируем обратно, чтобы пиксели были чётко видны ──
preview_w = tw * preview_scale
preview_h = th * preview_scale
preview = small.resize((preview_w, preview_h), resample=Image.NEAREST)
info = (
f"Оригинал: {image.size[0]}×{image.size[1]}\n"
f"Результат: {tw}×{th}\n"
f"Превью: {preview_w}×{preview_h}{preview_scale})"
)
print(info)
return small, preview, info
# ─── Интерфейс ───────────────────────────────────────────────
with gr.Blocks(
title="Minecraft Texture Downscaler",
theme=gr.themes.Soft(),
css="""
/* чёткие пиксели при зуме в браузере */
.pixelated img {
image-rendering: pixelated !important;
image-rendering: -moz-crisp-edges !important;
image-rendering: crisp-edges !important;
}
""",
) as demo:
gr.Markdown(
"""
# 🟩 Minecraft Texture Downscaler
Загрузи текстуру любого размера — получи чёткий **Nearest Neighbor**
даунскейл без размытия (без bilinear / bicubic).
> Идеально для конвертации HD-текстур (128×, 256×, 512×)
> в ванильный формат 16×16.
"""
)
with gr.Row():
# ── Левая колонка: входные параметры ──
with gr.Column(scale=1):
img_input = gr.Image(
label="📥 Входная текстура",
type="pil",
image_mode="RGBA",
sources=["upload", "clipboard"],
)
target_size = gr.Dropdown(
label="🎯 Целевой размер",
choices=SIZES,
value="16×16",
)
preview_scale = gr.Slider(
label="🔍 Множитель превью",
minimum=1,
maximum=32,
step=1,
value=16,
info="Во сколько раз увеличить для наглядности",
)
btn = gr.Button("⚡ Даунскейлить", variant="primary", size="lg")
# ── Правая колонка: результаты ──
with gr.Column(scale=2):
with gr.Row(equal_height=True):
img_small = gr.Image(
label="📤 Результат (реальный размер)",
type="pil",
image_mode="RGBA",
format="png",
show_download_button=True,
elem_classes=["pixelated"],
)
img_preview = gr.Image(
label="🔎 Превью (увеличенное)",
type="pil",
image_mode="RGBA",
format="png",
elem_classes=["pixelated"],
)
info_box = gr.Textbox(
label="ℹ️ Информация",
interactive=False,
lines=3,
)
# ── Обработчик ──
btn.click(
fn=downscale,
inputs=[img_input, target_size, preview_scale],
outputs=[img_small, img_preview, info_box],
)
gr.Markdown(
"""
---
### Как использовать
1. Загрузи PNG-текстуру (например `diamond_ore.png` 128×128).
2. Выбери целевой размер — по умолчанию **16×16**.
3. Нажми **⚡ Даунскейлить**.
4. Скачай результат (кнопка ⬇️ на картинке).
**Nearest Neighbor** сохраняет резкие границы пикселей —
никакого мыла, как при Bilinear/Lanczos.
"""
)
# Запуск
if __name__ == "__main__":
demo.launch()