In this step you'll be expected to write a bunch of image manipulation operations from scratch. I've provided a Visual C++ program shell to start with and you'll create new menu options and functions that manipulate image data and produce new images. .
Most of these tasks are pretty easy and take very little code, but there are quite a few.
The program you'll be using for this step is called ImageProcess. You'll find all you need to build the project in ImageProcess.zip. You should get the program installed and compiled to start. ImageProcess uses a dynamic link library (DLL) for its JPEG support. After you load the program, right click on the project and select Properties. In the Debugging options, set the working directory to $(SolutionDir) You'll need to do this for both the debug and release configurations.

Here's some basic documentation on what ImageProcess does:
This program can load, manipulate, and save image files in the Windows Device Independent Bitmap format (.bmp files) and JPEG format. You'll find some images to play with in http://www.cse.msu.edu/~cse471/img and there are millions of images on the web, etc. Note that the program and file format only support RGB color images. The program can load indexed color images and will convert them to RGB. All processing is done internally on 8 bit per pixel RGB data. Notice: The actual data order is BGR, (blue, green, then red).
The user interface is relatively simple. You load an image using File:Open. This loads the image into an internal CImage (my type) object. This object is the member m_image1 of CImageProcessDoc. This image is also copied to member m_image2. The view displays m_image1 on the left and m_image2 on the right. m_image1 will always be the image prior to filtering, m_image2 will be after your filter. My program initially copies m_image1 into m_image2. If you select Save or Save As, you are saving the filtered version (m_image2).
A few filters are preinstalled and can be selected with the Filter menu. Select Filter:Negative. This is a simple negative filter. You'll see what happens when you select it. Note that a filter is an operation on the original image, so selecting Negative again does not put the modified version through the Negative filter again. You can always select the Copy option to copy back the original image.
There are two types of filters in this program. The filters in the Filter menu are filters than accept an input and produce an output. Any parameters you may need for these filters would be added using the dialog box. The Mouse menu contains filter options that require mouse input. As an example, select Mouse:Draw. You can now draw on the right image using the mouse. Some of the filters you will do will require mouse input.
The Mouse filters does not necessarily copy the image in each time, so you can keep making changes to the image. These filters are all intended to be demonstrative of the program functionality.
Now we're going to add a filter of our own. Let's add a simple low-pass filter that averages all pixels in a 3x3 window. Here's the fast way to do this in this program. We'll only implement the Filter option, not the mouse option, so do these steps:
Now we're going to add some filter functionality. In CImageProcessDoc::OnFilterLowpass, enter the following code:
void CImageProcessDoc::OnFilterLowpass()
{
// Turn on the hourglass cursor
BeginWaitCursor();
// Allocates an output image the same size as the input image
m_image2.SameSize(m_image1);
for(int r=0; r<m_image2.Height(); r++)
{
for(int c=0; c<m_image2.Width() * 3; c++)
{
int pixel = 0;
for(int i=-1; i<=1; i++)
{
if((r+i) >= 0 && (r+i) < m_image2.Height())
{
for(int j=-3; j<=3; j+=3)
{
if((c+j) >= 0 && (c+j) < m_image2.Width() * 3)
pixel += m_image1[r+i][c+j];
}
}
}
m_image2[r][c] = BYTE(pixel / 9);
}
}
// Force a redraw
UpdateAllViews(NULL);
// Turn off the hourglass cursor
EndWaitCursor();
}
Note how I handed the boundary conditions.
Management of images in your program are through the CImage class. Note that you can declare additional CImage objects if you need an intermediate image. CImage implements a two dimensional array of type BYTE that contains the image. If knows the width and height of the image. The image data is stored as RGB data and the array is 3 times as wide as inImg.Width()! Be very careful about that. The ordering of data in the image is Blue, Green, and Red (Windows standard order), not the normal Red, Green, Blue.
The image is oriented with 0,0 in the lower left corner
You can access pixels using the normal array notation as above. m_image1[y][x] gets the value at location x,y. Note that if you want the pixel at x,y on the screen, you should use m_image1[y][x*3] which will get you to the blue value. m_image1[y][x*3+1] gets the green, etc.
You can set the size of an image using CImage::SetSize(int x, int y). This is the image size, not the array size (which is 3 times larger, of course).
You can get the width and height using CImage::Width() and CImage::Height(). This the actual width, not the width * 3.
You can copy images using a copy constructor or the equal operator.
The function CImage::SameSize(const CImage &img) sets the size of an image to the same size as another image. I use that above.
I've provided the function CImage::Set(int x, int y, int r, int g, int b) which sets a pixel at x,y (actual pixel location) to a desired color.
You may find it useful to create filters as functions that accepts references to the input and output images or even as classes. If you do this, you will be able to "cascade" filters, using them in sequence.
Some filters will require the mouse to work. In this step we're going to create a simple mouse based-filter that draws a crosshair. Do the following steps:
This should all compile at this point and you should be able to select Crosshair and see it checked. The CImageProcessDoc::OnUpdateMouseCrosshair() function is called every time the menu is displayed. You added code to set the check mark if we have selected the crosshair mouse mode.
There are two functions in CImageProcessDoc related to mouse activity: CImageProcessDoc::MousePress(int x, int y) and CImageProcessDoc::MouseMove(int x, int y). To simplify things, these functions are called when the mouse is pressed or moved when the button pressed over the right image. The x,y values are the corresponding point in the image. I did this because I'm a bit lazy and did not want to repeatedly change mouse coordinates.
We'll just add code to MousePress. Add this new private function to CImageProcessDoc:
void CImageProcessDoc::Crosshair(int x, int y)
{
// Copy the image before we do the crosshair
m_image2 = m_image1;
// Draw the horizontal crosshair:
for(int c=0; c<m_image2.Width(); c++)
{
m_image2[y][c * 3] = 0;
m_image2[y][c * 3 + 1] = 255;
m_image2[y][c * 3 + 2] = 255;
}
// Draw the vertical crosshair:
for(int r=0; r<m_image2.Height(); r++)
{
m_image2[r][x * 3] = 0;
m_image2[r][x * 3 + 1] = 255;
m_image2[r][x * 3 + 2] = 255;
}
}
Then, go to CImageProcessDoc::MousePress() and add the following case to the switch statement:
case ID_MOUSE_CROSSHAIR:
Crosshair(x, y);
break;
When you run this, you should be able to select the crosshair option and draw the crosshairs. Add this case to CImageProcessDoc::MouseMove() and see what that does to the behavior of your program.
This section describes the tasks you are to do. Each creates a new filter.
Note that some filters require the definition of parameters in a dialog box. You are welcome to put all of these in the same dialog box or create separate dialog boxes for the filters.
Write a filter that converts a color image to monochrome. Simply take the average of the colors and save as the Red, Green, and Blue values in the result. Note that the output is still a color image, but it has monochrome content.
Write a mouse-based filter that shuffles colors (red to green, etc.). Each time you click the mouse you should get a new shuffle. You can choose to have a set of shuffles you cycle though, determine them randomly, or use the mouse location to choose the shuffle you use. There are 6 possible shuffles.
A shuffle means that you swap the colors around, but keep all of the values. As an example, BGR would be a shuffle with nothing moved. GRB would be a shuffle where green replaced blue, red replaced green, and blue replaced red.
Implement a 3x3 median filter. There is an image called speckle.jpg in the http://www.cse.msu.edu/~cse471/img. Your filter should remove the speckles in that image. Here's a before and after example:

Implement a 9x9 Gaussian filter. Implement a dialog box to set sigma for the filter. (See lecture set 9 and the Gaussian Filters text for details).
Write a filter that accepts a mouse click and uses the x,y location as the center for a rotation of the input image. Create a dialog box that allows you to set the amount of rotation in degrees. (See lecture set 10 for details).