In image processing, the convolution with a small matrix is used for blurring, sharpening, embossing, edge-detection, and more. This is accomplished by means of convolution between a kernel and an image. http://en.wikipedia.org/wiki/Kernel_%28image_processing%29
In this exercise we will focus on the gaussian blur and compare the execution time of two appoaches: direct space vs Fourier space for different width of gaussians: 1, 2, 4, 8, and 16pixels.
The raw image used is the famous 512x512 pixel image from Lena, and available as part of scipy.
In [1]:
%pylab inline
In [2]:
from scipy.misc import lena
img = lena()
imshow(img, cmap="gray")
Out[2]:
In [3]:
from IPython.display import Image
Image(filename="files/gaussian.png")
Out[3]:
In [4]:
from scipy.signal import gaussian
plot(gaussian(21,1))
Out[4]:
Choice of the window size ?
In [5]:
(gaussian(21,1)>1e-4).sum()
Out[5]:
It is important to have an odd window size, we choose w = 8 sigma+1.
Scipy.signal has a special function for direct space convolution: sepfir2d https://docs.scipy.org/doc/scipy-0.7.x/reference/generated/scipy.signal.sepfir2d.html
In [6]:
from scipy.signal import sepfir2d
def direct(img, sigma):
gaus = gaussian(8*sigma+1, sigma)
return sepfir2d(img, gaus, gaus)
In [7]:
imshow(direct(img,5),cmap="gray")
Out[7]:
In [8]:
%timeit direct(img,2)
The convolution in direct space is equivalent to a multiplication in Fourier space. http://en.wikipedia.org/wiki/Convolution_theorem
In [9]:
from IPython.display import Image
Image(filename="files/convolution.png")
Out[9]:
This is especially appealing for gaussian blurring as the FT of a gaussian is a gaussian. The total cost is reduced to one FT and its inverse.
In [10]:
from scipy.fftpack import fft2, ifft2, fftshift
def ft(img, sigma):
size = len(img)
gaus = gaussian(size, size / (2*pi*sigma))
sgau = fftshift(gaus)
return ifft2(fft2(img) * outer(sgau,sgau)).real
In [11]:
imshow(ft(img,5),cmap="gray")
Out[11]:
In [12]:
%timeit ft(img,2)
In [13]:
import time
sigmas = []
d = []
f = []
for i in range(5):
sigma = 1<<i
t0 = time.time()
direct(img, sigma)
t1 = time.time()
ft(img, sigma)
t2 = time.time()
print("sigma=%s \t Direct: %.3fs \t Fourier: %.3fs"%(sigma,t1-t0,t2-t1))
sigmas.append(sigma)
d.append(t1-t0)
f.append(t2-t1)
plot(sigmas,d,label="Direct")
plot(sigmas,f,label="Fourier")
legend()
Out[13]:
In [14]:
from scipy.ndimage import filters
In [15]:
%timeit filters.gaussian_filter(img, 2)
In [16]:
%timeit filters.gaussian_filter(img, 4)
In [17]:
import time
from scipy.ndimage import filters
sigmas = []
d = []
f = []
n = []
for i in range(5):
sigma = 1<<i
t0 = time.time()
direct(img, sigma)
t1 = time.time()
ft(img, sigma)
t2 = time.time()
filters.gaussian_filter(img, sigma)
t3=time.time()
print("sigma=%s \t Direct: %.3fs \t Fourier: %.3fs, ndimage: %.3fs"%(sigma,t1-t0,t2-t1, t3-t2))
sigmas.append(sigma)
d.append(t1-t0)
f.append(t2-t1)
n.append(t3-t2)
plot(sigmas,d,label="Direct")
plot(sigmas,f,label="Fourier")
plot(sigmas,n,label="Ndimage")
legend()
Out[17]:
In [17]: