Convolution on Python - python

So I suppose to calculate the convolution between Fourier Transformed image and the mask.
from scipy import fftpack
import numpy as np
import imageio
from PIL import Image, ImageDraw
import cv2
import matplotlib.pyplot as plt
import math
from scipy.ndimage.filters import convolve
input_image = Image.open('....image....')
input_image=np.array(input_image)
M,N = input_image.shape[0],input_image.shape[1]
FT_img = fftpack.fftshift(fftpack.fft2(input_image))
n = 2; # order value can change this value accordingly
D0 = 60; # cut-off frequency can change this value accordingly
# Designing filter
u = np.arange(0, M)
idx = u > M/2
u[idx] = u[idx] - M
v = np.arange(0, N)
idy = v > N/2
v[idy] = v[idy] - N
V,U = np.meshgrid(v,u)
# Calculating Euclidean Distance
D=np.linalg.norm(V-U)
# determining the filtering mask
H = 1/(1 + (D0/D)**(2*n));
# Convolution between the Fourier Transformed image and the mask
G = convolve(H, FT_img)
And I get "Runtime error:filter weights array has incorrect shape." error at the last line when I run this code snippet. What I understand is H is float and FT_img is array so I cannot perform convolution on these. But I don't know how to solve that.
How can I solve this problem?

calculating distance D, and filter H for each (u, v) this will yield an array with same size of input image, multiplying that array(H the Filter) with the image in Fourier Domain will be equivalent to convolution in the Time domain, and the results will be as following:
import numpy as np
import cv2
import matplotlib.pyplot as plt
# Read Image as Grayscale
img = cv2.imread('input.png', 0)
# Designing filter
#------------------------------------------------------
def butterworth_filter(shape, n=2, D0=60):
'''
n = 2; # order value can change this value accordingly
D0 = 60; # cut-off frequency can change this value accordingly
'''
M, N = shape
# Initialize filter with zeros
H = np.zeros((M, N))
# Traverse through filter
for u in range(0, M):
for v in range(0, N):
# Get euclidean distance from point D(u,v) to the center
D_uv = np.sqrt((u - M / 2) ** 2 + (v - N / 2) ** 2)
# determining the filtering mask
H[u, v] = 1/(1 + (D0/D_uv)**(2*n))
return H
#-----------------------------------------------------
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
phase_spectrumR = np.angle(fshift)
magnitude_spectrum = 20*np.log(np.abs(fshift))
# Generate Butterworth Filter
H = butterworth_filter(img.shape)
# Convolution between the Fourier Transformed image and the mask
G = H * fshift
# Obtain the Result
result = np.abs(np.fft.ifft2(np.fft.ifftshift((G))))
plt.subplot(222)
plt.imshow(img, cmap='gray')
plt.title('Original')
plt.axis('off')
plt.subplot(221)
plt.imshow(magnitude_spectrum, cmap='gray')
plt.title('magnitude spectrum')
plt.axis('off')
plt.subplot(223)
plt.imshow(H, "gray")
plt.title("Butterworth Filter")
plt.axis('off')
plt.subplot(224)
plt.imshow(result, "gray")
plt.title("Result")
plt.axis('off')
plt.show()

Related

vectorizing custom python function with numpy array

Not sure if that is the correct terminology. Basically trying to take a black and white image and first transform it such that all the white pixels that border black-pixels remain white, else turn black. That part of the program works fine, and is done in find_edges. Next I need to calculate the distance from each element in the image to the closest white-pixel. Right now I am doing it by using a for-loop that is insanely slow. Is there a way to make the find_nearest_edge function written solely with numpy without the need for a for-loop to call it on each element? Thanks.
####
from PIL import Image
import numpy as np
from scipy.ndimage import binary_erosion
####
def find_nearest_edge(arr, point):
w, h = arr.shape
x, y = point
xcoords, ycoords = np.meshgrid(np.arange(w), np.arange(h))
target = np.sqrt((xcoords - x)**2 + (ycoords - y)**2)
target[arr == 0] = np.inf
shortest_distance = np.min(target[target > 0.0])
return shortest_distance
def find_edges(img):
img = img.convert('L')
img_np = np.array(img)
kernel = np.ones((3,3))
edges = img_np - binary_erosion(img_np, kernel)*255
return edges
a = Image.open('a.png')
x, y = a.size
edges = find_edges(a)
out = Image.fromarray(edges.astype('uint8'), 'L')
out.save('b.png')
dists =[]
for _x in range(x):
for _y in range(y):
dist = find_nearest_edge(edges,(_x,_y))
dists.append(dist)
print(dists)
Images:
You can use KDTree to compute distances fast.
import numpy as np
import matplotlib.pyplot as plt
from scipy.ndimage import binary_erosion
from scipy.spatial import KDTree
def find_edges(img):
img_np = np.array(img)
kernel = np.ones((3,3))
edges = img_np - binary_erosion(img_np, kernel)*255
return edges
def find_closest_distance(img):
# NOTE: assuming input is binary image and white is any non-zero value!
white_pixel_points = np.array(np.where(img))
tree = KDTree(white_pixel_points.T)
img_meshgrid = np.array(np.meshgrid(np.arange(img.shape[0]), np.arange(img.shape[1]))).T
distances, _ = tree.query(img_meshgrid)
return distances
test_image = np.zeros((200, 200))
rectangle = np.ones((30, 80))
test_image[20:50, 60:140] = rectangle
test_image[150:180, 60:140] = rectangle
test_image[60:140, 20:50] = rectangle.T
test_image[60:140, 150:180] = rectangle.T
test_image = test_image * 255
edge_image = find_edges(test_image)
distance_image = find_closest_distance(edge_image)
fig, axes = plt.subplots(1, 3, figsize=(12, 5))
axes[0].imshow(test_image, cmap='Greys_r')
axes[1].imshow(edge_image, cmap='Greys_r')
axes[2].imshow(distance_image, cmap='Greys_r')
plt.show()
You can make your code 25X faster by just changing find_nearest_edge as follows. Many other optimizations are possible, but this is the biggest bottleneck in your code.
from numba import njit
#njit
def find_nearest_edge(arr, point):
x, y = point
shortest_distance = np.inf
for i in range(arr.shape[0]):
for j in range(arr.shape[1]):
if arr[i,j] == 0: continue
shortest_distance = min(shortest_distance, (i-x)**2 + (j-y)**2)
return np.sqrt(shortest_distance)

Why is ifft2(fft2(g)) different from g in NumPy?

I made an object g representing a plane wave through a circular aperture with a zero phase.
When I take the inverse Fourier transform of the Fourier transform of g, the result is different from g itself. The phase is very noisy.
Why? And how can I improve it?
import numpy as np
# object (circular aperture)
M, N = 256, 256 # resolution
D = M/2
Xo, Yo = np.ogrid[:M,:N]
r = np.sqrt( (Xo-M//2)**2 + (Yo-N//2)**2 )
amplitude = r <= D/2. # real part
g = amplitude * np.exp(1j * 0) # I set the phase at zero (imaginary part)
G = np.fft.fft2(g)
gi = np.fft.ifft2(G)
See attached plot:

How do I calculate the dot product between one vector and a list of vectors?

I have a vector and the Eigenvectors of the matrix H, id like to find the dot product on V1 with every vector in the array.
I want to multiply the vector PhiZero with every eigenvector that's calculated for H
import numpy as np
import scipy.linalg as la
import math
import networkx as nx
Alpha = []
n=3
p=0.5
G = nx.gnp_random_graph(n,p)
A = nx.to_numpy_matrix(G)
w = np.zeros(shape=(n,n))
w[1,2] = 1
gamma = 1/(n*p)
H = (-w) - (gamma * A)
ive chosen a random position in w,
evals, evecs = la.eig(H)
PhiZero = np.reciprocal(math.sqrt(n)) * np.ones((n,1), dtype=int)
i've tried to calculate it two ways, the first way, I get a 3x3 matrix
Alpha = np.dot(PhiZero.transpose(), evecs)
the other way, I tried it with a for loop:
for y in evecs:
alphaJ = np.dot(PhiZero.transpose(), evecs)
Alpha.append(alphaJ)
i've taken the transpose PhiZero to make the dimensions align with evecs (1x3 & 3x1)
In your second approach, should you not have:
alphaJ = np.dot(PhiZero.transpose(), y)
rather than
alphaJ = np.dot(PhiZero.transpose(), evecs)
?
If I try your first example, it works:
import numpy as np
import scipy.linalg as la
import math
import networkx as nx
Alpha = []
n=3
p=0.5
G = nx.gnp_random_graph(n,p)
A = nx.to_numpy_matrix(G)
w = np.zeros(shape=(n,n))
w[1,2] = 1
gamma = 1/(n*p)
H = (-w) - (gamma * A)
evals, evecs = la.eig(H)
PhiZero = np.reciprocal(math.sqrt(n)) * np.ones((n,1), dtype=int)
Alpha = np.dot(PhiZero.transpose(), evecs)
print(Alpha)
gives
[[ 0.95293215 -0.32163376 0.03179978]]
Are you sure you get a 3x3 if you run this?

python program to fade an image in radial direction

I am trying to write a program which fades an image in radial direction. which means as we move away from the centre of the image, the pixels fade to black. For this, I have written five different functions:
center: returns coordinate pair (center_y, center_x) of the image center.
radial_distance: returns for image with width w and height h an array with shape (h,w), where the number at index (i,j) gives the euclidean distance from the point (i,j) to the center of the image.
scale: returns a copy of the array 'a' (or image) with its elements scaled to be in the given range.
radial_mask: takes an image as a parameter and returns an array with same height and width filled with values between 0.0 and 1.0.
radial_fade: returns the image multiplied by its radial mask.
The program is:
import numpy as np
import matplotlib.pyplot as plt
def center(a):
y, x = a.shape[:2]
return ((y-1)/2,(x-1)/2) # note the order: (center_y, center_x)
def radial_distance(b):
h, w = b.shape[:2]
y, x = center(b)
o = b[:h,:w,0]
for i in range(h):
for j in range(w):
o[i,j] = np.sqrt((y-i)**2 + (x-j)**2)
return o
def scale(c, tmin=0.0, tmax=1.0):
"""Returns a copy of array 'a' with its values scaled to be in the range
[tmin,tmax]."""
mini, maxi = c.min(), c.max()
if maxi == 0:
return 0
else:
m = (tmax - tmin)/(maxi - mini)
f = tmin - m*mini
return c*m + f
def radial_mask(d):
f = radial_distance(d)
g = scale(f, tmin=0.0, tmax=1.0)
# f = g[:,:,0]
n = 1.0 - g
return n
def radial_fade(l):
f, g = l.shape[:2]
q = l[:f,:g,0]
return q * radial_mask(l)
image = plt.imread("src/painting.png")
fig, ax = plt.subplots(3)
masked = radial_mask(ima)
faded = radial_fade(ima)
ax[0].imshow(ima)
ax[1].imshow(masked)
ax[2].imshow(faded)
plt.show()
there is something wrong somewhere in the code as it does not do the expected job.
One problem is that in
o = b[:h,:w,0]
you're using the same precision as the image that may be integers (e.h. uint8).
You should use for example
o = np.zeros((h, w), np.float32)

Rectified image: interpolate missing points

I have an image that I try to rotate around an x, y and z axis (rectification). This works fine, but I loose a lot of data. This is the script I use:
# import libraries
import numpy as np
# import dateutil
# import pyparsing
import matplotlib.pyplot as plt
import cv2
import sys
from scipy import *
import Image
import matrotation as rmat
import math
from scipy.interpolate import griddata
# set variable with location of files
working_dir = 'C:\Users\Yorian\Desktop\TU\Stage Shore\python_files\Rectification'
sys.path.append(working_dir)
# C is 3x1 matrix met (Xc, Yc, Zc).transpose()
# neem voor nu: C is nulvector
C = np.zeros((3,1), dtype='float32')
# 3x3 Identity matrix
I = np.identity(3)
# k matrix 3x3, LOAD the center pixel automatically as the point to rate around
K = np.array([[1.49661077e+04, -4.57744650e-13, 0.0],
[0.0, -1.49661077e+04, 0.0],
[0.0, 0.0, 1.0]])
# rotatie matrix 1 (3x3) 0 graden om zowel x, y als z as
R1 = rmat.getR(25.0, 45.0, 0.0)
# [I|-C] (Zie Sierds paper) =
I_extended = np.hstack((I,C))
# P = K*R*I
P1 = K.dot(R1).dot(I_extended)
# rotatie matrix 2
R2 = rmat.getR(0.0, 0.0, 0.0)
P2 = K.dot(R2).dot(I_extended)
# Homography Matrix = H = P_rect * pinv(P) => P2 * pinv(P1)
H = P2.dot(np.linalg.pinv(P1))
# do image transform: x_uv_new = H * x_uv_original
# load image and convert it to grayscale (L)
img = Image.open('c5.jpg').convert('L')
# img.show()
img_array = np.array(img)
height = img_array.shape[0]
width = img_array.shape[1]
U, V = np.meshgrid(range(img_array.shape[1]),
range(img_array.shape[0]))
UV = np.vstack((U.flatten(),
V.flatten())).T
UV_warped = cv2.perspectiveTransform(np.array([UV]).astype(np.float32), H)
UV_warped = UV_warped[0]
UV_warped = UV_warped.astype(np.int)
x_translation = min(UV_warped[:,0])
y_translation = min(UV_warped[:,1])
new_width = np.amax(UV_warped[:,0])-np.amin(UV_warped[:,0])
new_height = np.amax(UV_warped[:,1])-np.amin(UV_warped[:,1])
# new_img_2 = cv2.warpPerspective(img_array, H, (new_height+1, new_width+1))
UV_warped[:,0] = UV_warped[:,0] - int(x_translation)
UV_warped[:,1] = UV_warped[:,1] - int(y_translation)
# create box for image
new_img = np.zeros((new_height+1, new_width+1)) # 0 = black 255 - white background
for uv_pix, UV_warped_pix in zip(UV, UV_warped):
x_orig = uv_pix[0] # x in origineel
y_orig = uv_pix[1] # y in origineel
color = img_array[y_orig, x_orig]
x_new = UV_warped_pix[0] # new x
y_new = UV_warped_pix[1] # new y
new_img[y_new, x_new] = np.array(color)
img = Image.fromarray(np.uint8(new_img))
img.save("testje.jpg")
This works fine. However I miss a lot of information. The larger the rotations the more information I loose. To get more information back I want to: interpolate the missing points. I tried to do this using grid(), but it returns an array that looks like this:
[nan]
The code for this:
# import libraries
import numpy as np
# import dateutil
# import pyparsing
import matplotlib.pyplot as plt
import cv2
import sys
from scipy import *
import Image
import matrotation as rmat
import math
from scipy.interpolate import griddata
# set variable with location of files
working_dir = 'C:\Users\Yorian\Desktop\TU\Stage Shore\python_files\Rectification'
sys.path.append(working_dir)
# C is 3x1 matrix met (Xc, Yc, Zc).transpose()
# neem voor nu: C is nulvector
C = np.zeros((3,1), dtype='float32')
# 3x3 Identity matrix
I = np.identity(3)
# k matrix 3x3, LOAD the center pixel automatically as the point to rate around
K = np.array([[1.49661077e+04, -4.57744650e-13, 0.0],
[0.0, -1.49661077e+04, 0.0],
[0.0, 0.0, 1.0]])
# rotatie matrix 1 (3x3) 0 graden om zowel x, y als z as
R1 = rmat.getR(25.0, 45.0, 0.0)
# [I|-C] (Zie Sierds paper) =
I_extended = np.hstack((I,C))
# P = K*R*I
P1 = K.dot(R1).dot(I_extended)
# rotatie matrix 2
R2 = rmat.getR(0.0, 0.0, 0.0)
P2 = K.dot(R2).dot(I_extended)
# Homography Matrix = H = P_rect * pinv(P) => P2 * pinv(P1)
H = P2.dot(np.linalg.pinv(P1))
# do image transform: x_uv_new = H * x_uv_original
# load image and convert it to grayscale (L)
img = Image.open('c5.jpg').convert('L')
# img.show()
img_array = np.array(img)
height = img_array.shape[0]
width = img_array.shape[1]
U, V = np.meshgrid(range(img_array.shape[1]),
range(img_array.shape[0]))
UV = np.vstack((U.flatten(),
V.flatten())).T
UV_warped = cv2.perspectiveTransform(np.array([UV]).astype(np.float32), H)
UV_warped = UV_warped[0]
UV_warped = UV_warped.astype(np.int)
x_translation = min(UV_warped[:,0])
y_translation = min(UV_warped[:,1])
new_width = np.amax(UV_warped[:,0])-np.amin(UV_warped[:,0])
new_height = np.amax(UV_warped[:,1])-np.amin(UV_warped[:,1])
UV_warped[:,0] = UV_warped[:,0] - int(x_translation)
UV_warped[:,1] = UV_warped[:,1] - int(y_translation)
# create box for image
data = np.zeros((len(UV_warped),1))
for i, uv_pix in enumerate(UV):
data[i,0] = img_array[uv_pix[1], uv_pix[0]]
grid = griddata(UV_warped, data, (new_width+1, new_height+1), method='linear')
Can anybody help me to get an image from this that is interpolated?
BTW: I used the function warpPerspective as someone has told me, but this stretches the image but doesn't "rotate" it.
I also looked at cv2.inpaint() but can't get that to work either. I found this: http://opencv-python-tutroals.readthedocs.org/en/latest/py_tutorials/py_photo/py_inpainting/py_inpainting.html but that plots it. I want to make an image of it.
EDIT:
The code I used to do this with warpTransform:
#Importing modules
import json
import urllib2
import numpy as np
import cv2
from scipy import *
import Image
# data is now a dictionairy containing list with dictionairies with the x, y, z, U, V
# example:
# data[cameraID][listnumber] = {'x': x, 'y': y, 'z': z, 'U': U, 'V': V}
T = {} # H is a list of Translation matrices, one for each camera
for cam in data:
if len(cam) > 4:
xyz_ar = np.array([[data[cam][0]['x'], data[cam][0]['y']],
[data[cam][1]['x'], data[cam][1]['y']],
[data[cam][2]['x'], data[cam][2]['y']],
[data[cam][3]['x'], data[cam][3]['y']]],np.float32)
UV_ar = np.array([[data[cam][0]['U'], data[cam][0]['V']],
[data[cam][1]['U'], data[cam][1]['V']],
[data[cam][2]['U'], data[cam][2]['V']],
[data[cam][3]['U'], data[cam][3]['V']]], np.float32)
T[cam] = cv2.getPerspectiveTransform(UV_ar, xyz_ar)
else:
print('niet genoeg meetpunten voor de camera')
# load image
img = cv2.imread('c5.jpg')
rows, cols, channels = img.shape
# warp voor camera 5
dst = cv2.warpPerspective(img, T[u'KDXX05C'], (rows, cols))
new_img = Image.fromarray(np.uint8(dst))
new_img.save('testje.jpg')
I am still convinced that warpPerspective does exactly what you want (jedi mind trick). Seriously, it should do in one line what you are trying to achieve with the meshgrid, vstack and griddata.
Can you try the following code ? (I am not familiar with Python, so this might require some adjustements) :
# load image and convert it to grayscale (L)
img = cv2.imread('c5.jpg')
rows, cols, channels = img.shape
# img.show()
# Homography Matrix = H = P_rect * pinv(P) => P2 * pinv(P1)
H = P2.dot(np.linalg.pinv(P1))
cv2.warpPerspective(img, H, (rows, cols), dst, cv2.INTER_LINEAR)
new_img = Image.fromarray(np.uint8(dst))
new_img.save('testje.jpg')
where H is the exact same matrix as you use in the first code sample you gave.
The third argument of griddata is a (M,D) shaped array of locations that need interpolation (D=2 here). You are inputting a tuple of (width, height), so that is probably why you get a [nan] array.

Categories