File size: 7,233 Bytes
4bec348
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
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)