-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbullettoolset.py
232 lines (177 loc) · 9.22 KB
/
bullettoolset.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
229
230
231
232
# import pygame
import random
import cv2 as cv2
import numpy as np
from PIL import Image
from visiontoolset import get_points, character_to_dots
def create_bullet_2D(points, shape, density=5, min_radius=2, max_radius=4, position_jitter=3):
""" creates an image with simulating bullets at the points, the bullets are circles of value 255, white!
:param points: list of points forming the character
:param shape: shape of the image to be created
:param density: density of the bullets, the number of points is divided by density
:param min_radius: minimum radius of the bullets
:param max_radius: maximum radius of the bullets
:param position_jitter: position jitter (same for x and y direction)
:return: bullets image with bullets at the points
"""
assert points is not None, "Points is None"
assert len(points) > 0, "Points is empty"
bullets = np.zeros(shape=shape)
# take a random sample of the points
nr_of_points = len(points) // density
points_rand = random.sample(points, random.randint(nr_of_points // 2, nr_of_points))
for point in points_rand:
position_point = (random.randint(-position_jitter, position_jitter) + point[1],
random.randint(-position_jitter, position_jitter) + point[0])
cv2.circle(bullets, position_point, random.randint(min_radius, max_radius), (255), -1)
return bullets
def create_bullet_3D(points, shape, density=5, min_radius=2, max_radius=8, position_jitter=3):
""" creates an image with simulating bullets at the points, the bullets have a 3D effect
:param points: list of points forming the character
:param shape: shape of the image to be created
:param density: density of the bullets, the number of points is divided by density
:param min_radius: minimum radius of the bullets
:param max_radius: maximum radius of the bullets
:param position_jitter: position jitter (same for x and y direction)
:return: bullets image with bullets at the points
"""
assert points is not None, "Points is None"
assert len(points) > 0, "Points is empty"
image = np.zeros(
shape=(shape[0] + max_radius + position_jitter + 1, shape[1] + max_radius + position_jitter + 1, 3),
dtype=np.uint8)
nr_of_points = len(points) // density
points_rand = random.sample(points, nr_of_points)
circles = [(point, random.randint(min_radius, max_radius)) for point in points_rand]
# points are (x,y), imagase are (y,x) -> (r,c)
for center, radius in circles:
center = (center[0] + random.randint(0, position_jitter), center[1] + random.randint(0, position_jitter))
for c in range(center[0] - radius, center[0] + radius):
for r in range(center[1] - radius, center[1] + radius):
if (c - center[0]) ** 2 + (r - center[1]) ** 2 <= radius ** 2:
# Calculate distance to the center of the circle
distance = np.sqrt((c - center[0]) ** 2 + (r - center[1]) ** 2)
# Normalize distance and calculate intensity
intensity = 255 - int((distance / radius) * 255)
# Adjust intensity for "glance" effect
glance_intensity = intensity + 50 - int(50 * (distance / radius))
glance_intensity = max(0, min(255, glance_intensity))
image[r, c] = glance_intensity
image_inv = cv2.bitwise_not(image)
image_inv[image_inv == 255] = 0
return image_inv
def affine_transform(points):
""" Applies an affine transformation to the points
:param points: list of points
:return: transformed points as a list of tuples
"""
assert points is not None, "Points is None"
assert len(points) > 0, "Points is empty"
points_matrix = np.column_stack((points, np.ones(len(points))))
min_scale = 0.75
max_scale = 1.25
# affine transformation matrix with random scale
a, b, c, d = (random.uniform(min_scale, max_scale),
random.uniform(min_scale, max_scale),
random.uniform(min_scale, max_scale),
random.uniform(min_scale, max_scale))
# translation not performed here
tx, ty = 0, 0
# random angle for rotation
angle_deg = random.randint(-20, 20)
angle_rad = np.radians(angle_deg)
a, b, c, d = (a * np.cos(angle_rad), -b * np.sin(angle_rad),
c * np.sin(angle_rad), d * np.cos(angle_rad))
transformation_matrix = np.array([[a, b, tx],
[c, d, ty],
[0, 0, 1]])
# Perform the affine transformation
transformed_points = points_matrix.dot(transformation_matrix.T)
transformed_coordinates = [(int(p[0]), int(p[1])) for p in transformed_points[:, :2]]
return list(map(tuple, transformed_coordinates))
def dancing_sentence(sentence, font_size, font_path):
""" Splits the characters in the sentence anc create bullet images
a space is regarded as being 50 pixels wide.
Skeleton points of characters are transformed an affine transformation for scaling and rotating.
The transformed characters are projected onto a sinus wave. The amplitude is hardcode as 50.
:param sentence: sentence to transform
:param font_size: font size of the characters
:param font_path: path to the font
:return: transformed points, max_y, max_x
"""
assert sentence is not None, "Sentence is None"
assert len(sentence) > 0, "Sentence is empty"
new_points = []
pitch = 20
# sample half an interval of a full sinus cycle with a random starting point
# the nr of samples is equal to the length of sentence
sample = np.sin(np.linspace(0, np.pi, len(sentence))) + np.random.uniform(-0.1, 0.1, len(sentence))
space = 0
for idx, char in enumerate(sentence):
img_char, img_skel = character_to_dots(char, font_path=font_path, font_size=font_size)
points = get_points(img_skel)
if (len(points) == 0):
space = 50
else:
points_transformed = affine_transform(points)
# get min and max coordinates of the points and transformed points
min_x, min_y, = min([p[0] for p in points]), min([p[1] for p in points])
min_x_t, min_y_t = min([p[0] for p in points_transformed]), min([p[1] for p in points_transformed])
# calculate the translation to the original position
translation = (min_x - min_x_t, min_y - min_y_t,)
points_transformed = [(p[0] + translation[0],
p[1] + translation[1]) for p in points_transformed]
# concatenate the points of the transformed character to the previous characters
if idx > 0:
max_x_t = max([p[0] for p in new_points])
else:
max_x_t = 0
points_transformed = [(p[0] + max_x_t + space + pitch, p[1] + 50 * sample[idx]) for p in points_transformed]
if space:
space = 0 # reset space, it is used
new_points.extend(points_transformed)
# convert new_points to int
new_points = [(int(p[0]), int(p[1])) for p in new_points]
# determine the size of the sentence
max_x, max_y = max([p[0] for p in new_points]), max([p[1] for p in new_points])
return new_points, max_y, max_x
def shoot_on_bg(bullet_img, background_img, position=(50, 50), scale_factor=4):
""" This function projects the bullet image onto an images at a specific position
:param bullet_img: image containing a string or a single character to project
:param background_img: image to project the bullet image on
:param position: position of the bullet image on the wall image
:param scale_factor: divider for the size of the bullet image
:return: combined image
"""
bullet_img = cv2.resize(bullet_img, (bullet_img.shape[1] // scale_factor, bullet_img.shape[0] // scale_factor))
img1 = Image.fromarray(background_img)
img2 = Image.fromarray(cv2.cvtColor(bullet_img, cv2.COLOR_BGR2RGB))
temp_image = Image.new("RGBA", img1.size)
temp_image.paste(img2, position)
img1_data = img1.getdata() # background image
img2_data = temp_image.getdata() # bullet image
new_data = []
for item1, item2 in zip(img1_data, img2_data):
# if pixel in bullet item2 is black, then use background, otherwise use the bullet
if item2[0] == 0 and item2[1] == 0 and item2[2] == 0:
new_data.append(item1)
else:
new_data.append(item2)
temp_image.putdata(new_data)
return np.array(temp_image)
def shoot_holes(image, nr_of_holes=100, min_radius=2, max_radius=4):
""" Takes an image and shoots black holes in it
This is finally not used in the project
:param image: image to shoot holes in
:param nr_of_holes: number of holes to shoot
:param min_radius: minimum radius of the holes
:param max_radius: maximum radius of the holes
:return: image with holes
"""
assert image is not None, "Image is None"
image_holes = image.copy()
for i in range(nr_of_holes):
x = random.randint(0, image.shape[1])
y = random.randint(0, image.shape[0])
cv2.circle(image_holes, (x, y), random.randint(min_radius, max_radius), (0), -1)
return image_holes