CS 215 ­ Fundamentals of Programming II Fall 2013 ­ Programming Project 4 30 points Out: October 18, 2013 Due: October 28, 2013 (Monday) Problem Statement Images may be thought of as objects that can be manipulated. For example, we may want to rotate or flip an image. Or we may want to do some processing on the image like inverting the image. Images are described by giving their geometry (width and height) and a grid of pixel values. Most image formats are binary formats that arrange the pixel components in various ways, usually to compress the data. However, there is a family of ASCII formats that we can read and write using regular C++ language file scanning and printing routines called the Portable Any Map (or PNM) formats. For this project we will use the plain Portable Grayscale Map (or PGM) format that can be used to represent grayscale images like the following (reduced to fit in this document). In the "plain" PGM format, the above image is represented as follows: P2 466 540 255 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 The first three lines are a "header" describing the characteristics of the image. The "P2" is a "magic" string that must be the first line of the file and tells programs this is a plain PGM file. The second line is the width (number of columns) and height (number of rows), respectively, of the image in pixels. The third line is the maximum 10/18/2013 Page 1 of 8 D. Hwang possible pixel value in the file, usually and at most 255, but not always. The lines after this are the values for each pixel in the image starting with the upper left corner going left to right and top to bottom. The values range from 0 for black to the maximum pixel value for white. (The first pixels of the image above are all black.) Note that while this format is easy for humans to read, it is inefficient in space. This image is around 700 kilobytes in the PGM format, while the equivalent PNG format file is only about 53.6 kilobytes. PGM files can be viewed using various image viewers. On Linux, emacs will display PGM files when loaded. To toggle between the visual image and the underlying text file, type Ctrl-c Ctrl-c. The program eog also can display PGM files (and most other image formats). Several PGM files will be made available in directory /home/hwang/cs215/project4 on csserver. Specification for Image Class Data Attributes For this project, an Image is modeled using a dynamically­allocated two­dimensional array of integers, as demonstrated in lecture, to allow images of differing sizes to be created. Thus the data attributes include at least those shown below. You may add additional appropriate attributes. Objects Type Name width of image (number of columns) int width height of image (number of rows) int height maximum pixel value int max_value pointer to (height x width) grid of pixel values int** grid Operations ● Explicit­value constructor ­ receives the width and the height of an image, and a maximum pixel value. Default values are given below. If initial_width or initial_height is 0, then the grid pointer should be initialized to the null pointer. This is state will be called an empty image. Otherwise the constructor should dynamically allocate a two­dimensional grid of integers in the specified size as demonstrated in lecture, and initialize all elements to 0. Note: this will create a solid black image. Analysis 10/18/2013 Objects Default Type Movement Name initial width of image 0 int received initial_width initial height of image 0 int received initial_height maximum pixel value 255 int received initial_max Page 2 of 8 D. Hwang ● Copy constructor ­ creates a new Image that is identical to an existing one. Analysis Objects Type Movement Name source image object Image received original ● Destructor ­ deallocates the grid if the image is not empty Analysis ­ no objects ● swap – utility member function that swaps the contents of the received image with this image for use with operator= Analysis ● Objects Type Movement Name image object to swap with Image received & passed back other operator= ­ overloaded assignment operator member function. Makes an existing Image object identical to the source Image object. It is recommended that the technique given in the textbook and covered in lecture be used to implement this function. Analysis ● Objects Type Movement Name source Image object Image received original empty – returns true if there is no image currently loaded in this object (i.e., grid is the null pointer). Analysis ● Objects Type Movement Name result bool returned ­­­­­ get_width ­ returns the width of the image Analysis 10/18/2013 Objects Type Movement Name width of image int returned width Page 3 of 8 D. Hwang ● get_height ­ returns the height of the image Analysis ● Objects Type Movement Name height of image int returned get_height get_max_value ­ returns the maximum pixel value of the image Analysis ● Objects Type Movement Name maximum pixel value int returned max_value rotate_left ­ returns a new image that is this image rotated to the left 90 degrees. Analysis ● Objects Type Movement Name rotated image Image returned new_image flip_horizontal ­ returns a new image that is this image flipped about the horizontal axis Analysis ● Objects Type Movement Name horizontally flipped image Image returned new_image flip_vertical ­ returns a new image that is this image flipped about the vertical axis Analysis ● Objects Type Movement Name vertically flipped image Image returned new_image invert – returns a new image that is this image inverted Analysis Objects Type Movement Name inverted image Image returned new_image The inverted value of a pixel value is simply the pixel value subtracted from the maximum pixel value. 10/18/2013 Page 4 of 8 D. Hwang ● load – load an image from an input file stream attached to a PGM file. Assumes the file has been opened, and the input stream is at the beginning of the file and it is in a valid state. If the file does not contain a PGM image (i.e., the magic string is not "P2") it should throws a runtime_error exception (see notes below) without changing this image. Otherwise, this function should read the data from the file and store it in this image object. Note that the existing image is not likely to be the same size as the image in the file. A swapping technique similar to that recommended for operator= could be used here to make sure there are no memory leaks. Analysis ● Objects Type Movement Name input file stream ifstream received & passed back in_file write – writes this image to an output file stream. Assumes the file has been opened, and the output stream is in a valid state and nothing has been written to it. If the image is empty, it should not write anything. (I.e., do not write an image header). Analysis Objects Type Movement Name output file stream ofstream received & passed back out_file The format of the output file is as shown above. The header information is written into the output file first. The magic string (P2) goes on a line by itself. The width and the height go on the second line with exactly one space in between. The maximum pixel value goes on the third line by itself. The pixel values are written in field widths of 4 with exactly 15 values on a line, except perhaps the last line. The last line of pixel values must end with an newline (but not two newlines). Assignment (20 points) Write the implementation of the Image class specified above. The Image class definition must be put in header file image.h with suitable compilation guards. The implementations of the Image member functions must be put in source file image.cpp. The Image class must be implemented using a dynamically­allocated two­dimensional array as discussed in lecture. Projects that do not use a dynamically­allocated two­dimensional array will be returned for resubmission with late penalty. The member function names and the order of the parameters must be as specified above. Your code will be linked with a grading driver program that expects this. Note that all of the functions must be correct to receive full credit whether or not the main program uses them. (10 points) Write a main program in file image_manipulator.cpp that is menu­driven and interactive. The program will have the concept of a current image and a new image that are both empty initially. Users use the following command set to load an image, create a new image that is a manipulation of the current image, and write the new image to a file. Commands may be entered in upper or lower case. An example run is shown below. Note that this program may or may not make use of all of the Image class operations. 10/18/2013 Page 5 of 8 D. Hwang Command Description L Load current image from file. Must handle any generated runtime_error exceptions by printing the error message contained in the thrown exception. A Create new image rotated 90 degrees left B Create new image flipped along horizontal axis C Create new image flipped along vertical axis D Create new image that is inverted W Write new image to file Q Quit the program Unsuccessfully file opens should cause an error message to be printed and the command to be ignored. You must submit a makefile named Makefile that creates an executable named image_manipulator for your project. It should conform to the examples as demonstrated in class. REMINDER: Your project must compile for it to be graded. Submissions that do not compile will be returned for resubmission and assessed a late penalty. Submissions that do not substantially work also will be returned for resubmission and assessed a late penalty. Follow the guidelines in the C++ Programming Style Guideline handout. As stated in the syllabus, part of the grade on a programming project depends on how well you adhere to the guidelines. The grader will look at your code listing and grade it according to the guidelines. What to submit Electronically submit a tarfile containing files Makefile, image.h, image.cpp, and image_manipulator.cpp as explained in the handout Submission Instructions for CS 215. Do not submit object or executable files. The submission system will compile and test the Image class implementation against an autotest driver program. It will compile the project main program and check that an executable named image_manipulator is created, but will not run it The submission system will accept submissions no earlier than the evening of Monday, October 21. Notes about using the runtime_error exception Exception handling in C++ was covered in lecture on Monday, September 23, and is in Section 2.2 of the textbook. Here is a recap. Standard exception classes, including runtime_error, are defined in the <stdexcept> library. These classes have an explicit­value constructor that has one received parameter that is a message string. This parameter generally is used to store an error message. There is one member function, what(), that returns the message string. An exception is thrown when an error has occurred, such as trying to access a data structure with invalid indexes, by constructing an exception object and using the throw operator. Throwing an exception causes the function to terminate. For example, in the Image load function, the code to prevent trying to load data from a file that is not a plain PGM file might be: 10/18/2013 Page 6 of 8 D. Hwang if (magic_string != "P2") // construct and throw an exception object, terminates the function throw runtime_error ("File is not a PGM image file."); // everything is good, perform the load In the main program, exceptions are caught and handled using a try-catch construct. The function call that may throw an exception is placed inside the try block, while the exception object is listed like a parameter in the catch block header. The handler code for the exception is placed in the catch block. For example, to catch an runtime_error exception thrown by the load function, the code might be: try { cout << "Enter an input file name: "; cin >> infile_name; in.open(infile_name.c_str()); // code to check open succeed omitted current_image.load(in); } // end try catch (const runtime_error & re) // the thrown exception object { // print out its message cout << re.what() << " Try again!" << endl; } // end catch Sample run user@csserver:~$ ls image_manipulator moon.pgm user@csserver:~$ ./image_manipulator Choose L A B C D W Q - from the following commands: Load current image from file Create new image rotated 90 degrees left Create new image flipped along horizontal axis Create new image flipped along vertical axis Create new inverted image Write new image to file Quit the program Enter your command: l Enter name of image file to load into current image: moon.pgm 8< snip – menu list omitted in the rest of the sample run >8 Enter your command: a Creating rotated image Enter your command: w Enter name of file to write new image: moon-rotated.pgm 10/18/2013 Page 7 of 8 D. Hwang Enter your command: d Creating inverted image Enter your command: w Enter name of file to write new image: moon-inverted.pgm Enter your command: q Bye!! user@csserver:~$ ls image_manipulator moon.pgm user@csserver:~$ 10/18/2013 moon-inverted.pgm Page 8 of 8 moon-rotated.pgm D. Hwang