Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
automatic1111
GitHub Repository: automatic1111/stable-diffusion-webui
Path: blob/master/modules/masking.py
3055 views
1
from PIL import Image, ImageFilter, ImageOps
2
3
4
def get_crop_region_v2(mask, pad=0):
5
"""
6
Finds a rectangular region that contains all masked ares in a mask.
7
Returns None if mask is completely black mask (all 0)
8
9
Parameters:
10
mask: PIL.Image.Image L mode or numpy 1d array
11
pad: int number of pixels that the region will be extended on all sides
12
Returns: (x1, y1, x2, y2) | None
13
14
Introduced post 1.9.0
15
"""
16
mask = mask if isinstance(mask, Image.Image) else Image.fromarray(mask)
17
if box := mask.getbbox():
18
x1, y1, x2, y2 = box
19
return (max(x1 - pad, 0), max(y1 - pad, 0), min(x2 + pad, mask.size[0]), min(y2 + pad, mask.size[1])) if pad else box
20
21
22
def get_crop_region(mask, pad=0):
23
"""
24
Same function as get_crop_region_v2 but handles completely black mask (all 0) differently
25
when mask all black still return coordinates but the coordinates may be invalid ie x2>x1 or y2>y1
26
Notes: it is possible for the coordinates to be "valid" again if pad size is sufficiently large
27
(mask_size.x-pad, mask_size.y-pad, pad, pad)
28
29
Extension developer should use get_crop_region_v2 instead unless for compatibility considerations.
30
"""
31
mask = mask if isinstance(mask, Image.Image) else Image.fromarray(mask)
32
if box := get_crop_region_v2(mask, pad):
33
return box
34
x1, y1 = mask.size
35
x2 = y2 = 0
36
return max(x1 - pad, 0), max(y1 - pad, 0), min(x2 + pad, mask.size[0]), min(y2 + pad, mask.size[1])
37
38
39
def expand_crop_region(crop_region, processing_width, processing_height, image_width, image_height):
40
"""expands crop region get_crop_region() to match the ratio of the image the region will processed in; returns expanded region
41
for example, if user drew mask in a 128x32 region, and the dimensions for processing are 512x512, the region will be expanded to 128x128."""
42
43
x1, y1, x2, y2 = crop_region
44
45
ratio_crop_region = (x2 - x1) / (y2 - y1)
46
ratio_processing = processing_width / processing_height
47
48
if ratio_crop_region > ratio_processing:
49
desired_height = (x2 - x1) / ratio_processing
50
desired_height_diff = int(desired_height - (y2-y1))
51
y1 -= desired_height_diff//2
52
y2 += desired_height_diff - desired_height_diff//2
53
if y2 >= image_height:
54
diff = y2 - image_height
55
y2 -= diff
56
y1 -= diff
57
if y1 < 0:
58
y2 -= y1
59
y1 -= y1
60
if y2 >= image_height:
61
y2 = image_height
62
else:
63
desired_width = (y2 - y1) * ratio_processing
64
desired_width_diff = int(desired_width - (x2-x1))
65
x1 -= desired_width_diff//2
66
x2 += desired_width_diff - desired_width_diff//2
67
if x2 >= image_width:
68
diff = x2 - image_width
69
x2 -= diff
70
x1 -= diff
71
if x1 < 0:
72
x2 -= x1
73
x1 -= x1
74
if x2 >= image_width:
75
x2 = image_width
76
77
return x1, y1, x2, y2
78
79
80
def fill(image, mask):
81
"""fills masked regions with colors from image using blur. Not extremely effective."""
82
83
image_mod = Image.new('RGBA', (image.width, image.height))
84
85
image_masked = Image.new('RGBa', (image.width, image.height))
86
image_masked.paste(image.convert("RGBA").convert("RGBa"), mask=ImageOps.invert(mask.convert('L')))
87
88
image_masked = image_masked.convert('RGBa')
89
90
for radius, repeats in [(256, 1), (64, 1), (16, 2), (4, 4), (2, 2), (0, 1)]:
91
blurred = image_masked.filter(ImageFilter.GaussianBlur(radius)).convert('RGBA')
92
for _ in range(repeats):
93
image_mod.alpha_composite(blurred)
94
95
return image_mod.convert("RGB")
96
97
98