import gradio as gr from src.utils.change_format import change_format from src.utils.remove_background import remove_background from src.utils.generate_image import generate_image from src.utils.add_text import add_text_to_image_base64 from src.utils.compress import compress_image_memory from src.utils.generate_image import generate_image from src.utils.apply_filter import apply_filter_direct from src.utils.watermark import add_watermark, remove_watermark from src.utils.describe import describe_image import base64 from PIL import Image import io import requests from io import BytesIO from typing import Union def change_format(image: Union[str, BytesIO], target_format: str) -> str: """ Change the format of an image from a URL to the specified target format. """ if not isinstance(image, BytesIO): response = requests.get(image, timeout=30) response.raise_for_status() img = Image.open(BytesIO(response.content)) else: img = Image.open(image) output = BytesIO() img.save(output, format=target_format) output.seek(0) encoded_image = base64.b64encode(output.getvalue()).decode('utf-8') return encoded_image def image_to_base64(image): if image is None: return None buffer = io.BytesIO() image.save(buffer, format="PNG") return base64.b64encode(buffer.getvalue()).decode() def base64_to_image(base64_str): if not base64_str: return None if isinstance(base64_str, str) and "base64," in base64_str: base64_str = base64_str.split("base64,", 1)[1] try: if isinstance(base64_str, str): base64_str = base64_str.strip() image_data = base64.b64decode(base64_str) if not image_data: print("Decoded base64 data is empty") return None image = Image.open(io.BytesIO(image_data)) return image.copy() except binascii.Error as e: print(f"Base64 decoding error: {str(e)}") if isinstance(base64_str, str): preview = base64_str[:30] + "..." if len(base64_str) > 30 else base64_str print(f"Base64 preview: {preview}") return None except Exception as e: print(f"Error converting base64 to image: {str(e)}") if isinstance(base64_str, str): preview = base64_str[:30] + "..." if len(base64_str) > 30 else base64_str print(f"Base64 preview: {preview}") if 'image_data' in locals() and image_data: try: magic_bytes = image_data[:12].hex() print(f"First 12 bytes: {magic_bytes}") except: pass return None def url_to_base64(url): response = requests.get(url) return base64.b64encode(response.content).decode() def gradio_remove_background(image): """ Attempt to remove watermarks from an image using contrast and brightness adjustment. Args: image_input: PIL Image object or base64 string alpha: Contrast control (1.0-3.0, default 2.0). Higher values increase contrast. beta: Brightness control (-255 to 255, default -160). Negative values decrease brightness. Returns: PIL Image object with watermark removal attempted """ if image is None: return None base64_img = image_to_base64(image) result = remove_background(f"data:image/png;base64,{base64_img}") if isinstance(result, str): return base64_to_image(result) elif isinstance(result, dict) and "image_data" in result: if isinstance(result["image_data"], str) and result["image_data"].startswith("data:"): return base64_to_image(result["image_data"]) else: try: return base64_to_image(result["image_data"]) except Exception as e: print(f"Error processing image data: {e}") return None else: print(f"Unexpected response format from remove_background: {type(result)}") return None def gradio_describe_image(image): if image is None: return "No image provided" try: base64_img = image_to_base64(image) return describe_image(base64_img) except Exception as e: print(f"Error describing image: {e}") return f"Error: {str(e)}" def gradio_change_format(image, format_type): if image is None: return None try: base64_img = image_to_base64(image) result = change_format(base64_img, format_type) return base64_to_image(result) except Exception as e: print(f"Error changing format: {e}") return image def gradio_generate_image(prompt, width=512, height=512): result = generate_image(prompt, width, height) return base64_to_image(result["b64"]) def gradio_apply_filter(image, filter_type, intensity=1.0): if image is None: print("No image provided") return None return apply_filter_direct(image, filter_type, intensity) def update_text_image(image, text, centered, x, y, font_size, color): if image is None: return None if not text or text.strip() == "": return image result = add_text_to_image_base64(image, text, int(x), int(y), int(font_size), color, centered) return result def toggle_position_fields(centered): return ( gr.Number(interactive=not centered), gr.Number(interactive=not centered) ) def toggle_intensity_slider(filter_type): intensity_filters = ['blur', 'brightness', 'contrast', 'saturation'] return gr.Slider(interactive=filter_type in intensity_filters) def gradio_add_watermark(image, watermark_text, opacity=0.5): """ Gradio wrapper for add_watermark function """ if image is None: return None if not watermark_text or watermark_text.strip() == "": return image try: result = add_watermark(image, watermark_text, opacity) return result except Exception as e: print(f"Error in gradio_add_watermark: {e}") return image def gradio_remove_watermark(image): if image is None: return None try: base64_img = image_to_base64(image) result = remove_watermark(base64_img) return base64_to_image(result) except Exception as e: print(f"Error removing watermark: {e}") return image def gradio_compress_image(image, quality=80): """ Compress image for Gradio interface """ if image is None: return None try: compressed_image = compress_image_memory(image, quality, "JPEG") return compressed_image except Exception as e: print(f"Error compressing image: {e}") return image def create_gradio_interface(): with gr.Blocks(title="ImageUtilitiesMCP", theme=gr.themes.Soft()) as demo: gr.Markdown("# 🖼️ ImageUtilitiesMCP") gr.Markdown("Some tools for image processing and generation.") with gr.Tabs(): with gr.Tab("🎨 Generate Image"): with gr.Row(): prompt_input = gr.Textbox(label="Prompt", placeholder="Describe the image you want to generate") with gr.Column(): width_input = gr.Slider(256, 1024, 512, label="Width") height_input = gr.Slider(256, 1024, 512, label="Height") generate_btn = gr.Button("Generate", variant="primary") generated_output = gr.Image(label="Generated Image") generate_btn.click( gradio_generate_image, [prompt_input, width_input, height_input], generated_output ) with gr.Tab("🔍 Describe Image"): with gr.Row(): describe_input = gr.Image(label="Upload Image", type="pil") description_output = gr.Textbox(label="Description", lines=4) describe_input.change(gradio_describe_image, describe_input, description_output) with gr.Tab("✂️ Remove Background"): with gr.Row(): bg_input = gr.Image(label="Upload Image", type="pil") bg_output = gr.Image(label="Background Removed") bg_input.change(gradio_remove_background, bg_input, bg_output) with gr.Tab("🎭 Apply Filters"): with gr.Row(): filter_input = gr.Image(label="Upload Image", type="pil") with gr.Column(): filter_type = gr.Dropdown( ["blur", "sharpen", "vintage", "black_white", "sepia", "emboss", "edge", "smooth", "brightness", "contrast", "saturation", "grayscale"], label="Filter Type", value="blur" ) intensity_slider = gr.Slider( minimum=0.1, maximum=300.0, value=1.0, step=0.1, label="Intensity", interactive=True ) filter_output = gr.Image(label="Filtered Image") filter_type.change( toggle_intensity_slider, filter_type, intensity_slider ) filter_inputs = [filter_input, filter_type, intensity_slider] for inp in filter_inputs: inp.change(gradio_apply_filter, filter_inputs, filter_output) with gr.Tab("📝 Add Text"): with gr.Row(): text_input = gr.Image(label="Upload Image", type="pil") with gr.Column(): text_content = gr.Textbox( label="Text", placeholder="Enter text to add", value="" ) text_centered = gr.Checkbox(label="Center Text", value=False) with gr.Row(): text_x = gr.Number( label="X Position", value=50, interactive=True, minimum=0 ) text_y = gr.Number( label="Y Position", value=50, interactive=True, minimum=0 ) with gr.Row(): font_size = gr.Slider( minimum=10, maximum=100, value=20, label="Font Size" ) text_color = gr.ColorPicker( label="Color", value="#FFFFFF" ) add_text_btn = gr.Button("Add Text", variant="primary") text_output = gr.Image(label="Image with Text") text_centered.change( toggle_position_fields, text_centered, [text_x, text_y] ) inputs = [text_input, text_content, text_centered, text_x, text_y, font_size, text_color] add_text_btn.click( update_text_image, inputs, text_output ) for inp in inputs: inp.change(update_text_image, inputs, text_output) with gr.Tab("💧 Watermark"): with gr.Tabs(): with gr.Tab("Add Watermark"): with gr.Row(): watermark_input = gr.Image(label="Upload Image", type="pil") with gr.Column(): watermark_text = gr.Textbox(label="Watermark Text") watermark_opacity = gr.Slider(0.1, 1.0, 0.5, label="Opacity") watermark_output = gr.Image(label="Watermarked Image") inputs = [watermark_input, watermark_text, watermark_opacity] for inp in inputs: inp.change(gradio_add_watermark, inputs, watermark_output) with gr.Tab("Remove Watermark"): with gr.Row(): unwatermark_input = gr.Image(label="Upload Image", type="pil") unwatermark_output = gr.Image(label="Watermark Removed") unwatermark_input.change(gradio_remove_watermark, unwatermark_input, unwatermark_output) with gr.Tab("🗜️ Compress"): with gr.Row(): compress_input = gr.Image(label="Upload Image", type="pil") with gr.Column(): quality_slider = gr.Slider(0, 100, 80, label="Quality %") compress_output = gr.Image(label="Compressed Image") compress_input.change(gradio_compress_image, [compress_input, quality_slider], compress_output) quality_slider.change(gradio_compress_image, [compress_input, quality_slider], compress_output) with gr.Tab("🔄 Change Format"): with gr.Row(): format_input = gr.Image(label="Upload Image", type="pil") with gr.Column(): format_type = gr.Dropdown( ["PNG", "JPEG", "WEBP", "BMP"], label="Output Format", value="PNG" ) format_output = gr.Image(label="Converted Image") format_input.change(gradio_change_format, [format_input, format_type], format_output) format_type.change(gradio_change_format, [format_input, format_type], format_output) return demo if __name__ == "__main__": demo = create_gradio_interface() demo.launch( mcp_server=True, server_name="0.0.0.0", server_port=7860, show_error=True )