Revision 56951127715517cc9c5575fe800547352fe5823f (click the page title to view the current version)

Image Filters

Changes from 56951127715517cc9c5575fe800547352fe5823f to 4156203a63dc2a24f3a91b5bcb488d914186d12b

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

**Briefing** [Filters]()

# 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
f5 = np.ones((5,5)) /  25
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