In this assignment you will create a simple image processing program. The operations that you implement will be mostly filters which take an input image, process the image, and produce an output image.
You should use the following skeleton code (1.zip or 1.tar.gz ) as a starting point for your assignment. We provide you with several files, but you should mainly change
main.cpp: Parses the command line arguments, and calls the appropriate image functions.
image.[cpp/h]: Image processing.
pixel.[cpp/h]: Pixel processing.
vector.[cpp/h]: Simple 2D vector class you may find useful.
bmp.[cpp/h]: Functions to read and write Windows BMP files.
Makefile: A Makefile suitable for UNIX platforms.
visual.dsp: Project file suitable for Visual C++ on Windows 2000 platform.
You can find test images at
/u/cos426/images(on the OIT accounts, such as
After you copy the provided files to your directory, the first thing to do is compile the program. If you are developing on a Windows machine, double click on
image.dswand select "build" from the build menu. If you are developing on a UNIX machine, type
make. In either case, an executable called
image.exe) will be created.
The user interface for this assignment was kept to the simplest possible, so you can concentrate on the image processing issues. The program runs on the command line. It reads an image from the standard input, processes the image using the filters specified by the command line arguments, and writes the resulting image to the standard output. For example, to increase the brightness of the image
in.bmpby 10%, and save the result in the image
out.bmp, you would type:For each available image filter there is a corresponding optional argument. To see the complete list of options, type:
% image -brightness 0.1 < in.bmp > out.bmpIf you specify more than one option, the options are processed in the order that they are found. For example,
% image -helpwould first decrease the contrast of the input image by 20%, and then scale down the result by 50% in both x and y directions. Another way of doing the same thing would be
% image -contrast 0.8 -scale 0.5 0.5 < in.bmp > out.bmpwhich uses pipes instead of multiple options per command line. The ability to use pipes can be used to avoid creating temporary files. For example,
% image -contrast 0.8 | image -scale 0.5 0.5 < in.bmp > out.bmpdoes the same thing as the previous command, but it redirects the final output to be the input of xv, which simply shows you the result on the screen, without creating any temporary files in your directory. Another advantage of this kind of interface is that you can combine different filters in shell scripts, and automate image processing steps, which can save you time.
% image -contrast 0.8 | image -scale 0.5 0.5 < in.bmp | xv -
The assignment is worth 20 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. The features in bold face are required. The other ones are optional. Refer to this web page for more details on the implementation of each filter and example output images.
For any feature that involves resampling (i.e., scale, rotate, "fun," and morph), you have to provide three sampling methods: point sampling, bilinear sampling, and Gaussian sampling.
- (1/2) Random noise: Add noise to an image.
- (1/2) Brighten: Individually scale the RGB channels of an image.
- (1/2) Contrast: Change the contrast of an image. See Graphica Obscura.
- (1/2) Saturation: Change the saturation of an image. See Graphica Obscura.
- (1/2) Crop: Extract a subimage specified by two corners.
- (1/2) Extract Channel: Leave specified channel intact and set all others to zero.
- (1) Quantize: Change the number of bits per channel of an image, using simple rounding.
- (1) Random dither: Convert an image to a given number of bits per channel, using a random threshold.
- (1) Composite: Compose one image with a second image, using a third image as a matte.
- (1) Create a composite image of yourself and a famous person.
- (2) Blur: Blur an image by convolving it with a Gaussian low-pass filter.
- (2) Edge detect: Detect edges in an image by convolving it with an edge detection kernel.
- (2) Ordered dither: Convert an image to a given number of bits per channel, using a 4x4 ordered dithering matrix.
- (2) Floyd-Steinberg dither: Convert an image to a given number of bits per channel, using dithering with error diffusion.
- (2) Scale: Scale an image up or down by a real valued factor.
- (2) Rotate: Rotate an image by a given angle.
- (2) Fun: Warp an image using a non-linear mapping of your choice (examples are fisheye, sine, bulge, swirl).
- (3) Morph: Morph two images using the method in Beier & Neeley's "Feature-based Image Metamorphosis."
- (up to 3) Nonphotorealism: Implement any non-trivial painterly filter. For inspiration, take a look at the effects available in programs like
xv, PhotoShop, and Image Composer (e.g., impressionist, charcoal, stained glass, etc.). The points awarded for this feature will depend on the creativity and difficulty of the filter. At most one such filter will receive points.
By implementing all the required features, you get 12 points. There are many ways to get more points:
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.
- implementing the optional features listed above;
- (1) submitting one or more images for the art contest,
- (1) submitting a
.gifmovie animating the results of one or more filters with continuously varying parameters (e.g., use the
makemoviecommand on the SGIs),
- (2) winning the art contest.
It is possible to get more than 20 points. However, after 20 points, each point is divided by 2, and after 22 points, each point is divided by 4. If your raw score is 19, your final score will be 19. If the raw score is 23, you'll get 21.25. For a raw score of 26, you'll get 22.
You should submit one archive (zip or tar file) containing:
The writeup should be a HTML document called assignment1.html which may include other documents or pictures. It should be brief, describing what you have implemented, what works and what doesn't, how you created the composite image, how you created the art contest images, and instructions on how to run the fun filters you have implemented.
- the complete source code with a Visual C project file;
.bmpimages (sources, masks, and final) for the composite feature;
.mpegmovie for the movie feature (optional);
- the images for the art contest (optional); and
- a writeup.
Make sure the source code compiles on the workstations in Friend 017. If it doesn't, you will have to attend to a grading session with a TA, and your grade will suffer.
Always remember the late policy and the collaboration policy.
A few hints:
- Do the simplest filters first!
- Look at the example pages here, here, and here.
- There are functions to manipulate pixel components and pixels in
image.[cpp/h]. You may find them helpful while writing your filters.
- There are functions to manipulate 2D vectors in
vector.[cpp/h]. You may find them helpful while writing the morphing functions.
- Send mail to the cos426 staff.
- Stay tuned for more hints.
Stay tuned for more questions.
The compose image parameters don't make sense. Why?
We are using PixelOver operator for composing images. Unfortunately, the bmp format doesn't store alpha values, so we need a way of getting alpha values for both the top and bottom image. The way we are doing this is by loading another image and treating its blue channel as the alpha value (see the function SetImageMask in main.cpp). So... we need 4 images: top, top_mask, bottom, bottom_mask. top is the current image that's already loaded, so the ComposeImage option takes the remaining 3 filenames as input. In the parameter section of
main.cppthese 4 images are put together to create two images (main.cpp, lines 148-170) with the alpha values filled in, which is then passed to the ComposeImage function that you write in image.cpp. The three parameters of ComposeImage are stylistic, you could get away with two, or return an image pointer, or something equivalent.
- For the quantization function, can we safely assume that the image channels for input are always 8 bit and not any greater?
- For the quantization function, should we assume that the number of bits to which we're quantizing is always a multiple of 3?
No. The parameter is the number of bits per channel, not per pixel.
- I get error messages and a core dump when the program tries to read in my bmp files. What should I do?
Make sure your files are 24-bit, uncompressed bitmaps. The provided code does not read any other kind of files. If you are using xv: right-click to open the xv controls window, then click on the 24/8 Bit menu-button, then select the 24-bit mode option. Alt-8 toggles between 8-bit and 24-bit mode. Under windows, the error msg:means the image is not correctly formatted. One solution is to open the image, save it a
Assertion failed: bmih.biSizeImage == (DWORD) lineLength * (DWORD) bmih.biHeight, file c:\temp\one\1\bmp.cpp, line 252
.jpgthen save as back to
- When doing convolutions, each pixel becomes a weighted average of its neighbors. What do I do when the pixel is close to the border, and therefore doesn't have some of its neighbors?
There are a few things you could do. First of all, here is what what not to do: don't just skip those pixels. One thing you could do is pretend that the image is "toroidal" -- the right side is connected to the left side, and the top to the bottom. So, the pixel just to the right of the rightmost pixel is the leftmost pixel. Another thing you could do is cut out the parts of the matrix that don't fit in the image. The trick here is to renormalize the entries in the matrix so that they still sum to 1.0. So, for example, if you were applying the matrix
|-1 -1 -1|
|-1 +8 -1|
|-1 -1 -1|
to a pixel in the middle of the right column of pixels, you would use
|-1 -1 0|
|-1 +8 0| / 3.0
|-1 -1 0|
(The 3.0 came from adding up all the remaining entries.) In Floyd-Steinberg, you won't be able to transfer any error from the bottom row, but this doesn't matter.
- What do I do in the case of kernels of odd size? Is it acceptable, in the case of an even argument (n mod 2 = 0), to increase n by 1 and use the corresponding odd gaussian matrix for the filter?
Yes, but feel free to try more elegant solutions.
- Is it ok to just do nothing if the filter argument in a convolution is larger than the size of the image.
Yes, but a warning message would be nice.
- My blur filter is darkening the image. What is wrong?
Make sure that the entries in your kernel add up to 1. Be especially careful with rounding errors: although small in each entry, collectively they can be large enough to cause this undesired darkening effect.
- I don't have a decent digitized photo of myself for the assignment. What do I do?
Find someone who owns a digital camera (for example, the TA).
- Can I assume my functions will always be called with valid arguments?
No, but bailing out is acceptable (print a message and exit). Please avoid segmentation faults, core dumps, blue screens of death, etc.