Spaces:
Running
Running
File size: 3,733 Bytes
b7dafe5 b9f0706 b7dafe5 b9f0706 b7dafe5 b9f0706 b7dafe5 b9f0706 b7dafe5 b9f0706 b7dafe5 b9f0706 b7dafe5 b9f0706 b7dafe5 b9f0706 b7dafe5 b9f0706 b7dafe5 b9f0706 b7dafe5 b9f0706 b7dafe5 b9f0706 b7dafe5 b9f0706 b7dafe5 b9f0706 b7dafe5 b9f0706 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | import gradio as gr
from PIL import Image
import pillow_heif
import os
import shutil
# 1. Register HEIF opener with Pillow so it can handle .heic/.heif files
pillow_heif.register_heif_opener()
def format_bytes(size):
# Helper to make file sizes readable
power = 2**10
n = 0
power_labels = {0 : '', 1: 'KB', 2: 'MB', 3: 'GB', 4: 'TB'}
while size > power:
size /= power
n += 1
return f"{size:.2f} {power_labels[n]}"
def compress_image(image_path):
if image_path is None:
return None, "No image uploaded."
try:
# Get original file info
original_size = os.path.getsize(image_path)
filename = os.path.basename(image_path)
name_without_ext = os.path.splitext(filename)[0]
# Define output path
output_filename = f"{name_without_ext}_compressed.png"
output_path = os.path.join(os.path.dirname(image_path), output_filename)
# Open the image
# pillow-heif handles the HEIC/HEIF format automatically here
with Image.open(image_path) as img:
# Create a copy of the image to remove specific metadata that might
# conflict with saving (though we keep pixel data lossless)
# We convert to RGBA if it supports transparency, or RGB otherwise
if img.mode in ('RGBA', 'LA') or (img.mode == 'P' and 'transparency' in img.info):
img = img.convert('RGBA')
else:
img = img.convert('RGB')
# SAVE AS PNG
# compress_level=9 is maximum compression (slowest, smallest size)
# optimize=True performs additional passes to reduce size
img.save(output_path, "PNG", compress_level=9, optimize=True)
# Get new file info
new_size = os.path.getsize(output_path)
# Calculate stats
diff = new_size - original_size
status = "Reduced" if diff < 0 else "Increased"
percent = (abs(diff) / original_size) * 100
info_text = (
f"Original Size: {format_bytes(original_size)}\n"
f"Compressed Size: {format_bytes(new_size)}\n"
f"Difference: {status} by {format_bytes(abs(diff))} ({percent:.1f}%)\n\n"
f"Note: Converting JPEGs/HEICs (lossy) to PNG (lossless) may increase file size "
f"because PNG preserves the exact pixel data without compression artifacts."
)
return output_path, info_text
except Exception as e:
return None, f"Error processing image: {str(e)}"
# 2. Create the Gradio Interface
with gr.Blocks(title="Lossless PNG Compressor") as app:
gr.Markdown("## 🗜️ Lossless Image Compressor (Max PNG Level)")
gr.Markdown(
"Upload any image (JPG, PNG, HEIC, WEBP). It will be converted to **PNG** "
"saved at **Compression Level 9** (Maximum). \n\n"
"*Note: This preserves pixel quality perfectly. However, converting a lossy photo (JPG/HEIC) "
"to lossless PNG often results in a larger file size.*"
)
with gr.Row():
with gr.Column():
# Input: Expects a file path
image_input = gr.Image(type="filepath", label="Upload Image")
submit_btn = gr.Button("Compress / Convert", variant="primary")
with gr.Column():
# Output: Returns a downloadable file and text stats
file_output = gr.File(label="Download PNG")
stats_output = gr.Textbox(label="Compression Stats")
# Bind function
submit_btn.click(
fn=compress_image,
inputs=image_input,
outputs=[file_output, stats_output]
)
if __name__ == "__main__":
app.launch() |