mcp_images / app.py
Chris4K's picture
Create app.py
4bec348 verified
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)