masking.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. from PIL import Image, ImageFilter, ImageOps
  2. def get_crop_region(mask, pad=0):
  3. """finds a rectangular region that contains all masked ares in an image. Returns (x1, y1, x2, y2) coordinates of the rectangle.
  4. For example, if a user has painted the top-right part of a 512x512 image", the result may be (256, 0, 512, 256)"""
  5. h, w = mask.shape
  6. crop_left = 0
  7. for i in range(w):
  8. if not (mask[:, i] == 0).all():
  9. break
  10. crop_left += 1
  11. crop_right = 0
  12. for i in reversed(range(w)):
  13. if not (mask[:, i] == 0).all():
  14. break
  15. crop_right += 1
  16. crop_top = 0
  17. for i in range(h):
  18. if not (mask[i] == 0).all():
  19. break
  20. crop_top += 1
  21. crop_bottom = 0
  22. for i in reversed(range(h)):
  23. if not (mask[i] == 0).all():
  24. break
  25. crop_bottom += 1
  26. return (
  27. int(max(crop_left-pad, 0)),
  28. int(max(crop_top-pad, 0)),
  29. int(min(w - crop_right + pad, w)),
  30. int(min(h - crop_bottom + pad, h))
  31. )
  32. def expand_crop_region(crop_region, processing_width, processing_height, image_width, image_height):
  33. """expands crop region get_crop_region() to match the ratio of the image the region will processed in; returns expanded region
  34. for example, if user drew mask in a 128x32 region, and the dimensions for processing are 512x512, the region will be expanded to 128x128."""
  35. x1, y1, x2, y2 = crop_region
  36. ratio_crop_region = (x2 - x1) / (y2 - y1)
  37. ratio_processing = processing_width / processing_height
  38. if ratio_crop_region > ratio_processing:
  39. desired_height = (x2 - x1) / ratio_processing
  40. desired_height_diff = int(desired_height - (y2-y1))
  41. y1 -= desired_height_diff//2
  42. y2 += desired_height_diff - desired_height_diff//2
  43. if y2 >= image_height:
  44. diff = y2 - image_height
  45. y2 -= diff
  46. y1 -= diff
  47. if y1 < 0:
  48. y2 -= y1
  49. y1 -= y1
  50. if y2 >= image_height:
  51. y2 = image_height
  52. else:
  53. desired_width = (y2 - y1) * ratio_processing
  54. desired_width_diff = int(desired_width - (x2-x1))
  55. x1 -= desired_width_diff//2
  56. x2 += desired_width_diff - desired_width_diff//2
  57. if x2 >= image_width:
  58. diff = x2 - image_width
  59. x2 -= diff
  60. x1 -= diff
  61. if x1 < 0:
  62. x2 -= x1
  63. x1 -= x1
  64. if x2 >= image_width:
  65. x2 = image_width
  66. return x1, y1, x2, y2
  67. def fill(image, mask):
  68. """fills masked regions with colors from image using blur. Not extremely effective."""
  69. image_mod = Image.new('RGBA', (image.width, image.height))
  70. image_masked = Image.new('RGBa', (image.width, image.height))
  71. image_masked.paste(image.convert("RGBA").convert("RGBa"), mask=ImageOps.invert(mask.convert('L')))
  72. image_masked = image_masked.convert('RGBa')
  73. for radius, repeats in [(256, 1), (64, 1), (16, 2), (4, 4), (2, 2), (0, 1)]:
  74. blurred = image_masked.filter(ImageFilter.GaussianBlur(radius)).convert('RGBA')
  75. for _ in range(repeats):
  76. image_mod.alpha_composite(blurred)
  77. return image_mod.convert("RGB")