{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Lecture 03: Image Filtering (exercises)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "from skimage import io, data, color" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# EX1: spatial domain filtering\n", "Build your own function(s) to convolve a filter kernel with an image." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## create zero-padding function" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " 1. Build a function to pad an image with zeros, adapting to various filter kernel sizes.
\n", " The function should take the original image and kernel shape as inputs, and return the zero-padded image.
\n", " The function should handle both grayscale (2D) and color (3D) images.

\n", " Hint 1: use numpy's pad() function
\n", " Hint 2: the number of values to pad (i.e., \"pad_width\") to both X and Y image axis can be taken as the floor division of the kernel shape of each axis. In the case of color images (3D), the third axis (depth) should not be padded, set pad_width to 0.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " 2. Plot a demo with a (10,10) or (10,10,3) image (composed of random integers ranging between 0-255), and a 3x3 kernel.
\n", " Hint: you can use numpy's random.randint() function to create a random image.
\n", "
\n", " Apply to the \"Popocatepetl_HD_crop.jpg\" image located in the sub-directory \"images\".\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## create convolution function" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " 1. Build a function to convolve an image by a kernel.
\n", " The function should take the original image and kernel as inputs, and return the convolved image.
\n", "
\n", " Start with a function supporting grayscale images (2D). (Adapt to support color images (3D), or create a distinct function to handle them).
\n", "
\n", " Hint 1: remember that your function should first zero-pad the original image (using the function previously created), and afterwards loop over its X, Y (and Z) axis to filter the image at pixel coordinate (x,y,(z)).
\n", "
\n", " Hint 2: remember that in order to convolve an \"image crop\" with a \"filter kernel\", you should compute the element-wise product of the crop and the filter kernel, and return the sum the resulting array. The returned value corresponds to the new pixel value at coordinate (x,y,(z)) of the new filtered image (which you'll have previously created as an empty array filled with zeros, having the same dimension as your padded image).
\n", "
\n", " Hint 3: remember that the returned filtered image should have the same size as the original image (i.e., remove padded pixels!)\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## create your own kernels and convolve!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### boxcar filter" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " 1. Create an averaging kernel (box-filter), whereby the convolved pixel will have the mean value of the pixels in the kernel neighborhood. Use an odd-shaped kernel (e.g., 3x3, 5x5, etc.).
\n", "
\n", " Test on a grayscale version of \"Popocatepetl_HD_crop.jpg\" (use skimage.color.rgb2gray() to convert to grayscale). Test various kernel sizes.
\n", "
\n", " Test on the color version of \"Popocatepetl_HD_crop.jpg\". What shape should your kernel have?
\n", " (If your function only supports 2D images, use it on each band, and use np.dstack() to reconstruct the RGB filtered image). \n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### gaussian filter" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " 1. Create a gaussian kernel
\n", "
\n", " Hint 1: use the 1D Gaussian distribution formula: wikipedia
\n", " Hint 2: create an odd-size kernel whose size is 6 times the chose standard deviation sigma ( 99% of the probability mass for a gaussian is within +/-3 sigma).
\n", " Hint 3: populate the kernel coefficients by computing the 2D gaussian distribution. Set the distribution peak to be at the kernel center (mu=0), and calulate the distance x to the peak using Pythagore.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " 2. Try using the scikit-image filters.gaussian() implementation. Is it faster than yours?
\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " 3. Recover the high-frequency component from the your filtered image.
\n", " Hint: remember a Gaussian filter is a true low-pass filter for the image.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### sobel filter" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " 1. Create two sobel kernels, to compute respectively gradients in x- and y-directions on a grayscale image.
\n", " Hint 1: check out the formulation here: wikipedia
\n", "
\n", " Plot the image filtered with both filters. \n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# EX2: frequency domain filtering" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## compute the 2D discrete Fourier transform" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " 1. Compute the 2D discrete Fourier transform of your grayscale image.
\n", " Hint: use numpy's np.fft.fft2() function, and np.fft.fftshift() to get frequency 0 at center of image.
\n", "
2. Plot the amplitude spectrum of the image.
\n", " Hint: remember that the discrete Fourier Transform returned from np.fft.fft2() is complex. Take the abs() value to get the amplitude.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## create a low-pass function" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " Create a function to low-pass the FFT spectrum, i.e., cut-off the high frequencies of the spectrum.
\n", "
\n", " Hint: create a mask having the same size as your Fourier transform spectrum, and set value to 1 at low frequencies, and to 0 at high frequencies. Multiply your spectrum by this mask to obtain a low-pass filtered spectrum.
\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## reconstruct the low-pass filtered image" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " Reconstruct the image from your low-pass filtered spectrum.
\n", "
\n", " Hint: Compute the inverse Fourier transform on the low-pass filtered spectrum using numpy's np.fft.ifft2() function.
Take the real part of the result to reconstruct the low-pass filtered image.\n", "
" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": { "height": "calc(100% - 180px)", "left": "10px", "top": "150px", "width": "282.883px" }, "toc_section_display": true, "toc_window_display": true } }, "nbformat": 4, "nbformat_minor": 4 }