We will be using C++ throughout this course. Grab the basecode for this assignment from Canvas. C++ is an object-oriented programming language like Java and it's syntax is similar to both Java and C. If you are familiar with Java, this page can be a great resource for adapting that knowledge to C++. www.cplusplus.com is also a great general reference. C++ is one of the most widely used programming languages in the world. Therefore, if you come across an error it is very likely that someone else has already encountered this problem. Just Googling your errors can often be the best first step in debugging.
C++ is a compiled language, which means you must compile your code first (using a compiler) before executing it. We'll first go over several possible compilers you can use. Regardless of your choice, you must ensure that your code compiles and runs successfully on the Mac OS X machines in Sudikoff 003 & 005 or the Linux servers which you can ssh into.
You are welcome to work on your programming assignments on your own computer, in the computer labs, or on one of the departmental Linux servers. However, the code you write should be portable enough to work in any modern C++ compiler. We will be checking your solutions using the clang compiler on Mac OS X.
Mac OS: You can set up the compiler on Mac OS X by installing command line tools either through Xcode (Preferences → Downloads) or the terminal. The process may be different for different versions of OS X; similar tutorials can be easily found online.
GNU/Linux: In Ubuntu, you can install g++ with the following command: sudo apt-get install build-essential
.
Windows: You can use Cygwin on Windows to create a UNIX-like shell and use the same compilation setup. The alternative is using Visual Studio on Windows, however, you must ensure your code compiles and runs on the departmental OS X or Linux machines.
Once you have g++ installed, you can compile your code by going to the code directory and typing:
make
on the command line. This will compile the starter code into an executable called a0. You can run this by typing
make run
If you are successful, you should get a message that says
Congratulations, you have compiled the starter code!
float safeDivision(float dividend, float divisor) { assert(divisor != 0.0f); return dividend/divisor; }If the divisor is zero, the program will abort. Otherwise, the returned value would be Inf or NaN, which might be undesired.
Our Image class supports reading and writing PNG files only. All the sampled images we give you will be PNGs and you can use one of a handful of tools to convert your own images to this format. You can use your favorite image viewer to view the images produced by your code.
In this section, we will introduce a few C++ language features that are different from previous languages you may have used. C++ is most similar to Java. The big differences are that C++ has explicit memory management, distinguishes between references and pointers and organizes code into header and source files. You can find more information in this C++ tutorial for Java programmers and this reference website.
C++ programs are usually organized into header and source files. Most executable code is in source files, while function and class declarations are in headers. Open the attached a0.h and a0.cpp files in a text editor or IDE of your choice. The function
void helloworld(float a, float b);
is in both the header file (a0.h) and the source file (a0.cpp). We will give you key function definitions in the header file, and you will implement them in the source file.
When you compile and execute your code, the program will run all commands in the main
function located in a0_main.cpp. We will not grade the contents of this function and will replace it by our own to run our unit tests, but it will allow you to execute your own functions and verify that your code is correct.
All C++ variables must have a type. In this class, we will use IEEE single-precision floats to represent the value of a pixel.
c
that is the sum of a
and b
in the function void helloworld(float a, float b)
.
You may find it useful to print values to screen for the purposes of debugging or getting information about what you are working on. You can do this using the syntax
cout << "Hello World!" << endl;
You can also use cout
to display variables using the same syntax.
Write statements in void helloworld(float a, float b)
that print the following
Hello World! The value of a is _. The value of b is _. The sum of a and b is _.
where the underscores are replaced by the actual numbers that make the sentences true.
We have provided the specification for a FloatImage
class to represent image date (floatimage.h). The specification contains a number of methods and variables that belong to each instantiation of the FloatImage
class. Most of these methods are implemented in the source file (floatimage.cpp). The FloatImage
class derives from the template class Array3D
—a generic 3D array that can hold any type of data—with the data type set to float
. If you are not familiar with templates, this tutorial may be a good place to start.
In a later part of this programming assigment, you are going to implement some of the definitions in the source file floatimage.cpp and array3D.h. For now, just look over the header files and look for the three constructors on lines 19–21 of floatimage.h. They are
FloatImage(); FloatImage(int width, int height, int channels); FloatImage(const std::string & filename);
You can create an instance of the FloatImage
class using any one of these constructors. The first creates an empty image of size zero, and the second creates a blank image of dimensions width * height * channels
. For instance, you can use the second constructor to create a FloatImage
variable my_im
that is 100 × 100 pixels with three color channels by typing
FloatImage my_im(100,100,3);
FloatImage readAnImage(const std::string & filename)
in a0.cpp, which returns a FloatImage
created using the third constructor taking filename
as an input. Note that your code will likely seg-fault for now, until you finish section 3 of the assignment.
The FloatImage
class specification is given in floatimage.h and array3D.h. Images are three dimensional arrays (width by height by color channel) that store pixels as a vector of floats called m_data
(array3D.h). In memory, the pixels in the image are stored sequentially in row-major order in three adjacent color planes. That is, the distance or stride between adjacent values in the same row in m_data
is equal to 1. The stride between adjacent values in the same column is equal to the width of the image and the stride between the different color channels at the same pixel is width * height. The Array3D
class defines protected member variables with these values.
m_data
is a C++ vector, which is essentially an array which manages its own memory. You can access elements of it using brackets. For instance, m_data[0]
returns the front element in the vector. More information about vectors can be found here.
For images that correspond to pictures, the floating points in image data will be between 0 and 1. There is nothing guaranteeing that the values of image data stay in this range and there may be times when you want to use the FloatImage
class to store intermediate data that doesn’t correspond to pictures, in which case the range is not meaningful. When the image is written to a file, it will assume the data lies in this range and will clamp values outside of the range to one of the endpoints.
You can use the FloatImage::write
method to write images to PNG files. For example,
my_im.write("./my_image.png");
Then, you can view them in your favorite image viewer. This may be useful for debugging. Alternatively, if you don’t feel like providing a filename, you can use FloatImage::debugWrite()
to write an image to an automatically named file. This might make debugging easier.
You are going to implement the accessor and setter operators for pixel values in the image. We adopt the convention that the elements are accessed via the () operator. That is, the pixel at location (x, y) in the third color channel of my_im
is accessed via
my_im(x,y,2);
The third color channel is accessed via the number 2, not 3. That is because we want to use 0-indexing, in which the first element in a given index is specified by 0.
Implement two accessors with 0-indexing and bounds checking to make sure the input is valid. If the input is not valid, throw an exception using the command throw OutOfBoundsException();
.
In array3D.h implement:
my_im(x)
: returns the value of image data at location x as long as x is less than (<) the total number of pixels and at least (≥) 0. You need to implement the functions
T & operator()(int i); const T & operator()(int i) const;with identical code. Use
Array3D::size()
for bounds checking.my_im(x,y,z)
and my_im(x,y)
: returns the value at location (x,y) in the zth color channel. You need to implement the functions
T & operator()(int x, int y, int z = 0); const T & operator()(int x, int y, int z = 0) const;with identical code. Use
Array3D::width()
, Array3D::height()
and Array3D::depth()
for bounds checking (or their synonyms Array3D::sizeX()
, Array3D::sizeY()
and Array3D::sizeZ()
). You are can use the indexing order discussed in class, or come up with your own. You should ensure the rest of the code works properly with whatever indexing scheme you choose.Now for the fun part. Once we have these accessors, we can perform simple operations like increasing the brightness or contrast of an image.
brightness
in a0.cpp, which multiplies the pixels in an image by the value factor
. Make sure to create a new image and return that rather than modifying the input image. This is good practice in case you want to use the input again.contrast
, which increases the contrast of an image around a specified midpoint by the value factor
. That is, you should apply the following function to every pixel’s value
\[
I_\text{out} = \texttt{factor} \times (I_\text{in} - \texttt{midpoint}) + \texttt{midpoint}
\]
You can test your functions on the included Input/windmill_low_contrast.png. This input image has very low contrast. You can use your contrast function to increase it.
Turn in your files using Canvas and make sure all your files are in the a0 directory under the root of the zip file. Include all sources (.cpp and .h) files, any required input, and the output of your program. Don't include your actual executable, and remove any superfluous files before submission.
In your readme.txt file, you should also answer the following questions: