Crealink's picture
Upload app.py
fe1faf7 verified
Raw
History Blame Contribute Delete
9.58 kB
import gradio as gr
from PIL import Image, ImageFilter, ImageEnhance
import numpy as np
import tempfile
# ── Resolution Presets ────────────────────────────────────────────────────────
RESOLUTIONS = {
"Original (keep size)": None,
"Instagram Square (1080x1080)": (1080, 1080),
"Instagram Portrait (1080x1350)": (1080, 1350),
"Instagram Story (1080x1920)": (1080, 1920),
"TikTok (1080x1920)": (1080, 1920),
"HD (1280x720)": (1280, 720),
"Full HD (1920x1080)": (1920, 1080),
"2K (2560x1440)": (2560, 1440),
"4K UHD (3840x2160)": (3840, 2160),
"8K UHD (7680x4320)": (7680, 4320),
"Twitter/X Header (1500x500)": (1500, 500),
"LinkedIn Banner (1584x396)": (1584, 396),
"YouTube Thumbnail (1280x720)": (1280, 720),
"YouTube Banner (2560x1440)": (2560, 1440),
"Custom": "custom",
}
FORMATS = ["PNG", "JPEG", "WebP"]
# ── Quality Engine ────────────────────────────────────────────────────────────
def maximize_quality(image, resolution, custom_w, custom_h, upscale_factor,
sharpen, denoise, auto_enhance, format_type, quality,
optimize_colours, reduce_noise_ml):
if image is None:
return None, None, None, None
img = image.copy().convert("RGB")
orig = img.copy()
# ── Auto Enhance ──
if auto_enhance:
enhancer = ImageEnhance.Contrast(img)
img = enhancer.enhance(1.08)
enhancer = ImageEnhance.Sharpness(img)
img = enhancer.enhance(1.15)
enhancer = ImageEnhance.Color(img)
img = enhancer.enhance(1.05)
# ── Auto White-Balance ──
if optimize_colours:
arr = np.array(img).astype(np.float32)
avg_r = arr[:, :, 0].mean()
avg_g = arr[:, :, 1].mean()
avg_b = arr[:, :, 2].mean()
avg_gray = (avg_r + avg_g + avg_b) / 3
if avg_gray > 0:
arr[:, :, 0] = np.clip(arr[:, :, 0] * (avg_gray / avg_r), 0, 255)
arr[:, :, 1] = np.clip(arr[:, :, 1] * (avg_gray / avg_g), 0, 255)
arr[:, :, 2] = np.clip(arr[:, :, 2] * (avg_gray / avg_b), 0, 255)
img = Image.fromarray(arr.astype(np.uint8))
# ── Upscale ──
if upscale_factor > 1.0:
w, h = img.size
new_size = (int(w * upscale_factor), int(h * upscale_factor))
img = img.resize(new_size, Image.Resampling.LANCZOS)
# ── Target Resolution ──
target = RESOLUTIONS[resolution]
if target == "custom":
target = (int(custom_w), int(custom_h))
if target:
img = img.resize(target, Image.Resampling.LANCZOS)
# ── Sharpen ──
if sharpen > 0:
enhancer = ImageEnhance.Sharpness(img)
img = enhancer.enhance(1 + sharpen)
# ── Denoise ──
if denoise > 0:
blurred = img.filter(ImageFilter.GaussianBlur(radius=denoise))
img = Image.blend(img, blurred, 0.25)
# ── Smart Noise Reduction ──
if reduce_noise_ml:
img = img.filter(ImageFilter.MedianFilter(size=3))
# ── Format Export ──
fmt = format_type.upper()
suffix = ".png" if fmt == "PNG" else ".jpg" if fmt == "JPEG" else ".webp"
tmp = tempfile.NamedTemporaryFile(suffix=suffix, delete=False)
if fmt == "JPEG":
img.save(tmp.name, format="JPEG", quality=int(quality), optimize=True, progressive=True)
elif fmt == "PNG":
img.save(tmp.name, format="PNG", optimize=True)
elif fmt == "WEBP":
img.save(tmp.name, format="WEBP", quality=int(quality), method=6)
tmp.close()
info = f"Output: {img.size[0]}x{img.size[1]} px | Format: {fmt} | Quality: {quality}%"
return orig, img, tmp.name, info
# ── CSS ───────────────────────────────────────────────────────────────────────
CSS = """
.chain-nav {
display: flex; align-items: center; justify-content: center;
gap: 12px; padding: 14px; background: #1a1a2e; border-radius: 12px;
margin-bottom: 20px; box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
.chain-nav .step {
padding: 8px 18px; border-radius: 24px; font-size: 14px;
font-weight: 600; text-decoration: none; transition: all 0.3s;
}
.chain-nav .step.active {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
color: white; box-shadow: 0 2px 10px rgba(79,172,254,0.4);
}
.chain-nav .step:not(.active) {
background: #16213e; color: #a0a0a0; border: 1px solid #0f3460;
}
.chain-nav .step:not(.active):hover {
background: #0f3460; color: white;
}
.chain-nav .arrow { font-size: 20px; color: #e94560; }
.main-title { text-align: center; margin: 8px 0 4px; font-size: 2.2rem; }
.subtitle { text-align: center; color: #888; margin-bottom: 20px; }
.info-box { background: #16213e; padding: 12px; border-radius: 8px; text-align: center; color: #4facfe; font-weight: 600; }
"""
# ── Gradio App ────────────────────────────────────────────────────────────────
with gr.Blocks(css=CSS, title="Quality Maximizer") as demo:
gr.HTML("""
<div class="chain-nav">
<a href="https://huggingface.co/spaces/Crealink/ai-image-forge" target="_blank" class="step">1. AI Image Forge</a>
<span class="arrow">&rarr;</span>
<a href="https://huggingface.co/spaces/Crealink/color-effects-studio" target="_blank" class="step">2. Color &amp; Effects</a>
<span class="arrow">&rarr;</span>
<span class="step active">3. Quality &amp; Export</span>
</div>
<h1 class="main-title">Quality Maximizer</h1>
<p class="subtitle">Set exact resolutions, upscale, sharpen, and export in your perfect format</p>
""")
with gr.Row():
# ── Input Panel ──
with gr.Column(scale=1, min_width=340):
input_image = gr.Image(type="pil", label="Upload Image", height=350)
with gr.Row():
reset_btn = gr.Button("Reset", variant="secondary", size="sm")
apply_btn = gr.Button("Maximise Quality", variant="primary", size="sm")
with gr.Accordion("Resolution & Scale", open=True):
resolution = gr.Dropdown(
choices=list(RESOLUTIONS.keys()),
value="Original (keep size)",
label="Target Resolution",
)
custom_w = gr.Number(label="Width", value=1920, precision=0, visible=False)
custom_h = gr.Number(label="Height", value=1080, precision=0, visible=False)
upscale_factor = gr.Slider(1.0, 4.0, value=1.0, step=0.1, label="Upscale Factor")
with gr.Accordion("Quality Tools", open=True):
auto_enhance = gr.Checkbox(label="Auto-Enhance (contrast + sharpness + colour)", value=True)
optimize_colours = gr.Checkbox(label="Auto White-Balance", value=False)
reduce_noise_ml = gr.Checkbox(label="Smart Noise Reduction", value=False)
sharpen = gr.Slider(0, 2.0, value=0.3, step=0.05, label="Sharpen")
denoise = gr.Slider(0, 5, value=0.5, step=0.1, label="Denoise (Gaussian Blur)")
with gr.Accordion("Export Settings", open=True):
format_type = gr.Dropdown(choices=FORMATS, value="PNG", label="File Format")
quality = gr.Slider(50, 100, value=95, step=1, label="Quality / Compression")
# ── Output Panel ──
with gr.Column(scale=2):
with gr.Row():
before_img = gr.Image(label="Before", show_label=True, height=240)
after_img = gr.Image(label="After", show_label=True, height=240)
info_text = gr.HTML()
download_file = gr.File(label="Download Final Image")
# ── Show/Hide Custom Resolution ──
def toggle_custom(res):
is_custom = (RESOLUTIONS[res] == "custom")
return gr.Number.update(visible=is_custom), gr.Number.update(visible=is_custom)
resolution.change(fn=toggle_custom, inputs=resolution, outputs=[custom_w, custom_h])
# ── Event Wiring ──
all_inputs = [
input_image, resolution, custom_w, custom_h, upscale_factor,
sharpen, denoise, auto_enhance, format_type, quality,
optimize_colours, reduce_noise_ml,
]
def on_apply(*args):
return maximize_quality(*args)
apply_btn.click(fn=on_apply, inputs=all_inputs, outputs=[before_img, after_img, download_file, info_text])
for component in [upscale_factor, sharpen, denoise, auto_enhance, format_type,
quality, optimize_colours, reduce_noise_ml]:
component.change(fn=on_apply, inputs=all_inputs, outputs=[before_img, after_img, download_file, info_text])
# Reset
def reset():
return [
None, "Original (keep size)", 1920, 1080, 1.0,
0.3, 0.5, True, "PNG", 95, False, False,
]
reset_btn.click(
fn=reset,
outputs=[input_image, resolution, custom_w, custom_h, upscale_factor,
sharpen, denoise, auto_enhance, format_type, quality,
optimize_colours, reduce_noise_ml]
)
demo.launch()