Spaces:
Sleeping
Sleeping
| import io | |
| from pathlib import Path | |
| from PIL import Image | |
| import gradio as gr | |
| # Optional support for extra formats requires plugins: | |
| # - JPEG2000: built-in via Pillow | |
| # - AVIF: install 'pillow-avif-plugin' | |
| # - HEIF/HEIC: install 'pillow-heif' | |
| # Supported formats and their default options | |
| offered_formats = [ | |
| "JPEG", "JPEG2000", "PNG", "BMP", "TIFF", "WEBP", "AVIF", "HEIF" | |
| ] | |
| # Map formats to save kwargs defaults | |
| format_options = { | |
| "JPEG": {"quality": 85, "optimize": True}, | |
| "JPEG2000": {"quality_layers": [25, 50, 75], "codec": "jp2"}, | |
| "PNG": {"compress_level": 6, "optimize": True}, | |
| "BMP": {}, | |
| "TIFF": {"compression": "tiff_deflate"}, | |
| "WEBP": {"quality": 80, "lossless": False}, | |
| "AVIF": {"quality": 50, "speed": 4}, | |
| "HEIF": {"quality": 50, "subsampling": "4:2:0"} | |
| } | |
| def convert_image( | |
| img_file, output_format, | |
| quality, compress_level, | |
| jpeg2000_layers, avif_speed, | |
| subsampling, professional_lossless | |
| ): | |
| # img_file is a filesystem path (string) | |
| img_path = Path(img_file) | |
| img_bytes = img_path.read_bytes() | |
| # Load image and convert to RGB | |
| image = Image.open(io.BytesIO(img_bytes)).convert("RGB") | |
| # Prepare save kwargs | |
| fmt = output_format | |
| save_kwargs = {} | |
| if professional_lossless: | |
| # Force true lossless: use PNG | |
| fmt = "PNG" | |
| save_kwargs.update({"compress_level": 9, "optimize": True}) | |
| else: | |
| # Base defaults for chosen format | |
| save_kwargs.update(format_options.get(fmt, {})) | |
| # Overrides per-format | |
| if fmt == "JPEG": | |
| save_kwargs["quality"] = quality | |
| elif fmt == "WEBP": | |
| save_kwargs["quality"] = quality | |
| save_kwargs["lossless"] = False | |
| elif fmt == "PNG": | |
| save_kwargs["compress_level"] = compress_level | |
| save_kwargs["optimize"] = True | |
| elif fmt == "JPEG2000": | |
| save_kwargs["quality_layers"] = [int(l) for l in jpeg2000_layers.split(",")] | |
| elif fmt == "AVIF": | |
| save_kwargs["quality"] = quality | |
| save_kwargs["speed"] = avif_speed | |
| elif fmt == "HEIF": | |
| save_kwargs["quality"] = quality | |
| save_kwargs["subsampling"] = subsampling | |
| # Save into a bytes buffer | |
| buf = io.BytesIO() | |
| image.save(buf, format=fmt, **save_kwargs) | |
| buf.seek(0) | |
| # Return bytes along with a filename | |
| filename = f"converted_image.{fmt.lower()}" | |
| return (buf.getvalue(), filename) | |
| def build_app(): | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# Advanced Image Converter") | |
| with gr.Row(): | |
| # Use 'filepath' so Gradio returns a local file path string | |
| inp = gr.File(label="Upload Image", file_types=["image"], type="filepath") | |
| out = gr.File(label="Download Converted") | |
| with gr.Row(): | |
| fmt = gr.Dropdown(offered_formats, value="PNG", label="Output Format") | |
| with gr.Row(): | |
| quality = gr.Slider(1, 100, value=85, step=1, | |
| label="Quality (JPEG/AVIF/HEIF/WEBP)") | |
| compress = gr.Slider(0, 9, value=6, step=1, | |
| label="PNG Compress Level") | |
| with gr.Row(): | |
| jpeg2000_layers = gr.Textbox(value="25,50,75", | |
| label="JPEG2000 Quality Layers (comma-separated)") | |
| avif_speed = gr.Slider(0, 8, value=4, step=1, label="AVIF Speed") | |
| subsample = gr.Dropdown(["4:4:4", "4:2:2", "4:2:0"], value="4:2:0", | |
| label="HEIF Subsampling") | |
| professional = gr.Checkbox(label="Professional Lossless Mode", | |
| info="Force highest-quality lossless conversion as optimized PNG") | |
| convert_btn = gr.Button("Convert") | |
| convert_btn.click( | |
| fn=convert_image, | |
| inputs=[inp, fmt, quality, compress, | |
| jpeg2000_layers, avif_speed, | |
| subsample, professional], | |
| outputs=out | |
| ) | |
| return demo | |
| if __name__ == "__main__": | |
| app = build_app() | |
| app.launch() |