3636 InputImageContent ,
3737 InputTextContent ,
3838)
39- from max .pipelines .architectures .qwen2_5vl .image_processor import (
40- Qwen2_5VLPromptImageProcessor ,
41- )
4239from max .pipelines .core import PixelContext
4340from transformers import AutoTokenizer
4441
4542from .diffusion_schedulers import SchedulerFactory
43+ from .qwen_image_processor import Qwen2_5VLPromptImageProcessor
4644
4745if TYPE_CHECKING :
4846 import PIL .Image
@@ -364,6 +362,37 @@ def _randn_tensor(
364362 rng = np .random .RandomState (seed )
365363 return rng .standard_normal (shape ).astype (np .float32 )
366364
365+ @staticmethod
366+ def _resize_with_center_crop (
367+ image : PIL .Image .Image , target_width : int , target_height : int
368+ ) -> PIL .Image .Image :
369+ ratio = target_width / target_height
370+ src_ratio = image .width / image .height
371+
372+ src_w = (
373+ target_width
374+ if ratio > src_ratio
375+ else image .width * target_height // image .height
376+ )
377+ src_h = (
378+ target_height
379+ if ratio <= src_ratio
380+ else image .height * target_width // image .width
381+ )
382+
383+ resized = image .resize (
384+ (src_w , src_h ), resample = PIL .Image .Resampling .LANCZOS
385+ )
386+ canvas = PIL .Image .new ("RGB" , (target_width , target_height ))
387+ canvas .paste (
388+ resized ,
389+ box = (
390+ target_width // 2 - src_w // 2 ,
391+ target_height // 2 - src_h // 2 ,
392+ ),
393+ )
394+ return canvas
395+
367396 def _preprocess_input_image (
368397 self ,
369398 image : PIL .Image .Image | npt .NDArray [np .uint8 ],
@@ -372,13 +401,14 @@ def _preprocess_input_image(
372401 ) -> PIL .Image .Image :
373402 """Preprocess input image for image-to-image generation.
374403
375- This method preprocesses images for condition-based image-to-image generation.
376- Matching diffusers behavior: resizes large images, ensures dimensions are multiples
377- of vae_scale_factor * 2, and optionally resizes to target dimensions.
404+ Matches the shared image-to-image behavior by default:
405+ - cap image area when needed
406+ - floor dimensions to multiples of vae_scale_factor * 2
407+ - apply aspect-ratio preserving center-crop resize to the floored size
378408
379- Note: This is a simplified version compared to pipeline_flux2.py which uses
380- image_processor.preprocess. This tokenizer-level preprocessing is sufficient
381- for the Max framework's condition-based approach .
409+ Qwen image edit is the exception. It preserves the requested output
410+ size separately and uses explicit target resize here only for the
411+ condition image branch .
382412
383413 Args:
384414 image: PIL Image or numpy array (uint8) to preprocess.
@@ -388,12 +418,9 @@ def _preprocess_input_image(
388418 Returns:
389419 Preprocessed PIL Image with adjusted dimensions.
390420 """
391- import PIL .Image
392-
393421 if isinstance (image , np .ndarray ):
394422 image = PIL .Image .fromarray (image .astype (np .uint8 ))
395423
396- # Ensure RGB mode (e.g. RGBA PNGs would break 3-channel VAE input)
397424 if image .mode != "RGB" :
398425 image = image .convert ("RGB" )
399426
@@ -412,18 +439,34 @@ def _preprocess_input_image(
412439 )
413440 image_width , image_height = image .size
414441
415- image_width = (image_width // multiple_of ) * multiple_of
416- image_height = (image_height // multiple_of ) * multiple_of
442+ if target_height is None :
443+ image_height = max (
444+ (image_height // multiple_of ) * multiple_of , multiple_of
445+ )
446+ else :
447+ image_height = max (
448+ (target_height // multiple_of ) * multiple_of , multiple_of
449+ )
417450
418- if target_height is not None :
419- image_height = (target_height // multiple_of ) * multiple_of
420- if target_width is not None :
421- image_width = (target_width // multiple_of ) * multiple_of
451+ if target_width is None :
452+ image_width = max (
453+ (image_width // multiple_of ) * multiple_of , multiple_of
454+ )
455+ else :
456+ image_width = max (
457+ (target_width // multiple_of ) * multiple_of , multiple_of
458+ )
422459
423460 if image .size != (image_width , image_height ):
424- image = image .resize (
425- (image_width , image_height ), PIL .Image .Resampling .LANCZOS
426- )
461+ if target_height is None and target_width is None :
462+ image = self ._resize_with_center_crop (
463+ image , image_width , image_height
464+ )
465+ else :
466+ image = image .resize (
467+ (image_width , image_height ),
468+ PIL .Image .Resampling .LANCZOS ,
469+ )
427470
428471 return image
429472
@@ -457,11 +500,11 @@ def _prepare_qwen_edit_condition_images(
457500 self ._resize_image_to_area (image , QWEN_EDIT_PROMPT_IMAGE_SIZE )
458501 for image in input_images
459502 ]
460- vae_images = [
503+ vae_condition_images = [
461504 self ._resize_image_to_area (image , QWEN_EDIT_VAE_IMAGE_SIZE )
462505 for image in input_images
463506 ]
464- return prompt_images , vae_images
507+ return prompt_images , vae_condition_images
465508
466509 def _prepare_qwen_edit_tokens (
467510 self ,
@@ -1049,9 +1092,9 @@ async def new_context(
10491092 )
10501093
10511094 prompt_images : list [npt .NDArray [np .uint8 ]] | None = None
1052- vae_images : list [npt .NDArray [np .uint8 ]] | None = None
1095+ vae_condition_images : list [npt .NDArray [np .uint8 ]] | None = None
10531096 if self ._is_qwen_image_edit_family :
1054- prompt_images , vae_images = (
1097+ prompt_images , vae_condition_images = (
10551098 self ._prepare_qwen_edit_condition_images (
10561099 preprocessed_image_arrays
10571100 )
@@ -1134,7 +1177,7 @@ async def new_context(
11341177 model_name = request .body .model ,
11351178 input_images = preprocessed_image_arrays ,
11361179 prompt_images = prompt_images ,
1137- vae_images = vae_images ,
1180+ vae_condition_images = vae_condition_images ,
11381181 )
11391182
11401183 for validator in self ._context_validators :
0 commit comments