-
Notifications
You must be signed in to change notification settings - Fork 0
/
lighting.py
228 lines (218 loc) · 10.1 KB
/
lighting.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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
import cv2
import numpy as np
import random
from scipy.stats import norm
def _decayed_value_in_norm(x, max_value, min_value, center, range):
"""
decay from max value to min value following Gaussian/Normal distribution
"""
radius = range / 3
center_prob = norm.pdf(center, center, radius)
x_prob = norm.pdf(x, center, radius)
x_value = (x_prob / center_prob) * (max_value - min_value) + min_value
return x_value
def _decayed_value_in_linear(x, max_value, padding_center, decay_rate):
"""
decay from max value to min value with static linear decay rate.
"""
x_value = max_value - abs(padding_center - x) * decay_rate
if x_value < 0:
x_value = 1
return x_value
def _generate_parallel_light_mask(mask_size,
position=None,
direction=None,
max_brightness=255,
min_brightness=0,
mode="gaussian",
linear_decay_rate=None):
"""
Generate decayed light mask generated by light strip given its position, direction
Args:
mask_size: tuple of integers (w, h) defining generated mask size
position: tuple of integers (x, y) defining the center of light strip position,
which is the reference point during rotating
direction: integer from 0 to 360 to indicate the rotation degree of light strip
max_brightness: integer that max brightness in the mask
min_brightness: integer that min brightness in the mask
mode: the way that brightness decay from max to min: linear or gaussian
linear_decay_rate: only valid in linear_static mode. Suggested value is within [0.2, 2]
Return:
light_mask: ndarray in float type consisting value from 0 to strength
"""
if position is None:
pos_x = random.randint(0, mask_size[0])
pos_y = random.randint(0, mask_size[1])
else:
pos_x = position[0]
pos_y = position[1]
if direction is None:
direction = random.randint(0, 360)
#print("Rotate degree: ", direction)
if linear_decay_rate is None:
if mode == "linear_static":
linear_decay_rate = random.uniform(0.2, 2)
if mode == "linear_dynamic":
linear_decay_rate = (max_brightness - min_brightness) / max(mask_size)
assert mode in ["linear_dynamic", "linear_static", "gaussian"], \
"mode must be linear_dynamic, linear_static or gaussian"
padding = int(max(mask_size) * np.sqrt(2))
# add padding to satisfy cropping after rotating
canvas_x = padding * 2 + mask_size[0]
canvas_y = padding * 2 + mask_size[1]
mask = np.zeros(shape=(canvas_y, canvas_x), dtype=np.float32)
# initial mask's up left corner and bottom right corner coordinate
init_mask_ul = (int(padding), int(padding))
init_mask_br = (int(padding+mask_size[0]), int(padding+mask_size[1]))
init_light_pos = (padding + pos_x, padding + pos_y)
# fill in mask row by row with value decayed from center
for i in range(canvas_y):
if mode == "linear":
i_value = _decayed_value_in_linear(i, max_brightness, init_light_pos[1], linear_decay_rate)
elif mode == "gaussian":
i_value = _decayed_value_in_norm(i, max_brightness, min_brightness, init_light_pos[1], mask_size[1])
else:
i_value = 0
mask[i] = i_value
# rotate mask
rotate_M = cv2.getRotationMatrix2D(init_light_pos, direction, 1)
mask = cv2.warpAffine(mask, rotate_M, (canvas_x, canvas_y))
# crop
mask = mask[init_mask_ul[1]:init_mask_br[1], init_mask_ul[0]:init_mask_br[0]]
mask = np.asarray(mask, dtype=np.uint8)
# add median blur
mask = cv2.medianBlur(mask, 9)
mask = 255 - mask
return mask
def add_parallel_light(frame, light_position=None, direction=None, max_brightness=255, min_brightness=0,
mode="gaussian", linear_decay_rate=None, transparency=None):
"""
Add mask generated from parallel light to given image
"""
if transparency is None:
transparency = random.uniform(0.5, 0.85)
height, width, _ = frame.shape
try:
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
except Exception:
hsv = cv2.cvtColor(frame.astype('uint8'),cv2.COLOR_BGR2HSV)
mask = _generate_parallel_light_mask(mask_size=(width, height),
position=light_position,
direction=direction,
max_brightness=max_brightness,
min_brightness=min_brightness,
mode=mode,
linear_decay_rate=linear_decay_rate)
hsv[:, :, 2] = hsv[:, :, 2] * transparency + mask * (1 - transparency)
frame = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
frame[frame > 255] = 255
frame = np.asarray(frame, dtype=np.uint8)
return frame
def _generate_spot_light_mask(mask_size,
position=None,
max_brightness=255,
min_brightness=0,
mode="gaussian",
linear_decay_rate=None,
speedup=False):
"""
Generate decayed light mask generated by spot light given position, direction. Multiple spotlights are accepted.
Args:
mask_size: tuple of integers (w, h) defining generated mask size
position: list of tuple of integers (x, y) defining center of spotlight light position, which is the reference point during rotating
max_brightness: integer that max brightness in the mask
min_brightness: integer that min brightness in the mask
mode: the way that brightness decay from max to min: linear or gaussian
linear_decay_rate: only valid in linear_static mode. Suggested value is within [0.2, 2]
speedup: use `shrinkage then expansion` strategy to speed up vale calculation
Return:
light_mask: ndarray in float type consisting value from max_brightness to min_brightness. If in 'linear' mode minimum value could be smaller than given min_brightness.
"""
if position is None:
position = [(random.randint(0, mask_size[0]), random.randint(0, mask_size[1]))]
if linear_decay_rate is None:
if mode == "linear_static":
linear_decay_rate = random.uniform(0.25, 1)
assert mode in ["linear", "gaussian"], \
"mode must be linear_dynamic, linear_static or gaussian"
mask = np.zeros(shape=(mask_size[1], mask_size[0]), dtype=np.float32)
if mode == "gaussian":
mu = np.sqrt(mask.shape[0]**2+mask.shape[1]**2)
dev = mu / 3.5
mask = _decay_value_radically_norm_in_matrix(mask_size, position, max_brightness, min_brightness, dev)
mask = np.asarray(mask, dtype=np.uint8)
# add median blur
mask = cv2.medianBlur(mask, 5)
mask = 255 - mask
# cv2.imshow("mask", mask)
# cv2.waitKey(0)
return mask
def _decay_value_radically_norm_in_matrix(mask_size, centers, max_value, min_value, dev):
"""
_decay_value_radically_norm function in matrix format
"""
center_prob = norm.pdf(0, 0, dev)
x_value_rate = np.zeros((mask_size[1], mask_size[0]))
for center in centers:
coord_x = np.arange(mask_size[0])
coord_y = np.arange(mask_size[1])
xv, yv = np.meshgrid(coord_x, coord_y)
dist_x = xv - center[0]
dist_y = yv - center[1]
dist = np.sqrt(np.power(dist_x, 2) + np.power(dist_y, 2))
x_value_rate += norm.pdf(dist, 0, dev) / center_prob
mask = x_value_rate * (max_value - min_value) + min_value
mask[mask > 255] = 255
return mask
def _decay_value_radically_norm(x, centers, max_value, min_value, dev):
"""
Calculate point value decayed from center following Gaussian decay. If multiple centers are given, value
from each center sums up while limiting the accumulated value into [0, 255]
NOTE: assuming light at each center is identical: same brightness and same decay rate
"""
center_prob = norm.pdf(0, 0, dev)
x_value_rate = 0
for center in centers:
distance = np.sqrt((center[0]-x[0])**2 + (center[1]-x[1])**2)
x_value_rate += norm.pdf(distance, 0, dev) / center_prob
x_value = x_value_rate * (max_value - min_value) + min_value
x_value = 255 if x_value > 255 else x_value
return x_value
def add_spot_light(frame, light_position=None, max_brightness=255, min_brightness=0,
mode='gaussian', linear_decay_rate=None, transparency=None):
"""
Add mask generated from spot light to given image
"""
if transparency is None:
transparency = random.uniform(0.5, 0.85)
height, width, _ = frame.shape
try:
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
except Exception:
hsv = cv2.cvtColor(frame.astype('uint8'),cv2.COLOR_BGR2HSV)
mask = _generate_spot_light_mask(mask_size=(width, height),
position=light_position,
max_brightness=max_brightness,
min_brightness=min_brightness,
mode=mode,
linear_decay_rate=linear_decay_rate)
hsv[:, :, 2] = hsv[:, :, 2] * transparency + mask * (1 - transparency)
frame = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
frame[frame > 255] = 255
frame = np.asarray(frame, dtype=np.uint8)
return frame
def add_lighting(image, method='parallel'):
#illuminate base image randomly
assert method in ['parallel', 'all', 'spot', None, ''], \
"Valid Methods Include Only ['parallel', 'all', 'spot', None]"
adj_image = None
if method is None or method == '':
return image
if method == 'parallel':
adj_image = add_parallel_light(image)
if method == 'spot':
adj_image = add_spot_light(image)
if method == 'all':
adj_image = add_parallel_light(image)
adj_image = add_spot_light(adj_image)
return adj_image