| CSC 161 | Grinnell College | Spring, 2015 |
| Imperative Problem Solving and Data Structures | ||
In this digital age, images commonly consist of a large number of dots or pixels, arranged in a grid pattern.
Although several image formats exist, a common specification for a pixel involves a red, green, and blue component, and each color component may take values between 0 and 255. Since the values 0..255 comprise one byte (the size of a char in C), each component is commonly stored as a char value. In this context, negative values are not meaningful, so each component is considered an unsigned char. Further, the red, green, and blue (R/G/B) components naturally fit together as part of a logical package — a struct in the context of C. With this in mind, MyroC defines a pixel as follows:
**
* @brief Struct for a pixel
*/
typedef struct
{
unsigned char R; // The value of the red component
unsigned char G; // The value of the green component
unsigned char B; // The value of the blue component
} Pixel;
Within this framework, R/G/B values of 0 correspond to black and R/G/B values of 255 correspond to white. This leads to the following natural definitions:
Pixel blackPix = {0, 0, 0};
Pixel whitePix = {255, 255, 255);
Perhaps the most conceptually-simple structure for a picture involves a two-dimensional array of R/G/B pixels. Each picture has a height and a width, and an overall picture is just a two-dimensional array with those dimensions. When working with a Scribbler 2 robot, the camera takes a picture that is 192 pixels high and 256 pixels wide. Of course, other cameras or images may have a different dimensions.
A pragmatic detail: You may recall from working with one- and two-dimensional arrays that the declaration of a two-dimensional array allocates space, but the array name just gives the base address, not the height and width dimensions, We cannot infer the dimensions of the array given only the variable name. For this reason, it is convenient to store the dimensions of an image together with the two-dimensional array. Thus, in much processing, the height, width, and pixel array are naturally part of a single package, so a picture is defined as a struct:
/**
* @brief Struct for a picture object
* @note the picture size is always 256 in width and 192 in height
*/
typedef struct
{
int height; /* The height of the image -- set to 192 for robot camera */
int width; /* The width of the image -- set to 256 for robot camera */
Pixel pix_array[256][192]; /* The array of pixels comprising the image */
} Picture;
Building on this background, program white-picture-with-black-dot.c creates a white picture, places a [2 pixel by 2-pixel] black dot in the middle, and displays the result using rDisplayPicture from the MyroC library.
Copy white-picture-with-black-dot.c to your account, compile and run it, and review how the program works.
Since image processing is used in a wide variety of applications, several common formats are used to store image data. The approach here, with an array of R/G/B pixels, is conceptually simple. However, other formats are possible as well.
The camera in a Scirbbler 2 robot actually uses a different color designation (YUV format). Behind the scenes, the Scribbler transmits YUV values to your workstation, where the rGetPicture function transforms the YUV color coding to the more common RGB format.
Since images can consume much space, various formats are used to compress file sizes to speed the transmission of images and to reduce storage requirements. Each format has specific advantages for certain purposes. The .jpg or JPEG format was created by the Joint Photographic Experts Group (hence the acronym) and is largely based on what people actually see. Since this format is particularly common, MyroC provides functions to convert between RGB format and JPEG format for various purposes.
See the MyroC header file for details on each of these functions.
Write a program drawBox.c that creates a picture with a solid green box in the middle of a white background. You likely will need loops to set each pixel within the box to green. Display the resulting picture.
Write a function,
void colorToGray (Picture * pic)
which takes in a Picture * and then converts each pixel to its grayscale value. A grayscale pixel is a pixel where the RGB are all set to 30% of the red value + 59% of the green value + 11% of the blue value (source: wikipedia.org/Grayscale). Your function should then display the Picture. Test this out on a few pictures you take with your robot.
Notes:
Write a function, void setPictureMax (Picture * pic), which finds what the highest RGB value for each pixel is and sets that pixel to just that value (e.g. a pixel of RGB (50,135,85) will have a new RGB of (0,135,0)). That is, setPictureMax should not change the RGB value of the color with the highest value, but the other two colors should be set to 0.
Your function should change the parameter variable, so the original picture in the calling procedure (e.g., in main) is changed. The main program then can display the Picture. Test this out on a few pictures you take with your robot.
Write a program flip-picture that takes a picture from the robot (using rTakePicture), displays the picture for 5 seconds, flips the picture upside down, and finally displays the result for 5 seconds
Notes:
Flipping a picture upside down requires swapping pixels in one row (e.g., near the top) with pixels in a corresponding row (e.g., near the bottom). In the swap, the column index of the swapped pixels will be the same, but the corresponding pixels in two rows will be interchanged.
a = b; b = a;
In this code, the original value of a is overwritten in the first assignment, so there is no way to give b the original value of a. Instead, in normal processing, interchanging the values of two variables requires an additional storage location:
T temp = a; // declare temporary variable strong data of the relevant type a = b; b = temp;
Write a program create-negative.c that changes each RGB value V of each pixel to 255 - V, and display the result.
Write a program left-right-picture that takes a picture from the robot (using rTakePicture), displays the picture for 5 seconds, flips the picture left-to-right, and finally displays the result for 5 seconds
Consider the brightness of a pixel as being the sum of its R, G, and B values.
Write a program find-brightest.c that takes a picture from the Scribbler 2's camera, and finds the coordinates of the brightest pixel. (If there are two pixels with the same brightness, the program could report any of the bright pixels — your choice.)
Modify the program from the previous step, so that once the brightest pixel is found, the program draws a small red circle around that location. (If the circle would go outside the picture, just draw the part of the circle within the picture itself.) Be sure your program displays the resulting picture with the identifying red circle.
Modify the previous two programs to consider the collective brightness of each 3-by-3 collection of pixels. That is, brightness will be computed as the sum of all pixels in a 3-by-3 square. Once found, the program should draw a small circle around this bright region.
Development of laboratory exercises is an iterative process. Prof. Walker welcomes your feedback! Feel free to talk to him during class or stop by his office.