import gradio as gr import PIL from PIL import Image import os import tempfile import zipfile import shutil from pathlib import Path import numpy as np # Define target sizes for different viewports SIZES = [ (480, 423), (696, 613), (960, 846), (1095, 965), (1280, 1128), (1440, 1269), (1670, 1472), (1870, 1648), (2048, 1805) ] def resize_image(img, target_size): """Resize image maintaining aspect ratio""" img_copy = img.copy() return img_copy.resize(target_size, PIL.Image.Resampling.LANCZOS) def generate_html_snippet(image_path_prefix): """Generate HTML code snippet with srcset""" srcset_items = [f"{image_path_prefix}-{w}x{h}.jpg {w}w" for w, h in SIZES] srcset_str = ",\n ".join(srcset_items) sizes_items = [ f"(max-width: {w}px) {w}px" for w, _ in SIZES[:-1] ] + [f"{SIZES[-1][0]}px"] sizes_str = ",\n ".join(sizes_items) # Using min-width for better responsive behavior # sizes_str = "(min-width: 1000px) 45vw, (min-width: 780px) 696px, (min-width: 580px) 480px, calc(100vw - 60px)" # Use the middle size as default src default_size = SIZES[4] # 1280x1128 html = f''' Your image description''' return html def get_path_components(filepath): """Split filepath into directory and filename""" dirpath = os.path.dirname(filepath) filename = os.path.basename(filepath) base_filename = os.path.splitext(filename)[0] return dirpath, base_filename def process_image(input_img, input_path): """Main processing function""" if input_img is None: return None, None, "Please upload an image" # Create temporary directory for processed images temp_dir = tempfile.mkdtemp() zip_path = os.path.join(temp_dir, "responsive_images.zip") processed_paths = [] # Initialize the list outside try block try: # Get path components if input_path and input_path.strip(): dirpath, base_filename = get_path_components(input_path.strip()) else: # If no path provided, use default dirpath, base_filename = "", "image" # Convert to PIL Image if needed if isinstance(input_img, np.ndarray): img = Image.fromarray(input_img) else: img = input_img # Convert to RGB if necessary if img.mode != 'RGB': img = img.convert('RGB') # Process each size for width, height in SIZES: # For zip file, only use filename without path output_path = os.path.join(temp_dir, f"{base_filename}-{width}x{height}.jpg") resized = resize_image(img, (width, height)) resized.save(output_path, "JPEG", quality=90) processed_paths.append(output_path) # Create zip file with zipfile.ZipFile(zip_path, 'w') as zf: for path in processed_paths: zf.write(path, os.path.basename(path)) # Generate HTML snippet using full path full_path = dirpath + ("/" if dirpath else "") + base_filename html_snippet = generate_html_snippet(full_path) return zip_path, html_snippet, "Processing completed successfully!" except Exception as e: if os.path.exists(zip_path): try: os.remove(zip_path) except: pass return None, None, f"Error processing image: {str(e)}" finally: # Clean up temporary files except zip for path in processed_paths: try: os.remove(path) except: pass # Try to remove the temp directory if it's empty try: os.rmdir(temp_dir) except: pass # Create Gradio interface with gr.Blocks(title="Responsive Image Generator") as app: gr.Markdown(""" # Responsive Image Generator Upload an image to generate optimized versions for different viewport sizes. You'll receive: 1. A ZIP file containing all sized versions 2. HTML code snippet with proper srcset attributes Optional: Specify the full path where images will be stored (e.g., 'assets/images/about/mock-up.png') """) with gr.Row(): with gr.Column(): with gr.Row(): input_image = gr.Image( label="Upload Original Image", type="pil", show_label=True ) with gr.Row(): input_path = gr.Textbox( label="Image Path (optional)", placeholder="e.g., assets/images/about/mock-up.png", value="" ) process_btn = gr.Button("Process Image") with gr.Column(): output_zip = gr.File(label="Download Processed Images") output_html = gr.Code( label="HTML Code Snippet", language="html" ) output_message = gr.Textbox(label="Status") def handle_upload(img): """Handle image upload to get filename""" if isinstance(img, dict) and 'name' in img: return img['name'] return "" # Update filename when image is uploaded input_image.upload( fn=handle_upload, inputs=[input_image], outputs=[input_path] ) # Process button click process_btn.click( fn=process_image, inputs=[input_image, input_path], outputs=[output_zip, output_html, output_message] ) # Launch the app with share=True for public access app.launch(share=True)