Starting from:
$35

$29

Project 1: Image Editor Solution

Purpose

To review basic C++ syntax: loops, if/else statements, functions, classes, and file I/O
To practice with simple linear and 2-D structures (e.g., vectors)
To make a cool image editing program
Downloads

sample images (sampleinputs.zip)
starting source (project1.zip)
Before You Begin

Read the sections in their entirety before you start programming
Create a project named "image_editor" (or your choice) in your Cloud 9 environment
Download the starter code and sample images into your project folder:
cd image_editor
wget https://cs.mines.edu/Courses/csci262/spring2018/projects/1-image_editor/files/project1.zip
wget https://cs.mines.edu/Courses/csci262/spring2018/projects/1-image_editor/files/sampleinputs.zip
unzip project1.zip
unzip sampleinputs.zip
Use make to build the project:
make
Test that the provided starter code has compiled correctly and works. You will need to first run the program c9vnc, whcih will let you see the graphical output of the program in a web browser window. Execute
c9vnc
in a terminal window. It should respond with something like

VNC client running at https://csci262-yourid.c9users.io/vnc.html
Starting c9vnc daemon
If you click the URL, Cloud9 will offer to start a brower window for you, or you can type in the URL by hand. In the browser window, you will need to click on the "Connect" button. Finally, go back to the terminal window and execute the program you just built:

./image_editor
When it asks for a filename, try sampleinputs/blocks.ppm; you should see a test image (that has nothing to do the blocks.ppm image) appear in the VNC browser window. (If the program crashes with a Segmentation fault, try opening a new terminal window and working in that.) You can end the program from the terminal window by choosing the "quit" option, or by typing Ctrl-c in the terminal window.

Introduction

PPM Image Format
The PPM (or Portable Pix Map) image format is encoded in human-readable ASCII text. For those of you who wish to have the experience of reading real documentation, the formal image specification can be found here.

Sample ppm file:
P3
4 4
255
0 0 0 100 0 0 0 0 0 255 0 255
0 0 0 0 255 175 0 0 0 0 0 0
0 0 0 0 0 0 0 15 175 0 0 0
255 0 255 0 0 0 0 0 0 255 255 255

Image Header
You can think of the image as having two parts, a header and a body. The header consists of four entries:

P3
4 4
255

"P3" is a "magic number". It indicates what type of PPM (full color, ASCII encoding) image this is. For this assignment it will always be P3.

Next comes the number of columns and the number of rows in the image (4 x 4). Note: Columns come before rows!

Finally, we have the maximum color value 255. This can be any value, but a common value is 255. For this assignment, we'll assume it is always 255.

Note that the header here is presented in a particular format; magic number on the first line, columns and rows on the second line separated by a space, and color depth on the third. While this is the usual way (and your saved images should follow this format for easier debugging), there is no particular requirement in the standard other than that each element is separated by whitespace. In particular, you should not assume the files you will be reading follows the line-by-line format above.

Image Body
The image body contains the actual picture information. Each pixel of the image is a tiny, colored square. The color is determined by how much red, green, and blue are present. So, 0 0 0 is the first color of the image, which is black, and the last pixel in the image is 255 255 255, which is white. By varying the levels of the RGB values you can come up with any color in between.

Note that color values must be separated by a space, but after that additional whitespace is ignored by the image viewer. In the sample ppm above we used additional whitespace to format the image so that it is easy for a human to understand, but the computer doesn't care if everything is on one line, if there is one line per line of the image, or some mix (we suggest your outputs should keep 1 line of text = 1 line of the image for this assignment to make the images easier to read in a text editor).

Putting it all together
The example image above would look something like this:



Keep in mind, each square is one pixel, so the real thing is much smaller (the rendered image was blown up by 5000%).

How to view PPM files
While PPM files are easy to view as text (you can use a text editor, for instance), and easy to work with in code, they are highly inefficient. Most modern image formats use some kind of compression to make their size reasonable while preserving the image appearance. This is not to say that PPMs don't still have some life in them - one modern use for PPM is as an intermediate format when converting images from one type to another.

Unfortunately, PPM images are not understood natively by many platforms, including Cloud9 and Windows. If you want to view the images outside the program you will be making, there are software packages that will read and edit them, such as GIMP (which should be on the school machines). Because of this limitation, the software you create will include an image viewer built on top of the SFML graphics library.

Outline

You are going to create a simple editor for applying effects to images. This may sound like a lot of work, but it's really pretty simple. To make it easy to code up I suggest you tackle the problem in parts; in the first part, concentrate just on reading in the image file and writing it back out without making any changes to it. Next, get the image to display in the graphical window. Then you can worry about getting individual effects to work.

Reading/writing the image file:
When the program first runs, it will prompt the user for the name of an image file to work with initially. The file will be a text file in PPM format as described in the discussion above. Note the user interface aspects of this have already been written for you in main.cpp; your job is to finish up the code in image_editor.cpp (specifically the load method, which we've started for you).

The load method needs to read in the image header and all of the image data from the provided input stream (the code in main.cpp opens the actual file for you), and store the image data in some fashion in your image_editor object (see below for details). If there are any errors in loading, then the method should return false, otherwise it should return true - this will let the user interface code in main.cpp know whether or not to prompt the user for a different file.

After the initial image load, the user will see a menu prompting them to either apply effects to the image, load a new image, save the image, or quit. Every time the user chooses to load, the image_editor::load method is called.

Similarly, if the user chooses to save, the image_editor::save method is called, with a valid output file stream passed in as a parameter. In this case, your code should write out all the elements of a valid PPM file to the output stream - all of the header elements and the pixel data.

Displaying the image:
Initially, your program will display a bogus test image that has nothing to do with the file you load in. Getting the image to display properly is relatively easy; you just need to replace the code in the get_rows, get_columns, get_red, get_green, and get_blue methods of image_editor. The first two methods should just return the rows and columns values you read from the file header (and probably stored somewhere in your image_editor object). The last three methods return just the red/green/blue values for a pixel (specified by row and column), which you can get from the stored image data.

Applying effect filters:
The user will be presented with a list of effect filters that can be applied (code implementing the user interaction is supplied to you). Each of these filters involves some simple manipulation of the red, green, and/or blue values in the image. You should put each of the filters in its own method.

For example, one filter will correspond to the option "negate red". It will change just the RED color numbers into their "negative". That is, if the red number is low, it should become high and vice versa. The maximum color depth number is useful here. If the red were 0, it would become 255; if it were 255 it would become 0. If the red were 100, it would become 155.

When you have this filter written, insert it into your code and try it out. Apply the effect to one of the provided sample images, and see if the output looks as you expected. Create similar filters for the other color values. Then proceed with the remaining filters:

Create a filter for "convert to grayscale" which will change the picture into a gray scale image. This is done by averaging the values of all three color numbers for a pixel, the red, green and blue, and then replacing them all by that average. So if the three colors were 25, 75 and 250, the average would be 116 (rounding down is fine), and all three numbers would become 116.
Create a filter for "flatten red" which will set all red values to zero. Write similar functions for the other color numbers.
Create a filter for "flip horizontally" which will flip the picture horizontally. That is, the pixel that is on the far right end of the row ends up on the far left of the row and vice versa (remember to preserve RGB order!). Also create a vertical flip effect filter.
Finally, create a filter for "extreme contrast". For this effect, change each color value to either the highest color number possible or to 0. This change is based on whether it is greater than the midpoint of the color range, or less. If it is greater than half of the color depth, replace it with the colordepth. If it is less or equal, replace it with zero.
In summary, you must implement the following effect filters:

negate red to negate the red number of each pixel.
negate green, as above but change the green
negate blue, as above but change the blue
flip horizontal that flips each row horizontally
flip vertical that flips each column vertically
gray scale sets each pixel value to the average of the three
flatten red sets the red values to zero
flatten green sets the green values to zero
flatten blue sets the blue values to zero
extreme contrast sets each color value to either the highest color number possible or to 0
Implementation details:

Source code:
Files have been created for you implementing main(), and the beginnings of a class which you can complete to implement the various image editor functions. You can freely change any of the code as you need or desire, although you should be able to complete this project with changes only to image_editor.h and image_editor.cpp.

As a first step, please read through the code in image_editor.h and image_editor.cpp before reading the sections below, so you have a sense for the program design we are looking for. Comments are scattered throughout the code starting with "TODO" - these are hints to you as to what code you need to add to make a working program.

The code as provided should compile and run - it is recommended that you work incrementally, adding in one method at a time and testing your code - it is a bad idea to try to write it all at once and only then try to compile and test!

Reading in and storing image data
Note that once you read in the header information, you know exactly how many integers you need to read from the rest of the file! Reading in integers is most easily achieved by using the operator on the file input stream into an int variable. This will look for the next contiguous chunk of non-whitespace characters and try to turn it into an integer.

You should probably use vectors to store your image data. There are many ways to go here - you can store the image data in a 2-D grid (vector of vector of ints); some people go 3-D (rows x columns x 3); still others create a class or struct to represent a pixel, and do a 2-D grid of pixel objects. Whatever makes the most sense to you is fine.

Here are some hints on using vectors in this project. First note that simply declaring a vector object creates a data structure of size zero; if you try to write or read a location in this structure, you will get an error:

vector<vector<int data;
data[0][0] = 0; // ERROR

If you create an empty vector in this way, you must build it up using the push_back() method to add in each new element:

vector<vector<int data;
for (...) {
vector<int row;
for (...) {
// read in some value
row.push_back(some_value);
}
data.push_back(row);
}

Note you should probably clear the vector every time load is called, to get rid of any old image data.

Applying effect filters
Since your data is stored in an instance variable of the same object as your effect filter method, you can simply loop over the rows and columns (or whatever is appropriate) and directly change the values using the [ ] operator.

Documentation:

Please follow the style guidelines for the course in commenting your code. In addition, you must submit with your source code a README file. This file is just a plain text file (traditionally named README or README.txt). Your README must contain the following:

Include names of all people who helped/collaborated as per the syllabus
Describe the challenges you encountered and how you surmounted them
What did you like/dislike about the assignment?
How long (approximately) did you spend on this assignment?
Extra Credit:

Implement any of the following for extra credit points.

enlarge/reduce - for enlarge, duplicate each row and column to make a picture which is 4x the original size. For reduce, remove every other row and column, to make a picture which is 1/4 the original size. Note that enlarge/reduce result in a changed image window, which is subject to the bug noted below.
rotate - rotate the image 90 degrees.
blur/sharpen or other kernel effects - see information on ways to do this at https://en.wikipedia.org/wiki/Kernel_(image_processing). Note that a 5x5 blur is much more noticeable than a 3x3 blur.
??? - come up with your own cool effect not described above and implement it. For ideas, you might check out the image filters included in the GIMP image processing software. Be sure to document your effect in your README so we know to test for it!
Known Issue

NOTE: There is a known bug in the drawing window code that exhibits only when the window is resized. Somewhat randomly after a resize event, the program will quit with an error message like

X Error of failed request: GLXBadContext
Major opcode of failed request: 154 (GLX)
Minor opcode of failed request: 4 (X_GLXDestroyContext)
Serial number of failed request: 114
Current serial number in output stream: 115
Since this only occurs when the image size changes, it will only occur when loading a new image or performing a reduce, enlarge, or rotate command. We apologize for any inconvenience!

Grading:

README
5 points
Program compiles and executes
5 points
Code conforms to the course style guideline
5 points
Program reads and displays images correctly
10 points
Program saves files correctly
5 points
color negation
10 points
flatten color
10 points
convert to grayscale
10 points
flip horizontal/vertical
10 points
extreme contrast
5 points
Total:
75 points
 
 
Extra credit: enlarge/reduce
+1 point each
Extra credit: rotate
+1 point
Extra credit: blur/sharpen/etc.
+1 point each
Extra credit: your original effect
+??? points
Submission instructions:

Submit a zip file on Canvas containing:

all source code (image_editor.h, image_editor.cpp, main.cpp, drawing_window.h, drawing_window.cpp, Makefile)
your README file
It is not required, but helpful to execute the command

make clean
on your project directory before zipping everything up to submit. This command will remove the program and intermediate objects files that are produced in the compilation process.

More products