1212
1313def resize_image (image_data : bytes ,
1414 target_width : int = 951 ,
15- target_height : int = 1268 ) -> bytes :
15+ target_height : int = 1268 ,
16+ allow_upscale : bool = False ) -> bytes :
1617 """
1718 Resize an image to fit within target dimensions while preserving aspect ratio.
1819 No padding, no distortion - pure proportional scaling.
20+ Preserves original format when possible.
1921
2022 Args:
2123 image_data: Raw image bytes
2224 target_width: Target width in pixels
2325 target_height: Target height in pixels
26+ allow_upscale: Whether to allow making the image larger than original
2427
2528 Returns:
26- Resized image as JPEG bytes
29+ Resized image bytes in original format (or JPEG if format cannot be preserved)
2730 """
2831 image = Image .open (io .BytesIO (image_data ))
2932 current_width , current_height = image .size
33+ original_format = image .format # Store original format
3034
3135 # Calculate scaling factor to fit within bounds while preserving aspect ratio
3236 width_ratio = target_width / current_width
3337 height_ratio = target_height / current_height
3438 scale_factor = min (width_ratio , height_ratio ) # Fit within bounds
3539
36- # Only resize if we're making it smaller
37- if scale_factor < 1.0 :
40+ # Determine if resizing is needed
41+ needs_resize = (scale_factor < 1.0 ) or (allow_upscale and scale_factor > 1.0 )
42+
43+ if needs_resize :
3844 new_width = int (current_width * scale_factor )
3945 new_height = int (current_height * scale_factor )
4046 logger .info (f"Resizing image from { current_width } x{ current_height } to { new_width } x{ new_height } (scale: { scale_factor :.3f} )" )
4147 image = image .resize ((new_width , new_height ), Image .LANCZOS )
48+
49+ # Save in original format if possible
50+ img_byte_array = io .BytesIO ()
51+
52+ # Determine save format - use original if available, otherwise JPEG
53+ if original_format and original_format in ['JPEG' , 'PNG' , 'GIF' , 'BMP' , 'TIFF' , 'WEBP' ]:
54+ save_format = original_format
55+ else :
56+ save_format = 'JPEG'
57+ logger .info (f"Converting from { original_format or 'unknown' } to JPEG" )
58+
59+ # Prepare save parameters
60+ save_kwargs = {"format" : save_format }
61+
62+ # Add quality parameters for JPEG
63+ if save_format in ['JPEG' , 'JPG' ]:
64+ save_kwargs ["quality" ] = 95 # High quality
65+ save_kwargs ["optimize" ] = True
66+
67+ # Handle format-specific requirements
68+ if save_format == 'PNG' and image .mode not in ['RGBA' , 'LA' , 'L' , 'P' ]:
69+ # PNG requires specific modes
70+ if image .mode == 'CMYK' :
71+ image = image .convert ('RGB' )
72+
73+ image .save (img_byte_array , ** save_kwargs )
74+ return img_byte_array .getvalue ()
4275 else :
43- logger .debug (f"Image { current_width } x{ current_height } already fits within { target_width } x{ target_height } , no resizing needed" )
44-
45- # Convert to JPEG bytes
46- img_byte_array = io .BytesIO ()
47- image .save (img_byte_array , format = "JPEG" )
48- return img_byte_array .getvalue ()
76+ # No resizing needed - return original data unchanged
77+ logger .info (f"Image { current_width } x{ current_height } already fits within { target_width } x{ target_height } , returning original" )
78+ return image_data
4979
5080def prepare_image (image_source : Union [str , bytes ],
5181 target_width : int = 951 ,
52- target_height : int = 1268 ) -> bytes :
82+ target_height : int = 1268 ,
83+ allow_upscale : bool = False ) -> bytes :
5384 """
5485 Prepare an image for model input from either S3 URI or raw bytes
5586
5687 Args:
5788 image_source: Either an S3 URI (s3://bucket/key) or raw image bytes
5889 target_width: Target width in pixels
5990 target_height: Target height in pixels
91+ allow_upscale: Whether to allow making the image larger than original
6092
6193 Returns:
62- Processed image as JPEG bytes ready for model input
94+ Processed image bytes ready for model input (preserves format when possible)
6395 """
6496 # Get the image data
6597 if isinstance (image_source , str ) and image_source .startswith ('s3://' ):
@@ -70,7 +102,7 @@ def prepare_image(image_source: Union[str, bytes],
70102 raise ValueError (f"Invalid image source: { type (image_source )} . Must be S3 URI or bytes." )
71103
72104 # Resize and process
73- return resize_image (image_data , target_width , target_height )
105+ return resize_image (image_data , target_width , target_height , allow_upscale )
74106
75107def apply_adaptive_binarization (image_data : bytes ) -> bytes :
76108 """
0 commit comments