diff --git a/src/basic_memory/mcp/tools/resource.py b/src/basic_memory/mcp/tools/resource.py index c1383d0e..b3fccf2c 100644 --- a/src/basic_memory/mcp/tools/resource.py +++ b/src/basic_memory/mcp/tools/resource.py @@ -12,14 +12,18 @@ from PIL import Image as PILImage def resize_image(img, max_size): """Resize image maintaining aspect ratio""" + original_dimensions = {"width": img.width, "height": img.height} + if img.width > max_size or img.height > max_size: ratio = min(max_size / img.width, max_size / img.height) new_size = (int(img.width * ratio), int(img.height * ratio)) - logger.debug("Resizing image", original={ - "width": img.width, - "height": img.height - }, target=new_size) + logger.debug("Resizing image", + original=original_dimensions, + target=new_size, + ratio=ratio) return img.resize(new_size, PILImage.Resampling.LANCZOS) + + logger.debug("No resize needed", dimensions=original_dimensions) return img def optimize_image(img, max_output_bytes=500000): # 500KB limit @@ -29,16 +33,22 @@ def optimize_image(img, max_output_bytes=500000): # 500KB limit mode=img.mode, max_bytes=max_output_bytes) - # Convert to RGB if needed - if img.mode in ('RGBA', 'LA') or (img.mode == 'P' and 'transparency' in img.info): - img = img.convert('RGB') - logger.debug("Converted image to RGB") + # Start with higher quality for better color preservation + quality = 60 - quality = 30 - size = 400 + # Make initial size relative to input dimensions + initial_size = min(800, max(img.width, img.height)) + size = initial_size + + original_mode = img.mode + if img.mode in ('RGBA', 'LA') or (img.mode == 'P' and 'transparency' in img.info): + # Keep original mode info for logging + img = img.convert('RGB') + logger.debug("Converted color mode", + from_mode=original_mode, + to_mode='RGB') while True: - # Try current settings buf = io.BytesIO() resized = resize_image(img, size) @@ -49,23 +59,35 @@ def optimize_image(img, max_output_bytes=500000): # 500KB limit subsampling='4:2:0') output_size = buf.getbuffer().nbytes - logger.debug("Optimization attempt", quality=quality, size=size, output_bytes=output_size) + logger.debug("Optimization attempt", + quality=quality, + size=size, + output_bytes=output_size, + target_bytes=max_output_bytes) if output_size < max_output_bytes: + compression_ratio = output_size / max_output_bytes logger.info("Image optimization complete", - final_size=output_size, - quality=quality, - dimensions={"width": resized.width, "height": resized.height}) + final_size=output_size, + quality=quality, + dimensions={"width": resized.width, "height": resized.height}, + compression_ratio=compression_ratio) return buf.getvalue() - # Try lower quality first - if quality > 10: - quality -= 10 - logger.debug("Reducing quality", new_quality=quality) - # Then reduce size if quality is already minimum - elif size > 200: - size -= 50 - logger.debug("Reducing size", new_size=size) + # More gradual quality reduction for better color preservation + if quality > 30: + quality_step = 5 if quality > 50 else 10 + quality -= quality_step + logger.debug("Reducing quality", + new_quality=quality, + step=quality_step) + # Smaller size reduction steps + elif size > 300: + size_step = 25 if size > 600 else 50 + size -= size_step + logger.debug("Reducing size", + new_size=size, + step=size_step) else: logger.warning("Reached minimum optimization parameters", final_size=output_size,