Spaces:
Runtime error
Runtime error
| 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">→</span> | |
| <a href="https://huggingface.co/spaces/Crealink/color-effects-studio" target="_blank" class="step">2. Color & Effects</a> | |
| <span class="arrow">→</span> | |
| <span class="step active">3. Quality & 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() | |