Laboratory Exercise on Image Storage and Processing
Overview
This laboratory exercise builds upon the framework of an image (with a length, a width, and a 2-dimensional array of pixels) to provide practice with the creation, transformation, and analysis of pictures.
Creating a White Picture with a Black Dot in the Middle
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.
- Note that this program does not utilize a Scribbler 2 robot, so no rConnect or rDisconnect statements are needed!
- The program uses MyroC.h, which includes definitions of Pixel and Picture. What happens if you define Pixel and Picture (as given above) in this program as well?
- Variables whitePix and blackPix are declared and initialized at the start of the program. Review how a struct is defined and initialized, and explain what the expression = {0, 0, 0} does. What values are assigned to which fields within the whitePix struct?
- Review how the program places a black dot in the middle of the picture. Enhance the program to place a red dot near the top middle of the picture, a green dot near the left middle of the picture, and a blue dot near the middle bottom of the picture.
-
Review the definition of rDisplayPicture in the MyroC header file.
- Where is the text, "display of myArt", displayed?
- What happens if the parameter 10 is changed to 20? to -3? to 0;
Some Initial Experiments with Pictures
-
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:
- When a picture is represented in a grayscale, the red, green, and blue values of each pixel should be identical. Thus, in this problem, you will need to compute the composite value: 30% of the red value + 59% of the green value + 11% of the blue value. Then, this composite should be assigned to the red, green, and blue components of a pixel.
- The colorToGray is intended to change the corresponding picture in main memory. Thus, the calling context will need to provide the address of the picture (.e.g., &pix), and code within colorToGray will need to reference the original picture (e.g., *pic).
-
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:
-
If a and b are two values of type T,
then interchanging the values in these variables cannot be done easily
with simple assignments:
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;
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.
-
If a and b are two values of type T,
then interchanging the values in these variables cannot be done easily
with simple assignments:
A Quick Reference for Pictures
Conceptually, digital images are represented as a 2-dimension array dots or pixels.
Pixels
Although several image formats exist, a common specification for a pixel involves a red, green, and blue component. Each color component may take values between 0 and 255, and these components naturally fit together as a struct. The following presents the definition of a pixel in MyroC:
** * @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);
Pictures
An image has three basic components: a height, a width, and a 2-dimensional array of pixels. Due to storage limitations, MyroC limits the size of a picture to be no larger than 266 pixels high and 427 pixels wide. Since the actual height, width, and 2-dimensional array are logically related, MyroC packages them together in a struct
/** * @brief Struct for a picture object */ typedef struct { int height; //!< The actual height of the image, but no more than 266 int width; //!< The actual width of the image, but no more than 427 Pixel pix_array[266][427]; // The array of pixels comprising the image } Picture;
Notes:
- images from robot cameras have varying sizes, depending on the Fluke.
-
pix_array is sufficiently large to accommodate any Fluke version.
- images for the original Fluke are 192 (height) by 256 (width).
- low-resolution images for the Fluke 2 are 266 by 427.
- high-resolution images (e.g., 800 by 1280) are not practical, due to memory constraints and thus are not available in MyroC.
- user-defined images may have any size, as long as height ≤ 266 and width ≤ 427.
- Following standard mathematical convention for a 2D matrix, all references to a pixel are given within an array as [row][col].
Warning:
- The Picture struct is defined to be sufficiently large to store several low-resolution camera images (340756 bytes each).
- Experimentally, an array of up to 94 (not 95) Pictures may be allowed.
-
However, the display of images requires that image data
be copied, so display of many images may not work.
- If a program hangs when working with Picture variables, the issue may involve lack of space on the run-time stack.
- To utilize a modest number of Pictures, use "ulimit -s" command, as needed, in a terminal window.
- For example, ulimit -s 32768
- Sizes above 32768 may not be allowed in Linux or Mac OS X.
Homework
Transforming Pixel Arrays
-
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
Find Bright Light
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.
created 2 August 2011 by Erik Opavsky revised 8 August 2011 by Erik Opavsky modest editing 23 October 2011 by Dilan Ustek and Henry M. Walker modest reformatting 6 November 2011 by Henry M. Walker minor editing 25 October 2013 by Henry M. Walker reformatting, modest editing, discussion of 2D storage 1-2 February 2014 by Henry M. Walker readings added 19 September 2014 by Henry M. Walker lab reworked 29 December 2014, 20 October 2016 by Henry M. Walker |
![]() ![]() |
For more information, please contact Henry M. Walker at walker@cs.grinnell.edu. |