JuanjoSG5 commited on
Commit
bcaa65b
ยท
2 Parent(s): cc083b4 94fccca

Merge branch 'main' of https://huggingface.co/spaces/AgentsGuards/agents-guard-mcp

Browse files
README.md CHANGED
@@ -1,13 +1,13 @@
1
  ---
2
- title: Agents Guard Mcp
3
- emoji: ๐Ÿ 
4
  colorFrom: pink
5
  colorTo: red
6
  sdk: gradio
7
  sdk_version: 5.32.0
8
- app_file: gradio_interface/app.py
9
  pinned: false
10
- short_description: a security framework designed for MCP deployments.
11
  tags: [mcp-server-track]
12
  ---
13
 
 
1
  ---
2
+ title: ImageUtilitiesMCP
3
+ emoji: ๐Ÿ–ผ๏ธ
4
  colorFrom: pink
5
  colorTo: red
6
  sdk: gradio
7
  sdk_version: 5.32.0
8
+ app_file: app.py
9
  pinned: false
10
+ short_description: Useful tools for image editing.
11
  tags: [mcp-server-track]
12
  ---
13
 
mcp_server.py โ†’ app.py RENAMED
@@ -4,13 +4,40 @@ from src.utils.remove_background import remove_background
4
  from src.utils.generate_image import generate_image
5
  from src.utils.apply_filter import apply_filter
6
  from src.utils.add_text import add_text_to_image
 
 
 
 
 
 
7
  from src.utils.watermark import add_watermark, remove_watermark
8
  from src.utils.describe import describe_image
9
- from src.utils.compress import compress_image
10
  import base64
11
  from PIL import Image
12
  import io
13
  import requests
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  def image_to_base64(image):
16
  if image is None:
@@ -103,59 +130,93 @@ def gradio_remove_background(image):
103
  def gradio_describe_image(image):
104
  if image is None:
105
  return "No image provided"
106
- base64_img = image_to_base64(image)
107
- return describe_image(base64_img)
 
 
 
 
108
 
109
  def gradio_change_format(image, format_type):
110
  if image is None:
111
  return None
112
- base64_img = image_to_base64(image)
113
- result = change_format(base64_img, format_type)
114
- return base64_to_image(result)
 
 
 
 
115
 
116
  def gradio_generate_image(prompt, width=512, height=512):
117
  result = generate_image(prompt, width, height)
118
  return base64_to_image(result["b64"])
119
 
120
- def gradio_apply_filter(image, filter_type):
121
  if image is None:
 
122
  return None
123
- base64_img = image_to_base64(image)
124
- result = apply_filter(base64_img, filter_type)
125
- return base64_to_image(result)
126
 
127
- def gradio_add_text(image, text, x=50, y=50, font_size=20, color="white"):
128
  if image is None:
129
  return None
130
- base64_img = image_to_base64(image)
131
- result = add_text_to_image(base64_img, text, x, y, font_size, color)
132
- return base64_to_image(result)
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
  def gradio_add_watermark(image, watermark_text, opacity=0.5):
135
  if image is None:
136
  return None
137
- base64_img = image_to_base64(image)
138
- result = add_watermark(base64_img, watermark_text, opacity)
139
- return base64_to_image(result)
 
 
 
 
140
 
141
  def gradio_remove_watermark(image):
142
  if image is None:
143
  return None
144
- base64_img = image_to_base64(image)
145
- result = remove_watermark(base64_img)
146
- return base64_to_image(result)
 
 
 
 
147
 
148
  def gradio_compress_image(image, quality=80):
 
 
 
149
  if image is None:
150
  return None
151
- base64_img = image_to_base64(image)
152
- result = compress_image(base64_img, quality)
153
- return base64_to_image(result)
 
 
 
154
 
155
  def create_gradio_interface():
156
- with gr.Blocks(title="Image Processing Service", theme=gr.themes.Soft()) as demo:
157
- gr.Markdown("# ๐Ÿ–ผ๏ธ Image Processing Service")
158
- gr.Markdown("Servicio completo de procesamiento de imรกgenes")
159
 
160
  with gr.Tabs():
161
  with gr.Tab("๐ŸŽจ Generate Image"):
@@ -192,31 +253,89 @@ def create_gradio_interface():
192
  filter_input = gr.Image(label="Upload Image", type="pil")
193
  with gr.Column():
194
  filter_type = gr.Dropdown(
195
- ["blur", "sharpen", "vintage", "black_white", "sepia"],
196
  label="Filter Type",
197
  value="blur"
198
  )
 
 
 
 
 
 
 
 
 
 
199
  filter_output = gr.Image(label="Filtered Image")
200
 
201
- filter_input.change(gradio_apply_filter, [filter_input, filter_type], filter_output)
202
- filter_type.change(gradio_apply_filter, [filter_input, filter_type], filter_output)
 
 
 
 
 
 
 
 
203
 
204
  with gr.Tab("๐Ÿ“ Add Text"):
205
  with gr.Row():
206
  text_input = gr.Image(label="Upload Image", type="pil")
207
  with gr.Column():
208
- text_content = gr.Textbox(label="Text", placeholder="Enter text to add")
 
 
 
 
 
 
209
  with gr.Row():
210
- text_x = gr.Number(label="X Position", value=50)
211
- text_y = gr.Number(label="Y Position", value=50)
 
 
 
 
 
 
 
 
 
 
 
212
  with gr.Row():
213
- font_size = gr.Slider(10, 100, 20, label="Font Size")
214
- text_color = gr.ColorPicker(label="Color", value="#FFFFFF")
 
 
 
 
 
 
 
 
 
 
215
  text_output = gr.Image(label="Image with Text")
216
 
217
- inputs = [text_input, text_content, text_x, text_y, font_size, text_color]
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  for inp in inputs:
219
- inp.change(gradio_add_text, inputs, text_output)
220
 
221
  with gr.Tab("๐Ÿ’ง Watermark"):
222
  with gr.Tabs():
@@ -243,7 +362,7 @@ def create_gradio_interface():
243
  with gr.Row():
244
  compress_input = gr.Image(label="Upload Image", type="pil")
245
  with gr.Column():
246
- quality_slider = gr.Slider(10, 100, 80, label="Quality %")
247
  compress_output = gr.Image(label="Compressed Image")
248
 
249
  compress_input.change(gradio_compress_image, [compress_input, quality_slider], compress_output)
@@ -271,8 +390,8 @@ def create_gradio_interface():
271
  if __name__ == "__main__":
272
  demo = create_gradio_interface()
273
  demo.launch(
 
274
  server_name="0.0.0.0",
275
- server_port=7860,
276
- share=True,
277
  show_error=True
278
  )
 
4
  from src.utils.generate_image import generate_image
5
  from src.utils.apply_filter import apply_filter
6
  from src.utils.add_text import add_text_to_image
7
+ from src.utils.add_text import add_text_to_image_base64
8
+ from src.utils.compress import compress_image_memory, compress_image_file
9
+ from src.utils.remove_background import remove_background_from_url
10
+ from src.utils.visualize_image import visualize_base64_image
11
+ from src.utils.generate_image import generate_image
12
+ from src.utils.apply_filter import apply_filter_direct
13
  from src.utils.watermark import add_watermark, remove_watermark
14
  from src.utils.describe import describe_image
 
15
  import base64
16
  from PIL import Image
17
  import io
18
  import requests
19
+ from io import BytesIO
20
+ from typing import Union
21
+
22
+ def change_format(image: Union[str, BytesIO], target_format: str) -> str:
23
+ """
24
+ Change the format of an image from a URL to the specified target format.
25
+ """
26
+
27
+ if not isinstance(image, BytesIO):
28
+ response = requests.get(image, timeout=30)
29
+ response.raise_for_status()
30
+ img = Image.open(BytesIO(response.content))
31
+ else:
32
+ img = Image.open(image)
33
+
34
+ output = BytesIO()
35
+ img.save(output, format=target_format)
36
+ output.seek(0)
37
+
38
+ encoded_image = base64.b64encode(output.getvalue()).decode('utf-8')
39
+
40
+ return encoded_image
41
 
42
  def image_to_base64(image):
43
  if image is None:
 
130
  def gradio_describe_image(image):
131
  if image is None:
132
  return "No image provided"
133
+ try:
134
+ base64_img = image_to_base64(image)
135
+ return describe_image(base64_img)
136
+ except Exception as e:
137
+ print(f"Error describing image: {e}")
138
+ return f"Error: {str(e)}"
139
 
140
  def gradio_change_format(image, format_type):
141
  if image is None:
142
  return None
143
+ try:
144
+ base64_img = image_to_base64(image)
145
+ result = change_format(base64_img, format_type)
146
+ return base64_to_image(result)
147
+ except Exception as e:
148
+ print(f"Error changing format: {e}")
149
+ return image
150
 
151
  def gradio_generate_image(prompt, width=512, height=512):
152
  result = generate_image(prompt, width, height)
153
  return base64_to_image(result["b64"])
154
 
155
+ def gradio_apply_filter(image, filter_type, intensity=1.0):
156
  if image is None:
157
+ print("No image provided")
158
  return None
159
+
160
+ return apply_filter_direct(image, filter_type, intensity)
 
161
 
162
+ def update_text_image(image, text, centered, x, y, font_size, color):
163
  if image is None:
164
  return None
165
+ if not text or text.strip() == "":
166
+ return image
167
+
168
+ result = add_text_to_image_base64(image, text, int(x), int(y), int(font_size), color, centered)
169
+ return result
170
+
171
+ def toggle_position_fields(centered):
172
+ return (
173
+ gr.Number(interactive=not centered),
174
+ gr.Number(interactive=not centered)
175
+ )
176
+
177
+ def toggle_intensity_slider(filter_type):
178
+ intensity_filters = ['blur', 'brightness', 'contrast', 'saturation']
179
+ return gr.Slider(interactive=filter_type in intensity_filters)
180
 
181
  def gradio_add_watermark(image, watermark_text, opacity=0.5):
182
  if image is None:
183
  return None
184
+ try:
185
+ base64_img = image_to_base64(image)
186
+ result = add_watermark(base64_img, watermark_text, opacity)
187
+ return base64_to_image(result)
188
+ except Exception as e:
189
+ print(f"Error adding watermark: {e}")
190
+ return image
191
 
192
  def gradio_remove_watermark(image):
193
  if image is None:
194
  return None
195
+ try:
196
+ base64_img = image_to_base64(image)
197
+ result = remove_watermark(base64_img)
198
+ return base64_to_image(result)
199
+ except Exception as e:
200
+ print(f"Error removing watermark: {e}")
201
+ return image
202
 
203
  def gradio_compress_image(image, quality=80):
204
+ """
205
+ Compress image for Gradio interface
206
+ """
207
  if image is None:
208
  return None
209
+ try:
210
+ compressed_image = compress_image_memory(image, quality, "JPEG")
211
+ return compressed_image
212
+ except Exception as e:
213
+ print(f"Error compressing image: {e}")
214
+ return image
215
 
216
  def create_gradio_interface():
217
+ with gr.Blocks(title="ImageUtilitiesMCP", theme=gr.themes.Soft()) as demo:
218
+ gr.Markdown("# ๐Ÿ–ผ๏ธ ImageUtilitiesMCP")
219
+ gr.Markdown("Complete processing image tools")
220
 
221
  with gr.Tabs():
222
  with gr.Tab("๐ŸŽจ Generate Image"):
 
253
  filter_input = gr.Image(label="Upload Image", type="pil")
254
  with gr.Column():
255
  filter_type = gr.Dropdown(
256
+ ["blur", "sharpen", "vintage", "black_white", "sepia", "emboss", "edge", "smooth", "brightness", "contrast", "saturation", "grayscale"],
257
  label="Filter Type",
258
  value="blur"
259
  )
260
+
261
+ intensity_slider = gr.Slider(
262
+ minimum=0.1,
263
+ maximum=300.0,
264
+ value=1.0,
265
+ step=0.1,
266
+ label="Intensity",
267
+ interactive=True
268
+ )
269
+
270
  filter_output = gr.Image(label="Filtered Image")
271
 
272
+ filter_type.change(
273
+ toggle_intensity_slider,
274
+ filter_type,
275
+ intensity_slider
276
+ )
277
+
278
+ filter_inputs = [filter_input, filter_type, intensity_slider]
279
+
280
+ for inp in filter_inputs:
281
+ inp.change(gradio_apply_filter, filter_inputs, filter_output)
282
 
283
  with gr.Tab("๐Ÿ“ Add Text"):
284
  with gr.Row():
285
  text_input = gr.Image(label="Upload Image", type="pil")
286
  with gr.Column():
287
+ text_content = gr.Textbox(
288
+ label="Text",
289
+ placeholder="Enter text to add",
290
+ value=""
291
+ )
292
+ text_centered = gr.Checkbox(label="Center Text", value=False)
293
+
294
  with gr.Row():
295
+ text_x = gr.Number(
296
+ label="X Position",
297
+ value=50,
298
+ interactive=True,
299
+ minimum=0
300
+ )
301
+ text_y = gr.Number(
302
+ label="Y Position",
303
+ value=50,
304
+ interactive=True,
305
+ minimum=0
306
+ )
307
+
308
  with gr.Row():
309
+ font_size = gr.Slider(
310
+ minimum=10,
311
+ maximum=100,
312
+ value=20,
313
+ label="Font Size"
314
+ )
315
+ text_color = gr.ColorPicker(
316
+ label="Color",
317
+ value="#FFFFFF"
318
+ )
319
+
320
+ add_text_btn = gr.Button("Add Text", variant="primary")
321
  text_output = gr.Image(label="Image with Text")
322
 
323
+ text_centered.change(
324
+ toggle_position_fields,
325
+ text_centered,
326
+ [text_x, text_y]
327
+ )
328
+
329
+ inputs = [text_input, text_content, text_centered, text_x, text_y, font_size, text_color]
330
+
331
+ add_text_btn.click(
332
+ update_text_image,
333
+ inputs,
334
+ text_output
335
+ )
336
+
337
  for inp in inputs:
338
+ inp.change(update_text_image, inputs, text_output)
339
 
340
  with gr.Tab("๐Ÿ’ง Watermark"):
341
  with gr.Tabs():
 
362
  with gr.Row():
363
  compress_input = gr.Image(label="Upload Image", type="pil")
364
  with gr.Column():
365
+ quality_slider = gr.Slider(0, 100, 80, label="Quality %")
366
  compress_output = gr.Image(label="Compressed Image")
367
 
368
  compress_input.change(gradio_compress_image, [compress_input, quality_slider], compress_output)
 
390
  if __name__ == "__main__":
391
  demo = create_gradio_interface()
392
  demo.launch(
393
+ mcp_server=True,
394
  server_name="0.0.0.0",
395
+ server_port=7860,
 
396
  show_error=True
397
  )
src/utils/add_text.py CHANGED
@@ -2,12 +2,25 @@ from PIL import Image, ImageDraw, ImageFont
2
  import os
3
  from typing import Optional, Tuple, Dict, Any
4
 
5
- def add_text_to_image(
6
- image_path: str,
7
- text: str,
8
- color: Optional[Tuple[int, int, int]] = None,
9
- output_name: Optional[str] = None
10
- ) -> Dict[str, Any]:
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  """
12
  Adds centered text to an image and saves the result in the same folder.
13
  If no output_name is provided, '_edited' is appended to the original filename.
@@ -22,47 +35,28 @@ def add_text_to_image(
22
  Returns:
23
  Dictionary with success status and info.
24
  """
 
 
 
 
 
 
25
  try:
26
- if color is None:
27
- color = (0, 0, 0)
28
-
29
- image = Image.open(image_path)
30
- draw = ImageDraw.Draw(image)
31
- font = ImageFont.load_default()
32
-
33
- text_width = draw.textlength(text, font=font)
34
- x = (image.width - text_width) / 2
35
- y = image.height / 2
36
-
37
- draw.text((x, y), text, fill=color, font=font)
38
-
39
- base_dir = os.path.dirname(image_path)
40
- base_name, ext = os.path.splitext(os.path.basename(image_path))
41
-
42
- if output_name:
43
- new_filename = f"{output_name}{ext}"
44
- else:
45
- new_filename = f"{base_name}_edited{ext}"
46
-
47
- new_path = os.path.join(base_dir, new_filename)
48
-
49
- image.save(new_path)
50
- output_size = os.path.getsize(new_path)
51
-
52
- return {
53
- "success": True,
54
- "message": "Text added successfully to the image",
55
- "input_path": image_path,
56
- "output_path": new_path,
57
- "output_size_bytes": output_size,
58
- "text": text,
59
- "color": color
60
- }
61
-
62
- except Exception as e:
63
- return {
64
- "success": False,
65
- "error": str(e),
66
- "input_path": image_path,
67
- "output_path": None
68
- }
 
2
  import os
3
  from typing import Optional, Tuple, Dict, Any
4
 
5
+ def parse_color(color_str):
6
+ if color_str.startswith('rgba('):
7
+ values = color_str[5:-1].split(',')
8
+ r = int(float(values[0]))
9
+ g = int(float(values[1]))
10
+ b = int(float(values[2]))
11
+ return (r, g, b)
12
+ elif color_str.startswith('rgb('):
13
+ values = color_str[4:-1].split(',')
14
+ r = int(float(values[0]))
15
+ g = int(float(values[1]))
16
+ b = int(float(values[2]))
17
+ return (r, g, b)
18
+ elif color_str.startswith('#'):
19
+ return color_str
20
+ else:
21
+ return color_str
22
+
23
+ def add_text_to_image_base64(image, text, x, y, font_size, color, centered=False):
24
  """
25
  Adds centered text to an image and saves the result in the same folder.
26
  If no output_name is provided, '_edited' is appended to the original filename.
 
35
  Returns:
36
  Dictionary with success status and info.
37
  """
38
+ if image is None:
39
+ return None
40
+
41
+ img = image.copy()
42
+ draw = ImageDraw.Draw(img)
43
+
44
  try:
45
+ font = ImageFont.truetype("arial.ttf", font_size)
46
+ except:
47
+ try:
48
+ font = ImageFont.truetype("/System/Library/Fonts/Arial.ttf", font_size)
49
+ except:
50
+ font = ImageFont.load_default()
51
+
52
+ parsed_color = parse_color(color)
53
+
54
+ if centered:
55
+ bbox = draw.textbbox((0, 0), text, font=font)
56
+ text_width = bbox[2] - bbox[0]
57
+ text_height = bbox[3] - bbox[1]
58
+ x = (img.width - text_width) // 2
59
+ y = (img.height - text_height) // 2
60
+
61
+ draw.text((x, y), text, fill=parsed_color, font=font)
62
+ return img
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/utils/apply_filter.py CHANGED
@@ -3,74 +3,100 @@ from io import BytesIO
3
  import requests
4
  import base64
5
 
6
- def apply_filter(image_url: str, filter_type: str, intensity: float = 1.0, output_format: str = 'JPEG') -> str:
7
  """
8
- Apply various filters to an image from a URL.
9
-
10
- Args:
11
- image_url: The URL of the input image.
12
- filter_type: Type of filter to apply ('blur', 'sharpen', 'emboss', 'edge', 'smooth',
13
- 'brightness', 'contrast', 'saturation', 'sepia', 'grayscale').
14
- intensity: Filter intensity (0.1 to 3.0, default 1.0).
15
- output_format: The desired output format (e.g., 'JPEG', 'PNG').
16
-
17
- Returns:
18
- The filtered image as a base64-encoded string.
19
  """
 
 
20
 
21
- response = requests.get(image_url, timeout=30)
22
- response.raise_for_status()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
- img = Image.open(BytesIO(response.content))
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
- if img.mode != 'RGB':
27
- img = img.convert('RGB')
 
 
 
 
28
 
29
- if filter_type == 'blur':
30
- img = img.filter(ImageFilter.GaussianBlur(radius=intensity))
31
- elif filter_type == 'sharpen':
32
- img = img.filter(ImageFilter.UnsharpMask(radius=2, percent=int(intensity * 150), threshold=3))
33
- elif filter_type == 'emboss':
34
- img = img.filter(ImageFilter.EMBOSS)
35
- elif filter_type == 'edge':
36
- img = img.filter(ImageFilter.FIND_EDGES)
37
- elif filter_type == 'smooth':
38
- img = img.filter(ImageFilter.SMOOTH_MORE)
39
- elif filter_type == 'brightness':
40
- enhancer = ImageEnhance.Brightness(img)
41
- img = enhancer.enhance(intensity)
42
- elif filter_type == 'contrast':
43
- enhancer = ImageEnhance.Contrast(img)
44
- img = enhancer.enhance(intensity)
45
- elif filter_type == 'saturation':
46
- enhancer = ImageEnhance.Color(img)
47
- img = enhancer.enhance(intensity)
48
- elif filter_type == 'sepia':
49
- img = apply_sepia_filter(img)
50
- elif filter_type == 'grayscale':
51
- img = img.convert('L').convert('RGB')
52
 
53
- output = BytesIO()
54
- img.save(output, format=output_format, quality=95)
55
- output.seek(0)
56
 
57
- encoded_image = base64.b64encode(output.getvalue()).decode('utf-8')
58
 
59
- return encoded_image
60
 
61
- def apply_sepia_filter(img: Image.Image) -> Image.Image:
62
- """
63
- Apply sepia tone effect to an image.
64
-
65
- Args:
66
- img: PIL Image object.
67
-
68
- Returns:
69
- Image with sepia effect applied.
70
- """
71
-
72
- pixels = img.load()
73
  width, height = img.size
 
74
 
75
  for y in range(height):
76
  for x in range(width):
@@ -80,44 +106,24 @@ def apply_sepia_filter(img: Image.Image) -> Image.Image:
80
  tg = int(0.349 * r + 0.686 * g + 0.168 * b)
81
  tb = int(0.272 * r + 0.534 * g + 0.131 * b)
82
 
83
- pixels[x, y] = (min(255, tr), min(255, tg), min(255, tb))
 
 
 
 
84
 
85
  return img
86
 
87
- def apply_vintage_filter(image_url: str, output_format: str = 'JPEG') -> str:
88
- """
89
- Apply a vintage effect combining multiple filters.
90
-
91
- Args:
92
- image_url: The URL of the input image.
93
- output_format: The desired output format.
94
-
95
- Returns:
96
- The vintage-filtered image as a base64-encoded string.
97
- """
98
-
99
- response = requests.get(image_url, timeout=30)
100
- response.raise_for_status()
101
-
102
- img = Image.open(BytesIO(response.content))
103
-
104
- if img.mode != 'RGB':
105
- img = img.convert('RGB')
106
-
107
  contrast_enhancer = ImageEnhance.Contrast(img)
108
  img = contrast_enhancer.enhance(0.8)
109
 
110
  brightness_enhancer = ImageEnhance.Brightness(img)
111
  img = brightness_enhancer.enhance(1.1)
112
 
113
- img = apply_sepia_filter(img)
114
 
115
  img = img.filter(ImageFilter.GaussianBlur(radius=0.5))
116
 
117
- output = BytesIO()
118
- img.save(output, format=output_format, quality=90)
119
- output.seek(0)
120
-
121
- encoded_image = base64.b64encode(output.getvalue()).decode('utf-8')
122
-
123
- return encoded_image
 
3
  import requests
4
  import base64
5
 
6
+ def apply_filter_direct(image, filter_type, intensity=1.0):
7
  """
8
+ Apply filters directly to PIL image without base64 conversion
 
 
 
 
 
 
 
 
 
 
9
  """
10
+ if image is None:
11
+ return None
12
 
13
+ try:
14
+ print(f"Applying filter: {filter_type} with intensity: {intensity}")
15
+ img = image.copy()
16
+
17
+ if img.mode != 'RGB':
18
+ img = img.convert('RGB')
19
+
20
+ if filter_type == 'blur':
21
+ img = img.filter(ImageFilter.GaussianBlur(radius=max(0.1, intensity)))
22
+ elif filter_type == 'sharpen':
23
+ if intensity <= 1.0:
24
+ img = img.filter(ImageFilter.SHARPEN)
25
+ else:
26
+ img = img.filter(ImageFilter.UnsharpMask(radius=2, percent=int(intensity * 150), threshold=3))
27
+ elif filter_type == 'emboss':
28
+ img = img.filter(ImageFilter.EMBOSS)
29
+ elif filter_type == 'edge':
30
+ img = img.filter(ImageFilter.FIND_EDGES)
31
+ elif filter_type == 'smooth':
32
+ img = img.filter(ImageFilter.SMOOTH_MORE)
33
+ elif filter_type == 'brightness':
34
+ enhancer = ImageEnhance.Brightness(img)
35
+ img = enhancer.enhance(max(0.1, intensity))
36
+ elif filter_type == 'contrast':
37
+ enhancer = ImageEnhance.Contrast(img)
38
+ img = enhancer.enhance(max(0.1, intensity))
39
+ elif filter_type == 'saturation':
40
+ enhancer = ImageEnhance.Color(img)
41
+ img = enhancer.enhance(max(0.1, intensity))
42
+ elif filter_type == 'sepia':
43
+ img = apply_sepia_filter_direct(img)
44
+ elif filter_type == 'grayscale' or filter_type == 'black_white':
45
+ img = img.convert('L').convert('RGB')
46
+ elif filter_type == 'vintage':
47
+ img = apply_vintage_effect_direct(img)
48
+ else:
49
+ print(f"Unknown filter type: {filter_type}")
50
+ return image
51
+
52
+ print(f"Filter applied successfully")
53
+ return img
54
+
55
+ except Exception as e:
56
+ print(f"Error applying filter: {e}")
57
+ import traceback
58
+ traceback.print_exc()
59
+ return image
60
+
61
+ def apply_sepia_filter_direct(img):
62
+ """Apply sepia tone effect to an image."""
63
+ width, height = img.size
64
+ pixels = img.load()
65
 
66
+ for y in range(height):
67
+ for x in range(width):
68
+ r, g, b = pixels[x, y]
69
+
70
+ tr = int(0.393 * r + 0.769 * g + 0.189 * b)
71
+ tg = int(0.349 * r + 0.686 * g + 0.168 * b)
72
+ tb = int(0.272 * r + 0.534 * g + 0.131 * b)
73
+
74
+ tr = min(255, tr)
75
+ tg = min(255, tg)
76
+ tb = min(255, tb)
77
+
78
+ pixels[x, y] = (tr, tg, tb)
79
 
80
+ return img
81
+
82
+ def apply_vintage_effect_direct(img):
83
+ """Apply a vintage effect combining multiple filters."""
84
+ contrast_enhancer = ImageEnhance.Contrast(img)
85
+ img = contrast_enhancer.enhance(0.8)
86
 
87
+ brightness_enhancer = ImageEnhance.Brightness(img)
88
+ img = brightness_enhancer.enhance(1.1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
+ img = apply_sepia_filter_direct(img)
 
 
91
 
92
+ img = img.filter(ImageFilter.GaussianBlur(radius=0.5))
93
 
94
+ return img
95
 
96
+ def apply_sepia_filter_direct(img):
97
+ """Apply sepia tone effect to an image."""
 
 
 
 
 
 
 
 
 
 
98
  width, height = img.size
99
+ pixels = img.load()
100
 
101
  for y in range(height):
102
  for x in range(width):
 
106
  tg = int(0.349 * r + 0.686 * g + 0.168 * b)
107
  tb = int(0.272 * r + 0.534 * g + 0.131 * b)
108
 
109
+ tr = min(255, tr)
110
+ tg = min(255, tg)
111
+ tb = min(255, tb)
112
+
113
+ pixels[x, y] = (tr, tg, tb)
114
 
115
  return img
116
 
117
+ def apply_vintage_effect_direct(img):
118
+ """Apply a vintage effect combining multiple filters."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  contrast_enhancer = ImageEnhance.Contrast(img)
120
  img = contrast_enhancer.enhance(0.8)
121
 
122
  brightness_enhancer = ImageEnhance.Brightness(img)
123
  img = brightness_enhancer.enhance(1.1)
124
 
125
+ img = apply_sepia_filter_direct(img)
126
 
127
  img = img.filter(ImageFilter.GaussianBlur(radius=0.5))
128
 
129
+ return img
 
 
 
 
 
 
src/utils/compress.py CHANGED
@@ -2,7 +2,7 @@ from PIL import Image
2
  import os
3
  from typing import Literal, Optional
4
 
5
- def compress_image(
6
  input_path: str,
7
  output_path: str,
8
  quality: int = 85,
@@ -11,15 +11,7 @@ def compress_image(
11
  max_height: Optional[int] = None
12
  ) -> str:
13
  """
14
- Compress an image file.
15
-
16
- Args:
17
- input_path: Path to input image
18
- output_path: Path for compressed output
19
- quality: Compression quality 1-95 (for JPEG/WEBP)
20
- format: Output format
21
- max_width: Maximum width (optional)
22
- max_height: Maximum height (optional)
23
  """
24
  try:
25
  if not os.path.splitext(output_path)[1]:
@@ -46,4 +38,22 @@ def compress_image(
46
  return f"โœ… Compressed successfully!\nOriginal: {original_size:.2f}MB โ†’ Compressed: {compressed_size:.2f}MB\nReduction: {reduction:.1f}%"
47
 
48
  except Exception as e:
49
- return f"โŒ Error: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import os
3
  from typing import Literal, Optional
4
 
5
+ def compress_image_file(
6
  input_path: str,
7
  output_path: str,
8
  quality: int = 85,
 
11
  max_height: Optional[int] = None
12
  ) -> str:
13
  """
14
+ Compress an image file from disk.
 
 
 
 
 
 
 
 
15
  """
16
  try:
17
  if not os.path.splitext(output_path)[1]:
 
38
  return f"โœ… Compressed successfully!\nOriginal: {original_size:.2f}MB โ†’ Compressed: {compressed_size:.2f}MB\nReduction: {reduction:.1f}%"
39
 
40
  except Exception as e:
41
+ return f"โŒ Error: {str(e)}"
42
+
43
+ def compress_image_memory(image: Image.Image, quality: int = 80, format: str = "JPEG") -> Image.Image:
44
+ """
45
+ Compress an image in memory and return the compressed image.
46
+ """
47
+ if format == "JPEG" and image.mode in ("RGBA", "P"):
48
+ image = image.convert("RGB")
49
+
50
+ output = BytesIO()
51
+ save_kwargs = {"format": format, "optimize": True}
52
+
53
+ if format in ["JPEG", "WEBP"]:
54
+ save_kwargs["quality"] = quality
55
+
56
+ image.save(output, **save_kwargs)
57
+ output.seek(0)
58
+
59
+ return Image.open(output)