any-to-png / app.py
namelessai's picture
Update app.py
1fe4fdd verified
import gradio as gr
from PIL import Image, ImageCms
import pillow_heif # This is essential! It registers HEIF/HEIC/AVIF support
import io
import tempfile
# Explicitly register openers from pillow-heif to ensure PIL can see them
pillow_heif.register_heif_opener()
def convert_to_png(input_image, color_mode, color_profile, compress_setting):
"""
Converts an input PIL image to PNG format with specified options.
"""
if input_image is None:
raise gr.Error("Please upload an image first.")
# 1. Handle Compression Setting
# PNG compression is always lossless. This just controls file size vs. time.
compress_levels = {
"Fast (Large File)": 1,
"Balanced (Recommended)": 6,
"Best (Small File, Slow)": 9,
"None (No Compression)": 0
}
compress_level = compress_levels.get(compress_setting, 6)
# 2. Handle Color Mode Conversion
# We work on a copy to avoid changing the original input_image object
image_copy = input_image.copy()
if color_mode != "Auto (Keep Original)":
try:
image_copy = image_copy.convert(color_mode)
except Exception as e:
print(f"Warning: Could not convert to {color_mode}: {e}. Proceeding with original mode.")
# 3. Handle Color Profile Conversion
original_profile = image_copy.info.get("icc_profile")
final_icc_profile = original_profile
if color_profile == "Convert to sRGB":
try:
# Define standard sRGB profile
srgb_profile = ImageCms.createProfile("sRGB")
# Get input profile
if original_profile:
in_profile_bytes = io.BytesIO(original_profile)
in_prof = ImageCms.ImageCmsProfile(in_profile_bytes)
else:
# If no profile, assume sRGB.
in_prof = ImageCms.createProfile("sRGB")
print("Warning: No input profile found, assuming sRGB for conversion.")
# Ensure mode is compatible with ICC transform (e.g., not 'P' or 'L')
if image_copy.mode not in ('RGB', 'RGBA'):
image_copy = image_copy.convert('RGB')
print("Converted image to RGB for profile transform.")
# Build and apply the transform
transform = ImageCms.buildTransform(
in_prof,
srgb_profile,
image_copy.mode, # Input mode
image_copy.mode # Output mode (keep it)
)
image_copy = ImageCms.applyTransform(image_copy, transform)
# Set the new profile to be saved with the PNG
final_icc_profile = srgb_profile.tobytes()
except Exception as e:
# If transform fails, just print a warning and keep the original profile
print(f"Error during color profile transform: {e}. Profile will be kept as original.")
final_icc_profile = original_profile # Revert to original
# 4. Prepare save parameters for PNG
save_params = {
"format": "PNG",
"compress_level": compress_level
}
if final_icc_profile:
save_params["icc_profile"] = final_icc_profile
# 5. Save to a temporary file
# We must return a file *path* for the gr.File component
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as temp_file:
output_filepath = temp_file.name
image_copy.save(output_filepath, **save_params)
# Return the path to the saved file
return output_filepath
# --- Gradio UI ---
# Description for the user
title = "🖼️ Any-to-PNG Image Converter"
description = """
Upload any image to convert it to a high-quality PNG.
This tool uses `pillow-heif` to support modern formats like **HEIC, HEIF, and AVIF**, in addition to standards like **JPG, BMP, TIFF, and WEBP**.
"""
# Note on the color profiles
profile_info = """
**A Note on Color Profiles (P3, Adobe RGB, RGB):**
You requested P3 and Adobe RGB. These are wide-gamut profiles.
* **"Keep Original Profile"** is the true "lossless" mode. If your image has a P3 profile (e.g., from an iPhone), this will preserve it.
* **"Convert to sRGB"** is the "RGB" option you requested. It ensures the image is compatible with all browsers and viewers, as sRGB is the web standard.
"""
supported_formats = "Supported inputs: `JPG`, `JPEG`, `BMP`, `TIFF`, `WEBP`, `HEIF`, `HEIC`, `AVIF`, and more."
with gr.Blocks(theme=gr.themes.Soft()) as demo:
gr.Markdown(f"# {title}")
gr.Markdown(description)
gr.Markdown(supported_formats)
with gr.Row(variant="panel"):
with gr.Column(scale=1):
# Input Image
input_image = gr.Image(
type="pil",
label="Upload Image",
)
gr.Markdown("### ⚙️ Conversion Options")
compress_setting = gr.Dropdown(
["Fast (Large File)", "Balanced (Recommended)", "Best (Small File, Slow)", "None (No Compression)"],
label="PNG Compression Level",
value="Balanced (Recommended)",
info="PNG is always lossless. This just controls file size vs. speed."
)
color_mode = gr.Radio(
["Auto (Keep Original)", "RGB", "RGBA", "L (Grayscale)"],
label="Color Mode",
value="Auto (Keep Original)",
info="Force the output image to a specific color mode."
)
color_profile = gr.Radio(
["Keep Original Profile", "Convert to sRGB"],
label="Color Profile",
value="Keep Original Profile"
)
gr.Markdown(profile_info)
# Submit Button
convert_btn = gr.Button("Convert to PNG", variant="primary")
with gr.Column(scale=1):
# Output File
gr.Markdown("### 🔽 Download Result")
output_file = gr.File(label="Download PNG")
# Connect components
convert_btn.click(
fn=convert_to_png,
inputs=[input_image, color_mode, color_profile, compress_setting],
outputs=output_file
)
# Launch the app
if __name__ == "__main__":
demo.launch()