fdsgsfjsfg commited on
Commit
63da0ec
·
verified ·
1 Parent(s): d54626d

fix: pad-to-bucket instead of stretch, preserve exact aspect ratio, edge-fill padding

Browse files
Files changed (1) hide show
  1. app.py +54 -11
app.py CHANGED
@@ -380,23 +380,61 @@ def prepare_images_before_pipe(
380
  pil_images: List[Image.Image],
381
  allow_upscale: bool = UPSCALE_SMALL_IMAGES,
382
  divisible_by: int = 16,
383
- ) -> Tuple[List[Image.Image], int, int]:
 
 
 
 
384
  if not pil_images:
385
  raise ValueError("No input images.")
386
 
387
  base_w, base_h = pil_images[0].size
388
 
389
- # 用 bucket 系统最佳推理分辨率(~1MP匹配模型训练分辨率
390
- # 避免原图分辨率过大/过小导致内容偏移或裁剪
391
- target_w, target_h = pick_best_bucket(base_w, base_h, SAFE_BUCKETS, allow_upscale)
 
 
 
 
 
 
 
 
 
392
 
393
  processed = []
394
  for img in pil_images:
395
- if img.size != (target_w, target_h):
396
- img = img.resize((target_w, target_h), Image.LANCZOS)
397
- processed.append(img)
398
-
399
- return processed, target_w, target_h
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
 
401
 
402
  def extract_pil_from_source(source) -> Image.Image:
@@ -497,7 +535,7 @@ def infer(
497
 
498
  generator = torch.Generator(device=device).manual_seed(int(seed))
499
 
500
- processed_images, width, height = prepare_images_before_pipe(
501
  pil_images, allow_upscale=UPSCALE_SMALL_IMAGES
502
  )
503
 
@@ -519,7 +557,12 @@ def infer(
519
  true_cfg_scale=guidance_scale,
520
  ).images[0]
521
 
522
- # ── 还原到原始尺寸(消除 16 对齐造成的裁剪)──
 
 
 
 
 
523
  if result.size != orig_size:
524
  result = result.resize(orig_size, Image.LANCZOS)
525
 
 
380
  pil_images: List[Image.Image],
381
  allow_upscale: bool = UPSCALE_SMALL_IMAGES,
382
  divisible_by: int = 16,
383
+ ) -> Tuple[List[Image.Image], int, int, tuple]:
384
+ """准备图片:等比缩放 + 补边到最佳 bucket,保留原始比例。
385
+ 返回 (processed_images, width, height, pad_info)
386
+ pad_info = (pad_left, pad_top, content_w, content_h) 用于推理后裁剪补边。
387
+ """
388
  if not pil_images:
389
  raise ValueError("No input images.")
390
 
391
  base_w, base_h = pil_images[0].size
392
 
393
+ # 选最佳 bucket(~1MP,比例最接近)
394
+ bucket_w, bucket_h = pick_best_bucket(base_w, base_h, SAFE_BUCKETS, allow_upscale)
395
+
396
+ # 等比缩放 fit 到 bucket 内(不拉伸)
397
+ scale = min(bucket_w / base_w, bucket_h / base_h)
398
+ content_w = max(divisible_by, round(base_w * scale))
399
+ content_h = max(divisible_by, round(base_h * scale))
400
+
401
+ # 居中补边到 bucket 尺寸
402
+ pad_left = (bucket_w - content_w) // 2
403
+ pad_top = (bucket_h - content_h) // 2
404
+ pad_info = (pad_left, pad_top, content_w, content_h)
405
 
406
  processed = []
407
  for img in pil_images:
408
+ # 等比缩放
409
+ resized = img.resize((content_w, content_h), Image.LANCZOS)
410
+ # 创建 bucket 大小的画布,边缘用镜像填充减少接缝
411
+ canvas = Image.new("RGB", (bucket_w, bucket_h), (0, 0, 0))
412
+ canvas.paste(resized, (pad_left, pad_top))
413
+
414
+ # 用边缘像素填充补边区域(比纯黑效果好)
415
+ import numpy as _np
416
+ arr = np.array(canvas)
417
+ res_arr = np.array(resized)
418
+ # 填充左右
419
+ if pad_left > 0:
420
+ left_col = res_arr[:, 0:1, :]
421
+ arr[pad_top:pad_top+content_h, :pad_left, :] = np.broadcast_to(left_col, (content_h, pad_left, 3))
422
+ right_start = pad_left + content_w
423
+ if right_start < bucket_w:
424
+ right_col = res_arr[:, -1:, :]
425
+ arr[pad_top:pad_top+content_h, right_start:, :] = np.broadcast_to(right_col, (content_h, bucket_w - right_start, 3))
426
+ # 填充上下
427
+ if pad_top > 0:
428
+ top_row = arr[pad_top:pad_top+1, :, :]
429
+ arr[:pad_top, :, :] = np.broadcast_to(top_row, (pad_top, bucket_w, 3))
430
+ bottom_start = pad_top + content_h
431
+ if bottom_start < bucket_h:
432
+ bottom_row = arr[bottom_start-1:bottom_start, :, :]
433
+ arr[bottom_start:, :, :] = np.broadcast_to(bottom_row, (bucket_h - bottom_start, bucket_w, 3))
434
+
435
+ processed.append(Image.fromarray(arr))
436
+
437
+ return processed, bucket_w, bucket_h, pad_info
438
 
439
 
440
  def extract_pil_from_source(source) -> Image.Image:
 
535
 
536
  generator = torch.Generator(device=device).manual_seed(int(seed))
537
 
538
+ processed_images, width, height, pad_info = prepare_images_before_pipe(
539
  pil_images, allow_upscale=UPSCALE_SMALL_IMAGES
540
  )
541
 
 
557
  true_cfg_scale=guidance_scale,
558
  ).images[0]
559
 
560
+ # ── 裁掉补边,还原到原始比例内容区域 ──
561
+ pad_left, pad_top, content_w, content_h = pad_info
562
+ if pad_left > 0 or pad_top > 0 or content_w < width or content_h < height:
563
+ result = result.crop((pad_left, pad_top, pad_left + content_w, pad_top + content_h))
564
+
565
+ # ── 还原到原始尺寸 ──
566
  if result.size != orig_size:
567
  result = result.resize(orig_size, Image.LANCZOS)
568