Revision 2e11cce90a6c9e960d8243c1465c3a872ba3b2f5 (click the page title to view the current version)


Changes from 2e11cce90a6c9e960d8243c1465c3a872ba3b2f5 to 42f6118a4495ce373fabcb44a456db9ccaadc722

title: Filters
categories: lecture

# What are filters

Today we will investigate the use of blurring filters on images.
This is just one of many applications of filters in image processing,
and in the next session we will use the same basic techniques for
feature detection.

Blurring filters do exactly what it says on the tin.  They blur the
image.  This has the effect of smoothing out noise, so it is a useful
technique for noise removal.

One of the groups using machine vision in the final year project
last year had problems with the camera failing to return a value in
some pixel.  The result was black pixels, so-called pepper noise.
A blurring filter solves this problem very effectively.

If you have read signal processing or image processing previously,
all of this may be familiar to you already.

## Conceptual Application

![Lenna with Salt and Pepper Noise](Images/lenna-snp.png)

+ Noisy image.  Some pixels are just off.
+ Recalculate pixels using a neighbourhood
+ Use a matrix of coefficients for a weighted average
+ You could implement this with a fairly simple for-loop,
  but it would be slow.  

## Convolution

$$(f*g)[x] = \sum_{k=-\infty}^{\infty} f[k]g[x-k]$$

+ Note the convolution of a sampled signal and a continuous one
+ [Demo](

**Note** OpenCV uses correlation instead of convolution.
Conceptually this is different, but in practice, the only effect
is to mirror the filter.

$$(f\star g)[x] = \sum_{k=-\infty}^{\infty} f[k]g(k+x)$$

## Non-linear filtering

+ Convolution is a linear operation
    + Simple and fast
+ Non-linear filters may be better for denoising.
+ However, we need linear filters when we move on to edge detection.
+ Non-linear filters for be for another course.
+ Non-linear filters must be for another course.

## Blurring in Python

def normaliseImage(frame):
    grey = cv.cvtColor(frame, cv.COLOR_BGR2GRAY).astype(np.float64)
    grey /= 255
    return grey

frame = cv.imread("lenna.png")
grey = normaliseImage(frame)
f7 = np.ones((7,7)) /  49
blurred = cv.filter2D(grey,-1,f7)

The -1 means the same depth is used for output as for input.
As the image, we can use
[lenna-awgn.png](Images/lenna-awgn.png) with Gaussian noise or
[lenna-snp.png](Images/lenna-snp.png) with Salt and Pepper noise.

If we want to use convolution instead of correlation, we have
(requiring extra imports)

snp1 = sig.convolve2d(snp,f7)

## Gaussian filter

Above, we used a uniform filter (`f7`).

shape = (5,5)
sigma = 0.5
m,n = [(ss-1.)/2. for ss in shape]
y,x = np.ogrid[-m:m+1,-n:n+1]
h = np.exp( -(x*x + y*y) / (2.*sigma*sigma) )
h /= h.sum()

This gives a different matrix which we can use as a blurring filter.

array([[6.96247819e-08, 2.80886418e-05, 2.07548550e-04, 2.80886418e-05, 6.96247819e-08],
       [2.80886418e-05, 1.13317669e-02, 8.37310610e-02, 1.13317669e-02, 2.80886418e-05],
       [2.07548550e-04, 8.37310610e-02, 6.18693507e-01, 8.37310610e-02, 2.07548550e-04],
       [2.80886418e-05, 1.13317669e-02, 8.37310610e-02, 1.13317669e-02, 2.80886418e-05],
       [6.96247819e-08, 2.80886418e-05, 2.07548550e-04, 2.80886418e-05, 6.96247819e-08]])

Note, large numbers in the middle, smaller towards the edges.  Since
we divided by the sum in the last line, the entries now add to one.

## Generating Noisy Images for Testing 

def addGaussNoise(image,var=0.02):
      row,col = image.shape
      mean = 0
      sigma = var**0.5
      gauss = np.random.normal(mean,sigma,(row,col))
      gauss = gauss.reshape(row,col)
      noisy = image + gauss
      return noisy

def addSaltPepperNoise(image,amount=0.01):
      row,col = image.shape
      s_vs_p = 0.5
      out = np.copy(image)
      # Salt mode
      num_salt = np.ceil(amount * image.size * s_vs_p)
      coords = [np.random.randint(0, i - 1, int(num_salt))
              for i in image.shape]
      out[tuple(coords)] = 1

      # Pepper mode
      num_pepper = np.ceil(amount* image.size * (1. - s_vs_p))
      coords = [np.random.randint(0, i - 1, int(num_pepper))
              for i in image.shape]
      out[tuple(coords)] = 0
      return out