Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import PIL.Image as Image | |
| import io | |
| import base64 | |
| import json | |
| from typing import Union | |
| def analyze_image(image: Image.Image) -> str: | |
| """ | |
| Analyze an image and return detailed information about it. | |
| Args: | |
| image: The image to analyze (can be base64 string or file upload) | |
| Returns: | |
| str: JSON string with image analysis including dimensions, format, mode, and orientation | |
| """ | |
| if image is None: | |
| return json.dumps({"error": "No image provided"}) | |
| try: | |
| # Get image properties | |
| width, height = image.size | |
| format_type = image.format or "Unknown" | |
| mode = image.mode | |
| orientation = "Portrait" if height > width else "Landscape" if width > height else "Square" | |
| # Calculate aspect ratio | |
| aspect_ratio = round(width / height, 2) if height > 0 else 0 | |
| # Get color information | |
| colors = image.getcolors(maxcolors=256*256*256) | |
| dominant_colors = len(colors) if colors else "Many" | |
| analysis = { | |
| "dimensions": {"width": width, "height": height}, | |
| "format": format_type, | |
| "mode": mode, | |
| "orientation": orientation, | |
| "aspect_ratio": aspect_ratio, | |
| "approximate_colors": dominant_colors, | |
| "file_info": f"{width}x{height} {format_type} image in {mode} mode" | |
| } | |
| return json.dumps(analysis, indent=2) | |
| except Exception as e: | |
| return json.dumps({"error": f"Error analyzing image: {str(e)}"}) | |
| def get_image_orientation(image: Image.Image) -> str: | |
| """ | |
| Determine if an image is portrait, landscape, or square. | |
| Args: | |
| image: The image to check orientation | |
| Returns: | |
| str: "Portrait", "Landscape", or "Square" | |
| """ | |
| if image is None: | |
| return "No image provided" | |
| try: | |
| width, height = image.size | |
| if height > width: | |
| return "Portrait" | |
| elif width > height: | |
| return "Landscape" | |
| else: | |
| return "Square" | |
| except Exception as e: | |
| return f"Error: {str(e)}" | |
| def count_colors(image: Image.Image) -> str: | |
| """ | |
| Count the approximate number of unique colors in an image. | |
| Args: | |
| image: The image to analyze for color count | |
| Returns: | |
| str: Description of color count and dominant color information | |
| """ | |
| if image is None: | |
| return "No image provided" | |
| try: | |
| # Convert to RGB if not already | |
| if image.mode != 'RGB': | |
| image = image.convert('RGB') | |
| # Get colors (limit to prevent memory issues) | |
| colors = image.getcolors(maxcolors=256*256*256) | |
| if colors is None: | |
| return "Image has more than 16.7 million unique colors" | |
| # Sort by frequency | |
| colors.sort(key=lambda x: x[0], reverse=True) | |
| # Get top 3 colors | |
| top_colors = colors[:3] | |
| color_info = [] | |
| for count, color in top_colors: | |
| if isinstance(color, tuple) and len(color) >= 3: | |
| r, g, b = color[:3] | |
| hex_color = f"#{r:02x}{g:02x}{b:02x}" | |
| percentage = round((count / sum(c[0] for c in colors)) * 100, 1) | |
| color_info.append(f"RGB{color} ({hex_color}) - {percentage}%") | |
| result = f"Total unique colors: {len(colors)}\n" | |
| result += "Top colors by frequency:\n" + "\n".join(color_info) | |
| return result | |
| except Exception as e: | |
| return f"Error analyzing colors: {str(e)}" | |
| def extract_text_info(image: Image.Image) -> str: | |
| """ | |
| Extract basic information about text-like content in an image. | |
| Args: | |
| image: The image to analyze for text content | |
| Returns: | |
| str: Basic information about potential text content | |
| """ | |
| if image is None: | |
| return "No image provided" | |
| try: | |
| # Convert to grayscale for analysis | |
| gray = image.convert('L') | |
| # Get image statistics | |
| extrema = gray.getextrema() | |
| # Simple heuristics for text detection | |
| contrast = extrema[1] - extrema[0] | |
| analysis = { | |
| "image_mode": image.mode, | |
| "grayscale_range": f"{extrema[0]} to {extrema[1]}", | |
| "contrast_level": "High" if contrast > 200 else "Medium" if contrast > 100 else "Low", | |
| "potential_text": "Likely contains text" if contrast > 150 else "May contain text" if contrast > 100 else "Unlikely to contain text", | |
| "note": "This is a basic analysis. For proper OCR, use specialized text extraction tools." | |
| } | |
| return json.dumps(analysis, indent=2) | |
| except Exception as e: | |
| return f"Error analyzing for text: {str(e)}" | |
| # Create the Gradio interface | |
| with gr.Blocks(title="Image Analysis MCP Server") as demo: | |
| gr.Markdown(""" | |
| # Image Analysis MCP Server | |
| This Gradio app serves as an MCP server that can analyze images sent from Claude or other MCP clients. | |
| **Available Tools:** | |
| - `analyze_image`: Get comprehensive image analysis (dimensions, format, colors, etc.) | |
| - `get_image_orientation`: Check if image is portrait, landscape, or square | |
| - `count_colors`: Analyze color information and dominant colors | |
| - `extract_text_info`: Basic analysis for potential text content | |
| **Usage with Claude Desktop:** | |
| 1. Deploy this to HuggingFace Spaces | |
| 2. Add the MCP configuration to Claude Desktop | |
| 3. Send images to Claude and ask it to analyze them using these tools | |
| """) | |
| # Create interface for each function (these will be exposed as MCP tools) | |
| with gr.Tab("Image Analysis"): | |
| with gr.Row(): | |
| img_input1 = gr.Image(type="pil", label="Upload Image") | |
| analysis_output = gr.JSON(label="Analysis Result") | |
| analyze_btn = gr.Button("Analyze Image") | |
| analyze_btn.click(analyze_image, inputs=[img_input1], outputs=[analysis_output]) | |
| with gr.Tab("Orientation Check"): | |
| with gr.Row(): | |
| img_input2 = gr.Image(type="pil", label="Upload Image") | |
| orientation_output = gr.Textbox(label="Orientation") | |
| orientation_btn = gr.Button("Check Orientation") | |
| orientation_btn.click(get_image_orientation, inputs=[img_input2], outputs=[orientation_output]) | |
| with gr.Tab("Color Analysis"): | |
| with gr.Row(): | |
| img_input3 = gr.Image(type="pil", label="Upload Image") | |
| color_output = gr.Textbox(label="Color Analysis", lines=10) | |
| color_btn = gr.Button("Analyze Colors") | |
| color_btn.click(count_colors, inputs=[img_input3], outputs=[color_output]) | |
| with gr.Tab("Text Detection"): | |
| with gr.Row(): | |
| img_input4 = gr.Image(type="pil", label="Upload Image") | |
| text_output = gr.JSON(label="Text Analysis") | |
| text_btn = gr.Button("Analyze for Text") | |
| text_btn.click(extract_text_info, inputs=[img_input4], outputs=[text_output]) | |
| if __name__ == "__main__": | |
| # Launch with MCP server enabled | |
| demo.launch(mcp_server=True, share=True) |