%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import skimage as ski
from skimage.morphology import disk
import scipy.ndimage as ndimage
plt.figure(figsize=[15,6]);
plt.subplot(1,3,1);plt.imshow(np.random.normal(0,1,[100,100])); plt.title('Gaussian');plt.axis('off');
plt.subplot(1,3,2);plt.imshow(0.90<np.random.uniform(0,1,size=[100,100]),cmap='gray'); plt.title("Salt and pepper"),plt.axis('off');
plt.subplot(1,3,3);plt.imshow(ski.filters.gaussian(np.random.normal(0,1,size=[100,100]),sigma=1),cmap='gray'); plt.title("Structured"),plt.axis('off');
plt.tight_layout()
from scipy.stats import norm
rv = norm(loc = -1., scale = 1.0);rv1 = norm(loc = 0., scale = 2.0); rv2 = norm(loc = 2., scale = 3.0)
x = np.arange(-10, 10, .1)
#plot the pdfs of these normal distributions
plt.plot(x, rv.pdf(x),label='$\mu$=-1, $\sigma$=1')
plt.plot(x, rv1.pdf(x),label='$\mu$=0, $\sigma$=2')
plt.plot(x, rv2.pdf(x),label='$\mu$=2, $\sigma$=3')
plt.legend()
from scipy.stats import poisson
mu=3
fig, ax = plt.subplots(1, 1)
x = np.arange(poisson.ppf(0.01, mu), poisson.ppf(0.999, mu))
ax.plot(x, poisson.pmf(x, mu), 'bo', ms=8, label='poisson pmf')
ax.vlines(x, 0, poisson.pmf(x, mu), colors='b', lw=5, alpha=0.5)
plt.figure(figsize=[15,8])
x=np.linspace(0,2*np.pi,100);
y=10*np.sin(x)+11; ng=np.random.normal(0,1,size=len(x)); npoi = np.random.poisson(y);
plt.subplot(2,2,1); plt.plot(x,y+ng);plt.plot(x,y); plt.axis('off');plt.title('Gaussian'); plt.subplot(2,2,3);plt.plot(x,ng);plt.axis('off');
plt.subplot(2,2,2); plt.plot(x,npoi);plt.plot(x,y); plt.axis('off');plt.title('Poisson'); plt.subplot(2,2,4);plt.plot(x,npoi-y);plt.axis('off');
$sp(x)=\left\{\begin{array}{ll} -1 & x\leq\lambda_1\\ 0 & \lambda_1< x \leq \lambda_2\\ 1 & \lambda_2<x \end{array}\right.\qquad \begin{array}{l}x\in\mathcal{U}(0,1)\\\lambda_1<\lambda_2\\ \lambda_1+\lambda_2 = \mbox{noise fraction} \end{array}$
def snp(dims,Pblack,Pwhite) : # Noise model function
uni=np.random.uniform(0,1,dims)
img=(Pwhite<uni).astype(float)-(uni<Pblack).astype(float)
return img
img10_90=snp([100,100],0.1,0.9); img5_95=snp([100,100],0.05,0.95);img1_99=snp([100,100],0.01,0.99)
plt.figure(figsize=[15,5])
plt.subplot(1,3,1); plt.imshow(img1_99,cmap='gray'); plt.title('$\lambda_1$=1% and $\lambda_2$=99%',fontsize=16); plt.axis('off');
plt.subplot(1,3,2); plt.imshow(img5_95,cmap='gray'); plt.title('$\lambda_1$=5% and $\lambda_2$=95%',fontsize=16); plt.axis('off');
plt.subplot(1,3,3); plt.imshow(img10_90,cmap='gray'); plt.title('$\lambda_1$=10% and $\lambda_2$=90%',fontsize=16); plt.axis('off');
$SNR \sim \sqrt{N}$
exptime=np.array([50,100,200,500,1000,2000,5000,10000])
snr = np.array([ 8.45949767, 11.40011621, 16.38118766, 21.12056507, 31.09116641,40.65323123, 55.60833117, 68.21108979]);
marker_style = dict(color='cornflowerblue', linestyle='-', marker='o',markersize=10, markerfacecoloralt='gray');
plt.figure(figsize=(15,5))
plt.subplot(1,3,2);plt.plot(exptime/1000,snr, **marker_style);plt.xlabel('Exposure time [s]');plt.ylabel('SNR [1]')
img50ms=plt.imread('ext-figures/lecture03/tower_50ms.png'); img10000ms=plt.imread('ext-figures/lecture03/tower_10000ms.png');
plt.subplot(1,3,1);plt.imshow(img50ms); plt.subplot(1,3,3); plt.imshow(img10000ms);
Generate an $m \times n$ random fields with different distributions:
np.random.normal(mu,sigma, size=[rows,cols])
np.random.uniform(low,high,size=[rows,cols])
np.mean(f)
, np.var(f)
, np.std(f)
Computes the mean, variance, and standard deviation of an image $f$.np.min(f)
,np.max(f)
Finds minimum and maximum values in $f$.np.median(f)
, np.rank()
Selects different values from the sorted data.Filters are characterized by the type of information they suppress
Computed using the convolution operation
$$g(x)=h*f(x)=\int_{\Omega}f(x-\tau) h(\tau) d\tau$$where
Mean or Box filter | Gauss filter |
All weights have the same value. | $$G=\exp{-\frac{x^2+y^2}{2\,\sigma^2}}$$ |
Example: $$B=\frac{1}{25}\cdot\begin{array}{|c|c|c|c|c|} \hline 1 & 1 & 1 & 1& 1\\ \hline 1 & 1 & 1 & 1& 1\\ \hline 1 & 1 & 1 & 1& 1\\ \hline 1 & 1 & 1 & 1& 1\\ \hline 1 & 1 & 1 & 1& 1\\ \hline \end{array} $$ |
Example: |
img = plt.imread('ext-figures/lecture03/input_orig.png');
noise = np.random.normal(0,1,size=img.shape); snr10=img+0.1*noise; snr5=img+0.2*noise; snr2=img+0.5*noise;plt.figure(figsize=[15,12])
plt.subplot(3,4,1); plt.imshow(img); plt.axis('off');plt.title('No noise'); plt.subplot(3,4,2); plt.imshow(snr10); plt.title('SNR=10'); plt.axis('off');plt.subplot(3,4,3); plt.imshow(snr5); plt.title('SNR=5'); plt.axis('off');plt.subplot(3,4,4); plt.imshow(snr2); plt.title('SNR=2'); plt.axis('off');
plt.subplot(3,4,5); plt.imshow(ski.filters.gaussian(img,sigma=1)); plt.title('Gaussian $\sigma$=1'); plt.axis('off');plt.subplot(3,4,6); plt.imshow(ski.filters.gaussian(snr10,sigma=1)); plt.axis('off');plt.subplot(3,4,7); plt.imshow(ski.filters.gaussian(snr5,sigma=1)); plt.axis('off');plt.subplot(3,4,8); plt.imshow(ski.filters.gaussian(snr2,sigma=1)); plt.axis('off');
plt.subplot(3,4,9); plt.imshow(ski.filters.gaussian(img,sigma=3)); plt.title('Gaussian $\sigma$=3'); plt.axis('off');plt.subplot(3,4,10); plt.imshow(ski.filters.gaussian(snr10,sigma=3)); plt.axis('off');plt.subplot(3,4,11); plt.imshow(ski.filters.gaussian(snr5,sigma=3)); plt.axis('off');plt.subplot(3,4,12); plt.imshow(ski.filters.gaussian(snr2,sigma=3)); plt.axis('off');
The asociative and commutative laws apply to convoution
$$(a * b)*c=a*(b*c) \quad \mbox{ and } \quad a * b = b * a $$A convolution kernel is called separable if it can be split in two or more parts:
$$\begin{array}{|c|c|c|} \hline \cdot & \cdot & \cdot\\ \hline \cdot & \cdot & \cdot\\ \hline \cdot & \cdot& \cdot\\ \hline \end{array}= \begin{array}{|c|} \hline \cdot \\ \hline \cdot \\ \hline \cdot \\ \hline \end{array} * \begin{array}{|c|c|c|} \hline \cdot & \cdot & \cdot\\ \hline \end{array}$$ $$\exp{-\frac{x^2+y^2}{2\,\sigma^2}}=\exp{-\frac{x^2}{2\,\sigma^2}}*\exp{-\frac{y^2}{2\,\sigma^2}}$$Separability reduces the number of computations $\rightarrow$ faster processing
img = plt.imread('ext-figures/lecture03/grasshopper.png'); noise=img+np.random.normal(0,0.1,size=img.shape); spots=img+0.2*snp(img.shape,0,0.8); noise=(noise-noise.min())/(noise.max()-noise.min());spots=(spots-spots.min())/(spots.max()-spots.min());
plt.figure(figsize=[15,10]);
plt.subplot(2,3,1); plt.imshow(spots); plt.title('Spots'); plt.axis('off');
plt.subplot(2,3,2); plt.imshow(ski.filters.gaussian(spots,sigma=1)); plt.title('Gauss filter'); plt.axis('off');
plt.subplot(2,3,3); plt.imshow(ski.filters.median(spots,disk(3))); plt.title('Median'); plt.axis('off');
plt.subplot(2,3,4); plt.imshow(noise); plt.title('Gaussian noise'); plt.axis('off');
plt.subplot(2,3,5); plt.imshow(ski.filters.gaussian(noise,sigma=1)); plt.title('Gauss filter'); plt.axis('off');
plt.subplot(2,3,6); plt.imshow(ski.filters.median(noise,disk(3))); plt.title('Median'); plt.axis('off');
Problem | Example | Possible solutions |
---|---|---|
|
![]() |
|
High-pass filters enhance rapid changes $\rightarrow$ ideal for edge detection
$\frac{\partial}{\partial x}=\frac{1}{32}\cdot\begin{array}{|c|c|c|} \hline -3 & 0 & 3\\ \hline -10 & 0 & 10\\ \hline -3 & 0 & 3\\ \hline \end{array}$
$\frac{\partial}{\partial\,y}=\frac{1}{32}\cdot\begin{array}{|c|c|c|} \hline -3 & -10 & -3\\ \hline 0 & 0 & 0\\ \hline 3 & 10 & 3\\ \hline \end{array}$
img=plt.imread('ext-figures/lecture03/orig.png')
k = np.array([[-3,-10,-3],[0,0,0],[3,10,3]]);
plt.figure(figsize=[15,8])
plt.subplot(1,3,1); plt.imshow(img);plt.title('Original');
plt.subplot(1,3,2); plt.imshow(ndimage.convolve(img,np.transpose(k)));plt.title('$\partial / \partial x$');
plt.subplot(1,3,3); plt.imshow(ndimage.convolve(img,k));plt.title('$\partial / \partial y$');
img=plt.imread('ext-figures/lecture03/orig.png');
plt.figure(figsize=[15,6])
plt.subplot(1,2,1);plt.imshow(ski.filters.laplace(img),clim=[-0.08,0.08]); plt.title('Laplacian'); plt.colorbar();
plt.subplot(1,2,2);plt.imshow(ski.filters.sobel(img)); plt.title('Sobel'); plt.colorbar();
In practice - you never see the transform equations. The Fast Fourier Transform algorithm is available in numerical libraries and tools.
x = np.linspace(0,50,100); s0=np.sin(0.5*x); s1=np.sin(2*x);
plt.figure(figsize=[15,5])
plt.subplot(2,3,1);plt.plot(x,s0); plt.axis('off');plt.title('$s_0$');plt.subplot(2,3,4);plt.plot(np.abs(np.fft.fftshift(np.fft.fft(s0))));plt.axis('off');
plt.subplot(2,3,2);plt.plot(x,s1); plt.axis('off');plt.title('$s_1$');plt.subplot(2,3,5);plt.plot(np.abs(np.fft.fftshift(np.fft.fft(s1))));plt.axis('off');
plt.subplot(2,3,3);plt.plot(x,s0+s1); plt.axis('off');plt.title('$s_0+s_1$');plt.subplot(2,3,6);plt.plot(np.abs(np.fft.fftshift(np.fft.fft(s0+s1))));plt.axis('off');
$\mathcal{F}\{a * b\} = \mathcal{F}\{a\} \cdot \mathcal{F}\{b\} $ $\mathcal{F}\{a \cdot b\} = \mathcal{F}\{a\} * \mathcal{F}\{b\} $
img=plt.imread('ext-figures/lecture03/bp_ex_original.png'); noise=np.random.normal(0,0.2,size=img.shape); nimg=img+noise;
plt.figure(figsize=[15,3])
plt.subplot(1,3,1); plt.imshow(img);plt.title('Image');
plt.subplot(1,3,2); plt.imshow(noise);plt.title('Noise');
plt.subplot(1,3,3); plt.imshow(nimg);plt.title('Image + noise');
plt.figure(figsize=[15,3])
plt.subplot(1,3,1); plt.imshow(np.log(np.abs(np.fft.fftshift(np.fft.fft2(img)))));plt.title('Image');
plt.subplot(1,3,2); plt.imshow(np.log(np.abs(np.fft.fftshift(np.fft.fft2(noise)))));plt.title('Noise');
plt.subplot(1,3,3); plt.imshow(np.log(np.abs(np.fft.fftshift(np.fft.fft2(nimg)))));plt.title('Image + noise');
How can we suppress noise without destroying relevant image features?
def ripple(size=128,angle=0,w0=0.1) :
w=w0*np.linspace(0,1,size);
[x,y]=np.meshgrid(w,w);
img=np.sin((np.sin(angle)*x)+(np.cos(angle)*y));
return img
N=64;
d0=ripple(N,angle=1/180*np.pi,w0=100);
d30=ripple(N,angle=30/180*np.pi,w0=100);
d60=ripple(N,angle=60/180*np.pi,w0=100);
d90=ripple(N,angle=89/180*np.pi,w0=100);
plt.figure(figsize=[15,8])
plt.subplot(2,4,1); plt.imshow(d0); plt.title('$0^{\circ}$'); plt.axis('off');
plt.subplot(2,4,2); plt.imshow(d30); plt.title('$30^{\circ}$'); plt.axis('off');
plt.subplot(2,4,3); plt.imshow(d60); plt.title('$60^{\circ}$'); plt.axis('off');
plt.subplot(2,4,4); plt.imshow(d90); plt.title('$90^{\circ}$'); plt.axis('off');
plt.subplot(2,4,5); plt.imshow((np.abs(np.fft.fftshift(np.fft.fft2(d0))))); plt.axis('off');
plt.subplot(2,4,6); plt.imshow((np.abs(np.fft.fftshift(np.fft.fft2(d30))))); plt.axis('off');
plt.subplot(2,4,7); plt.imshow((np.abs(np.fft.fftshift(np.fft.fft2(d60))))); plt.axis('off');
plt.subplot(2,4,8); plt.imshow((np.abs(np.fft.fftshift(np.fft.fft2(d90))))); plt.axis('off');
plt.figure(figsize=[8,10])
plt.subplot(4,1,1);plt.imshow(plt.imread('ext-figures/lecture03/raw_img.png')); plt.title('$a$'); plt.axis('off');
plt.subplot(4,1,2);plt.imshow(plt.imread('ext-figures/lecture03/raw_spec.png')); plt.title('$\mathcal{F}(a)$'); plt.axis('off');
plt.subplot(4,1,3);plt.imshow(plt.imread('ext-figures/lecture03/filt_spec.png')); plt.title('Kernel $H$'); plt.axis('off');
plt.subplot(4,1,4);plt.imshow(plt.imread('ext-figures/lecture03/filt_img.png')); plt.title('$a_{filtered}$'); plt.axis('off');
Reconstructed CT slice before filter | Reconstructed CT slice after stripe filter |
![]() |
![]() |
Intensity variations are suppressed using the stripe filter on all projections.
e.g. from scipy import ndimage
ndimage.filters.convolve(f,h)
Linear filter using kernel $h$ on image $f$.ndimage.filters.median_filter(f,\[n,m\])
Median filter using an $n \times m$ filter neighborhoodnp.fft.fft2(f)
Computes the 2D Fast Fourier Transform of image $f$np.fft.ifft2(F)
Computes the inverse Fast Fourier Transform $F$.np.fft.fftshift()
Rearranges the data to center the $\omega$=0. Works for 1D and 2D.np.abs(f), np.angle(f)
Computes amplitude and argument of a complex number.np.real(f), np.imag(f)
Gives the real and imaginary parts of a complex number.Original | Wavelet transformed |
---|---|
![]() |
![]() |
The noise is found in the detail part of the WT
The heat transport equation $$\frac{\partial T}{\partial t}=\kappa\,\nabla^2 T$$
Original | Iterations |
![]() |
def g(x,lambd,n) :
g=1/(1+(x/lambd)**n)
return g
We want to control the diffusion process...
The contrast function $G$ is our control function $$G(x)=\frac{1}{1+\left(\frac{x}{\lambda}\right)^n}$$
x=np.linspace(0,1,100);
plt.plot(x,g(x,lambd=0.5,n=12));
plt.xlabel('Image intensity (x)'); plt.ylabel('G(x)');plt.tight_layout()
![]() |
![]() |
Image | Diffusivity map |
A more robust filter is obtained with
$$\frac{\partial u}{\partial t}=G(|\nabla_{\sigma} u|)\,\nabla^2 u$$ - _$u$_ Image to be filtered - _$G(\cdot)$_ Non-linear function to control the contrast - _$\tau$_ Time increment per numerical iteration - _$N$_ Number of iterations - _$\nabla_{\sigma}$_ Gradient smoothed by a Gaussian filter, width $\sigma$Neutron CT slice from a real-time experiment observing the coalescence of cold mixed bitumen.
Original | Iterations of non-linear diffusion |
![]() |
with $|u|_{BV}=\int_{\Omega}|\nabla u|^2$
We want smooth regions with sharp edges\ldots
The image $f$ is filtered by solving
$$\begin{eqnarray} \frac{\partial u}{\partial t}&=& \mathrm{div}\left(\frac{\nabla u}{|\nabla u|}\right)+\lambda\,(f-u+v)\nonumber\\ \frac{\partial v}{\partial t}&=& \alpha\, (f-u) \end{eqnarray}$$Smoothing normally consider information from the neighborhood like
Non-local smoothing average similiar intensities in a global sense.
The non-local means filter is defined as $$u(p)=\frac{1}{C(p)}\sum_{q\in\Omega}v(q)\,f(p,q)$$ where
$v$ and $u$ input and result images.
$C(p)$ is the sum of all pixel weights as
$C(p)=\sum_{q\in\Omega}f(p,q)$
$f(p,q)$ is the weighting function
$f(p,q)=e^{-\frac{|B(q)-B(p)|^2}{h^2}}$
B(x) is a neighborhood operator e.g. local average around $x$
The orignal filter compares all pixels with all pixels\ldots\
It has been shown that not all pixels have to be compared to achieve a good filter effect. i.e. $\Omega$ in the filter equations can be replaced by $\Omega_i<<\Omega$
Compute pixel-wise difference between image $f$ and $g$
Difference images provide first diagnosis about processing performance
General purpose can be controlled
![]() | ![]() |
Often 'real' data
An evaluation procedure need a metric to compare the performance
We have looked at different ways to suppress noise and artifacts:
Which one you select depends on