postprocessing_autosized_crop.py 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. from PIL import Image
  2. from modules import scripts_postprocessing, ui_components
  3. import gradio as gr
  4. def center_crop(image: Image, w: int, h: int):
  5. iw, ih = image.size
  6. if ih / h < iw / w:
  7. sw = w * ih / h
  8. box = (iw - sw) / 2, 0, iw - (iw - sw) / 2, ih
  9. else:
  10. sh = h * iw / w
  11. box = 0, (ih - sh) / 2, iw, ih - (ih - sh) / 2
  12. return image.resize((w, h), Image.Resampling.LANCZOS, box)
  13. def multicrop_pic(image: Image, mindim, maxdim, minarea, maxarea, objective, threshold):
  14. iw, ih = image.size
  15. err = lambda w, h: 1 - (lambda x: x if x < 1 else 1 / x)(iw / ih / (w / h))
  16. wh = max(((w, h) for w in range(mindim, maxdim + 1, 64) for h in range(mindim, maxdim + 1, 64)
  17. if minarea <= w * h <= maxarea and err(w, h) <= threshold),
  18. key=lambda wh: (wh[0] * wh[1], -err(*wh))[::1 if objective == 'Maximize area' else -1],
  19. default=None
  20. )
  21. return wh and center_crop(image, *wh)
  22. class ScriptPostprocessingAutosizedCrop(scripts_postprocessing.ScriptPostprocessing):
  23. name = "Auto-sized crop"
  24. order = 4020
  25. def ui(self):
  26. with ui_components.InputAccordion(False, label="Auto-sized crop") as enable:
  27. gr.Markdown('Each image is center-cropped with an automatically chosen width and height.')
  28. with gr.Row():
  29. mindim = gr.Slider(minimum=64, maximum=2048, step=8, label="Dimension lower bound", value=384, elem_id=self.elem_id_suffix("postprocess_multicrop_mindim"))
  30. maxdim = gr.Slider(minimum=64, maximum=2048, step=8, label="Dimension upper bound", value=768, elem_id=self.elem_id_suffix("postprocess_multicrop_maxdim"))
  31. with gr.Row():
  32. minarea = gr.Slider(minimum=64 * 64, maximum=2048 * 2048, step=1, label="Area lower bound", value=64 * 64, elem_id=self.elem_id_suffix("postprocess_multicrop_minarea"))
  33. maxarea = gr.Slider(minimum=64 * 64, maximum=2048 * 2048, step=1, label="Area upper bound", value=640 * 640, elem_id=self.elem_id_suffix("postprocess_multicrop_maxarea"))
  34. with gr.Row():
  35. objective = gr.Radio(["Maximize area", "Minimize error"], value="Maximize area", label="Resizing objective", elem_id=self.elem_id_suffix("postprocess_multicrop_objective"))
  36. threshold = gr.Slider(minimum=0, maximum=1, step=0.01, label="Error threshold", value=0.1, elem_id=self.elem_id_suffix("postprocess_multicrop_threshold"))
  37. return {
  38. "enable": enable,
  39. "mindim": mindim,
  40. "maxdim": maxdim,
  41. "minarea": minarea,
  42. "maxarea": maxarea,
  43. "objective": objective,
  44. "threshold": threshold,
  45. }
  46. def process(self, pp: scripts_postprocessing.PostprocessedImage, enable, mindim, maxdim, minarea, maxarea, objective, threshold):
  47. if not enable:
  48. return
  49. cropped = multicrop_pic(pp.image, mindim, maxdim, minarea, maxarea, objective, threshold)
  50. if cropped is not None:
  51. pp.image = cropped
  52. else:
  53. print(f"skipped {pp.image.width}x{pp.image.height} image (can't find suitable size within error threshold)")