NB – you may not use late days on this assignment.
The purpose of the Edit Distance assignments is to synthesize everything that you learned in the course. The specific goals of Part II are to:
At the end of this assignment, you will have created a new application of
CostMatrix.java
from Part I
to stitch two images together along a least visible seam.
This is an algorithm used often in digital photography.
Thank you for joining us this semester in CIS 110. We would like to ask you
to fill out an
In Part I of the Edit Distance assignment, you wrote
the CostMatrix
class to generate optimal string alignments.
In Part II, you will use the same CostMatrix
class to find least visible seams across two images.
You will write an ImageSeam
class that contains two
Picture
objects for which CostMatrix
can compute the minimum-cost seam.
Below is the API:
public class ImageSeam implements Matchable ------------------------------------------- public ImageSeam(Picture left, Picture right) // constructs an ImageSeam object with // the Picture objects left, right where left // and right are of the same size public ImageSeam(String left, String right) // constructs an ImageSeam object with // the images at filenames left, right // where left and right are of the same size public int height() // returns the height of left public int width() // returns the width of left public int matchCost(int row, int col, char direction) // returns the squared color difference // between the pixels at (row, col) of left // and right public void showSeam(Path p) // displays the seam represented by the // Path object p public Picture getStitchedPicture(Path p) // returns a new Picture with left and // right stitched along the seam p
As a reminder, we repeat the APIs for CostMatrix
and Path
.
public class CostMatrix ----------------------- public static Path costRecursive(Matchable m) // computes the minimum-cost alignment // recursively and returns a linked list // of Path objects representing the // minimum-cost alignment public static Path costIterative(Matchable m) // computes the minimum-cost alignment // iteratively and returns a linked list // of Path objects representing the // minimum-cost alignment
public class Path ----------------- public Path(int row, int col, int cost, Path next) // constructs a Path object that represents // the cost of matching // the Matchable objects at (row, col); next // points to the Path object // that provided the minimum cost for this match
In addition, we provide a client program Stitcher.java
that preprocesses the images to stitch only the overlapping areas.
Download Stitcher.java
.
You may also
need Picture.java
,
although the introcs installer should have set it up for you
already.
Because this assignment depends upon hw09: Edit Distance,
you will need to copy the following files into your
directory for this
assignment: CostMatrix.java
, Path.java
,
and Matchable.java
. If you have
implemented CostMatrix.java
correctly in hw09,
you should not need to make any changes to these files for
this assignment to work.
You will submit CostMatrix.java
to run our
tests for the ImageSeam.java
class. You are
welcome to update CostMatrix.java
to correct
bugs in CostMatrix.costIterative()
and add tests, but
you shouldn't need to if it is already working
properly. You will not be graded for style on
your CostMatrix.java
code.
Nevertheless, your CostMatrix
class must compile, and
your CostMatrix.costIterative()
function
should be correct, for all of our tests to pass; otherwise, you may
lose points. (We will not
use CostMatrix.costRecursive()
for any of our grading
on this assignment, so it does not matter if your recursive implementation doesn't
work.) Your CostMatrix
class must also be your own work, and you must record
any help or outside resources in you help log as usual.
You will not submit EditDistance.java
. If you used your
EditDistance
class to test CostMatrix
, you can leave
any references to EditDistance
in your CostMatrix.main()
,
since we will compile your CostMatrix
with a "dummy"
EditDistance
class on the server.
ImageSeam
Combining multiple images into a larger panorama is one of the oldest techniques in the field of computational photography, which combines computer algorithms with traditional photography to create images that are difficult or impossible to achieve purely with optical lenses. Camera lenses have a much narrower field of view than human vision, so a single photograph cannot capture an entire landscape as you see it. Panoramic stitching solves the problem by taking two overlapping images and "stitching" them together along a "least visible seam" into a single, large image, where the pixels to the left of that seam are taken from the left-hand image, and the pixels to the right are taken from the right-hand image. As long as the two images are nearly identical along the least visible seam, the seam will only be barely visible in the final image.
We restrict our image stitching implementation to seams that run down, right, or diagonally. This means that finding the least visible seam uses the same type of cost optimization as sequence alignment.
ImageSeam
class and the Picture
and Color
librariesYou will write an ImageSeam
class that provides the
methods necessary to allow CostMatrix
to find the least
visible seam.
The ImageSeam
class implements the Matchable
interface from hw09. Write the skeleton of the ImageSeam
class according to the API in Section 0B, making sure that it implements
the Matchable
interface.
The textbook's standard library, which was installed by the
introcs installer you ran in hw00, provides
a Picture
class. The API documentation
for Picture
is here.
In addition, because you will be working with
Color
values, you will need to include
the java.awt.Color
library in
your ImageSeam
class by including the statement
import java.awt.Color;
at the top of ImageSeam.java
, before your class
declaration. The booksite's documentation
for java.awt.Color
(which is actually built into
Java) is
here.
ImageSeam
When implementing the following methods, refer to the API
documentation for Picture
.
ImageSeam(Picture left, Picture right)
constructs the ImageSeam
object with the two Picture
s
left
and right
. If
either left
or right
is null
, or if the dimensions of right
are different from those of left
,
then ImageSeam()
should throw
an IllegalArgumentException
.
Your class must not modify left
or right
in any way.
ImageSeam(String left, String right)
constructs the ImageSeam
object with the images given by filenames
left
and right
. If either left
or right
is null
, then ImageSeam()
should throw an IllegalArgumentException
.
Avoid duplicating code between the ImageSeam(Picture, Picture)
and ImageSeam(String, String)
constructors.
height()
returns the height of left
(which is equal to the
height of right
, as enforced by the constructors).
width()
returns the width of left
(which is equal to the
width of right
, as enforced by the constructors).
ImageSeam.matchCost()
The least visible seam passes through the locations where the pixel colors are as close as possible
between left
and right
.
Therefore, the cost of stitching left
and right
along a seam passing through a pixel (row, col)
will be the difference in color between the (row, col) pixel of left
and the (row, col) pixel of right
.
Because a pixel's color is comprised of red, green, and blue
components, we will quantify the "difference in color" between
the (row, col) pixels of left
and right
using this expression, called the squared
pixel difference: (rL
- rR)2 +
(gL - gR)2
+ (bL
- bR)2,
where rL is the red value
of left
's pixel (row, col),
rR is the red value of the
right
's pixel (row, col), etc.
matchCost(int row, int col, char direction)
returns the squared pixel difference between the (row, col) pixels
of left
and right
. The direction
argument is ignored,
but your matchCost()
should check for invalid parameter values and throw
IllegalArgumentException
.
Refer to the API documentation for Picture
and
Color
to implement this method.
You should use the getRed()
, getGreen()
, and getBlue()
methods of the Color
object.
Be careful: we have written the pixel coordinates
above in the order (row, col), but the
order of arguments for the pixel methods
of Picture
is specified as (x, y),
which is actually (col, row).
Variable name change – We have renamed
the i
and j
variables from the Edit
Distance assignment to row
and col
,
which are less confusing. You do not need to
update Matchable.java
even though it still lists
the matchCost
parameters as i
and j
; just name the parameters of
the matchCost
function
in ImageSeam.java
row
and col
. To maintain compatibility with Edit
Distance, Path.java
cannot change and will
still refer to i
and j
. If you get
confused, just refer to this table:
Path | ImageSeam |
---|---|
i | row |
j | col |
Write tests in ImageSeam.main()
to test the functionality of the ImageSeam
class.
Save the images example-blue.png
and example-red.png
.
Construct an ImageSeam
object with these images, and pass it into
CostMatrix.costIterative()
. The cost of the resulting Path
should be 129211.
showSeam()
and
getStitchedPicture()
We will now write utility methods to display the location of a seam, and stitch
left
and right
into one new Picture
.
ImageSeam.showSeam()
showSeam(Path p)
should display the location of the pixels
on the seam:
Picture
p
and change the color of each pixel in
p
to a different color (say, white)Picture
Refer to the API for Picture
to find the correct method calls.
You should check whether the Path
is null
and throw a RuntimeException
if so.
However, you may assume that the cost values and pixel
coordinates in p
represent a valid
seam.
Warning – Do not
call Picture.save()
. Doing so will break our test
scripts, and you will receive a large point deduction.
ImageSeam.getStitchedPicture()
getStitchedPicture(Path p)
should return a new
Picture
that stitches left
and right
, where the areas of left
and right
are overlapping. Pixels to the left of
the seam and on the seam adopt the pixel colors
of left
; pixels to the right of the seam adopt the
pixel colors of right
.
Picture pic
row
, col
)
in p
and change the color of each pixel in pic
to the left of (row
, col
), including (row
, col
),
to the corresponding pixel color of left
,
and the color of each pixel to the right of (row
, col
) to the
corresponding pixel color of right
pic
You should check whether the Path
is null
and throw a RuntimeException
if so.
However, you may assume that the cost values and pixel
coordinates in p
represent a
valid seam.
Reminder – The i
instance variable
of each Path
object corresponds to
the row
. The j
instance variable
corresponds to the col
.
Warning – Do not call Picture.save()
. Doing so will break our
test scripts, and you will receive a large point deduction.
Write tests in ImageSeam.main()
to test the functionality of the
showSeam()
and getStitchedPicture()
methods.
Construct an ImageSeam
object
with example-blue.png
and example-red.png
. The result you should obtain
by calling showSeam()
with the path
from CostMatrix.costIterative()
is shown below.
Download
example-stitched.png
– it is the image that
you should obtain on
calling getStitchedPicture()
.
Stitcher
The getStitchedPicture()
method
of ImageSeam
assumes that the stitching area
overlaps completely between left
and right
and that the seam runs from the top
left to the bottom right. The more common case is where the
image stitching areas overlap only partially, and where the
seam may be running in a different direction. To stitch such
images together, we can specify the overlapping portion and
the seam direction, and create an ImageSeam
object with only this overlapping portion, flipping this image
horizontally or vertically such that the seam always runs from
the top left to the bottom right.
Figuring out how to crop out the overlapping portions of the
images and flip them so the seam runs from the top left to the
bottom right is tedious and involves a lot of trial and
error. Instead, we provide the
program Stitcher.java
to do this cropping and
stitching for you. It finds the overlapping portions of the
two images, flips them so that the seam runs from the top left
to the bottom right, uses an ImageSeam
object to
find the least visible seam for the overlapping portion, and
embeds the stitched overlapping portion into the combined
panorama.
Stitcher
Read the header comment for Stitcher.java
to
learn how to use it. To use it, you specify the two input
images and the shift of the second image relative to the first
image. Positive numbers for the shift coordinates shift the
second image right and down; negative numbers shift it left
and up.
Stitcher.java
also allows you to specify a
reference image; the pixels that differ between the stitched
image and the reference image will be colored black. (Because
of the possibility of multiple seams with the same total cost,
there may still be some black pixels even if your program is
correct.)
Download the images sea1.png
,
sea2.png
,
and sea-stitched.png
.
Try running the following commands to test your program:
% java Stitcher example-blue.png example-red.png 0 0 % java Stitcher example-blue.png example-red.png 0 0 example-stitched.png % java Stitcher sea1.png sea2.png 318 -7 % java Stitcher sea1.png sea2.png 318 -7 sea-stitched.png
The stitched images should look like the following:
Complete readme_imagestitching.txt
in the same way that you have done for previous assignments.
Submit ImageSeam.java
, CostMatrix.java
, and readme_imagestitching.txt
on the course website.
If you have not yet filled out the