Almost certainly, you're familiar with green screening, where a plain
green background is replaced with some other image. The most common
example is weather maps on the news - you'll never see a weather person
wearing green clothes, or they would disappear.
In this project, we're going to do some green screening, combining a
background from one picture with a foreground from another.
For example, consider a picture of Bill the Goat (on the left below). He
deserves to be someplace nice. So, for every pixel that is green, we'll
replace it with the corresponding pixel from the image in the middle
below, giving us the image on the right below.
Honor
The course policy and the
instructions it references, most pertinently COMPSCIDEPTINST
1531.1C, spell out what kinds of assistance are permissible for
programming projects. The instructor can give explicit permission for
things that would otherwise not be allowed.
For this project, you have explicit permission
to get help from other current IC210 instructors
(any assistance must be documented though)
and to discuss the "big picture" of how to approach the project with current IC210 MGSP leaders. You may not, however, discuss or view any code that is specific to the project
(see the instructors for such discussions), so this will probably be most useful when you are just getting started. If you are stuck on concepts, you may find it helpful to discuss
homeworks, lab, or lecture examples with MGSP leaders.
and to use general purpose C++ resources online (though such use must be
documented). Resources that might specifically address this project are not
allowed.
With the instructions referenced in the course policy and the explicit
permissions above, what you get is summarized as:
The only help you may receive on a project is from your instructor or
other current IC210 instructors, and that help must
be clearly cited. In turn, you cannot provide help to any IC210 students
on this project.
You may also discuss the "big picture" of how to approach the project with current IC210 MGSP leaders, but may not discuss or view any code (yours or anyone else's) that is specific to this project.
In no circumstance may you copy code and submit it as your own work. That is
the definition of plagiarism.
For projects, you may not look at other people's project code nor may you
show your own code to others.
You can look at online resources for general purpose C++ programming, but not
for help that is specific to the functionality required of your project.
Warning:Do not even vaguely talk about the
contents of the project with your colleagues.
If you have questions about any of these rules, email your instructor and ask
about it. We know you want to do what's right and we are here to help you.
Grading
80% of your grade will be based on functionality (i.e., whether your
program works as specified), mostly based on the test cases that you see
when you submit, but perhaps also extra test cases according to the
specifications of the project. The code you submit must
work if you expect to earn a passing grade.
20% of your grade will be based on coding style, which
includes:
Readability: You should use good spacing and proper indentation, and
avoid lines longer than 80 characters.
Documentation: Use meaningful variable names and write helpful
comments to make it obvious what your code is doing.
Organization: Use your tools wisely! Don't have unnecessary conditions
or loops. This is about the "art" of programming, beyond just "getting it
to work".
All of this is factored into your maximum score based on which part you have
completed. So for example, if you complete up to part 3 perfectly (i.e.,
85/100) working and also get 15/20 on style, then your final grade would be 85
* (0.80 + 0.15) = 80.75.
Tips for success
Start early! Because programming is such a creative progress, it's
notoriously difficult to predict how long ANY programming project will
take you to complete. If you start early, the worst that can happen is you
finish early and get to relax.
Work deliberately, stopping frequently to recompile and test
what you have so far. This is the best way to catch problems early and
easily.
Start small. Don't go for the big example right away. Instead,
use a smaller debugging example that you can
follow completely, so that you can check your work.
Run "submit" when you finish each part, or after you have done a lot of work (this provides a backup copy of your code in case of some computer problem).
Writing good documentation, variable names, etc., as you go along is
always easier than trying to add it in at the end.
Part 1: Understanding ppm image format (20/100)
An example run (with user input colored in red):
~$ ./readppm
Input file: bg.ppm
P3
width = 6, height = 5
max value = 255
row 0, col 0: r0 g255 b0
row 0, col 1: r255 g0 b0
row 0, col 2: r0 g255 b0
row 0, col 3: r255 g0 b0
row 0, col 4: r0 g255 b0
row 0, col 5: r255 g0 b0
row 1, col 0: r255 g0 b0
row 1, col 1: r255 g0 b0
row 1, col 2: r255 g0 b0
row 1, col 3: r255 g0 b0
row 1, col 4: r255 g0 b0
row 1, col 5: r255 g0 b0
row 2, col 0: r0 g255 b0
row 2, col 1: r0 g255 b0
row 2, col 2: r0 g255 b0
row 2, col 3: r0 g255 b0
row 2, col 4: r0 g255 b0
row 2, col 5: r0 g255 b0
row 3, col 0: r0 g0 b255
row 3, col 1: r0 g0 b255
row 3, col 2: r0 g0 b255
row 3, col 3: r0 g0 b255
row 3, col 4: r0 g0 b255
row 3, col 5: r0 g0 b255
row 4, col 0: r255 g255 b0
row 4, col 1: r255 g255 b0
row 4, col 2: r255 g255 b0
row 4, col 3: r255 g255 b0
row 4, col 4: r255 g255 b0
row 4, col 5: r255 g255 b0
Project files
Download proj01files.tgz. Extract the project
files by running following the command in the terminal:
tar -xvf proj01files.tgz
Understanding PPM format
In this project, to manipulate the pixels of images easily, we're going to use
a horribly space-inefficient image file format called "plain PPM."
The project files contain a bunch of PPM files that you can use. For example,
open bg.ppm in a text editor (i.e., vi, emacs, or atom),
you'll see something that looks like this:
[NOTE: if you use emacs, at first you will see the actual "rendered" picture. Press Ctrl-C twice to switch to seeing the actual ASCII text].
P3
6 5
255
0 255 0 255 0 0 ...
The P3 is the "magic number" or file header,
which tells the computer what the format is (remember this from SY110?).
6 is the width of the image in pixels, and 5 is the height of the image
in pixels.
255 is the largest possible value of all the following numbers.
The remainder describe the red, green, and blue values of each pixel in turn.
The first three numbers after the max value are 0 255 0, meaning that the FIRST pixel (top left corner, which is row=0,col=0) has
no red, the strongest possible green, and no blue.
The second pixel is (255, 0, 0). This pixel is the second pixel in the top row (row=0,col=1).
After all pixels from the top row (row=0) are specified, then starts the pixels for row=1, then row=2, etc.
Every PPM file must be in this format.
Viewing a PPM image
To view a PPM image on your
Linux machine, use the command:
eog thepicture.ppm
Your task: readppm.cpp
Write a program in a file called readppm.cpp.
The program should first prompt the user for the filename of an existing PPM
image. Then, it should print out the contents of the image as shown above on the
right. In particular, for each pixel, your program should provide its row and
column position in the image.
Some big hints!
You are working with two-dimensional images, so you might assume that you need to use multiple loops to manipulate the data.
However, that is NOT the case. You have two choices:
Read ahead to understand about nested loops, and use them if you like.
Or (recommended), just use a single loop to read through all the data. Keep a counter of how many pixels you have read so far. Then you can easily compute the "row" and "column" of "where you are" in the image
by using integer division and mod. For instance, suppose an image has a width of 6 (6 pixels in each row), and you have already read in 8 pixels, so counter=8. Then the NEXT pixel you are about to read in will be "row 1, col 2", and you can easily compute this from "counter" using appropriate division and mod commands (it's your job to work out the specifics). Likewise, if counter had been 34, the NEXT pixel to read would be at "row 5, col 4". Be sure you understand this before moving on!
You can solve this entire project using this "single loop with a counter" approach. You might want to use multiple loops later, for part 4, but it's not necessary.
Submitting Part 1
AFTER you test your program (be sure to try other files, not just bg.ppm), submit it:
~/bin/submit -c=IC210 -p=proj01 readppm.cpp
WARNING: after you run "submit", it will take 1-2 minutes (maybe more, if many students are using the server) before you see the updated test case results. So it will be SLOW for you to make small changes
and then run "submit" many times. Instead, test the code yourself, and submit when you are confident it works. Nonetheless, unlike with homeworks, the number of times you can run "submit" is NOT limited for this project.
Part 2: Grayscale an image (40/100)
To get used to the PPM format more, let's make an image grayscale (black
and white).
Write a program in a file called gray.cpp:
It prompts the user first for the filename of an existing PPM image, then for the
filename the user would like his grayscale version to be called.
The program then creates the new image, which is the gray version of the one
read in.
Tip: To find the gray value of a pixel, take the r, g, and b values,
average them (to an integer with fractions truncated), and set all three values
to that number. So, the gray version of (5, 100, 16) would be (40, 40, 40).
As an example, running this program on our picture of flowers gives us
a picture on the right.
Note:
If the input file to be read in does not exist, the program should print an
error message and gracefully exit. You must still "return" a value of "0" from your main() function in this case (otherwise, the "submit" system will consider your program to have failed with an error).
Checking your output with test vectors
You can check if your output files match the corresponding test files contained
in proj01files.tgz.
Change the permission of diff.sh (which is also found in
proj01files.tgz) to be executable:
~/$ chmod 700 diff.sh
Make sure that your readppm compiles and works correctly (the script
diff.sh uses your readppm, which MUST have that name).
~/$ g++ readppm.cpp -o readppm
Use the script to see if your output file is the same as the corresponding
test data.
Your program should be in a file called
green.cpp.
Your program should prompt
the user
first for the foreground image (our picture of Bill, with the green),
then the background image (the flowered hillside), and
finally the filename of the produced, combined image.
Your program should replace every pixel in the foreground that is pure "green" (for us, this means RGB values of 0 255 0 )
with the corresponding pixel in the background image. Note: In the output, you will probably see a one-pixel wide green border around your foreground subject;
this is fine.
Your program should work for any two images that are the same size as each
other. If the foreground and background images are not the same size,
your program should output an error message.
If one of the two files to be read in don't exist, the
program should print an error message and gracefully exit.
Checking your output with test vectors
Use the script to see if your output file is the same as the test data.
The problem with our program so far is that it only accepts pictures
that are the same size. What if we want Bill somewhere other than in the
middle of the picture?
Some pictures in the tarball you downloaded are cropped (i.e.,
fgc.ppm, billc.ppm, bbillc.ppm, and
mulec.ppm), so we should be able to put them anywhere in a
picture that is at least as big as they are.
Example runs of the program (with user input red):
Write a program in a file called
shift.cpp that prompts the user for:
a foreground image, a background image, a row shift, a column shift, and the output image
Example pictures illustrating how the row shift and column shift work:
row shift=0, column shift=0
row shift=0, column shift=450
row shift=100, column shift=0
More specifically, let rs be a row shift and cs be a
column shift. Then, when green screening is performed,
the output pixel at
row r+rs and column c+cs considers the foreground pixel at row r and
column c.
If the given row shift or column shift is so large that the foreground image would
extend past the background image, an error message should be given, and no
output image created. In addition, if one of the two images input does not
exist, the program should print an error message and gracefully exit.
Checking your output with test vectors
Use the script to see if your output file is the same as the test data.
Cutting out a foreground picture and putting it in front of a green
screen requires Photoshop or its open-source equivalent GIMP, and the
process is googleable, but longer than is appropriate for this page.
Your Task
Create your own background (mybg.ppm) and cropped foreground images
(myfgc.ppm). Use shift.cpp and create the green screened image (myshift.ppm);
choose the row/column shift values appropriately.