BookNotesSlidesP-sets Syllabusdraft · v0.2.49.1
◀▲⌂▶
💬Comments welcome. To leave a note, select any text and click the note / highlight button that pops up — or open the panel with the tab at the top-right (‹). Notes are visible only inside our private review group.

45.3 Problem Set 0 — Environment and C++ basics⧉

Problem Set 0: Introduction to C++ and the 6.8370/1 Image Class. Due Wednesday September 16, 9:00pm.

45.3.1 Summary⧉

45.3.2 Installation and Environment Setup⧉

Throughout this course, you will develop a C++ library for computational photography that you can use in future projects.

C++ is a compiled, statically typed, object-oriented language. If you come from Python, the free book C++ for Python Programmers (<https://runestone.academy/ns/books/published/cpp4python/index.html>) introduces each C++ construct next to the Python you would have written instead. If you come from TypeScript or JavaScript, the syntax will already look familiar and the type annotations work much as TypeScript's do, with two large differences: a compiler checks the types and produces a binary, and there is no garbage collector, so memory is yours to manage. <https://learnxinyminutes.com/c++/> is a fast tour of the syntax from that starting point. We also suggest <https://www.learncpp.com/> as a thorough tutorial and <http://www.cplusplus.com/> as a 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, or asking the class AI assistant about them, can often be the best first step in debugging.

C++ is a compiled language, which means C++ code must be compiled before it can be executed. In this section, we will go over how to compile and submit your code. Regardless of which compiler you choose to use, your code must compile on our online submission system.

Compilers⧉

The code you write should be portable enough to compile with any compiler. The submission system uses the GNU g++ compiler on Linux, so that is the setup to match. You need a C++ compiler and make on the command line:

There are four .cpp source files in the assignment folder: a0_ main.cpp, a0.cpp, Image.cpp and lodepng.cpp. Normally, you would compile this collection of files by executing the following command:

Algorithm 45.3.1
g++ -Wall -std=c++11 -o a0 a0_main.cpp a0.cpp Image.cpp lodepng.cpp -I.

This compiles the four .cpp files and generates the output (-o) a0. The -I. flag tells g++ to look for the headers (or include files) in the current directory (.). The assignment folder contains three headers: a0.h, Image.h, lodepng.h. See Headers and Source Files for more details on header and source files. The -Wall flag (for warn all') tells the compiler to display all warning messages emitted during compilation. The -std=c++11` flag tells the compiler to use the C++11 standard.

If you have g++ installed, compile the code by going to the code directory and typing the above command. Check that an executable named a0 is created. You can run the executable with the following command:

Algorithm 45.3.2
./a0

If you are successful, you should get a message that says Congratulations, you have compiled the starter code!

Makefiles⧉

Typing the compile command directly each time you want to re-compile can become cumbersome, especially if you have many .cpp files that have complicated dependencies. For example, if you are making changes to only one .cpp file, recompiling all of them every time (which is what the previous command does) is time consuming and inefficient. Makefiles are a simple way to organize code compilation. A Makefile contains a list of commands to compile a series of files as well as their dependencies. While in a directory containing this Makefile, you can type make and the commands in the Makefile will be executed. Open the Makefile in a text editor and see how compiler commands and source file dependencies are specified. Please take a moment to read through the "tutorial" comments we provided inside the Makefile to get a better idea of how the file is specified. Now, compile your code by going to the code directory and typing

Algorithm 45.3.3
make

on the command line. This will also create an executable called a0. You can run this by typing

Algorithm 45.3.4
make run

Alternatively you could use an IDE: Visual Studio Community (<https://visualstudio.microsoft.com/vs/community/>) on Windows, or Visual Studio Code with its C/C++ extension (<https://code.visualstudio.com/docs/languages/cpp>) on any platform.

We strongly recommend that you stick to the Makefile/g++ approach for this class. We will try to help you with compilation problems during office hours.

Submission System⧉

The online submission system compiles your code on our servers, executes it and then displays the output. All text written to the standard output and standard error streams is printed on screen. Any .png images written to the asst/Output directory is displayed by the submission system, so you can verify that your code is working properly through the system. We provide a Makefile rule to prepare a submission zipfile. Run it using

Algorithm 45.3.5
make prepare

This make rule will execute the necessary sequence of commands to produce a submission zip file. You can inspect the commands specified inside the Makefile. You can also manually zip your submission code. If you do so, please make sure to keep all your code in the asst directory at the root of the .zip archive and make sure to exclude all build outputs, executables as well as the Output folder and its contents as these will bloat the size of your zip file and slow down your upload.

Problem set 0 is due Wednesday September 16 at 9pm, and it does not use any of your 3 late days. The submission system keeps accepting it after the deadline, until Saturday September 19 at 9:15pm, without charging a late day.

Debugging⧉

You are welcome to use a debugger, such as gdb (<http://www.gnu.org/software/gdb/>) or the one that comes with your IDE. However, we also suggest you use assert statements or print statements to make sure conditions you think are true are actually true.

For example, if you are performing a division, you may want to assert that the divisor is not zero. The assert used here is the course's own two-argument macro from messageassert.h, which takes a condition and the message to print; it is not the one-argument assert of the standard library.

Algorithm 45.3.6
float safeDivision(float dividend, float divisor) {
  assert(divisor!=0, "Divisor is zero");
  return dividend/divisor;
}

If the divisor is zero, the program will abort and tell you which assertion failed. Otherwise, the returned value would be Inf or NaN, which might be undesirable.

Image Input/Output⧉

Our Image class supports reading .png files only. All the sample images we give you will be in the PNG format. To convert your own images, use the class's own converter, described in the File conversion tools appendix of the book:

<https://comp-photo-book.pages.dev/u/jpg-png-without-an-alpha-channel> It runs in your browser, uploads nothing, and writes a three-channel RGB PNG with no alpha channel. Our loader reads any PNG and discards the alpha channel if there is one. In addition, the Image class can only write to .png files. You can use your favorite image viewer to view the images.

Warning: Renaming a JPG file's extension to .png does not convert the file type! You might think this is a silly comment but we include this because people have tried and failed to do so. Instead, you may open the image in some image editor and change the format when saving to convert a JPG file.

45.3.3 C++⧉

In this section, we introduce a few C++ language features that are different from previous languages you may have used. Next to Python or TypeScript, 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 the tutorials linked above (<https://runestone.academy/ns/books/published/cpp4python/index.html> coming from Python, <https://www.learncpp.com/> otherwise) and on this reference website (<http://www.cplusplus.com/>).

Headers and Source Files⧉

C++ programs are usually organized into header and source files. Actual executable code is written in source .cpp files, while function and class declarations live in .h header files. Open the attached a0.h and a0.cpp files in a text editor or IDE of your choice. The function

Algorithm 45.3.7
void helloworld(float a, float b);

is in both the header file (a0.h) and the source file (a0.cpp). While the .h file has only a declaration of the function's name and signature, the .cpp file provides it with an actual body of code: its definition.

Throughout the assignments, we will give you key function declarations in the header file, and you will implement them in the source file.

When you compile and execute your code, the program runs all commands in the main function located in a0_main.cpp. This allows you to execute your own functions and verify that your code is correct.

We will not grade the content of a0_main.cpp as we will replace it with our own unit tests.

Static Typing⧉

All C++ variables must have a type. In 6.8370/1, we use IEEE single-precision floats to represent the value of a pixel.

Text Input/Output⧉

You may find it useful to print values to the screen to debug your program or get information about what you are working on. You can do this using the syntax

Algorithm 45.3.8
 cout << "Hello World!" << endl;

You can also use cout to display variables using the same syntax.

                   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.

Simple Image Class⧉

In a0.h we have provided a simple image class (SimpleImage). The aim of this class is to create a binary image (just zeros and ones) to demonstrate the basics of how data can be stored and manipulated. The specification of the class contains the declaration of various methods, all of which have dummy implementations in a0.cpp. Throughout this exercise, you will be asked to provide proper definitions.

Images are inherently two-dimensional (three if multiple channels are available), however, it is often desired to store them as a single-dimensional array and index into them appropriately (we'll see more details about this in future problem sets). For our SimpleImage class, we will store a ${\tt width} \times {\tt height}$ image as an array of ${\tt width} * {\tt height}$ elements in row-major order. That is, consecutive columns are tightly packed (no separation between them) while consecutive rows have a separation of ${\tt width}$ elements.

In general, we do not know the size of images ahead of time. As a result, our arrays must vary their sizes. Rather than handle memory directly, we will generally rely on the Standard Library's vector container (see line 53 of a0.h). The vector container provides various convenience methods for array-like operations where the size of the array can vary arbitrarily. See <http://www.cplusplus.com/reference/vector/vector/> for the container's documentation.

Now that the mechanisms for storing data have been outlined, we can move on to constructing the class, line 33: declares the class constructor:

Algorithm 45.3.9
SimpleImage(int width, int height);

The constructor takes care of creating an instance of the object when needed. As we would expect, the constructor above takes two input integers, the width and height of the desired image. For example, typing

Algorithm 45.3.10
SimpleImage my_im(10,10);

would form an empty image of size $10 \times 10$.

An empty image is not of much use so let's go ahead and add some detail by using the class method

Algorithm 45.3.11
    void make_rectangle();

to create a white rectangle (that is pixel value of 1.0) in the center of our image. Let's make the rectangle be 20% of the width (and height respectively). It is sometimes helpful to think of the image plane as a continuous space (observed at discrete locations) and we will adopt this convention in this example; as a result, the center of the image is allowed to be fractional and similarly, any pixel that partially overlaps with the rectangle should be denoted as white. That is, calling

Algorithm 45.3.12
    SimpleImage img2(11,11);
    img2.make_rectangle();

should produce a white rectangle spanning $[4, 4]$ to $[6, 6]$. (Note: the indices are zero-indexed and ranges are inclusive.)

To provide additional examples:

\medskip Don't spend too much time on the rectangle's exact boundary pixels. This is a warmup in C++ and image indexing. Aim for a reasonably centered rectangle; if the boundary wording seems ambiguous, choose a consistent convention and move on. Minor differences at the rectangle's edges are not a concern for this exercise.

Sometimes it is helpful to compute statistics across the entire image. A potentially useful statistic could be the average pixel intensity. Let's add that functionality to our binary image.

We have an image, but no way of visualizing it. We could print the pixel values directly to the terminal, but this could be tedious to parse. Instead, let's use characters, similar to ASCII art (<https://en.wikipedia.org/wiki/ASCII_art>). We only have two values in our images so let's use the characters - and + to encode zeros and ones respectively:

A Better Image Class⧉

We have provided the specification for a class that better represents images (Image.h). The specification contains a number of methods and variables that belong to each instantiation of the Image class. Most of these methods are implemented in the source file (Image.cpp).

In the next pset, you will implement some of the definitions in the source file Image.cpp. In the meantime, look over the header file and look for the two constructors on lines 34 and 39 of Image.h. They are

Algorithm 45.3.13
Image(int width_, int height_ = 1, int channels_ = 1,
        const std::string &name="");
Image(const std::string & filename);

You can create an instance of the Image class using either constructor. The first creates a blank image of dimensions ${\tt width\_}\times {\tt height\_}\times {\tt channels\_}$. You can use the first constructor to create an Image variable my_im that is $100\times 100$ pixels with three color channels by typing

Algorithm 45.3.14
Image my_im(100,100,3);

45.3.4 Submission⧉

Turn in your files to the online submission system and make sure all your files are in the asst directory under the root of the zip file. If your code compiles on the submission system, it is organized correctly. The submission system will run code in your main function, but we will not use this code for grading. The submission system should also show you the image your code writes to the ./Output directory

In the submission system, there will be a form in which you should answer the following questions: