Prasanthkumar commited on
Commit
a404a8f
·
verified ·
1 Parent(s): 47251d3

Update tools/image_processing.py

Browse files
Files changed (1) hide show
  1. tools/image_processing.py +311 -0
tools/image_processing.py CHANGED
@@ -0,0 +1,311 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import io
3
+ import uuid
4
+ import base64
5
+ import numpy as np
6
+ from PIL import Image
7
+ from langchain_core.tools import tool
8
+ from typing import List, Dict, Any, Optional
9
+ from PIL import Image, ImageDraw, ImageFont, ImageEnhance, ImageFilter
10
+
11
+ # Helper functions for image processing
12
+ def encode_image(image_path: str) -> str:
13
+ """Convert an image file to base64 string."""
14
+ with open(image_path, "rb") as image_file:
15
+ return base64.b64encode(image_file.read()).decode("utf-8")
16
+
17
+
18
+ def decode_image(base64_string: str) -> Image.Image:
19
+ """Convert a base64 string to a PIL Image."""
20
+ image_data = base64.b64decode(base64_string)
21
+ return Image.open(io.BytesIO(image_data))
22
+
23
+
24
+ def save_image(image: Image.Image, directory: str = "image_outputs") -> str:
25
+ """Save a PIL Image to disk and return the path."""
26
+ os.makedirs(directory, exist_ok=True)
27
+ image_id = str(uuid.uuid4())
28
+ image_path = os.path.join(directory, f"{image_id}.png")
29
+ image.save(image_path)
30
+ return image_path
31
+
32
+
33
+ @tool
34
+ def analyze_image(image_base64: str) -> Dict[str, Any]:
35
+ """
36
+ Analyze basic properties of an image (size, mode, color analysis, thumbnail preview).
37
+ Args:
38
+ image_base64 (str): Base64 encoded image string
39
+ Returns:
40
+ Dictionary with analysis result
41
+ """
42
+ try:
43
+ img = decode_image(image_base64)
44
+ width, height = img.size
45
+ mode = img.mode
46
+
47
+ if mode in ("RGB", "RGBA"):
48
+ arr = np.array(img)
49
+ avg_colors = arr.mean(axis=(0, 1))
50
+ dominant = ["Red", "Green", "Blue"][np.argmax(avg_colors[:3])]
51
+ brightness = avg_colors.mean()
52
+ color_analysis = {
53
+ "average_rgb": avg_colors.tolist(),
54
+ "brightness": brightness,
55
+ "dominant_color": dominant,
56
+ }
57
+ else:
58
+ color_analysis = {"note": f"No color analysis for mode {mode}"}
59
+
60
+ thumbnail = img.copy()
61
+ thumbnail.thumbnail((100, 100))
62
+ thumb_path = save_image(thumbnail, "thumbnails")
63
+ thumbnail_base64 = encode_image(thumb_path)
64
+
65
+ return {
66
+ "dimensions": (width, height),
67
+ "mode": mode,
68
+ "color_analysis": color_analysis,
69
+ "thumbnail": thumbnail_base64,
70
+ }
71
+ except Exception as e:
72
+ return {"error": str(e)}
73
+
74
+
75
+ @tool
76
+ def transform_image(
77
+ image_base64: str, operation: str, params: Optional[Dict[str, Any]] = None
78
+ ) -> Dict[str, Any]:
79
+ """
80
+ Apply transformations: resize, rotate, crop, flip, brightness, contrast, blur, sharpen, grayscale.
81
+ Args:
82
+ image_base64 (str): Base64 encoded input image
83
+ operation (str): Transformation operation
84
+ params (Dict[str, Any], optional): Parameters for the operation
85
+ Returns:
86
+ Dictionary with transformed image (base64)
87
+ """
88
+ try:
89
+ img = decode_image(image_base64)
90
+ params = params or {}
91
+
92
+ if operation == "resize":
93
+ img = img.resize(
94
+ (
95
+ params.get("width", img.width // 2),
96
+ params.get("height", img.height // 2),
97
+ )
98
+ )
99
+ elif operation == "rotate":
100
+ img = img.rotate(params.get("angle", 90), expand=True)
101
+ elif operation == "crop":
102
+ img = img.crop(
103
+ (
104
+ params.get("left", 0),
105
+ params.get("top", 0),
106
+ params.get("right", img.width),
107
+ params.get("bottom", img.height),
108
+ )
109
+ )
110
+ elif operation == "flip":
111
+ if params.get("direction", "horizontal") == "horizontal":
112
+ img = img.transpose(Image.FLIP_LEFT_RIGHT)
113
+ else:
114
+ img = img.transpose(Image.FLIP_TOP_BOTTOM)
115
+ elif operation == "adjust_brightness":
116
+ img = ImageEnhance.Brightness(img).enhance(params.get("factor", 1.5))
117
+ elif operation == "adjust_contrast":
118
+ img = ImageEnhance.Contrast(img).enhance(params.get("factor", 1.5))
119
+ elif operation == "blur":
120
+ img = img.filter(ImageFilter.GaussianBlur(params.get("radius", 2)))
121
+ elif operation == "sharpen":
122
+ img = img.filter(ImageFilter.SHARPEN)
123
+ elif operation == "grayscale":
124
+ img = img.convert("L")
125
+ else:
126
+ return {"error": f"Unknown operation: {operation}"}
127
+
128
+ result_path = save_image(img)
129
+ result_base64 = encode_image(result_path)
130
+ return {"transformed_image": result_base64}
131
+
132
+ except Exception as e:
133
+ return {"error": str(e)}
134
+
135
+
136
+ @tool
137
+ def draw_on_image(
138
+ image_base64: str, drawing_type: str, params: Dict[str, Any]
139
+ ) -> Dict[str, Any]:
140
+ """
141
+ Draw shapes (rectangle, circle, line) or text onto an image.
142
+ Args:
143
+ image_base64 (str): Base64 encoded input image
144
+ drawing_type (str): Drawing type
145
+ params (Dict[str, Any]): Drawing parameters
146
+ Returns:
147
+ Dictionary with result image (base64)
148
+ """
149
+ try:
150
+ img = decode_image(image_base64)
151
+ draw = ImageDraw.Draw(img)
152
+ color = params.get("color", "red")
153
+
154
+ if drawing_type == "rectangle":
155
+ draw.rectangle(
156
+ [params["left"], params["top"], params["right"], params["bottom"]],
157
+ outline=color,
158
+ width=params.get("width", 2),
159
+ )
160
+ elif drawing_type == "circle":
161
+ x, y, r = params["x"], params["y"], params["radius"]
162
+ draw.ellipse(
163
+ (x - r, y - r, x + r, y + r),
164
+ outline=color,
165
+ width=params.get("width", 2),
166
+ )
167
+ elif drawing_type == "line":
168
+ draw.line(
169
+ (
170
+ params["start_x"],
171
+ params["start_y"],
172
+ params["end_x"],
173
+ params["end_y"],
174
+ ),
175
+ fill=color,
176
+ width=params.get("width", 2),
177
+ )
178
+ elif drawing_type == "text":
179
+ font_size = params.get("font_size", 20)
180
+ try:
181
+ font = ImageFont.truetype("arial.ttf", font_size)
182
+ except IOError:
183
+ font = ImageFont.load_default()
184
+ draw.text(
185
+ (params["x"], params["y"]),
186
+ params.get("text", "Text"),
187
+ fill=color,
188
+ font=font,
189
+ )
190
+ else:
191
+ return {"error": f"Unknown drawing type: {drawing_type}"}
192
+
193
+ result_path = save_image(img)
194
+ result_base64 = encode_image(result_path)
195
+ return {"result_image": result_base64}
196
+
197
+ except Exception as e:
198
+ return {"error": str(e)}
199
+
200
+
201
+ @tool
202
+ def generate_simple_image(
203
+ image_type: str,
204
+ width: int = 500,
205
+ height: int = 500,
206
+ params: Optional[Dict[str, Any]] = None,
207
+ ) -> Dict[str, Any]:
208
+ """
209
+ Generate a simple image (gradient, noise, pattern, chart).
210
+ Args:
211
+ image_type (str): Type of image
212
+ width (int), height (int)
213
+ params (Dict[str, Any], optional): Specific parameters
214
+ Returns:
215
+ Dictionary with generated image (base64)
216
+ """
217
+ try:
218
+ params = params or {}
219
+
220
+ if image_type == "gradient":
221
+ direction = params.get("direction", "horizontal")
222
+ start_color = params.get("start_color", (255, 0, 0))
223
+ end_color = params.get("end_color", (0, 0, 255))
224
+
225
+ img = Image.new("RGB", (width, height))
226
+ draw = ImageDraw.Draw(img)
227
+
228
+ if direction == "horizontal":
229
+ for x in range(width):
230
+ r = int(
231
+ start_color[0] + (end_color[0] - start_color[0]) * x / width
232
+ )
233
+ g = int(
234
+ start_color[1] + (end_color[1] - start_color[1]) * x / width
235
+ )
236
+ b = int(
237
+ start_color[2] + (end_color[2] - start_color[2]) * x / width
238
+ )
239
+ draw.line([(x, 0), (x, height)], fill=(r, g, b))
240
+ else:
241
+ for y in range(height):
242
+ r = int(
243
+ start_color[0] + (end_color[0] - start_color[0]) * y / height
244
+ )
245
+ g = int(
246
+ start_color[1] + (end_color[1] - start_color[1]) * y / height
247
+ )
248
+ b = int(
249
+ start_color[2] + (end_color[2] - start_color[2]) * y / height
250
+ )
251
+ draw.line([(0, y), (width, y)], fill=(r, g, b))
252
+
253
+ elif image_type == "noise":
254
+ noise_array = np.random.randint(0, 256, (height, width, 3), dtype=np.uint8)
255
+ img = Image.fromarray(noise_array, "RGB")
256
+
257
+ else:
258
+ return {"error": f"Unsupported image_type {image_type}"}
259
+
260
+ result_path = save_image(img)
261
+ result_base64 = encode_image(result_path)
262
+ return {"generated_image": result_base64}
263
+
264
+ except Exception as e:
265
+ return {"error": str(e)}
266
+
267
+
268
+ @tool
269
+ def combine_images(
270
+ images_base64: List[str], operation: str, params: Optional[Dict[str, Any]] = None
271
+ ) -> Dict[str, Any]:
272
+ """
273
+ Combine multiple images (collage, stack, blend).
274
+ Args:
275
+ images_base64 (List[str]): List of base64 images
276
+ operation (str): Combination type
277
+ params (Dict[str, Any], optional)
278
+ Returns:
279
+ Dictionary with combined image (base64)
280
+ """
281
+ try:
282
+ images = [decode_image(b64) for b64 in images_base64]
283
+ params = params or {}
284
+
285
+ if operation == "stack":
286
+ direction = params.get("direction", "horizontal")
287
+ if direction == "horizontal":
288
+ total_width = sum(img.width for img in images)
289
+ max_height = max(img.height for img in images)
290
+ new_img = Image.new("RGB", (total_width, max_height))
291
+ x = 0
292
+ for img in images:
293
+ new_img.paste(img, (x, 0))
294
+ x += img.width
295
+ else:
296
+ max_width = max(img.width for img in images)
297
+ total_height = sum(img.height for img in images)
298
+ new_img = Image.new("RGB", (max_width, total_height))
299
+ y = 0
300
+ for img in images:
301
+ new_img.paste(img, (0, y))
302
+ y += img.height
303
+ else:
304
+ return {"error": f"Unsupported combination operation {operation}"}
305
+
306
+ result_path = save_image(new_img)
307
+ result_base64 = encode_image(result_path)
308
+ return {"combined_image": result_base64}
309
+
310
+ except Exception as e:
311
+ return {"error": str(e)}