Assignment 1: Image Processing
Due on September 21 (Sunday) at 11:59 PM
Announcements
- [8/31/25]
If your code appears to be crashing just reading in the input image, this could be because the image is poorly formatted. To see if this is happening, you can modify
main1.cpp by moving the reading of the image within the try block. In particular, you will need to replace the lines:
// Try to read in the input image
Image32 image;
image.read( Input.value );
cout << "Input dimensions: " << image.width() << " x " << image.height() << endl;
try
{
with the lines
try
{
// Try to read in the input image
Image32 image;
image.read( Input.value );
cout << "Input dimensions: " << image.width() << " x " << image.height() << endl;
Overview
In this assignment you will create a simple image processing
program, a pared-down version of Adobe Photoshop or The Gimp. The operations
that you implement will be mostly filters which take in an input image,
process the image, and produce an output image.
- The code skeleton can be downloaded here.
- A short description of the files can be found here.
- An overview of the code you will be using can be found here or downloaded here.
- Some test images can be found here.
- Examples of what some of the results should look like can be found here.
Note: The code support for reading BMP files is somewhat limited. (Specifically, it will only work if they are three-channel images with one byte per channel. Otherwise it carshes gracelessly.) You can either use JPEG images, or try using the provided test images.)
Getting Started
You should use the code (Assignments.zip) as a starting point for your assignment.
We provide you with numerous files, but you should only have to change
image.todo.cpp and
lineSegments.todo.cpp.
After you download the files, the first thing to do is compile the program.
To do this, you will first have to compile the Image, JPEG, and Util libraries and then compile the Assignment1 executable.
- On a Windows Machine
Begin by double-clicking on Assignments.sln to open the workspace in Microsoft Visual Studios.
- Compile the
Assignment1 executable by clicking on "Build" and then selecting "Build Solution". (If the JPEG.lib, Image.lib, and Util.lib libraries have not already been compiled, they will be compiled first.)
- The executable
Assignment1.exe is compiled in Release mode for the 64-bit architecture and will be placed in the root directory.
- On a Unix Machine
- Type
make -f Makefile1 to compile the Assignment1 executable. (If the libImage.a, and libUtil.a libraries have not already been compiled, they will be compiled first.) This assumes that the JPEG header files and library have already been installed on your machine. If they haven't been installed yet, you can install them on Linux by typing sudo apt-get install libjpeg-dev and on a Mac by typing brew install jpeg (you may also need to add the homebew header and library folders to the path.)
- The executable
Assignment is compiled in Release mode and will be placed in the root directory.
- On a Mac Machine
If jpeg is not installed on your machine, you will need to install it on your machine (e.g.) using homebrew
% brew install jpeg
If the compiler complains that it cannot find headers/libraries, you can try proceeding in one of two ways:
- Set the environment variables to search the
homebrew directories:
% export LDFLAGS="-L/opt/homebrew/opt/jpeg/lib"
% export CPPFLAGS="-I/opt/homebrew/opt/jpeg/include"
- Modify the
makefiles to point to the appropriate header/library directories obtained by typing
% brew info jpeg
- Change
Image/Makefile by adding -I/<homebrew include directory> to the line declaring the CFLAGS variable (with <homebrew include directory> the directory where homebrew put the header files).
- Change
Makefile1 by adding -L/<homebrew library directory> to the line declaring the LFLAGS variable (with <homebrew library directory> the directory where homewbrew put the library files).
How the Executable Works
The executable runs on the command line. It reads an image from a specified file, processes the
image using the filter specified by the command line arguments, and writes the resulting image to the specified
file.
The command line parser is set up to read the arguments from the command line that are specified in the following
format: First the parameter name is specified by pre-pending with two hyphens, and then the parameter values
are enumerated. (Note that the number of arguments associated to each parameter are fixed in main1.cpp
and the order in which the parameters are specified is ignored.)
For example, to increase the brightness of the image in.bmp by 10%, and save the result in the image
out.bmp, you would type:
% Assignment1 --brighten 1.1 --in in.bmp --out out.bmp
To see the full list of possible arguments, you can simply type:
% Assignment1
This gives you a list of all the possible parameter names and the number and meaning of the associated parameter
values. Bracketed parameters are optional (an input image file and an output image file need to be specified).
Note that since the order in which parameters are specified is ignored, if you want to apply two filters to a single
image you can specify parameters for both filters, but you cannot control the order in which the filters are applied.
For some filters, the order in which the filters are applied does not affect the output image while for others, the
output image may depend on the order. (For the latter case, you can control the ordering by applying one filter,
writing the output to a temporary image, and then applying the second filter to the temporary image.)
The current main1.cpp decides if the specified image file is Windows BMP file or a JPEG file based on the
file extension.
What You Have to Do
The assignment is worth 30 points. The following is a list of
features that you may implement (listed roughly from easiest to hardest). The
number in front of the feature corresponds to how many points the feature is
worth.
- (1)
Image::Image32::addRandomNoise (
image.todo.cpp):
Add random noise to an image.
- (1)
Image::Image32::brighten (
image.todo.cpp):
Individually scale the RGB channels of an image.
- (1)
Image::Image32::luminance (
image.todo.cpp):
Change a color image into a luminance (grayscale) image.
- (1)
Image::Image32::contrast (
image.todo.cpp):
Change the contrast of an image. (See Graphica Obscura.)
- (1)
Image::Image32::saturate (
image.todo.cpp):
Change the saturation of an image. (See Graphica Obscura.)
- (1)
Image::Image32::crop (
image.todo.cpp):
Extract a subimage specified by two corners (x1,y1) and (x2,y2).
- (2)
Image::Image32::quantize (
image.todo.cpp):
Change the number of bits per channel of an image, using simple rounding.
- (2)
Image::Image32::randomDither (
image.todo.cpp):
Convert an image to a given number of bits per channel, using a random threshold.
- (2)
Image::Image32::orderedDither2x2 (
image.todo.cpp):
Convert an image to a given number of bits per channel, using a 2x2 ordered dithering matrix.
- (2)
Image::Image32::floydSteinbergDither (
image.todo.cpp):
Convert an image to a given number of bits per channel, using dithering with error diffusion.
- (2)
Image::Image32::blur3x3 (
image.todo.cpp):
Blur an image by using a 3x3 mask of weights as a filter.
- (2)
Image::Image32::edgeDetect3x3 (
image.todo.cpp):
Detect edges in an image by using a 3x3 mask of weights as a filter.
- Image sampling: (Note that your implementation will need to handle the situation when evaluating requires accessing pixels outside of the image.)
- (1)
Image::Image32::nearestSample (
image.todo.cpp):
Return the value of the pixel closest to the position (x,y).
- (1)
Image::Image32::bilinearSample (
image.todo.cpp):
Return the value of the pixel obtained by interpolating the values of the four pixels closest to
the position (x,y).
- (1)
Image::Image32::gaussianSample (
image.todo.cpp):
Return the value of the pixel obtained by computing the weighted average of all pixels whose distance is less
than radius from the sample point (x,y) using Gaussian weighting where the variance of
the Gaussian is specified by variance
- (1)
Image::Image32::scaleNearest (
image.todo.cpp):
Scale an image up or down by a real valued factor using nearest pixel sampling.
- (1)
Image::Image32::scaleBilinear (
image.todo.cpp):
Scale an image up or down by a real valued factor using bilinear interpolation.
- (1)
Image::Image32::scaleGaussian (
image.todo.cpp):
Scale an image up or down by a real valued factor using Gaussian sampling. (Make sure to choose the variance
of the Gaussian and the radius of weighting so that they correspond to the scale factor in a meaningful way.)
- (2)
Image::Image32::rotateNearest (
image.todo.cpp):
Rotate an image by a given angle about the center pixel using nearest pixel sampling.
Note: The resolution of the output will not, in general, be the same as the resolution of the input. However:
- The center of the input should map to the center of the output, and
- The output should be just large enough to contain the rotated input.
- (2)
Image::Image32::rotateBilinear (
image.todo.cpp):
Rotate an image by a given angle about the center pixel using bilinear interpolation.
- (2)
Image::Image32::rotateGaussian (
image.todo.cpp):
Rotate an image by a given angle about the center pixel using Gaussian sampling.
- (2)
Image::Image32::funFilter (
image.todo.cpp):
Warp an image using a non-linear mapping of your choice (examples are fisheye, sine, bulge, swirl).
- (6) Beier-Neely Morphing:
Morph two images using the method in Beier Neely's
Feature-based Image Metamorphosis.
See this page for more information about the command line, input options, and some
test cases.
To make your life easier, the Image32::BeierNeelyMorph function already has a body which interpolates
the line segments, calls the warping function Image::Image32::warp and then cross-dissolves the resultant
images by calling Image::Image32::CrossDissolve.
The Image::Image32::warp function calls Image::OrientedLineSegmentPairs::getSourcePosition which determines
the position of the source pixel given the position of the target.
In order to get this function to actually return the desired image, you will need to implement the following methods:
The assignment will be graded out of 30 points. In addition to implementing these features, there are several other ways to get more points:
- (1) Submitting one or more images for the art contests.
- (1) Submitting a
.mpeg or .gif movie animating
the results of one or more filters with continuously varying parameters.
You can generate such a video by outputting the results of your filter in JPEG format and
then using an executable such as JPGAvi which stitches
together a collection of JPEG files into a single movie. Alternatively, if you are a Linux user,
you can use the mpeg_encode command or The Gimp to produce an animated gif for your results.
- (1) winning the regular art contest,
For images or movies that you submit, you also have to submit the sequence of commands used to created
them, otherwise they will not be considered valid.
It is possible to get more than 30 points. However, after 30 points, each point is divided by 2, and after
32 points, each point is divided by 4. If your raw score is 29, your final score will be 29. If the raw score
is 33, you'll get 31.25. For a raw score of 36, you'll get 32.
What to Submit
Submission intstructions are specified here.
Note that the autograder does not grade your submission. Passing the autograder only means that an image has been successfully generated.
Please include the following in your submission:
- For the code submission, only submit the two
*.todo.* files. If it compiles, you should be able to see your results in the Gradescope shortly.
- For the art contest submission, placed everything related into a folder named
artcontest.zip including the .mpeg movie or animated .gif for the movie feature and the images for the art contest.
- Add a write up describing what you have implemented and what you did for the art contest. See more info on the general submission page.
- In summary, the files in your submission should look like the following:
image.todo.cpp
lineSegments.todo.cpp
writeup.pdf
[artcontest.zip]
awsomepicture1.bmp
awsomepicture2.bmp
- ...
interestingartpiece1.gif
interestingartpiece2.gif
- ...