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("""
Set exact resolutions, upscale, sharpen, and export in your perfect format
""") 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()