preprocess.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import os
  2. from PIL import Image, ImageOps
  3. import math
  4. import platform
  5. import sys
  6. import tqdm
  7. import time
  8. from modules import shared, images, deepbooru
  9. from modules.paths import models_path
  10. from modules.shared import opts, cmd_opts
  11. from modules.textual_inversion import autocrop
  12. def preprocess(id_task, process_src, process_dst, process_width, process_height, preprocess_txt_action, process_flip, process_split, process_caption, process_caption_deepbooru=False, split_threshold=0.5, overlap_ratio=0.2, process_focal_crop=False, process_focal_crop_face_weight=0.9, process_focal_crop_entropy_weight=0.3, process_focal_crop_edges_weight=0.5, process_focal_crop_debug=False):
  13. try:
  14. if process_caption:
  15. shared.interrogator.load()
  16. if process_caption_deepbooru:
  17. deepbooru.model.start()
  18. preprocess_work(process_src, process_dst, process_width, process_height, preprocess_txt_action, process_flip, process_split, process_caption, process_caption_deepbooru, split_threshold, overlap_ratio, process_focal_crop, process_focal_crop_face_weight, process_focal_crop_entropy_weight, process_focal_crop_edges_weight, process_focal_crop_debug)
  19. finally:
  20. if process_caption:
  21. shared.interrogator.send_blip_to_ram()
  22. if process_caption_deepbooru:
  23. deepbooru.model.stop()
  24. def listfiles(dirname):
  25. return os.listdir(dirname)
  26. class PreprocessParams:
  27. src = None
  28. dstdir = None
  29. subindex = 0
  30. flip = False
  31. process_caption = False
  32. process_caption_deepbooru = False
  33. preprocess_txt_action = None
  34. def save_pic_with_caption(image, index, params: PreprocessParams, existing_caption=None):
  35. caption = ""
  36. if params.process_caption:
  37. caption += shared.interrogator.generate_caption(image)
  38. if params.process_caption_deepbooru:
  39. if len(caption) > 0:
  40. caption += ", "
  41. caption += deepbooru.model.tag_multi(image)
  42. filename_part = params.src
  43. filename_part = os.path.splitext(filename_part)[0]
  44. filename_part = os.path.basename(filename_part)
  45. basename = f"{index:05}-{params.subindex}-{filename_part}"
  46. image.save(os.path.join(params.dstdir, f"{basename}.png"))
  47. if params.preprocess_txt_action == 'prepend' and existing_caption:
  48. caption = existing_caption + ' ' + caption
  49. elif params.preprocess_txt_action == 'append' and existing_caption:
  50. caption = caption + ' ' + existing_caption
  51. elif params.preprocess_txt_action == 'copy' and existing_caption:
  52. caption = existing_caption
  53. caption = caption.strip()
  54. if len(caption) > 0:
  55. with open(os.path.join(params.dstdir, f"{basename}.txt"), "w", encoding="utf8") as file:
  56. file.write(caption)
  57. params.subindex += 1
  58. def save_pic(image, index, params, existing_caption=None):
  59. save_pic_with_caption(image, index, params, existing_caption=existing_caption)
  60. if params.flip:
  61. save_pic_with_caption(ImageOps.mirror(image), index, params, existing_caption=existing_caption)
  62. def split_pic(image, inverse_xy, width, height, overlap_ratio):
  63. if inverse_xy:
  64. from_w, from_h = image.height, image.width
  65. to_w, to_h = height, width
  66. else:
  67. from_w, from_h = image.width, image.height
  68. to_w, to_h = width, height
  69. h = from_h * to_w // from_w
  70. if inverse_xy:
  71. image = image.resize((h, to_w))
  72. else:
  73. image = image.resize((to_w, h))
  74. split_count = math.ceil((h - to_h * overlap_ratio) / (to_h * (1.0 - overlap_ratio)))
  75. y_step = (h - to_h) / (split_count - 1)
  76. for i in range(split_count):
  77. y = int(y_step * i)
  78. if inverse_xy:
  79. splitted = image.crop((y, 0, y + to_h, to_w))
  80. else:
  81. splitted = image.crop((0, y, to_w, y + to_h))
  82. yield splitted
  83. def preprocess_work(process_src, process_dst, process_width, process_height, preprocess_txt_action, process_flip, process_split, process_caption, process_caption_deepbooru=False, split_threshold=0.5, overlap_ratio=0.2, process_focal_crop=False, process_focal_crop_face_weight=0.9, process_focal_crop_entropy_weight=0.3, process_focal_crop_edges_weight=0.5, process_focal_crop_debug=False):
  84. width = process_width
  85. height = process_height
  86. src = os.path.abspath(process_src)
  87. dst = os.path.abspath(process_dst)
  88. split_threshold = max(0.0, min(1.0, split_threshold))
  89. overlap_ratio = max(0.0, min(0.9, overlap_ratio))
  90. assert src != dst, 'same directory specified as source and destination'
  91. os.makedirs(dst, exist_ok=True)
  92. files = listfiles(src)
  93. shared.state.job = "preprocess"
  94. shared.state.textinfo = "Preprocessing..."
  95. shared.state.job_count = len(files)
  96. params = PreprocessParams()
  97. params.dstdir = dst
  98. params.flip = process_flip
  99. params.process_caption = process_caption
  100. params.process_caption_deepbooru = process_caption_deepbooru
  101. params.preprocess_txt_action = preprocess_txt_action
  102. pbar = tqdm.tqdm(files)
  103. for index, imagefile in enumerate(pbar):
  104. params.subindex = 0
  105. filename = os.path.join(src, imagefile)
  106. try:
  107. img = Image.open(filename).convert("RGB")
  108. except Exception:
  109. continue
  110. description = f"Preprocessing [Image {index}/{len(files)}]"
  111. pbar.set_description(description)
  112. shared.state.textinfo = description
  113. params.src = filename
  114. existing_caption = None
  115. existing_caption_filename = os.path.splitext(filename)[0] + '.txt'
  116. if os.path.exists(existing_caption_filename):
  117. with open(existing_caption_filename, 'r', encoding="utf8") as file:
  118. existing_caption = file.read()
  119. if shared.state.interrupted:
  120. break
  121. if img.height > img.width:
  122. ratio = (img.width * height) / (img.height * width)
  123. inverse_xy = False
  124. else:
  125. ratio = (img.height * width) / (img.width * height)
  126. inverse_xy = True
  127. process_default_resize = True
  128. if process_split and ratio < 1.0 and ratio <= split_threshold:
  129. for splitted in split_pic(img, inverse_xy, width, height, overlap_ratio):
  130. save_pic(splitted, index, params, existing_caption=existing_caption)
  131. process_default_resize = False
  132. if process_focal_crop and img.height != img.width:
  133. dnn_model_path = None
  134. try:
  135. dnn_model_path = autocrop.download_and_cache_models(os.path.join(models_path, "opencv"))
  136. except Exception as e:
  137. print("Unable to load face detection model for auto crop selection. Falling back to lower quality haar method.", e)
  138. autocrop_settings = autocrop.Settings(
  139. crop_width = width,
  140. crop_height = height,
  141. face_points_weight = process_focal_crop_face_weight,
  142. entropy_points_weight = process_focal_crop_entropy_weight,
  143. corner_points_weight = process_focal_crop_edges_weight,
  144. annotate_image = process_focal_crop_debug,
  145. dnn_model_path = dnn_model_path,
  146. )
  147. for focal in autocrop.crop_image(img, autocrop_settings):
  148. save_pic(focal, index, params, existing_caption=existing_caption)
  149. process_default_resize = False
  150. if process_default_resize:
  151. img = images.resize_image(1, img, width, height)
  152. save_pic(img, index, params, existing_caption=existing_caption)
  153. shared.state.nextjob()