LiXiY commited on
Commit
38d5abe
·
verified ·
1 Parent(s): 37ac762

Upload utils.py

Browse files
Files changed (1) hide show
  1. utils.py +106 -0
utils.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import random
3
+
4
+ import numpy as np
5
+ from PIL import Image, ImageEnhance, ImageFilter
6
+
7
+
8
+
9
+ def binarize_mask(mask, threshold=0):
10
+ return mask.point(lambda p: 255 if p > threshold else 0).convert("L")
11
+
12
+
13
+ def square_crop_by_mask(image, mask, scale=1.2):
14
+ W, H = image.size
15
+ bbox = mask.getbbox()
16
+ if bbox is None:
17
+ cx, cy = W / 2.0, H / 2.0
18
+ side = float(min(W, H))
19
+ else:
20
+ l, u, r, lo = bbox
21
+ cx = (l + r) / 2.0
22
+ cy = (u + lo) / 2.0
23
+ side = max(r - l, lo - u) * scale
24
+ side_i = max(1, int(round(side)))
25
+ left = int(round(cx - side_i / 2.0))
26
+ top = int(round(cy - side_i / 2.0))
27
+ box = (left, top, left + side_i, top + side_i)
28
+ # PIL fills out-of-bounds crop regions with 0: black for the RGB image and
29
+ # "unmasked" (0) for the L mask - exactly the black padding we want.
30
+ return image.crop(box), mask.crop(box), box
31
+
32
+
33
+
34
+ def process_source(source_image, source_mask, image_size=1024, crop_scale=1.2, binarize_threshold=0):
35
+ source_mask = binarize_mask(source_mask, binarize_threshold)
36
+ source_image_c, source_mask_c, crop_box = square_crop_by_mask(source_image, source_mask, crop_scale)
37
+ size = (image_size, image_size)
38
+ image = source_image_c.resize(size, Image.LANCZOS) # GT crop
39
+ mask = source_mask_c.resize(size, Image.NEAREST)
40
+ white = Image.new("RGB", size, (255, 255, 255))
41
+ # composite(white, image, mask): mask==255 -> white, mask==0 -> image
42
+ background_image = Image.composite(white, image, mask)
43
+ return background_image, image, crop_box, source_mask_c
44
+
45
+
46
+ def process_reference(ref_image, ref_mask, image_size=1024, crop_scale=1.2,
47
+ binarize_threshold=0, augment=False, p_aug=0.8):
48
+ ref_mask = binarize_mask(ref_mask, binarize_threshold)
49
+ ref_image_c, ref_mask_c, _ = square_crop_by_mask(ref_image, ref_mask, crop_scale)
50
+ size = (image_size, image_size)
51
+ ref_image_r = ref_image_c.resize(size, Image.LANCZOS)
52
+ ref_mask_r = ref_mask_c.resize(size, Image.NEAREST)
53
+ white = Image.new("RGB", size, (255, 255, 255))
54
+ # composite(ref_image, white, mask): mask==255 -> object, mask==0 -> white
55
+ ref_image_out = Image.composite(ref_image_r, white, ref_mask_r)
56
+ if augment:
57
+ ref_image_out = augment_ref_image(ref_image_out, ref_mask_r, p_aug=p_aug)
58
+ return ref_image_out
59
+
60
+
61
+ def paste_back(generated_crop, source_image, crop_box, source_mask_cropped, feather=0):
62
+ left, top, right, bottom = crop_box
63
+ crop_side = right - left
64
+ generated_crop = generated_crop.resize((crop_side, crop_side), Image.LANCZOS)
65
+ original_crop = source_image.crop(crop_box)
66
+ if feather > 0:
67
+ source_mask_cropped = source_mask_cropped.filter(ImageFilter.GaussianBlur(feather))
68
+ edited_crop = Image.composite(generated_crop, original_crop, source_mask_cropped)
69
+ final_image = source_image.copy()
70
+ final_image.paste(edited_crop, (left, top))
71
+ return final_image
72
+
73
+
74
+ def _adjust_hue(img, shift):
75
+ """Shift the hue of an RGB image by ``shift`` (in 0..255 units)."""
76
+ hsv = img.convert("HSV")
77
+ h, s, v = hsv.split()
78
+ h_arr = (np.asarray(h).astype(np.int16) + int(round(shift))) % 256
79
+ h = Image.fromarray(h_arr.astype(np.uint8), "L")
80
+ return Image.merge("HSV", (h, s, v)).convert("RGB")
81
+
82
+
83
+ def augment_ref_image(ref_image, mask, p_aug=0.8):
84
+ img = ref_image.convert("RGB").copy()
85
+ m = mask.convert("L")
86
+
87
+ # Horizontal flip is safe: the object sits on a uniform white background.
88
+ if random.random() < 0.5:
89
+ img = img.transpose(Image.FLIP_LEFT_RIGHT)
90
+ m = m.transpose(Image.FLIP_LEFT_RIGHT)
91
+
92
+ # Colour jitter on the whole image; background is re-whitened below.
93
+ if random.random() < p_aug:
94
+ img = ImageEnhance.Brightness(img).enhance(random.uniform(0.85, 1.15))
95
+ if random.random() < p_aug:
96
+ img = ImageEnhance.Contrast(img).enhance(random.uniform(0.85, 1.15))
97
+ if random.random() < p_aug:
98
+ img = ImageEnhance.Color(img).enhance(random.uniform(0.85, 1.15))
99
+ if random.random() < p_aug:
100
+ img = ImageEnhance.Sharpness(img).enhance(random.uniform(0.9, 1.1))
101
+ if random.random() < p_aug:
102
+ img = _adjust_hue(img, random.uniform(-0.06, 0.06) * 255.0)
103
+
104
+ # Keep the (jittered) object, reset everything outside the mask to white.
105
+ white = Image.new("RGB", img.size, (255, 255, 255))
106
+ return Image.composite(img, white, m)