-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPDESolver.py
182 lines (153 loc) · 5.81 KB
/
PDESolver.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
import numpy as np
import cv2
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from skimage.metrics import structural_similarity as ssim
from multiprocessing import Pool
from functools import partial
import os
k = 0.01
dt = 0.02
def spread_one_colour(image, g, dt0, dim, ld=0.01):
"""
Helper function for multiprocessing. Do not call!!
Has to be defined at the top of the file for some reason
"""
image[:, :, dim] = spread_once(image[:, :, dim], dt=dt0, g=g, ld=ld)
return image[:, :, dim]
def func(x, k=0.01):
"""
User defined, smooth, positive, non-increasing function g:
:param x: Function input (float)
:param k: User defined parameter. Should be less than one and larger than 0
:return: Function output (float)
"""
return np.exp(-(x * k) ** 2)
def divergence(x):
"""
Calculates the divergence at each point of the matrix x.
:param x: Input matrix.
:return: Returns a matrix containing the divergence at each point of the input matrix
"""
grad = np.gradient(x)
xs = np.gradient(grad[0])[0]
ys = np.gradient(grad[1])[1]
return xs + ys
def spread_once(xs, g=func, dt=dt, ld=0.01):
"""
Evolves the input matrix by one timestep (dt) using the Perona-Malik equation.
:param xs: Input matrix to be evolved
:param g: User defined function to be used in the Perona-Malik algorithm
:param dt: Timestep (measure of amount of correction)
:param ld: Parameter in the user defined function
:return: Matrix after one timestep
"""
grad = np.gradient(xs)
magnitude_of_grad = np.sqrt(grad[0] ** 2 + grad[1] ** 2)
g_at_each_point = g(magnitude_of_grad, ld)
grad_of_g = np.gradient(g_at_each_point)
xs[1:-1, 1:-1] = xs[1:-1, 1:-1] + dt * (divergence(xs)[1:-1, 1:-1] * g_at_each_point[1:-1, 1:-1]
+ grad[0][1:-1, 1:-1] * grad_of_g[0][1:-1, 1:-1]
+ grad[1][1:-1, 1:-1] * grad_of_g[1][1:-1, 1:-1])
return xs
def spread_colours(original_image, g=func, dt=dt, ld=0.01):
"""
Evolves each colour in an image by one timestep (dt) according to the Perona-Malik equation.
:param original_image: Image to be evolved
:param g: User defined function to be used in the Perona-Malik algorithm
:param dt: Timestep (measure of amount of correction)
:param ld: Parameter in the user defined function
:return: Evolved image after one timestep
"""
image = original_image.copy()
f = partial(spread_one_colour, image, g, dt, ld=ld)
with Pool(3) as p:
res = p.map(f, [0, 1, 2])
for i in [0, 1, 2]:
image[:, :, i] = res[i]
return image
def numpysolver(im, noisy_im, gfunc, dt, ld, iterations, error_added):
"""
Smooth a 3d matrix with the Perona-Malik equation with gfunc, dt and ld over iterations number of times.
:param im: np.array 3d matrix (image)
:param noisy_im: np.array noised 3d matrix (image)
:param gfunc: g function used
:param dt: float time step used
:param ld: float lambda used
:param iterations: int number of iterations
:param error_added: int error added to the noised image
:return:
"""
xs = noisy_im.copy().astype(int)
start = xs
err = ssim(im, xs, data_range=xs.max() - xs.min(), multichannel=True)
err2 = np.var(im) / np.var(xs)
errs = [[err, err2]]
a = 0
for i in range(iterations):
xs = spread_colours(xs, gfunc, dt, ld)
prev = err
err = ssim(im, xs, data_range=xs.max() - xs.min(), multichannel=True)
err2 = np.var(im) / np.var(xs)
errs.append([err, err2])
if prev > err:
a = a + 1
else:
a = 0
if (prev > err) and (a == 1):
print("Only did " + str(i) + " runs")
print("Similarity value = " + str(err))
break
plt.figure(figsize=(10, 10))
plt.subplot(1, 2, 1)
plt.imshow(start)
plt.title("Image after +-" + str(error_added) + " of added noise")
plt.subplot(1, 2, 2)
plt.imshow(xs)
plt.title("Image after smoothing with\nk=" + str(k) + " and timestep of dt = " + str(dt))
plt.savefig(os.path.join('Results', 'numpy_solver.png'), format='png')
plt.subplot(1, 1, 1)
plt.plot(errs)
plt.legend(["Similarity index", "Variance ratio"])
plt.title("Evolution of similarity")
plt.savefig(os.path.join('Results', 'numpy_solver_graph.png'),format='png')
if __name__ == '__main__':
# Store Image as a numpy array:
im = cv2.imread("images/Test-img.png")
xs = im.copy().astype(int)
# Add noise to the image:
added_error = 50
noise = np.random.randint(-added_error, added_error, xs.shape)
xs[1:-1, 1:-1] = xs[1:-1, 1:-1] + noise[1:-1, 1:-1]
start = xs
err = ssim(im, xs, data_range=xs.max() - xs.min(), multichannel=True)
err2 = np.var(im) / np.var(xs)
errs = [[err, err2]]
a = 0
for i in range(200):
xs = spread_colours(xs)
prev = err
err = ssim(im, xs, data_range=xs.max() - xs.min(), multichannel=True)
err2 = np.var(im) / np.var(xs)
errs.append([err, err2])
if prev > err:
a = a + 1
else:
a = 0
if (prev > err) and (a == 1):
print("Only did " + str(i) + " runs")
print("Similarity value = " + str(err))
break
plt.figure(figsize=(20, 20))
plt.subplot(1, 3, 1)
plt.imshow(start)
plt.title("Image after +-" + str(added_error) + " of added noise")
plt.subplot(1, 3, 2)
plt.imshow(xs)
plt.title("Image after smoothing with\nk=" + str(k) + " and timestep of dt = " + str(dt))
ax3 = plt.subplot(1, 3, 3)
ax3.plot(errs)
ax3.set_aspect("auto")
plt.legend(["Similarity index", "Variance ratio"])
plt.title("Evolution of similarity")
plt.show()