-
Notifications
You must be signed in to change notification settings - Fork 2
/
distort.py
173 lines (141 loc) · 5.27 KB
/
distort.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
import math
import random
from collections import Counter
import cv2
import numpy as np
def random_gamma(image, max_gamma):
gamma = 1 + (max_gamma - 1) * np.random.random()
rand = np.random.random()
if rand > 0.5:
invGamma = gamma
else:
invGamma = 1.0 / gamma
table = np.array(
[((i / 255.0) ** invGamma) * 255 for i in np.arange(0, 256)]
).astype("uint8")
return cv2.LUT(image, table)
def random_noise(image):
row, col, ch = image.shape
mean = 0
var = 200
sigma = var ** 0.5
gauss = np.random.normal(mean, sigma, (row, col, ch))
gauss = gauss.reshape([row, col, ch])
noisy = image + gauss
return noisy
def random_blur(img, maxBlur):
blur_kernel = 1 + int(np.random.random() * maxBlur)
img = cv2.blur(img, (blur_kernel, blur_kernel))
return img
def random_flip(img):
if np.random.rand() > 0.5:
return cv2.flip(img, 1)
return img
def crop_box(img): # Input: Image with only the object
alpha = img[:, :, 3]
x, y, w, h = cv2.boundingRect(alpha)
return img[y : y + h, x : x + w]
def rotate_object(img):
height, width = img.shape[:2]
hypotenuse = int(np.ceil(np.sqrt(height ** 2 + width ** 2)))
blank_image = np.zeros([hypotenuse, hypotenuse, 4], np.uint8)
x_offset = (hypotenuse - width) // 2
y_offset = (hypotenuse - height) // 2
blank_image[y_offset : y_offset + height, x_offset : x_offset + width] = img
rotation_degrees = np.random.rand() * 360
M = cv2.getRotationMatrix2D((hypotenuse / 2, hypotenuse / 2), rotation_degrees, 1)
dst = cv2.warpAffine(blank_image, M, (hypotenuse, hypotenuse))
dst = crop_box(dst)
return dst
def resize_by_dim(
object_img,
bg_height,
bg_width,
lower_dim_bound=0.15,
upper_dim_bound=0.95,
):
obj_height, obj_width = object_img.shape[:2]
dim_scale = random.uniform(lower_dim_bound, upper_dim_bound)
if obj_height > obj_width:
new_obj_height = dim_scale * bg_height
scale = new_obj_height / obj_height
else:
new_obj_width = dim_scale * bg_width
scale = new_obj_width / obj_width
resized = cv2.resize(
object_img, (0, 0), fx=scale, fy=scale, interpolation=cv2.INTER_AREA
)
return resized
def attempt_composite(
obj_img, masks, segmentation_mask, height, width, occlusion_thresh=0.4
):
"""
:param obj_img: the BGRA object
:param masks: BGRA cv2 uint8 image, a black image with BGRA objects added in
:param segmentation_mask: single channel image showing what pixels in mask belong to
what object class
:param height:
:param width:
:param occlusion_thresh: max allowable occlusion
:return:
"""
obj_height, obj_width = obj_img.shape[:2]
max_y_coord = height - obj_height + 1
max_x_coord = width - obj_width + 1
if max_y_coord < 1 or max_x_coord < 1:
# The given obj_img is too large to fit
return False, masks, segmentation_mask
# Black out pixels in object img that don't have an alpha channel (just in case)
obj_img[obj_img[:, :, 3] == 0] = (0, 0, 0, 0)
num_objects = len(masks)
pixel_counts = Counter()
for mask_idx, mask in enumerate(masks):
alpha = mask[:, :, 3]
pixel_counts[mask_idx] = np.count_nonzero(alpha)
success = True
# Create a black BGRA mask the size of the composite with the object at a
# random position
candidate_mask = np.zeros([height, width, 4], dtype=np.uint8)
ymin = np.random.randint(max_y_coord)
xmin = np.random.randint(max_x_coord)
candidate_mask[ymin : ymin + obj_height, xmin : xmin + obj_width] = obj_img
candidate_alpha = candidate_mask[:, :, 3]
if num_objects > 0:
# Overlay the new object, and see if the new pixel counts indicate whether
# any of the objects have become over-occluded
candidate_segmentation_mask = segmentation_mask.copy()
candidate_segmentation_mask[candidate_alpha > 0] = num_objects + 1
for obj_idx in range(num_objects):
new_pixel_count = len(
candidate_segmentation_mask[candidate_segmentation_mask == obj_idx + 1]
)
if new_pixel_count < (1 - occlusion_thresh) * pixel_counts[obj_idx]:
success = False
break
if success:
masks.append(candidate_mask)
segmentation_mask[candidate_alpha > 0] = num_objects + 1
return True, masks, segmentation_mask
return False, masks, segmentation_mask
def get_poly_from_mask(mask):
"""Returns masks in poly format, and its area"""
cnt, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
area = 0
polygons = []
for c in cnt:
area += cv2.contourArea(c)
polygons.append(np.ravel(c).tolist())
return polygons, area
def alpha_blend(img, bgra_img):
# Convert uint8 to float
img_f = img.astype(float)
bgra_img_f = bgra_img.astype(float)
bgr_img_f, alpha_f = bgra_img_f[:, :, :3], bgra_img_f[:, :, 3:]
alpha_f /= 255.0
# Multiply the background with ( 1 - alpha )
background = (1.0 - alpha_f) * img_f
# Multiply the foreground with the alpha matte
foreground = alpha_f * bgr_img_f
# Add the masked foreground and background.
out_img = cv2.add(foreground, background).astype(np.uint8)
return out_img