Project 1: Edge and corner detector
Due: Thurs, Feb 16 (11:59 PM)
We recommend using Python for the assignments in this course, because it has high-level libraries for reading images and common array and linear algebra routines. If you are not interested in Python, we recommend Matlab for similar reasons.
We suggest to first get some familiarity with Python or MATLAB by doing some simple task like reading in an image, brightening it by a factor of two, and then showing the brightened image (alternatively, to practice manipulating the different color components, you could set the green and blue channels to zero, and then display the image, to see an only-red version of the image). We also suggest to start by installing Anaconda Python, which has most of the libraries preinstalled.
A few tutorials:
Assignment Overview

Left: an input image. Center: result of Canny edge detection. Right: Result of corner detection.
Your goal for this assignment is to implement a Canny edge detector, and a Harris corner detector, as shown above. For this assignment, and the other assignments, please submit code that you have individually authored. (However, for the final project, you can work in groups).
Clarification Regarding Submission: Make sure to submit an actual Python source code file (a .py file that can be run from the command-line, with a command such as python canny.py). Although you may find it helpful to "experiment" with pieces of Python code in the interactive Python shell (the result of typing 'python' at the command-line), or the Jupyter notebooks, we would like to see a finished Python program that can be run stand-alone in your submission.
Canny edge detector (65%)
Implement the Canny edge detection algorithm, as described in class, and on the Wikipedia article. This consists of four phases:
- Compute smoothed gradients:
- Load an image, convert it to float format, and extract its luminance as a 2D array.
- Find the x and y components Fx and Fy of the gradient of the image after smoothing with a Gaussian (for the Gaussian, you can use σ = 1). As discussed in the lecture, there are two ways to go about doing this: either (A) smooth with a Gaussian using a convolution, followed by taking the gradient; or (B) convolve with the x and y derivatives of the Gaussian.
Please use one of the convolution routines (e.g. scipy.signal.convolve2d or scipy.ndimage.filters.convolve), as opposed to specialized methods for Gaussian filtering, because this will give you practice using convolutions.
- At each pixel, compute the edge strength F (the magnitude of the gradient), and the edge orientation D = atan(Fy/Fx).
- Nonmaximum suppression:
Create a "thinned edge image" I[y, x] as follows:
- For each pixel, find the direction D* in (0, π/4, π/2, 3π/4) that is closest to the orientation D at that pixel.
- If the edge strength F[y, x] is smaller than at least one of its neighbors along the direction D*, set I[y, x] to zero, otherwise, set I[y, x] to F[y, x]. Note: Make a copy of the edge strength array before thinning, and perform comparisons on the copy, so that you are not writing to the same array that you are making comparisons on.
Note: After thinning, your "thinned edge image" should not have thick edges any more (so edges should not be more than 1 pixel wide).
- Hysteresis thresholding:
- We are given two thresholds: T_low, and T_high.
- Mark pixels as "definitely not edge" if less than T_low.
- Mark pixels as "strong edge" if greater than T_high.
- Mark pixels as "weak edge" if within [T_low, T_high].
- Strong pixels are definitely part of the edge. Weak pixels are debatable.
- Only include weak pixels that are connected in a chain to a strong pixel. How to do this?
- Visit pixels in chains starting from the strong pixels. For each strong pixel, recursively visit the weak pixels that are in the 8 connected neighborhood around the strong pixel, and label those also as strong (and as edge).
- Label as "not edge" any weak pixels that are not visited by this process.
- Hint: This is really a connected components algorithm, which can be solved by depth first search. Make sure to not revisit pixels when performing your depth first search!
Please run your edge detector on the flower image above, and an image of your choice from Flickr. Experiment with the choice of thresholds T_low and T_high to obtain an edge image that corresponds to human intuition for what are edges. You should find that any strong edge forms a connected (linked) chain of pixels in the output. As a sanity test, you could also use an input containing a few geometric shapes such as white circles and squares on a black background.
Corner detector (35%)
Implement the Harris corner detector, as described in class and on Wikipedia. This has the following steps:
- Filtered gradient: Compute x and y gradients Fx and Fy, the same as in the Canny edge detector.
- Find corners: For each pixel (x, y), look in a window of size 2m+1 x 2m+1 around the pixel (you can use say m = 4). Accumulate over this window the covariance matrix C, which contains the average of the products of x and y gradients:

Here (u, v) are the coordinates within the window: u = -m, ..., m, and v = -m, ..., m, the brackets (< and >) indicate a spatial averaging operation, and the gradients Fx and Fy on the right hand side of the above equation are read from the locations (x + u, y + v).
Compute the smaller eigenvalue, e, of C, which is the "corner response" (note that the original Harris corner detector paper uses a different and faster formula of e = Determinant(C)-kTrace(C)2, where k is a small constant such as 0.04, so you can alternatively use that formulation instead if you like). In Python, see the command numpy.linalg.eig, which computes the eigenvalues. In MATLAB, use the eig command. Save all pixels at which the corner response e is greater than a corner threshold T into a list L.
- Nonmaximum suppression: Sort L in decreasing order of the corner response e. See numpy.sort and numpy.argsort, or the Python built-in function sorted. For each point p, remove all points in the 8-connected neighborhood of p that occur later in the list L.
Please run your corner detector on the flower image above, and on an image of your choice from Flickr. Experiment with the choice of thresholds to obtain a reasonable number of corners, between say 10 and 100.
Policies
Feel free to collaborate on solving the problem but write your code individually.
Submission
Submit your assignment in a zip file named yourname_project1.zip
. Please include your source code as .py files, the input images and results of Canny edge detection, and the input images and results of Harris corner detection. Please also include a readme describing how to run your edge and your corner detector from the command-line.
Finally submit your zip to UVA Collab.