Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
iperov
GitHub Repository: iperov/deepfacelab
Path: blob/master/core/imagelib/sd/draw.py
628 views
1
"""
2
Signed distance drawing functions using numpy.
3
"""
4
import math
5
6
import numpy as np
7
from numpy import linalg as npla
8
9
10
def vector2_dot(a,b):
11
return a[...,0]*b[...,0]+a[...,1]*b[...,1]
12
13
def vector2_dot2(a):
14
return a[...,0]*a[...,0]+a[...,1]*a[...,1]
15
16
def vector2_cross(a,b):
17
return a[...,0]*b[...,1]-a[...,1]*b[...,0]
18
19
20
def circle_faded( wh, center, fade_dists ):
21
"""
22
returns drawn circle in [h,w,1] output range [0..1.0] float32
23
24
wh = [w,h] resolution
25
center = [x,y] center of circle
26
fade_dists = [fade_start, fade_end] fade values
27
"""
28
w,h = wh
29
30
pts = np.empty( (h,w,2), dtype=np.float32 )
31
pts[...,0] = np.arange(w)[:,None]
32
pts[...,1] = np.arange(h)[None,:]
33
34
pts = pts.reshape ( (h*w, -1) )
35
36
pts_dists = np.abs ( npla.norm(pts-center, axis=-1) )
37
38
if fade_dists[1] == 0:
39
fade_dists[1] = 1
40
41
pts_dists = ( pts_dists - fade_dists[0] ) / fade_dists[1]
42
43
pts_dists = np.clip( 1-pts_dists, 0, 1)
44
45
return pts_dists.reshape ( (h,w,1) ).astype(np.float32)
46
47
48
def bezier( wh, A, B, C ):
49
"""
50
returns drawn bezier in [h,w,1] output range float32,
51
every pixel contains signed distance to bezier line
52
53
wh [w,h] resolution
54
A,B,C points [x,y]
55
"""
56
57
width,height = wh
58
59
A = np.float32(A)
60
B = np.float32(B)
61
C = np.float32(C)
62
63
64
pos = np.empty( (height,width,2), dtype=np.float32 )
65
pos[...,0] = np.arange(width)[:,None]
66
pos[...,1] = np.arange(height)[None,:]
67
68
69
a = B-A
70
b = A - 2.0*B + C
71
c = a * 2.0
72
d = A - pos
73
74
b_dot = vector2_dot(b,b)
75
if b_dot == 0.0:
76
return np.zeros( (height,width), dtype=np.float32 )
77
78
kk = 1.0 / b_dot
79
80
kx = kk * vector2_dot(a,b)
81
ky = kk * (2.0*vector2_dot(a,a)+vector2_dot(d,b))/3.0;
82
kz = kk * vector2_dot(d,a);
83
84
res = 0.0;
85
sgn = 0.0;
86
87
p = ky - kx*kx;
88
89
p3 = p*p*p;
90
q = kx*(2.0*kx*kx - 3.0*ky) + kz;
91
h = q*q + 4.0*p3;
92
93
hp_sel = h >= 0.0
94
95
hp_p = h[hp_sel]
96
hp_p = np.sqrt(hp_p)
97
98
hp_x = ( np.stack( (hp_p,-hp_p), -1) -q[hp_sel,None] ) / 2.0
99
hp_uv = np.sign(hp_x) * np.power( np.abs(hp_x), [1.0/3.0, 1.0/3.0] )
100
hp_t = np.clip( hp_uv[...,0] + hp_uv[...,1] - kx, 0.0, 1.0 )
101
102
hp_t = hp_t[...,None]
103
hp_q = d[hp_sel]+(c+b*hp_t)*hp_t
104
hp_res = vector2_dot2(hp_q)
105
hp_sgn = vector2_cross(c+2.0*b*hp_t,hp_q)
106
107
hl_sel = h < 0.0
108
109
hl_q = q[hl_sel]
110
hl_p = p[hl_sel]
111
hl_z = np.sqrt(-hl_p)
112
hl_v = np.arccos( hl_q / (hl_p*hl_z*2.0)) / 3.0
113
114
hl_m = np.cos(hl_v)
115
hl_n = np.sin(hl_v)*1.732050808;
116
117
hl_t = np.clip( np.stack( (hl_m+hl_m,-hl_n-hl_m,hl_n-hl_m), -1)*hl_z[...,None]-kx, 0.0, 1.0 );
118
119
hl_d = d[hl_sel]
120
121
hl_qx = hl_d+(c+b*hl_t[...,0:1])*hl_t[...,0:1]
122
123
hl_dx = vector2_dot2(hl_qx)
124
hl_sx = vector2_cross(c+2.0*b*hl_t[...,0:1], hl_qx)
125
126
hl_qy = hl_d+(c+b*hl_t[...,1:2])*hl_t[...,1:2]
127
hl_dy = vector2_dot2(hl_qy)
128
hl_sy = vector2_cross(c+2.0*b*hl_t[...,1:2],hl_qy);
129
130
hl_dx_l_dy = hl_dx<hl_dy
131
hl_dx_ge_dy = hl_dx>=hl_dy
132
133
hl_res = np.empty_like(hl_dx)
134
hl_res[hl_dx_l_dy] = hl_dx[hl_dx_l_dy]
135
hl_res[hl_dx_ge_dy] = hl_dy[hl_dx_ge_dy]
136
137
hl_sgn = np.empty_like(hl_sx)
138
hl_sgn[hl_dx_l_dy] = hl_sx[hl_dx_l_dy]
139
hl_sgn[hl_dx_ge_dy] = hl_sy[hl_dx_ge_dy]
140
141
res = np.empty( (height, width), np.float32 )
142
res[hp_sel] = hp_res
143
res[hl_sel] = hl_res
144
145
sgn = np.empty( (height, width), np.float32 )
146
sgn[hp_sel] = hp_sgn
147
sgn[hl_sel] = hl_sgn
148
149
sgn = np.sign(sgn)
150
res = np.sqrt(res)*sgn
151
152
return res[...,None]
153
154
def random_faded(wh):
155
"""
156
apply one of them:
157
random_circle_faded
158
random_bezier_split_faded
159
"""
160
rnd = np.random.randint(2)
161
if rnd == 0:
162
return random_circle_faded(wh)
163
elif rnd == 1:
164
return random_bezier_split_faded(wh)
165
166
def random_circle_faded ( wh, rnd_state=None ):
167
if rnd_state is None:
168
rnd_state = np.random
169
170
w,h = wh
171
wh_max = max(w,h)
172
fade_start = rnd_state.randint(wh_max)
173
fade_end = fade_start + rnd_state.randint(wh_max- fade_start)
174
175
return circle_faded (wh, [ rnd_state.randint(h), rnd_state.randint(w) ],
176
[fade_start, fade_end] )
177
178
def random_bezier_split_faded( wh ):
179
width, height = wh
180
181
degA = np.random.randint(360)
182
degB = np.random.randint(360)
183
degC = np.random.randint(360)
184
185
deg_2_rad = math.pi / 180.0
186
187
center = np.float32([width / 2.0, height / 2.0])
188
189
radius = max(width, height)
190
191
A = center + radius*np.float32([ math.sin( degA * deg_2_rad), math.cos( degA * deg_2_rad) ] )
192
B = center + np.random.randint(radius)*np.float32([ math.sin( degB * deg_2_rad), math.cos( degB * deg_2_rad) ] )
193
C = center + radius*np.float32([ math.sin( degC * deg_2_rad), math.cos( degC * deg_2_rad) ] )
194
195
x = bezier( (width,height), A, B, C )
196
197
x = x / (1+np.random.randint(radius)) + 0.5
198
199
x = np.clip(x, 0, 1)
200
return x
201
202