File size: 6,599 Bytes
fe6327d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import glob
import os
import cv2
import argparse
import shutil
import math
from PIL import Image
import numpy as np


def resize_images(src_img_folder, dst_img_folder, max_resolution="512x512", divisible_by=2, interpolation=None, save_as_png=False, copy_associated_files=False):
  # Split the max_resolution string by "," and strip any whitespaces
  max_resolutions = [res.strip() for res in max_resolution.split(',')]

  # # Calculate max_pixels from max_resolution string
  # max_pixels = int(max_resolution.split("x")[0]) * int(max_resolution.split("x")[1])

  # Create destination folder if it does not exist
  if not os.path.exists(dst_img_folder):
    os.makedirs(dst_img_folder)

  # Select interpolation method
  if interpolation == 'lanczos4':
    cv2_interpolation = cv2.INTER_LANCZOS4
  elif interpolation == 'cubic':
    cv2_interpolation = cv2.INTER_CUBIC
  else:
    cv2_interpolation = cv2.INTER_AREA

  # Iterate through all files in src_img_folder
  img_exts = (".png", ".jpg", ".jpeg", ".webp", ".bmp")                   # copy from train_util.py
  for filename in os.listdir(src_img_folder):
    # Check if the image is png, jpg or webp etc...
    if not filename.endswith(img_exts):
      # Copy the file to the destination folder if not png, jpg or webp etc (.txt or .caption or etc.)
      shutil.copy(os.path.join(src_img_folder, filename), os.path.join(dst_img_folder, filename))
      continue

    # Load image
    image = Image.open(os.path.join(src_img_folder, filename))
    if not image.mode == "RGB":
      image = image.convert("RGB")
    img = np.array(image, np.uint8)

    base, _ = os.path.splitext(filename)
    for max_resolution in max_resolutions:
      # Calculate max_pixels from max_resolution string
      max_pixels = int(max_resolution.split("x")[0]) * int(max_resolution.split("x")[1])

      # Calculate current number of pixels
      current_pixels = img.shape[0] * img.shape[1]

      # Calculate current resolution
      current_resolution = (img.shape[0], img.shape[1])
      
      # Calculate target resolution
      target_resolution = (int(max_resolution.split("x")[0]), int(max_resolution.split("x")[1]))

      # Skip to the next image if the current resolution is less than the target resolution
      if current_resolution[0] < target_resolution[0] or current_resolution[1] < target_resolution[1]:
        print(f"Skipped image: {filename} as its resolution is smaller than target resolution")
        continue

      # Check if the image needs resizing
      if current_pixels > max_pixels:
        # Calculate scaling factor
        scale_factor = max_pixels / current_pixels

        # Calculate new dimensions
        new_height = int(img.shape[0] * math.sqrt(scale_factor))
        new_width = int(img.shape[1] * math.sqrt(scale_factor))

        # Resize image
        img = cv2.resize(img, (new_width, new_height), interpolation=cv2_interpolation)
      else:
        new_height, new_width = img.shape[0:2]

      # Calculate the new height and width that are divisible by divisible_by (with/without resizing)
      new_height = new_height if new_height % divisible_by == 0 else new_height - new_height % divisible_by
      new_width = new_width if new_width % divisible_by == 0 else new_width - new_width % divisible_by

      # Center crop the image to the calculated dimensions
      y = int((img.shape[0] - new_height) / 2)
      x = int((img.shape[1] - new_width) / 2)
      img = img[y:y + new_height, x:x + new_width]

      # Split filename into base and extension
      new_filename = base + '+' + max_resolution + ('.png' if save_as_png else '.jpg')

      # Save resized image in dst_img_folder
      # cv2.imwrite(os.path.join(dst_img_folder, new_filename), img, [cv2.IMWRITE_JPEG_QUALITY, 100])
      image = Image.fromarray(img)
      image.save(os.path.join(dst_img_folder, new_filename), quality=100)

      proc = "Resized" if current_pixels > max_pixels else "Saved"
      print(f"{proc} image: {filename} with size {img.shape[0]}x{img.shape[1]} as {new_filename}")

    # If other files with same basename, copy them with resolution suffix
    if copy_associated_files:
      asoc_files = glob.glob(os.path.join(src_img_folder, base + ".*"))
      for asoc_file in asoc_files:
        ext = os.path.splitext(asoc_file)[1]
        if ext in img_exts:
          continue
        for max_resolution in max_resolutions:
          new_asoc_file = base + '+' + max_resolution + ext
          print(f"Copy {asoc_file} as {new_asoc_file}")
          shutil.copy(os.path.join(src_img_folder, asoc_file), os.path.join(dst_img_folder, new_asoc_file))


def setup_parser() -> argparse.ArgumentParser:
  parser = argparse.ArgumentParser(
      description='Resize images in a folder to a specified max resolution(s) / 指定されたフォルダ内の画像を指定した最大画像サイズ(面積)以下にアスペクト比を維持したままリサイズします')
  parser.add_argument('src_img_folder', type=str, help='Source folder containing the images / 元画像のフォルダ')
  parser.add_argument('dst_img_folder', type=str, help='Destination folder to save the resized images / リサイズ後の画像を保存するフォルダ')
  parser.add_argument('--max_resolution', type=str,
                      help='Maximum resolution(s) in the format "512x512,384x384, etc, etc" / 最大画像サイズをカンマ区切りで指定 ("512x512,384x384, etc, etc" など)', default="512x512,384x384,256x256,128x128")
  parser.add_argument('--divisible_by', type=int,
                      help='Ensure new dimensions are divisible by this value / リサイズ後の画像のサイズをこの値で割り切れるようにします', default=1)
  parser.add_argument('--interpolation', type=str, choices=['area', 'cubic', 'lanczos4'],
                      default='area', help='Interpolation method for resizing / リサイズ時の補完方法')
  parser.add_argument('--save_as_png', action='store_true', help='Save as png format / png形式で保存')
  parser.add_argument('--copy_associated_files', action='store_true',
                      help='Copy files with same base name to images (captions etc) / 画像と同じファイル名(拡張子を除く)のファイルもコピーする')

  return parser


def main():
  parser = setup_parser()

  args = parser.parse_args()
  resize_images(args.src_img_folder, args.dst_img_folder, args.max_resolution,
                args.divisible_by, args.interpolation, args.save_as_png, args.copy_associated_files)


if __name__ == '__main__':
  main()