--- 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 # 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