image-math 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
image_math/__init__.py
ADDED
image_math/main.py
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Provide the vectorised inverted function. e.g.
|
|
3
|
+
Droste-to-Escher Image:
|
|
4
|
+
|
|
5
|
+
z` = e ^ (ln(z) * c)
|
|
6
|
+
where, c = e ^ (i * arctan(ln(scale)/2pi))
|
|
7
|
+
|
|
8
|
+
Inverted function:
|
|
9
|
+
z = e ^ (ln(z`) * (1/c))
|
|
10
|
+
|
|
11
|
+
The second (i.e. inverted function) is what we want
|
|
12
|
+
|
|
13
|
+
Vectorised: Uses numpy functions, allowing a tremendous speed up.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import numpy as np
|
|
17
|
+
from PIL import Image
|
|
18
|
+
from skimage.draw import polygon
|
|
19
|
+
from tqdm import tqdm
|
|
20
|
+
|
|
21
|
+
def image_function(my_func):
|
|
22
|
+
def apply_function(input_image:str, output_image:str, zoom = 1.0, output_scale = 1.0, *args, **kwargs):
|
|
23
|
+
"""
|
|
24
|
+
scale: refers to range of z to try out. Input is mapped from f(± 1 ± i) * scale. Output is always mapped from ± 1 ± i
|
|
25
|
+
"""
|
|
26
|
+
img = Image.open(input_image)
|
|
27
|
+
arr = np.array(img)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
h,w,channels = arr.shape
|
|
31
|
+
ho,wo = int(h*output_scale),int(w*output_scale)
|
|
32
|
+
arr2 = np.zeros((ho,wo,channels), dtype=arr.dtype)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
empty_pixel = (0,0,0,255) if channels == 4 else (0,0,0)
|
|
36
|
+
|
|
37
|
+
x = np.linspace(-1,1, wo+1)
|
|
38
|
+
y = np.linspace(-1,1,ho+1)
|
|
39
|
+
|
|
40
|
+
mesh_output = x[None, :] + 1j * y[:,None]
|
|
41
|
+
mesh_input = my_func(mesh_output/zoom, *args, **kwargs) # Gets input mesh
|
|
42
|
+
|
|
43
|
+
#Convert mesh to pixel borders
|
|
44
|
+
mesh_inp_x = (mesh_input.real * ((w-1)/2)) + ((w-1)/2)
|
|
45
|
+
mesh_inp_y = (mesh_input.imag * ((h-1)/2)) + ((h-1)/2)
|
|
46
|
+
|
|
47
|
+
for y in tqdm(range(ho)):
|
|
48
|
+
for x in range(wo):
|
|
49
|
+
x_l = np.array([mesh_inp_x[y,x], mesh_inp_x[y,x+1], mesh_inp_x[y+1,x+1], mesh_inp_x[y+1,x]])
|
|
50
|
+
y_l = np.array([mesh_inp_y[y,x], mesh_inp_y[y,x+1], mesh_inp_y[y+1,x+1], mesh_inp_y[y+1,x]])
|
|
51
|
+
|
|
52
|
+
if not (np.isfinite(x_l).all() and np.isfinite(y_l).all()):
|
|
53
|
+
arr2[y, x] = empty_pixel
|
|
54
|
+
continue
|
|
55
|
+
|
|
56
|
+
#Draw mask
|
|
57
|
+
rr, cc = polygon(y_l, x_l, shape=(h,w))
|
|
58
|
+
|
|
59
|
+
#Get final pixel
|
|
60
|
+
if len(rr):
|
|
61
|
+
arr2[y,x] = arr[rr,cc].mean(axis=0)
|
|
62
|
+
else:
|
|
63
|
+
inside = ((0 <= x_l) & (x_l <= w-1) & (0 <= y_l) & (y_l <= h-1)).any()
|
|
64
|
+
if inside:
|
|
65
|
+
ix = np.clip(x_l.mean(), 0, w-1)
|
|
66
|
+
iy = np.clip(y_l.mean(), 0, h-1)
|
|
67
|
+
arr2[y,x] = arr[int(round(iy)), int(round(ix))]
|
|
68
|
+
else:
|
|
69
|
+
arr2[y,x] = empty_pixel
|
|
70
|
+
|
|
71
|
+
Image.fromarray(arr2).save(output_image)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
return apply_function
|
|
76
|
+
|
|
77
|
+
def img_func(my_func):
|
|
78
|
+
return image_function(my_func) #For pylance
|
|
79
|
+
|
|
80
|
+
def escher_function(scale:float, center = (0,0), subject = (0,0), rev_warp = False):
|
|
81
|
+
"""
|
|
82
|
+
Creates a function to convert a repeating picture into an escher-style image
|
|
83
|
+
scale: size of edge of image compared to 1st nested image.
|
|
84
|
+
center: center of repeating pattern in droste image (center = 0,0; top right = (1,1))
|
|
85
|
+
subject: subject position (remains stationary-ish)
|
|
86
|
+
"""
|
|
87
|
+
b = 1 + 1j*(np.log(scale)/(2*np.pi)) * (-1 if rev_warp else 1)
|
|
88
|
+
c = center[0] + 1j* center[1]
|
|
89
|
+
|
|
90
|
+
d = 0 + 0j if subject == (0,0) else np.log(subject[0] + 1j * subject[1])
|
|
91
|
+
|
|
92
|
+
@image_function
|
|
93
|
+
def f(z):
|
|
94
|
+
w = z-c
|
|
95
|
+
w = np.where(w == 0, 1e-8 + 1e-8j, w) #Buffer
|
|
96
|
+
|
|
97
|
+
log_w_new = np.log(w)
|
|
98
|
+
low_w_old = b * (log_w_new - d) + d
|
|
99
|
+
z_raw = np.exp(low_w_old)
|
|
100
|
+
|
|
101
|
+
mag = np.abs(z_raw)
|
|
102
|
+
wrap_p = np.floor(-np.log(mag)/np.log(scale))
|
|
103
|
+
return z_raw * (scale ** wrap_p) + c
|
|
104
|
+
|
|
105
|
+
return f
|
|
106
|
+
|
|
107
|
+
def wp_lattice(w1 = 1, w2 = np.exp(1j * np.pi / 3), N = 10, scale = 5):
|
|
108
|
+
w1 *= scale
|
|
109
|
+
w2 *= scale
|
|
110
|
+
|
|
111
|
+
m = np.arange(-N, N+1)
|
|
112
|
+
n = np.arange(-N, N+1)
|
|
113
|
+
|
|
114
|
+
A, B = np.meshgrid(m,n, indexing='ij')
|
|
115
|
+
|
|
116
|
+
w = A*w1 + B*w2
|
|
117
|
+
return w[~((A==0)&(B==0))]
|
|
118
|
+
|
|
119
|
+
# ----
|
|
120
|
+
# Cool functions to try out
|
|
121
|
+
@image_function
|
|
122
|
+
def rotate(z):
|
|
123
|
+
return z * 1j
|
|
124
|
+
|
|
125
|
+
@image_function
|
|
126
|
+
def zoom_into_corner(z):
|
|
127
|
+
return (z/3) + 2j/3 + 2/3
|
|
128
|
+
|
|
129
|
+
@image_function
|
|
130
|
+
def kaleidoscope(z):
|
|
131
|
+
return np.pow(z,6)
|
|
132
|
+
|
|
133
|
+
@image_function
|
|
134
|
+
def assymetrical_kaleidoscope(z):
|
|
135
|
+
return np.pow(z,3) - 3*np.pow(z,2)
|
|
136
|
+
|
|
137
|
+
@image_function
|
|
138
|
+
def logarithm(z):
|
|
139
|
+
return np.exp(z)
|
|
140
|
+
|
|
141
|
+
@image_function
|
|
142
|
+
def inverse(z):
|
|
143
|
+
return 1/z
|
|
144
|
+
|
|
145
|
+
WP_LATTICE = None
|
|
146
|
+
@image_function
|
|
147
|
+
def weierstrass(z):
|
|
148
|
+
global WP_LATTICE
|
|
149
|
+
if WP_LATTICE is None:
|
|
150
|
+
WP_LATTICE = wp_lattice()
|
|
151
|
+
|
|
152
|
+
w = WP_LATTICE[:,None,None]
|
|
153
|
+
r = (1/(z**2))
|
|
154
|
+
|
|
155
|
+
for wi in w:
|
|
156
|
+
r += (1/((z-wi)**2)) - (1/(wi**2)) #For memory reasons
|
|
157
|
+
|
|
158
|
+
return r
|
|
159
|
+
|
|
160
|
+
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
image_math/__init__.py,sha256=0KK7QkqDaBS5fLL7Gudrq7xY78EQN-U10Um_Ecyt0fk,196
|
|
2
|
+
image_math/main.py,sha256=cvzh9xei0DXN0B3Lr-jXritl3w4N4cUs3Wu2Er4vf_g,4669
|
|
3
|
+
image_math-1.0.0.dist-info/METADATA,sha256=z4fWKOkugdBtouS8xzyfIfaJjIODy7RxEDQtyzIXeuk,184
|
|
4
|
+
image_math-1.0.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
5
|
+
image_math-1.0.0.dist-info/top_level.txt,sha256=bAtBRWKz0Z_rTr0U4DlCusY2TL_yl_uXEyRKJS43V1w,11
|
|
6
|
+
image_math-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
image_math
|