Revision 90d2802d33ffff9c011064134f8ae99b387113a0 (click the page title to view the current version)

Image Filters

Changes from 90d2802d33ffff9c011064134f8ae99b387113a0 to 470a1f437c37d67e1c3a47fbd4e8b1deddbdb8e7

---
title: Image Filters
categories: tutorial
---

**Briefing** [Filters]()

+ [Sample Files](Python/Blurring)
+ [Sample Files](Python/Blurring/)

# A simple blurring filter.

In addition to OpenCV, we need numpy for arrays and the signal
processing library from SciPy.
```python
import cv2 as cv
import numpy as np
import scipy.signal as sig
```

We need a test image which we convert to greyscale.
Here we use [lenna.ppm](http://www.hlevkin.com/hlevkin/TestImages/lenna.ppm),
which you have to download and place in your working directory.
```python
frame =cv.imread( "lenna.ppm" )
grey = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
```
Other test images can be found at the 
[same source](http://www.hlevkin.com/hlevkin/06testimages.htm).

We display the image to check that everything works:
```python
cv.imshow("img",grey)
cv.waitKey(0)
```

## Averaging pixels

1. Consider the following python code.  What does it do?

```python
(n,m) = grey.shape
new = np.zeros((n-6,m-6),dtype=np.uint8)

for i in range(n-6):
   for j in range(m-6):
      orig = grey[i:i+7,j:j+7]
      new[i,j] = round(sum(orig.flatten())/49)
```

2.  Run the python code.  What does the matrix `new` look like?
3.  Display `new` as an image and compare it to the original `grey`.
    What is the visual effect?

What you should observe is a blurring effect.  
Contours are smoothened by averaging a neighbourhood.

## Using a signal processing library

What we did above is such a standard operation that we have an API
therefore.
We can define a $7\times7$ averaging filter like this.
```python
f7 = np.ones((7,7)) /  49
```

+ What does this look like as a matrix?

To apply the filter to the image, we can use the standard
convolution operator as follows:
```python
g7 = sig.convolve2d(grey,f7).astype(np.uint8)
```
Note that we have to convert the result to integers explicitly,
lest OpenCV will not interpret it as an image.
The result can be displayed as
```python
cv.imshow("filtered",g7)
cv.waitKey(0)
```
+ Compare the two images.  What does the filter do?
+ Compare the filtered image to the image `new` from
  your manual averaging.  Do they look different in any way?

The API has different methods to handle the boundaries.  
We simply cropped a few pixel around the border, and thus `new`
may be smaller than images filtered with the API.

We can do the same thing using the OpenCV library, like this.
```python
c7 = cv.filter2D(grey,-1,f7)
```
The second argument (-1) specifies the colour depth of the output
which should be the same as for the input.

## Other test images

1.  Download a couple of other test images that you can use.
    It is instructive to use a few with very sharp edges, such as text,
    as well as smoother images.
2.  Using the procedures above, test how the filter works on different
    images.

## Other smoothing filters

Test a couple of different filters in the same way, such as the following.

### Averaging filters

1.  We can make square averaging filters with different sizes.

```python
f3 = np.ones((3,3)) /  9    # 3x3 averaging
f5 = np.ones((5,5)) /  25   # 5x5 averaging
f9 = np.ones((5,5)) /  81   # 9x9 averaging
```

2.  We could make a circular averaging filter, such as this:

```python
circle = np.array([[0,0,1,1,1,0,0],
   [0,1,1,1,1,1,0],
   [1,1,1,1,1,1,1],
   [1,1,1,1,1,1,1],
   [1,1,1,1,1,1,1],
   [0,1,1,1,1,1,0],
   [0,0,1,1,1,0,0]])/37
```

Note that the divisor, 37, is the number of ones in the matrix.

3.  Test different filters on different images.  What do you see?

### A Gaussian filter

The Gaussian filter is a little trickier to implement, since we
want to be able to vary its parameters.
We use the Gaussian function
$$g(x,y) = \frac{1}{2\pi\sigma^2}\exp\frac{-(x^2+y^2)}{2\sigma^2},$$
where $\sigma$ is the standard deviation and $(x,y)$ is the pixel co-ordinates
with $(0,0)$ in the centre of the filter.

Firstly, the Gaussian function in `python` becomes
```python
def gauss(x,y,sigma=1):
    c1 = 1/(2*np.pi*sigma**2)
    c2 = 2*sigma**2
    return c1*np.exp(-(x**2+y**2)/c2)
```

1.  Check that the above code matches the mathematical formula.

We can make a list of lists of Gaussian coefficients in a simple
one-liner:
```python
B = [ [ gauss(x,y) for x in range(-t,t+1) ] for y in range(-t,t+1) ]
```
where `t` is an integer and the resulting filter is $(2t+1)\times(2t+1)$.

2.  How does the above code work?

To turn `B` into a matrix, we do
```python
A = np.array(B)
```

3.  Make the matrix `A`.  What does the matrix look like?

4.  The elements of the filter should add to one, to maintain the 
    luminence of the image.
    You can check this by calculating
    `sum(A.flatten()`.
    What do you think, is this sufficiently close to one?
    Why is it less than one?

5.  We can normalise the filter by calculating
    `AA = A/sum(A.flatten())`.
    Why does this give unit sum?

6.  Now, test the Gaussian filter on your test images.
    Use a couple of different sizes, e.g. $t=3,7,11$,
    and a couple of different standard deviations,
    e.g. $\sigma=0.5,1,2$.
    What do you observe?

## Noisy images

1. Download this noisy version of Lenna: 
  [lenna-awgn.png](Images/lenna-awgn.png)
    + Compare it to the original image.  How do they differ?
    + Apply the blurring filters to the image.  What happens?
1. Download another noisy version of Lenna: 
  [lenna-snp.png](Images/lenna-snp.png)
    + Compare it to the other versions.  How does it differ?
    + Apply the blurring filters to the image.  What happens?


## Making Noisy Images for Testing

You can try to make your own noisy images using the code below.

1.  How does each code snippet work?

### Gaussian Noise

```python
(m,n) = grey.shape
noise = np.random.randn(m,n)*sigma
noisy = (grey + noise).astype(np.uint8)
```

```python
def gnoise(img,sigma=1):
     (m,n) = grey.shape
     noise = np.random.randn(m,n)*sigma
     return (grey + noise).astype(np.uint8)
```

### Salt noise

```python
def snoise(img,p=0.1):
     (m,n) = grey.shape
     noise = (np.random.rand(m,n) > (1-p)).astype(np.uint8)*255
     return np.maximum(grey,noise)
```

### Pepper noise

```python
def pnoise(img,p=0.1):
     (m,n) = grey.shape
     noise = (np.random.rand(m,n) > p).astype(np.uint8)*255
     return np.minimum(grey,noise)
```

## Reflection

1.  Review today's tutorial.  What are the key concepts that you want to
    take with you?
2.  Next week we will introduce a new set of filters for differentiation 
    of the image.  
    Each filter is defined by a relatively small matrix.
    What do you need to change in the above examples to use different filters?
3.  What does the filter size matter?
    For instance, 
    how does the blurred image change when you decrease the size of the
    averaging filter from $7\times$ to $3\times3$?