Repository: huggingface/diffusers
File: src/diffusers/modular_pipelines/qwenimage/encoders.py (Python)

Code before:

        if block_state.vae_image is None and block_state.image is None:
            raise ValueError("`vae_image` and `image` cannot be None at the same time")

        vae_image_sizes = None
        if block_state.vae_image is None:
            image = block_state.image
            self.check_inputs(
                height=block_state.height, width=block_state.width, vae_scale_factor=components.vae_scale_factor
            )
            height = block_state.height or components.default_height
            width = block_state.width or components.default_width
            block_state.processed_image = components.image_processor.preprocess(
                image=image, height=height, width=width
            )
        else:
            processed_images = []
            vae_image_sizes = []
            for img in block_state.vae_image:
                width, height = img.size
                vae_width, vae_height, _ = calculate_dimensions(self.vae_image_size, width / height)
                vae_image_sizes.append((vae_width, vae_height))
                processed_images.append(
                    components.image_processor.preprocess(image=img, height=vae_height, width=vae_width)
                )
            block_state.processed_image = torch.stack(processed_images, dim=0).squeeze(1)

        block_state.vae_image_sizes = vae_image_sizes

        self.set_block_state(state, block_state)
        return components, state


class QwenImageVaeEncoderDynamicStep(ModularPipelineBlocks):
    model_name = "qwenimage"

    def __init__(
        self,
        input_name: str = "processed_image",
        output_name: str = "image_latents",
    ):
        """Initialize a VAE encoder step for converting images to latent representations.

        Both the input and output names are configurable so this block can be configured to process to different image
        inputs (e.g., "processed_image" -> "image_latents", "processed_control_image" -> "control_image_latents").

        Args:
            input_name (str, optional): Name of the input image tensor. Defaults to "processed_image".
                Examples: "processed_image" or "processed_control_image"
            output_name (str, optional): Name of the output latent tensor. Defaults to "image_latents".
                Examples: "image_latents" or "control_image_latents"

Review comment (suggestion):
Each input image can have a different resolution and that is why separate `preprocess` is needed (also following the original implementation). This came into fruition because we started testing with multiple input images.

Code after:

        if block_state.vae_image is None and block_state.image is None:
            raise ValueError("`vae_image` and `image` cannot be None at the same time")

        vae_image_sizes = None
        if block_state.vae_image is None:
            image = block_state.image
            self.check_inputs(
                height=block_state.height, width=block_state.width, vae_scale_factor=components.vae_scale_factor
            )
            height = block_state.height or components.default_height
            width = block_state.width or components.default_width
            block_state.processed_image = components.image_processor.preprocess(
                image=image, height=height, width=width
            )
        else:
            # QwenImage Edit Plus can allow multiple input images with varied resolutions
            processed_images = []
            vae_image_sizes = []
            for img in block_state.vae_image:
                width, height = img.size
                vae_width, vae_height, _ = calculate_dimensions(self.vae_image_size, width / height)
                vae_image_sizes.append((vae_width, vae_height))
                processed_images.append(
                    components.image_processor.preprocess(image=img, height=vae_height, width=vae_width)
                )
            block_state.processed_image = processed_images

        block_state.vae_image_sizes = vae_image_sizes

        self.set_block_state(state, block_state)
        return components, state


class QwenImageVaeEncoderDynamicStep(ModularPipelineBlocks):
    model_name = "qwenimage"

    def __init__(
        self,
        input_name: str = "processed_image",
        output_name: str = "image_latents",
    ):
        """Initialize a VAE encoder step for converting images to latent representations.

        Both the input and output names are configurable so this block can be configured to process to different image
        inputs (e.g., "processed_image" -> "image_latents", "processed_control_image" -> "control_image_latents").

        Args:
            input_name (str, optional): Name of the input image tensor. Defaults to "processed_image".
                Examples: "processed_image" or "processed_control_image"
            output_name (str, optional): Name of the output latent tensor. Defaults to "image_latents".