ECEP 596, Autumn 2019 Homework 1: Filtering, Edge finding

DUE: October 13, 2019 (Sunday) 11:59 P.M.

Late date: October 16, 2019 (Wednesday) 11:59 P.M. (Late policy: 10% penalty per day)

Total points: 20 (+2 extra credit)

Before you start:

Download the zip file here.

Please read the word document on Getting started with Qt.docx to install and use Qt. Pay careful attention to all the instructions.

 

Assignment Description:

For this assignment, you'll be implementing the functionality of the ImgFilter program, which allows you to load an image and apply various images filters. You need to write code for some basic functions, many of which will be needed in the future assignments too.

In the project, you should only update the file - "Project1.cpp". The other files like "mainwindow.cpp" and "main.cpp" contain code to execute the application. When you compile and run the "ImgFilter" project, a user interface (UI) will appear on the screen. The buttons in the UI will call the corresponding functions in "Project1.cpp". Several of the buttons have already been implemented, including "B/W", "Add noise", "Mean" and "Half size". The implementations of these buttons are in "Project1.cpp", and should provide helper sample code. Please see these functions to understand how to work with images in Qt.

The QImage class in the Qt package provides a hardware-independent image representation and the QRgb class holds a color pixel. You can find more information here. The origin of the image is the top left corner, and the width and height of the image are accessible through the global variables imageWidth and imageHeight, respectively. For this assignment, the input image is converted from QImage form to a matrix form (having double values). The size of the image matrix is (imageWidth * imageHeight) * 3 (for 3 color channels). Thus, a pixel of 'image' at position (c,r) in color channel say 0 (red) is mapped to the matrix 'Image' as: Image[r * imageWidth + c][0] = qRed(image.pixel(c,r)). Refer to the function ConvertQImage2Double() provided in the starter code.

Important note: In all the functions, if you perform any operation on the input image by calling a function, the original image is changed. If you want to retain the original image for a task, first create a copy of the original image and perform the operations on the copy. Also, all the operations are performed on each of the 3 color bands (R, G and B as 0, 1 and 2) separately unless mentioned otherwise.

What to turn in:

To receive credit for the project, you need to turn in HERE the 'Project1.cpp' file and a PDF file (name it as report.pdf) containing the requested images for each task with proper captions. Your code needs to be correct and efficient in terms of speed and/or memory; just getting correct outputs is not enough. Please write necessary comments in your code so that it is easily understandable.

Please Note: We have modified the original assignment to convert the input image from the QImage class to double class. Please do not use old versions of this code. Leave the resultant images in double form; they are converted back to the QImage class in the mainwindow.cpp file which you don't need to worry about.

FIRST WEEK TASKS (Oct. 1 - Oct. 6):

Task 1: Convolution (5 points)

Implement the function Convolution(double **image, double *kernel, int kernelWidth, int kernelHeight, bool add) to convolve an image with a kernel of size kernelHeight*kernelWidth. Use zero-padding around the borders for simplicity (see first extra credit task if you wish to do otherwise). Here:

Write a general convolution function that can handle all possible cases as mentioned above. You can get help from the convolution part of the function MeanBlurImage to write this function. Note that MeanBlurImage deals with an image in QImage form, but you need to write in double form. Read the other parts of Project1.cpp provided in the starter code for further help.

Task 2: Gaussian Blur (5 points)

Implement the function GaussianBlurImage(double **image, double sigma) to Gaussian blur an image. "sigma" is the standard deviation of the Gaussian. Use the function MeanBlurImage as a template, create a 2D Gaussian filter as the kernel and call the Convolution function of Task 1 (details). Normalize the created kernel using the given NormalizeKernel() function before convolution. For the Gaussian kernel, use kernel size = 2*radius + 1 (same as the Mean filter) and radius = (int)(ceil(3 * sigma)) and the proper normalizing constant.

To do: Gaussian blur the image "Seattle.jpg" using this function with a sigma of 4.0, and save as "task2.png".

SECOND WEEK TASKS (Oct. 6 - Oct. 13):

Task 3: Image derivatives (5 points)

Implement the functions FirstDerivImage_x(double **image, double sigma) and FirstDerivImage_y(double **image, double sigma) to find the first derivatives of an image and then Gaussian blur the derivative image by calling the GaussianBlurImage function. "sigma" is the standard deviation of the Gaussian used for blurring. To compute the first derivatives, first compute the x-derivative of the image (using the horizontal 1*3 kernel: {-1, 0, 1}) followed by Gaussian blurring the resultant image. Then compute the y-derivative of the original image (using the vertical 3*1 kernel: {-1, 0, 1}) followed by Gaussian blurring the resultant image. Note that the kernel values sum to 0 in these cases, so you don't need to normalize the kernels. Remember to add 128 to the final pixel values in both cases, so you can see the negative values. Note that the resultant images of the two first derivatives will be shifted a bit because of the uneven size of the kernels.

To do: Compute the x-derivative and the y-derivative of the image "LadyBug.jpg" with a sigma of 1.0 and save the final images as "task3a.png" and "task3b.png", respectively.

Task 4: Edge Detection (5 points)

Implement SobelImage (double **image) to compute edge magnitude and orientation information. The input image is converted into grayscale in the starter code. The input image still has 3 channels but all the channels have the same values (grayscale values), so you can use any of the channels for further operations. Use the standard Sobel masks in X and Y directions: {{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}} and {{1, 2, 1}, {0, 0, 0}, {-1, -2, -1}} respectively to compute the edges. Note that the kernel values sum to 0 in these cases, so you don't need to normalize the kernels before convolving. Divide the image gradient values by 8 before computing the magnitude and orientation in order to avoid spurious edges. SobelImage should then display both the magnitude and orientation of the edges in the image (see commented code in Project1.cpp for displaying orientations and magnitudes).

To do: Compute Sobel edge magnitude and orientation on "LadyBug.jpg" and save as "task4.png".

Bells and Whistles (EXTRA CREDIT: 2 points)

Description: Description: Description: Description: [whistle]

Implement an improved image border padding method, i.e. "fixed" or "reflected", inside the Convolution function. Refer to Lecture 2 notes for details. Comment this portion of the code if you plan to use zero-padding for the tasks. Also write a comment in the Convolution function if you have implemented this task.