CS 332
Assignment 1 Due: Wednesday, September 29 |
The first problem on this assignment reinforces some basics of MATLAB and the use of 2D matrices to store images, where the value at each matrix location represents the light intensity at a particular location in a 2D image. In Problems 2 and 3, you will examine the intensity changes in a natural image at multiple scales and explore how the early stages of processing in human vision can lead to visual illusions. Two final programming problems explore simple applications, counting the number of cells in an image of a cell culture and recognizing fingerprints. Your primary tasks are highlighted in purple.
Code files (M-Files) and images for this assignment are contained in the edges
,
assign1
, and assign1images
subfolders stored inside a high-level
folder named Assign1_edge_detection
. You can access this folder through this
Assign1_edge_detection zip
link, which is also
posted on the course schedule
page. Unzip the file and drag the folder to your Desktop, or drag to a folder
that you create on your local machine, to store your work for this course. (Note that you can also
download the Assign1_edge_detection
folder from the /home/cs332/download
directory on the CS server — in this case, connect to your account using
Cyberduck
on a Mac or PC, navigate up to the root directory
/
, then down to /home/cs332/download
, and drag the
Assign1_edge_detection
folder to your Desktop or CS332
folder.)
In MATLAB, set your Current Folder to the assign1
subfolder and add the
assign1images
and edges
folders to the MATLAB search path.
To complete the second step, first click on the Set Path
icon in the toolbar
at the top of the MATLAB Desktop. In the dialog box that appears, click on the
Add with Subfolders
button. In the new dialog box that appears, navigate to the
top-level Assign1_edge_detection
folder and click the Open
button. This should add the top-level folder and its three subfolders to the MATLAB
search path, enabling direct access to all files contained in these
folders. When done with adding the folders to the Search Path, click on the
Close
button in the original dialog box.
Submission details: Submit an electronic copy
of your code files by dragging your assign1
subfolder into the
cs332/drop
subdirectory that was added to your account on the CS file server.
All of the code in this assignment can be completed collaboratively with your partner(s), but each
of you should drop a copy of this folder into your own account on the file server.
Be sure to document all the code that you write.
For Problems 2 and 3, you will describe in words, your observations about the results of edge
detection, and provide explanations for two visual illusions. You and your partner(s) should
try to compose these answers together, in a shared Google doc. You can then submit this work
by sharing the Google doc with me — please be sure to grant Edit privileges to me
so that I can provide feedback directly in the Google doc.
In MATLAB, it is easy to create an image with rectangular blocks of uniform intensity.
The following function adds a rectangular block to
an input image and returns the modified image. The horizontal coordinates range
from xleft
to xright
and vertical coordinates range
from ytop
down to ybottom
.
function newImage = addBlock(image, xleft, xright, ytop, ybottom, intensity)
newImage = image; % create a copy of the input image
% set a rectangular area of newImage to the input intensity
newImage(ytop:ybottom, xleft:xright) = intensity;
The commands below create an initial blank image (2D matrix of 8-bit numbers), add one gray rectangular block, and display the new image. A diagram of the image with labeled coordinates is shown below the code.
>> image = uint8(zeros(100, 100));
>> image = addBlock(image, 20, 80, 30, 60, 120);
>> imtool(image);
The coordinates shown at the four corners of the diagram above, and at the borders of the
gray rectangle, correspond to the pixel coordinates (X,Y)
printed
in the bottom left corner of the Image Tool display window. In pixel coordinates, the
location of the upper left corner of the gray block is (X,Y) = (20,30)
. When
referring to the indices of the corresponding location in the image
matrix,
the order of the two dimensions is reversed, so the matrix location of
this upper left corner is image(30,20)
(i.e. row 30, column 20, of the
image
matrix). Carefully examine the relationship between the input coordinates
in the above MATLAB commands, the indices in the addBlock
function, and the
position and size of the block in the image.
The assign1
folder contains the addBlock
function (defined
in the addBlock.m
code file) and a
script file named makeBlocks.m
that uses addBlock
to create
an image of three blocks with different shades of gray.
Complete a MATLAB function named addCircle
that
adds a circular block of uniform intensity to an input image. The
addCircle.m
file in the assign1
folder contains the header for
this function and comments about what the function should do. The function has five inputs and
one output as shown below:
function newImage = addCircle(image, xcenter, ycenter, radius, intensity)
Assume that the input image
is a 2D matrix
that already exists. The circular patch of intensity should be centered at
pixel location (X,Y) = (xcenter,ycenter)
and should have the specified input radius
and intensity
.
Unlike the addBlock
function, you will need to write nested for
loops
to step through each location in the region of the matrix where the circle is added,
to determine whether the location lies inside the circle. Think about how to implement
this function in a way that avoids unnecessary computations, and place a semicolon at the
end of statements that generate values, to avoid unnecessary printout. You can assume
that this function will be called with appropriate input values for a circle that lies within
the bounds of the matrix.
The makeBlocks.m
script in the assign1
folder contains two calls to the addCircle
function that you can uncomment to
test your function.
This problem introduces you to the software we will use for edge detection, and is also an
exercise in careful observation. In particular, you will describe your observations of the
zero-crossings obtained from the
convolution of a real image with Laplacian-of-Gaussian convolution operators of different size.
The yachtScript.m
script in the assign1
folder illustrates the processing
steps needed to generate the zero-crossing representations and to display the image and results.
The functions to perform these steps can be found in the edges
folder, which also
contains a file named edgesCode.pdf
that provides a brief summary of all the
functions.
First open the yachtScript.m
file
in the MATLAB editor to see the code that will be executed. The yacht
image is a
512x480
8-bit gray-level image of a sailing scene that is displayed using
imtool
. The image is convolved with two Laplacian-of-Gaussian operators with
w = 4
and w = 8
. Two representations of the zero-crossings of each
convolution are computed — the first stores the slopes of the zero-crossings in the matrices
zc4
and zc8
, and the second stores only their locations in the
matrices zcMap4
and zcMap8
. All of the zero-crossings are preserved
in the latter representation. The two zero-crossing maps are displayed superimposed on the
original image, using imtool
. Execute this script by typing yachtScript
in the Command Window (it may take some time to compute the convolutions). The three images
that are displayed may initially be superimposed in the same region of the screen and can be
dragged apart with the mouse. You can enlarge each image by dragging a corner of the imtool window
to a larger size and using the magnifying-glass icon at the top of the imtool window.
Carefully examine the behavior of the zero-crossings for the two different operator sizes and answer the
following questions, in a Google doc shared with your partner(s).
How well do they match up with intensity changes that you see in the
original image? How accurately do they seem to reflect the positions of the intensity changes?
Are there "spurious" zero-crossings that do not seem to correspond to real edges in the original image?
Describe specific examples of features in the image that are captured well by the zero-crossings,
and places where the zero-crossing contours do not seem to correspond to a real edge in the
image. Observe the image and the slopes of the zero-crossings side-by-side (the
zc4
and zc8
matrices can be displayed using the displayImage
function, with a border of 8 and 16, respectively). Do the slopes seem
to be correlated with the contrast of the corresponding intensity changes in the image?
In this problem, you will relate the results of edge detection to human perception.
Part a: The sun illusion
We sometimes perceive contours in the visual image that do not correspond to real changes of intensity or edges in the original image. Such contours are referred to as illusory or subjective contours. Some examples of this phenomenon are shown below:
In the case of the "sun illusion" shown on the far right, we perceive a bright central disk that is not really present in the image. Sometimes we can offer a possible explanation for why these illusory contours arise, based on the nature of the early processing of intensity changes that takes place in the visual system. In this part of the problem, you will examine the zero-crossings that result from the convolution of the sun image with Laplacian-of-Gaussian operators of different size, in search of such an explanation.
The sunScript.m
code file in the assign1
folder provides some initial
code for analyzing a sun image created with the makeSun
function. This
code convolves the image with a Laplacian-of-Gaussian operator of size w = 5
and
computes the zero-crossings. The image and zero-crossings are both displayed using
imtool
. At this scale, the zero-crossing contours surround each spoke of the sun
wheel. Add code to the sunScript.m
file to generate
zero-crossings from
convolutions with larger Laplacian-of-Gaussian operators of size w = 10
and
w = 20
. Observe the zero-crossing contours obtained from all three operator sizes and
describe how they change as the operator size is increased. Based on this analysis, formulate a
possible explanation for the sun illusion. Add your comments to the Google doc shared
with your partner(s).
Part b: The Hermann grid illusion
The image on the left below illustrates the famous Hermann grid illusion, where you perceive dark shadowy dots in the vicinity of the intersections of the white bars of the grid, except at the intersection that you are looking at directly. The top figure in the middle shows a small window of the zero-crossing contours derived from convolving the Hermann grid image with a large Laplacian-of-Gaussian operator (analogous to the larger operator sizes that exist in the peripheral regions of the visual field in human vision). The other two figures display the slopes of the zero-crossings obtained in the middle of the white bars (bottom figure in the middle) and in the vicinity of an intersection (figure on the right). The slopes are large in the middle of the bars (value of 36) and smaller in the vicinity of the intersection (values of 25-29). Suppose the human visual system interprets the slopes of the zero-crossings as proportional to the contrast (change in brightness) across edges, and then infers the brightness within regions of an image from these measurements of contrast. Assume that the large black square regions of the grid are perceived as having uniform darkness. How could you explain the perception of the shadowy dots at the intersections, given the slopes of the zero-crossings shown below?
One way to blur an image is to calculate the average intensity within a neighborhood of each
location. Write a function named blurImage
that creates a blurred version of an image
using this technique. This function should have two inputs that represent the input image and the
size of the neighborhood, and a single output that is the new, blurred version of the image. Assume
that the input image is a 2D matrix of gray-level intensities ranging from 0 to 255 (i.e. of type
uint8
). The output matrix should be the same size as the input matrix, and can be created
initially using the uint8
and zeros
functions. Suppose we let
nsize
denote the input neighborhood size. Then the value stored in location (x,y)
of the output matrix should be the average of the intensities in the input matrix calculated over a
square region from (x-nsize,y-nsize)
to (x+nsize,y+nsize)
. This average can
be converted to an integer using the round function. You do not need to calculate average values for
the region of width nsize
around the outer border of the image (these values can remain
zero). Think about how the built-in mean
function combined with colon notation can be used
to compute the average intensity values within a square neighborhood around each location — you do not
need to create an additional pair of nested for
loops to perform this step. Test your
function by creating blurry versions of simple images that you created in Problem 1.
This problem provides some practice with writing a MATLAB script to analyze an
image of red blood cells. This image is contained in the file cells.jpg
inside the assign1images
folder.
Your goal is to compute a rough estimate of the number of cells in the image. Fortunately, the cells are all darker than the background and fairly well separated from one another. If you can determine the approximate number of pixels covered by a typical cell, you can then estimate the number of cells by counting the total number of dark pixels in the image and dividing this quantity by the number of pixels in a single cell.
Create a new script file named countCells.m
and save it
in your assign1
folder. The script should first load the color image cells.jpg
(this file will be directly accessible if the assign1images
folder is on the MATLAB search
path), convert it to a gray-level image using the MATLAB rgb2gray
function, and display
the resulting gray-level image.
Use the MATLAB help
system to learn how to use rgb2gray
.
Next, add code to create a binary image that is a 2D
matrix of logical values 0 and 1, where 1 is stored at locations where the cells
image contains a dark intensity value (indicating a blood cell) and 0 is stored at locations of bright
intensity (the background). To implement this step, note that a logical expression can be applied
to an entire vector or matrix all at once, producing a vector or matrix of logical values 1 and 0
(analogous to true and false, respectively). For example, the following statement creates a binary image
containing a logical value 1 at locations where the image intensity is greater than 100, and 0 elsewhere:
>> newImage = (image > 100);
Your binary image of cells might look something like this:
It may take some trial-and-error to find a threshold between dark and light that does
a good job of separating the cells from the background — the Pixel Region tool in
imtool
can be helpful here. Add code to display the binary
image in a new figure window, by calling the figure
function before displaying the image.
Your final steps are to estimate (1) the number of pixels covered by all the blood cells in the image, (2) the number of pixels in a single, isolated cell, and finally, (3) the approximate number of cells in the image, using the strategy described earlier. Given the binary representation of the cells image, how can you determine the total number of pixels corresponding to cells? Hint: You can do this with one statement, and no loops! To estimate the number of pixels in a single, isolated cell, create a copy of a small region of the binary image that includes a single, isolated cell. You can create a copy of an image region as shown in the following example:
>> patch = image(30:60,40:80);
It may again take some
trial-and-error to find the indices of a rectangular region that just spans a single cell - if
you use imtool
to help, remember that the X
and Y
coordinates listed in the lower left corner of the
imtool
window indicate the column and
row, respectively, of the matrix location corresponding to your cursor position.
Display your small matrix to see if you
succeeded in capturing a single cell. Determine the number of pixels covered by the single cell using
the same strategy that you used for the full image. Add statements to
the countCells
script to perform these final steps and print your estimate of the number
of cells in the original image (you
can just omit the semicolon at the end of the statement that performs this final calculation).
A heinous crime was committed in the Computer Science department this past weekend — someone stole Captain Abstraction's suit while he assumed his alter-ego as a mild-mannered computer science professor! Even more shocking, it appears to have been an inside job! The circumstances of the crime point in the direction of a CS faculty member — Takis, Sohie, Orit, or Brian! One piece of evidence was left at the scene, a lone partial fingerprint. The CS332 class has been recruited by the Wellesley campus police to help nail the culprit. Fortunately we have a fingerprint on file for each of our four suspects. Your mission is to complete a MATLAB program to identify the mystery fingerprint.
The fingerprint images for this problem can be found in the assign1images
folder,
and partial code is provided in the fingerprintScript.m
code file in
the assign1
folder. The fingerprintScript
code reads in five images
that include fingerprints
for the four suspects (240x256 pixel images) and the mystery partial fingerprint (a 51x51 pixel
image named finger.jpg
). This script displays the four known fingerprints in a
2x2 arrangement within a single figure window using the MATLAB subplot
function, and
displays the mystery partial fingerprint in a separate figure window. There is additional code
at the end of the script that determines the identity of the culprit. This code is initially
commented out and can be executed after you complete the definition of the getMatch
function described below.
The goal of the program is to determine which of the four suspect fingerprint images
best matches the mystery partial fingerprint image. Of course, the partial fingerprint
will only match a small portion of one of the full fingerprint images. To assist in this
process, define a new function getMatch
that has two inputs,
a partial fingerprint image and a full fingerprint image. This function should return a number that
quantifies the best match between the partial fingerprint and the most similar patch of the
full fingerprint image.
The similarity between two image patches of the same size can be measured, for example, by first
computing the absolute difference between the values stored in corresponding locations
of the two patches, and then summing these differences over the patch. This so-called
sum of absolute differences is a common measure used in computer vision.
Suppose, for example, you want to
search for a 2x2
patch in an image
that best matches the
2x2
pattern shown below on the left, named part
. For each
2x2
patch in the image, you can first compute the element-by-element difference between
the patch and the desired part
. For the example below, this gives the values on
the right labeled patch-part
. The absolute value of these differences, which can be
computed with the abs
function in MATLAB, indicates how much the corresponding
pixel values differ, as shown below on the far right.
These values can be summed over the patch to yield an overall measure of
similarity between the patch and part
, which yields a value of 7
for
the example below. The best matching patch in the image would be the one with the smallest sum of
absolute differences. Using this logic, the getMatch
functiion should return the
smallest sum-of-absolute-differences that exists between the input partial fingerprint and a patch of
the same size in the full fingerprint image.
Tip: You will need to write nested for
loops to systematically check
all possible image patches, but you do not need additional loops to compute the
sum-of-absolute-differences between two patches. The function named fuzzy
in the
MATLAB documentation may provide
a hint about how to implement this.
After completing the definition of getMatch
, uncomment the statements
in the fingerprintScript.m
code file that compute values that capture how well the
partial fingerprint matches the fingerprints of Takis, Sohie, Orit, and Brian, and then use these
match values to determine who committed this horrific crime.