I recently used a Gaussian convolution to blur an image. It works well, in fact it works excellently. However, now I'm trying to reverse it and my inverse filter keeps running into problems. The filter is supposed to run on the following principle:
G(x, y) / H(x, y) = F(x, y) where G(x, y) is the Fourier transform of the blurred image, H(x, y) is the Fourier transform of the blurring function and F(x, y) is the Fourier transform of the original image.
Currently, the resulting image looks exactly the same as the original. My algorithm is as follows:
from PIL import Image
import math
import cmath
import numpy as np
def reverseGaussianBlur(picture, r):
rs = int(math.ceil(r * 2.57) # Calculate significant radius
w, h = picture.size
pixels = list(picture.getdata()) # Image's pixels as list
fft_pixels = runFourier(pixels, False) # Run FFT
temp_picture = []
for u in range(0, h):
for v in range(0, w):
val = [0] * 3
wsum = 0
for iy in range(u - rs, u + rs + 1):
y = min(h - 1, max(0, iy))
for ix in range(v - rs, v + rs + 1):
x = min(w - 1, max(0, ix))
weight = (2 * math.pi) ** 0.5 * cmath.exp(-r * r * ((ix - v) *
(ix - v) + (iy - u) * (iy - u)) / 2)
if (weight.real > 1e-5):
val = [n + p / weight for n, p in zip(val, fft_pixels[y * w + x])]
wsum += weight
temp_picture.append(tuple([v * wsum for v in val]))
return_picture = [tuple(int(round(p)) for p in pixel) for pixel in
runFourier(temp_picture, True)] # Run Inverse FFT
return return_picture
Anyway, I'm not quite sure what's wrong and any help would be great.
When you blur an image, you're basically removing the high frequency components. They're gone. There is no reverse filter.
If you had applied a "filter" that took each pixel and replaced it with flat white, you wouldn't expect there to be a reverse filter for that, because all the details (except the size of the the original image) are lost. It's the same thing with blurring, only you've lost just the high frequency components.
Related
I want to project an image from spherical to cubemap. From what I understood studying maths, I need to create a theta, phi distribution for each pixel and then convert it into cartesian system to get a normalized pixel map.
I used the following code to do so
theta = 0
phi = np.pi/2
squareLength = 2048
# theta phi distribution for X-positive face
t = np.linspace(theta + np.pi/4, theta - np.pi/4, squareLength)
p = np.linspace(phi + np.pi/4, phi - np.pi/4, squareLength)
x, y = np.meshgrid(t, p)
# converting into cartesion sytem for X-positive face (where r is the distance from sphere center to cube plane and X is constantly 0.5 in cartesian system)
X = np.zeros_like(y)
X[:,:] = 0.5
r = X / (np.cos(x) * np.sin(y))
Y = r * np.sin(x) * np.sin(y)
Z = r * np.cos(y)
XYZ = np.stack((X, Y, Z), axis=2)
# shifting pixels from the negative side
XYZ = XYZ + [0, 0.5, 0.5]
# since i want to project on X-positive face my map should be
x_map = -XYZ[:, :, 1] * squareLength
y_map = XYZ[:,:, 2] * squareLength
The above map created should give me my desired result with cv2.remap() but it's not. Then I tried looping through pixels and implement my own remap without interpolation or extrapolation. With some hit and trial, I deduced the following formula which gives me the correct result
for i in range(2048):
for j in range(2048):
try:
image[int(y_map[i,j]), int(x_map[i,j])] = im[i, j]
except:
pass
which is reverse of actual cv2 remapping which says dst(x,y)=src(mapx(x,y),mapy(x,y))
I do not understand if did the math all wrong or is there a way to covert x_map and y_map to correct forms so that cv2.remap() gives the desired result.
INPUT IMAGE
DESIRED RESULT (this one is without interpolation using loops)
CURRENT RESULT (using cv2.remap())
I'm quite new in opencv and I didn't work with so difficult math algorithms before but I tried to do this. I rewrote your code a bit and here it is:
import numpy as np
import cv2
src = cv2.imread("data/pink_sq.png")
def make_map():
theta = 0
phi = np.pi / 2
squareLength = 4000
# theta phi distribution for X-positive face
t = np.linspace((theta - np.pi / 4), (theta + np.pi / 4), squareLength)
p = np.linspace((phi + np.pi / 4), (phi - np.pi / 4), squareLength)
x, y = np.meshgrid(t, p)
x_res = np.zeros_like(y)
x_res[:, :] = 0.5
r = x_res * (np.cos(x))
r /= np.amax(r)
y_res = r * x
z_res = r * np.cos(y)
xyz = np.stack((x_res, y_res, z_res), axis=2)
# shifting pixels from the negative side
xyz = xyz + [0, 0.5, 0.5]
# since i want to project on X-positive face my map should be
x_map = xyz[:, :, 1] * squareLength
y_map = xyz[:, :, 2] * squareLength
map_x = y_map.astype("float32")
map_y = x_map.astype("float32")
return map_x, map_y
map_x, map_y = make_map()
dst = cv2.remap(src, map_y, map_x, cv2.INTER_LINEAR)
cv2.imwrite("res.png", dst)
I don't understand the math in this code at all but I rewrote it a bit and I should say that it works quite good. Here is the result image:
And yes, there is a bit difference between my result picture and yours but I hope it is ok :) If I'm not right somewhere of course downvote this answer because I'm not sure that it is correct one.
I'm almost certain the issue has to do with the orientation of the reference frame in space. Maybe if you clarify the Math a bit we can help.
I am trying to implement a simple version of the immersed boundary method for fluid-structure interactions in a periodic domain using the fast Fourier transform. This is done by first setting a force on the fluid, taking its FFT, solving a series of matrix equations, and taking the inverse FFT to recover the solution to the matrix system in the real domain. I am currently getting very strange results, and I think it might be because the solutions in Fourier space (u_hat, v_hat, p_hat) are not ordered correctly for the numpy ifft2 function, but I don't know how to fix that. I'd appreciate any help!
Edit: the idea is that given f, g, we want to find u, v, p. The algorithm is to take the fft in 2D of f and g, use that to compute the fft of u, v, and p, and then take the inverse fft to recover the real u, v, p. I've added the specific function relating f_hat, g_hat and u_hat, v_hat, p_hat.
Edit2: what I'd really like to know is how to order an arbitrary array so that it can be passed to the numpy ifft2 function, which requires the following: input should have the term for zero frequency in the low-order corner of the two axes, the positive frequency terms in the first half of these axes, the term for the Nyquist frequency in the middle of the axes and the negative frequency terms in the second half of both axes, in order of decreasingly negative frequency.
N = 20 # Number of cartesian grid points
l = 1.0 # Length of domain
dx = l / N # Grid step size
mu = 8.9 * 10**(-4)
f_hat = np.fft.fft2(f) # Fourier transform of x component of the force
g_hat = np.fft.fft2(g) # Fourier transform of y component of the force
u_hat = np.zeros((N, N), dtype=np.complex)
v_hat = np.zeros((N, N), dtype=np.complex)
p_hat = np.zeros((N, N), dtype=np.complex)
for k in range(0, N):
for m in range(0, N):
if k == 0 and m == 0:
u_hat[k,m] = 0.0
v_hat[k,m] = 0.0
p_hat[k,m] = 0.0
else:
u_hat[k,m], v_hat[k,m], p_hat[k,m] = fluid_system(k, m, N, dx, mu, f_hat[k,m], g_hat[k,m])
# Take inverse Fourier transform to get u and p
u = np.real((np.fft.ifft2(u_hat))).reshape((len(x),1))
v = np.real((np.fft.ifft2(v_hat))).reshape((len(x),1))
p = np.real(np.fft.ifft2(p_hat))
def fluid_system(l, m, N, h, mu, fft_f, fft_g):
L = - 4.0 / (h**2) * ((np.sin(np.pi * l / N))**2 + (np.sin(np.pi * m / N))**2)
D_l = - 1j / h * np.sin(2.0 * np.pi * l / N)
D_m = - 1j / h * np.sin(2.0 * np.pi * m / N)
A = np.zeros((3,3), dtype=np.complex)
A[0,0] = mu * L
A[0,2] = - D_l
A[1,1] = mu * L
A[1,2] = - D_m
A[2,0] = D_l
A[2,1] = D_m
b = np.zeros((3,1), dtype=np.complex)
b[0] = fft_f
b[1] = fft_g
res = (np.linalg.solve(A, b)).ravel()
return res
I will very briefly try to explain what I'm doing to those who are less experienced with mathematics, it's really quite simple.
We are trying to fill a grid, as follows:
We find the orange point, U(j,n+1), using three points in a row below it, U(j-1,n), U(j,n), U(j,n+1)
Where the value of U in the entire bottom row is given, and is periodic. So theoretically we can fill this entire grid.
The formula for calculating the orange point is:
U(j,n+1) = U(j,n) + (delta_t / (2 * delta_x)) * (U(j+1,n) - U(j-1,n))
We can write it easily as a system of linear equations as follows:
And now we just repeat this process of multiplying by this matrix (iterating through the time variable) as much as we want. That's a simple way to numerically approximate a solution to a partial differential equation.
I wrote a code that does this, and then I compare my final row, to the known solution of the differential equation.
This is the code
import math
import numpy
def f(x):
return math.cos(2 * math.pi * x)
def solution(x, t):
return math.cos(2 * math.pi * (x + t))
# setting everything up
N = 16
Lambda = 10 ** (-20)
Delta_x = 1/(N+1)
Delta_t = Lambda * Delta_x * Delta_x
t_f = 5
v_0 = numpy.zeros((N, 1))
# Filling first row, initial condition was given
for i in range(N):
v_0[i, 0] = f(i * Delta_x)
# Create coefficient matrix
M = numpy.zeros((N, N))
for i in range(N):
M[i, i - 1] = -Delta_t / (2 * Delta_x)
M[i, i] = 1
M[i, (i + 1) % N] = Delta_t / (2 * Delta_x)
# start iterating through time
v_i = v_0
for i in range(math.floor(t_f / Delta_t) - 1):
v_i = numpy.dot(M, v_i)
v_final = v_i
if (Delta_t * math.ceil(t_f / Delta_t) != t_f): #we don't reach t_f exactly using Delta_t
v_final = (1/2) * (v_i + numpy.dot(M, v_i))
u = numpy.zeros(v_final.shape)
for i in range(N):
u[i, 0] = solution(i * Delta_x, t_f)
for x in range(v_final.shape[0]):
print (v_final[x], u[x])
theoretically speaking, I should be able to find lambda small enough such that v_final and the known solution, u, will be very similar.
But I can't. No matter how small I make lambda, how finde I make the grid, I seem to converge to something incorrect. They aren't close.
I can't for the life of me figure out the problem.
Does anyone have an idea what might be wrong?
You should have Delta_x = 1.0/N, as you divide the interval into N cells.
You get N+1 points on the grid from u[0] to u[N], but as per boundary condition u[N]=u[0], there you also only use an array of length N to hold all the node values.
Per your given formulas you have gamma = dt/(2*dx), thus the reverse computation should be dt = gamma*2*dx or in your variable names
Delta_t = Lambda * 2 * Delta_x
Or you are aiming at the error of the method which is O(dt, dx²) so that it would make sense to have dt = c*dx^2, but not with a ridiculous factor like of c=1e-20, if you want the time discretization error small against the space discretization error, c=0.1 or c=0.01 should be sufficient.
import numpy as np
def f(x):
return np.cos(2 * np.pi * x)
def solution(x, t):
return f(x + t)
# setting everything up
N_x = 16
Lambda = 1e-2
Delta_x = 1./N_x
Delta_t = Lambda * Delta_x * Delta_x
t_f = 5
N_t = int(t_f/Delta_t+0.5); t_f = N_t*Delta_t
# Filling first row, initial condition was given
x = np.arange(0,N_x,1) * Delta_x
v_0 = f(x)
# Create coefficient matrix
M = np.zeros((N_x, N_x))
for i in range(N_x):
M[i, i - 1] = -Delta_t / (2 * Delta_x)
M[i, i] = 1
M[i, (i + 1) % N_x] = Delta_t / (2 * Delta_x)
# start iterating through time
v_i = v_0[:]
for i in range(N_t):
v_i = np.dot(M, v_i)
v_final = v_i
u = solution(x, t_f)
for vx, ux in zip(v_final, u):
print (vx, ux)
The Euler method is also not the most precise method, the expected error is in the range exp(L*t_f)*dx^2 = e^5/N_x^2=0.58 for N_x=16 where L=1 was taken as approximate Lipschitz constant. Now if you increase to N_x=50 this error estimate reduces to 0.06 which is also visible in the results.
The t exact solution of the x discretized problem is cos(2*pi*(x+c*t)) where c=sin(2*pi*dx)/(2*pi*dx). If you compare against that formula, the errors should be really small of size O(dt).
I want to generate x and y having a uniform distribution and limited by [xmin,xmax] and [ymin,ymax]
The points (x,y) should be inside a triangle.
How can I solve such a problem?
Here's some code that generates points uniformly on an arbitrary triangle in the plane.
import random
def point_on_triangle(pt1, pt2, pt3):
"""
Random point on the triangle with vertices pt1, pt2 and pt3.
"""
x, y = sorted([random.random(), random.random()])
s, t, u = x, y - x, 1 - y
return (s * pt1[0] + t * pt2[0] + u * pt3[0],
s * pt1[1] + t * pt2[1] + u * pt3[1])
The idea is to compute a weighted average of the three vertices, with the weights given by a random break of the unit interval [0, 1] into three pieces (uniformly over all such breaks). Here x and y represent the places at which we break the unit interval, and s, t and u are the length of the pieces following that break. We then use s, t and u as the barycentric coordinates of the point in the triangle.
Here's a variant of the above that avoids the need to sort, instead making use of an absolute value call:
def point_on_triangle2(pt1, pt2, pt3):
"""
Random point on the triangle with vertices pt1, pt2 and pt3.
"""
x, y = random.random(), random.random()
q = abs(x - y)
s, t, u = q, 0.5 * (x + y - q), 1 - 0.5 * (q + x + y)
return (
s * pt1[0] + t * pt2[0] + u * pt3[0],
s * pt1[1] + t * pt2[1] + u * pt3[1],
)
Here's an example usage that generates 10000 points in a triangle:
pt1 = (1, 1)
pt2 = (2, 4)
pt3 = (5, 2)
points = [point_on_triangle(pt1, pt2, pt3) for _ in range(10000)]
And a plot obtained from the above, demonstrating the uniformity. The plot was generated by this code:
import matplotlib.pyplot as plt
x, y = zip(*points)
plt.scatter(x, y, s=0.1)
plt.show()
Here's the image:
And since you tagged the question with the "numpy" tag, here's a NumPy version that generates multiple samples at once. Note that it uses the matrix multiplication operator #, introduced in Python 3.5 and supported in NumPy >= 1.10. You'll need to replace that with a call to np.dot on older Python or NumPy versions.
import numpy as np
def points_on_triangle(v, n):
"""
Give n random points uniformly on a triangle.
The vertices of the triangle are given by the shape
(2, 3) array *v*: one vertex per row.
"""
x = np.sort(np.random.rand(2, n), axis=0)
return np.column_stack([x[0], x[1]-x[0], 1.0-x[1]]) # v
# Example usage
v = np.array([(1, 1), (2, 4), (5, 2)])
points = points_on_triangle(v, 10000)
Ok, time to add another version, I guess. There is known algorithm to sample uniformly in triangle, see paper, chapter 4.2 for details.
Python code:
import math
import random
import matplotlib.pyplot as plt
def trisample(A, B, C):
"""
Given three vertices A, B, C,
sample point uniformly in the triangle
"""
r1 = random.random()
r2 = random.random()
s1 = math.sqrt(r1)
x = A[0] * (1.0 - s1) + B[0] * (1.0 - r2) * s1 + C[0] * r2 * s1
y = A[1] * (1.0 - s1) + B[1] * (1.0 - r2) * s1 + C[1] * r2 * s1
return (x, y)
random.seed(312345)
A = (1, 1)
B = (2, 4)
C = (5, 2)
points = [trisample(A, B, C) for _ in range(10000)]
xx, yy = zip(*points)
plt.scatter(xx, yy, s=0.2)
plt.show()
And result looks like
Uniform on the triangle?
import numpy as np
N = 10 # number of points to create in one go
rvs = np.random.random((N, 2)) # uniform on the unit square
# Now use the fact that the unit square is tiled by the two triangles
# 0 <= y <= x <= 1 and 0 <= x < y <= 1
# which are mapped onto each other (except for the diagonal which has
# probability 0) by swapping x and y.
# We use this map to send all points of the square to the same of the
# two triangles. Because the map preserves areas this will yield
# uniformly distributed points.
rvs = np.where(rvs[:, 0, None]>rvs[:, 1, None], rvs, rvs[:, ::-1])
Finally, transform the coordinates
xmin, ymin, xmax, ymax = -0.1, 1.1, 2.0, 3.3
rvs = np.array((ymin, xmin)) + rvs*(ymax-ymin, xmax-xmin)
Uniform marginals? The simplest solution would be to uniformly concentrate the mass on the line (ymin, xmin) - (ymax, xmax)
rvs = np.random.random((N,))
rvs = np.c_[ymin + (ymax-ymin)*rvs, xmin + (xmax-xmin)*rvs]
but that is not very interesting, is it?
I try to implement the Fourier series function according to the following formulas:
...where...
...and...
Here is my approach to the problem:
import numpy as np
import pylab as py
# Define "x" range.
x = np.linspace(0, 10, 1000)
# Define "T", i.e functions' period.
T = 2
L = T / 2
# "f(x)" function definition.
def f(x):
return np.sin(np.pi * 1000 * x)
# "a" coefficient calculation.
def a(n, L, accuracy = 1000):
a, b = -L, L
dx = (b - a) / accuracy
integration = 0
for i in np.linspace(a, b, accuracy):
x = a + i * dx
integration += f(x) * np.cos((n * np.pi * x) / L)
integration *= dx
return (1 / L) * integration
# "b" coefficient calculation.
def b(n, L, accuracy = 1000):
a, b = -L, L
dx = (b - a) / accuracy
integration = 0
for i in np.linspace(a, b, accuracy):
x = a + i * dx
integration += f(x) * np.sin((n * np.pi * x) / L)
integration *= dx
return (1 / L) * integration
# Fourier series.
def Sf(x, L, n = 10):
a0 = a(0, L)
sum = 0
for i in np.arange(1, n + 1):
sum += ((a(i, L) * np.cos(n * np.pi * x)) + (b(i, L) * np.sin(n * np.pi * x)))
return (a0 / 2) + sum
# x axis.
py.plot(x, np.zeros(np.size(x)), color = 'black')
# y axis.
py.plot(np.zeros(np.size(x)), x, color = 'black')
# Original signal.
py.plot(x, f(x), linewidth = 1.5, label = 'Signal')
# Approximation signal (Fourier series coefficients).
py.plot(x, Sf(x, L), color = 'red', linewidth = 1.5, label = 'Fourier series')
# Specify x and y axes limits.
py.xlim([0, 10])
py.ylim([-2, 2])
py.legend(loc = 'upper right', fontsize = '10')
py.show()
...and here is what I get after plotting the result:
I've read the How to calculate a Fourier series in Numpy? and I've implemented this approach already. It works great, but it use the expotential method, where I want to focus on trigonometry functions and the rectangular method in case of calculating the integraions for a_{n} and b_{n} coefficients.
Thank you in advance.
UPDATE (SOLVED)
Finally, here is a working example of the code. However, I'll spend more time on it, so if there is anything that can be improved, it will be done.
from __future__ import division
import numpy as np
import pylab as py
# Define "x" range.
x = np.linspace(0, 10, 1000)
# Define "T", i.e functions' period.
T = 2
L = T / 2
# "f(x)" function definition.
def f(x):
return np.sin((np.pi) * x) + np.sin((2 * np.pi) * x) + np.sin((5 * np.pi) * x)
# "a" coefficient calculation.
def a(n, L, accuracy = 1000):
a, b = -L, L
dx = (b - a) / accuracy
integration = 0
for x in np.linspace(a, b, accuracy):
integration += f(x) * np.cos((n * np.pi * x) / L)
integration *= dx
return (1 / L) * integration
# "b" coefficient calculation.
def b(n, L, accuracy = 1000):
a, b = -L, L
dx = (b - a) / accuracy
integration = 0
for x in np.linspace(a, b, accuracy):
integration += f(x) * np.sin((n * np.pi * x) / L)
integration *= dx
return (1 / L) * integration
# Fourier series.
def Sf(x, L, n = 10):
a0 = a(0, L)
sum = np.zeros(np.size(x))
for i in np.arange(1, n + 1):
sum += ((a(i, L) * np.cos((i * np.pi * x) / L)) + (b(i, L) * np.sin((i * np.pi * x) / L)))
return (a0 / 2) + sum
# x axis.
py.plot(x, np.zeros(np.size(x)), color = 'black')
# y axis.
py.plot(np.zeros(np.size(x)), x, color = 'black')
# Original signal.
py.plot(x, f(x), linewidth = 1.5, label = 'Signal')
# Approximation signal (Fourier series coefficients).
py.plot(x, Sf(x, L), '.', color = 'red', linewidth = 1.5, label = 'Fourier series')
# Specify x and y axes limits.
py.xlim([0, 5])
py.ylim([-2.2, 2.2])
py.legend(loc = 'upper right', fontsize = '10')
py.show()
Consider developing your code in a different way, block by block. You should be surprised if a code like this would work at the first try. Debugging is one option, as #tom10 said. The other option is rapid prototyping the code step by step in the interpreter, even better with ipython.
Above, you are expecting that b_1000 is non-zero, since the input f(x) is a sinusoid with a 1000 in it. You're also expecting that all other coefficients are zero right?
Then you should focus on the function b(n, L, accuracy = 1000) only. Looking at it, 3 things are going wrong. Here are some hints.
the multiplication of dx is within the loop. Sure about that?
in the loop, i is supposed to be an integer right? Is it really an integer? by prototyping or debugging you would discover this
be careful whenever you write (1/L) or a similar expression. If you're using python2.7, you're doing likely wrong. If not, at least use a from __future__ import division at the top of your source. Read this PEP if you don't know what I am talking about.
If you address these 3 points, b() will work. Then think of a in a similar fashion.