Professional Noob commited on
Commit
be261a8
·
verified ·
1 Parent(s): 597f648

Update qwenimage/pipeline_qwenimage_edit_plus.py

Browse files
qwenimage/pipeline_qwenimage_edit_plus.py CHANGED
@@ -44,7 +44,7 @@ EXAMPLE_DOC_STRING = """
44
  Examples:
45
  ```py
46
  >>> import torch
47
- >>> from PIL import Image, ImageOps
48
  >>> from diffusers import QwenImageEditPlusPipeline
49
  >>> from diffusers.utils import load_image
50
 
@@ -67,23 +67,25 @@ CONDITION_IMAGE_SIZE = 384 * 384
67
  VAE_IMAGE_SIZE = 1024 * 1024
68
 
69
 
 
70
  def pad_to_aspect(img: Image.Image, target_w: int, target_h: int) -> Image.Image:
71
- """Pad (letterbox) to target aspect ratio without warping, then return padded image."""
72
  return ImageOps.pad(
73
- img,
74
  (int(target_w), int(target_h)),
75
  method=Image.Resampling.LANCZOS,
76
  color=(0, 0, 0),
77
  centering=(0.5, 0.5),
78
  )
79
 
 
80
  def choose_condition_area(canvas_area: int, base_area: int = CONDITION_IMAGE_SIZE) -> int:
81
  """Choose a conditioning target area derived from canvas area with sensible bounds."""
82
- # Scale conditioning area roughly with canvas, anchored at 384^2 when canvas is ~1MP.
83
  scaled = int(canvas_area * (base_area / (1024 * 1024)))
84
  return int(min(base_area, max(256 * 256, scaled)))
85
 
86
 
 
87
  # Copied from diffusers.pipelines.qwenimage.pipeline_qwenimage.calculate_shift
88
  def calculate_shift(
89
  image_seq_len,
@@ -685,42 +687,40 @@ class QwenImageEditPlusPipeline(DiffusionPipeline, QwenImageLoraLoaderMixin):
685
 
686
  device = self._execution_device
687
  # 3. Preprocess image
 
688
  if image is not None and not (isinstance(image, torch.Tensor) and image.size(1) == self.latent_channels):
689
  if not isinstance(image, list):
690
  image = [image]
 
 
 
 
 
 
691
  condition_image_sizes = []
692
  condition_images = []
693
  vae_image_sizes = []
694
  vae_images = []
695
 
696
- # Decide which images participate in the VAE latent stream.
697
- # Default: all images. This can be overridden (e.g. extras conditioning-only).
698
  if vae_image_indices is None:
699
  vae_image_indices = list(range(len(image)))
700
  vae_set = set(int(i) for i in vae_image_indices)
701
 
702
- # Conditioning resolution: derived from canvas unless explicitly provided.
703
- canvas_area = int(width) * int(height)
704
- cond_area = int(condition_area) if condition_area is not None else choose_condition_area(canvas_area)
705
- cond_w, cond_h = calculate_dimensions(cond_area, float(width) / float(height))
706
-
707
  for idx, img in enumerate(image):
708
- # Always ensure RGB PIL
709
- if isinstance(img, Image.Image):
710
- pil = img.convert("RGB")
711
- else:
712
- pil = img
713
 
 
714
  if pad_to_canvas and isinstance(pil, Image.Image):
715
  pil = pad_to_aspect(pil, int(width), int(height))
716
 
717
- # Conditioning (VL) path: always include
718
  condition_image_sizes.append((cond_w, cond_h))
719
  condition_images.append(self.image_processor.resize(pil, cond_h, cond_w))
720
 
721
- # VAE latent path: include only selected indices
722
  if idx in vae_set:
723
- # IMPORTANT: VAE preprocessing uses the *canvas* size to avoid drift/zoom.
724
  vae_image_sizes.append((int(width), int(height)))
725
  vae_images.append(self.image_processor.preprocess(pil, int(height), int(width)).unsqueeze(2))
726
 
 
44
  Examples:
45
  ```py
46
  >>> import torch
47
+ >>> from PIL import Image
48
  >>> from diffusers import QwenImageEditPlusPipeline
49
  >>> from diffusers.utils import load_image
50
 
 
67
  VAE_IMAGE_SIZE = 1024 * 1024
68
 
69
 
70
+
71
  def pad_to_aspect(img: Image.Image, target_w: int, target_h: int) -> Image.Image:
72
+ """Pad (letterbox) to target aspect ratio without warping."""
73
  return ImageOps.pad(
74
+ img.convert("RGB"),
75
  (int(target_w), int(target_h)),
76
  method=Image.Resampling.LANCZOS,
77
  color=(0, 0, 0),
78
  centering=(0.5, 0.5),
79
  )
80
 
81
+
82
  def choose_condition_area(canvas_area: int, base_area: int = CONDITION_IMAGE_SIZE) -> int:
83
  """Choose a conditioning target area derived from canvas area with sensible bounds."""
 
84
  scaled = int(canvas_area * (base_area / (1024 * 1024)))
85
  return int(min(base_area, max(256 * 256, scaled)))
86
 
87
 
88
+
89
  # Copied from diffusers.pipelines.qwenimage.pipeline_qwenimage.calculate_shift
90
  def calculate_shift(
91
  image_seq_len,
 
687
 
688
  device = self._execution_device
689
  # 3. Preprocess image
690
+
691
  if image is not None and not (isinstance(image, torch.Tensor) and image.size(1) == self.latent_channels):
692
  if not isinstance(image, list):
693
  image = [image]
694
+
695
+ # Conditioning resolution derived from canvas area (or overridden)
696
+ canvas_area = int(width) * int(height)
697
+ cond_area = int(condition_area) if condition_area is not None else choose_condition_area(canvas_area)
698
+ cond_w, cond_h = calculate_dimensions(cond_area, float(width) / float(height))
699
+
700
  condition_image_sizes = []
701
  condition_images = []
702
  vae_image_sizes = []
703
  vae_images = []
704
 
705
+ # Which images participate in the VAE latent stream (default: all)
 
706
  if vae_image_indices is None:
707
  vae_image_indices = list(range(len(image)))
708
  vae_set = set(int(i) for i in vae_image_indices)
709
 
 
 
 
 
 
710
  for idx, img in enumerate(image):
711
+ # Ensure PIL RGB for padding/resize stability
712
+ pil = img.convert("RGB") if isinstance(img, Image.Image) else img
 
 
 
713
 
714
+ # Strong recommendation: pad to canvas aspect to avoid warping
715
  if pad_to_canvas and isinstance(pil, Image.Image):
716
  pil = pad_to_aspect(pil, int(width), int(height))
717
 
718
+ # Conditioning (VL) path: always include, using a canvas-derived size
719
  condition_image_sizes.append((cond_w, cond_h))
720
  condition_images.append(self.image_processor.resize(pil, cond_h, cond_w))
721
 
722
+ # VAE path: include only selected indices, and use the *canvas* size
723
  if idx in vae_set:
 
724
  vae_image_sizes.append((int(width), int(height)))
725
  vae_images.append(self.image_processor.preprocess(pil, int(height), int(width)).unsqueeze(2))
726