@@ -52,6 +52,68 @@ class ConfigSelectionStrategy(AutoEnum):
5252 import boto3
5353 import imageio
5454 from botocore .exceptions import ClientError
55+ from PIL import Image
56+
57+ def _compress_image_for_bedrock (image_bytes : bytes , max_size_mb : float = 4.5 ) -> bytes :
58+ """
59+ Compress an image to stay under the specified size limit for Bedrock.
60+
61+ Args:
62+ image_bytes (bytes): Original image bytes
63+ max_size_mb (float): Maximum size in MB (default 4.5 to stay well under 5MB limit)
64+
65+ Returns:
66+ bytes: Compressed image bytes in PNG format
67+ """
68+ max_size_bytes = int (max_size_mb * 1024 * 1024 )
69+
70+ # Load image using PIL for better compression control
71+ image = Image .open (BytesIO (image_bytes ))
72+
73+ # Convert to RGB if necessary (for PNG compatibility)
74+ if image .mode in ('RGBA' , 'LA' , 'P' ):
75+ # Convert RGBA/LA to RGB with white background
76+ background = Image .new ('RGB' , image .size , (255 , 255 , 255 ))
77+ if image .mode == 'P' :
78+ image = image .convert ('RGBA' )
79+ background .paste (image , mask = image .split ()[- 1 ] if image .mode in ('RGBA' , 'LA' ) else None )
80+ image = background
81+ elif image .mode != 'RGB' :
82+ image = image .convert ('RGB' )
83+
84+ # Start with original size and progressively reduce if needed
85+ quality = 95
86+ scale_factor = 1.0
87+
88+ while True :
89+ # Scale down image if needed
90+ if scale_factor < 1.0 :
91+ new_width = int (image .width * scale_factor )
92+ new_height = int (image .height * scale_factor )
93+ resized_image = image .resize ((new_width , new_height ), Image .Resampling .LANCZOS )
94+ else :
95+ resized_image = image
96+
97+ # Compress to PNG with specified quality
98+ output = BytesIO ()
99+ resized_image .save (output , format = 'PNG' , optimize = True )
100+ compressed_bytes = output .getvalue ()
101+
102+ # Check if we're under the size limit
103+ if len (compressed_bytes ) <= max_size_bytes :
104+ return compressed_bytes
105+
106+ # If still too large, try reducing quality or scale
107+ if quality > 60 :
108+ quality -= 10
109+ elif scale_factor > 0.5 :
110+ scale_factor -= 0.1
111+ quality = 95 # Reset quality when scaling
112+ else :
113+ # If we can't compress enough, return what we have
114+ Log .warning (f"Unable to compress image below { max_size_mb } MB limit. "
115+ f"Final size: { len (compressed_bytes ) / (1024 * 1024 ):.2f} MB" )
116+ return compressed_bytes
55117
56118 def process_image_url (image_url : str ) -> Optional [str ]:
57119 """
@@ -74,15 +136,11 @@ def process_image_url(image_url: str) -> Optional[str]:
74136 response .raise_for_status ()
75137 image_bytes = response .content
76138
77- ## Convert the image to a standard format (PNG):
78- image_array = imageio .imread (BytesIO (image_bytes ))
79- memfile = BytesIO ()
80- imageio .imwrite (memfile , image_array , format = "png" )
81- memfile .seek (0 )
82- png_bytes = memfile .read ()
139+ ## Compress the image to stay under Bedrock's 5MB limit:
140+ compressed_bytes = _compress_image_for_bedrock (image_bytes )
83141
84142 ## Encode as base64:
85- base64_image = base64 .b64encode (png_bytes ).decode ("utf-8" )
143+ base64_image = base64 .b64encode (compressed_bytes ).decode ("utf-8" )
86144 return base64_image
87145 except Exception as e :
88146 Log .error (f"Failed to process image from URL { image_url } : { e } " )
@@ -125,15 +183,11 @@ def process_image_s3(s3_path: str, aws_session: Optional[Any] = None) -> Optiona
125183 response = s3_client .get_object (Bucket = bucket_name , Key = object_key )
126184 image_bytes = response ["Body" ].read ()
127185
128- # Convert the image to a standard format (PNG)
129- image_array = imageio .imread (BytesIO (image_bytes ))
130- memfile = BytesIO ()
131- imageio .imwrite (memfile , image_array , format = "png" )
132- memfile .seek (0 )
133- png_bytes = memfile .read ()
186+ # Compress the image to stay under Bedrock's 5MB limit
187+ compressed_bytes = _compress_image_for_bedrock (image_bytes )
134188
135189 # Encode as base64
136- base64_image = base64 .b64encode (png_bytes ).decode ("utf-8" )
190+ base64_image = base64 .b64encode (compressed_bytes ).decode ("utf-8" )
137191 return base64_image
138192 except Exception as e :
139193 Log .error (f"Failed to process image from S3 { s3_path } : { e } " )
0 commit comments