created 06/24/2005; revised 03/28/2007, 08/03/2012, 11/14/15


Puzzle 01 — Puzzle 10

Read the sections on images and gray level images before playing with these puzzles.


Puzzle 01 — Create an Image of Uniform Gray Level

Write a main() program with a command line that lists a file name, image rows, image columns, and a gray level. Create a PGM image file of that name with all of its pixels of that gray level. For example,

C:\PuzzleFolder>makeGray gray240.pgm 200 300 240

creates a gray level image file called gray240.pgm of size 200 rows by 300 columns with all pixels at level 240. Here is a skeleton program that does most of the work. If you have a programming environment such as Bloodshed Dev-C++ copy-and-paste this code into a source file.

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
  int r, nrows, c, ncols, grayLevel;
  FILE *image;

  /* check the command line parameters */
  if ( argc != 5 )
  {
    printf("makeGray fileName.pgm nrows ncols grayLevel\n");
    return 0;
  }

  nrows = atoi( argv[2] );
  if ( nrows < 1 )
  {
    printf("number of rows must be positive\n");
    return 0;
  }

  ncols = atoi( argv[3] );
  if ( nrows < 1 )
  {
    printf("number of columns must be positive\n");
    return 0;
  }

  grayLevel = atoi( argv[4] );
  if ( !(grayLevel >= 0 && grayLevel <=255) )
  {
    printf("grayLevel must be between 0 and 255\n");
    return 0;
  }

  /* open the image file for writing in binary mode (see "gotcha!" in Gray Level Images) */
  if ( (image = fopen( argv[1], "wb") ) == NULL )
  {
    printf("file %s could not be created\n", argv[1]);
    return 0;
  }

  /* write out the PGM Header information */
  fprintf( image, "P5 ");
  fprintf( image, "# Created by makeGray\n");
  fprintf( image, "%d %d %d ", ncols, nrows, 255 );  /* width, height, max gray level */

  /* write out the pixel data */
  .... Your work goes here .... 

  /* close the file */
  fclose ( image );

  return 1;
}

Finish the program by adding a few lines. When you are done, click on the answer icon to see a suggested answer.

gray level image

Use IrfanView to examine the image file that your program creates. You should see something like the above image (except larger). Check that the number of rows and columns displayed are consistent with what was specified.

Go to Answer


Puzzle 02 — Image function

Rearrange the previous program so that main() does only command line processing, file creation, and file closing. Create a function, called by main(), that does the specialized work of writing out the pixel data. The command line will be the same as in the previous program:

C:\PuzzleFolder>makeGray gray240.pgm 200 300 240

It would be sensible (of course) to make a copy of the previous program and rearrange it with your text editor.

Go to Answer


Puzzle 03 — Create a Gray Wedge Image

Write a main() program that creates an image that is a gray wedge. A gray wedge is an image that has a gray level 0 in its first row and gray level 255 in its last row, and the rows in between increasing in gray level. The command line specifies the size of the image.

C:\PuzzleFolder>grayWedge imageName.pgm nrows ncols 

A 200 by 400 gray wedge image is:

Gray Wedge

Use nearly the same main() as the previous program. Make a few small changes in the parameters and in the function call. The function writes the pixels to the image file, similar to the previous program:

void writeWedge( FILE *image, int nrows, int ncols)  

The function makes no assumptions about the size of the image. In particular, the image is not assumed to have exactly 256 rows.

Go to Answer


Puzzle 04 — Create an image of N vertical bands

Write a program that creates an image with N vertical bands of gray. The first band is gray level 0 and the last band is gray level 255. The command line specifies the size of the image and number of bands.

C:\PuzzleFolder>grayBands grayBands.pgm nrows ncols bands

For example,

C:\PuzzleFolder>grayBands tenBands.pgm 100 300 10

creates the image:

Ten Bands

It is hard to figure out what to do when the number of bands do not evenly divide the number of columns. For example, when the number of bands is 10 and the number of columns is 256. Each band should be 25.6 pixels wide, but of course, this can't be done. Instead, make each band but the last 25 pixels wide, and make the last band as wide as needed to complete the image.

A similar problem is when the number of bands does not evenly divide the number of gray levels. For example, when the number of bands is 10 (and the number of gray levels is 256.) Make each band the same step up from the previous, even though the last band might not be exactly 255.

Here is a main() for use with this program. Your job is to write the function that it calls:

int main( int argc, char *argv[] )
{
  int nrows, ncols, nbands;
  FILE *image;

  /* check the command line parameters */
  if ( argc != 5 )
  {
    printf("grayBands fileName.pgm nrows ncols nbands\n");
    return 0;
  }

  /* open the image file for writing */
  if ( (image = fopen( argv[1], "wb") ) == NULL )
  {
    printf("file %s could not be created\n", argv[1]);
    return 0;
  }

  nrows = atoi( argv[2] );
  if ( nrows < 1 )
  {
    printf("number of rows must be positive\n");
    return 0;
  }

  ncols = atoi( argv[3] );
  if ( nrows < 1 )
  {
    printf("number of columns must be positive\n");
    return 0;
  }

  /* Get the number of bands */
  nbands = atoi( argv[4] );
  if ( nbands<=0 || nbands>256 )
  {
    printf("Can't create %d bands\n", nbands);
    return 0;
  }

  /* write out the PGM Header information */
  fprintf( image, "P5\n");
  fprintf( image, "# Created by grayBands\n");
  fprintf( image, "%d %d %d\n", ncols, nrows, 255 );

  /* write out the pixels */
  writeBands( image, nrows,ncols, nbands);

  /* close the file */
  fclose ( image );
  return 1;
}

Mach Bands: If you examine the above banded image carefully, it looks as if each band is not quite all the same level of gray. Starting from the left, each band looks like it gets a little bit darker just before the next band. And the next band looks like it starts out a little bit brighter than it should.

This is a result of how your human vision system works. The phenomenon is called Mach bands (named after the same person who brought us "Mach numbers" for expressing supersonic speeds). Your vision system exaggerates changes near edges so that edges stand out. Edges usually convey the most important information in a scene.

solid gray image


Puzzle 05 — Create a checkerboard image

Write a program that creates an image with nAcross by nDown checkerboard squares. Alternate the squares between level 100 and level 200. If the number of squares across (or down) does not evenly divide the image size, pad the image with pixels of level 50.

C:\PuzzleFolder>checkerBoard check.pgm nrows ncols nDown nAcross 

For example,

C:\PuzzleFolder>checkerBoard check.pgm 256 256 7 9

creates the image:

gray wedge

In this image, the width of 256 is to be divided into 9 squares. So each square is 256/9 = 28 pixels across. But 28 * 9 = 252 so there are 4 pixels of padding at the right of the image. A similar calculation for the rows results in 4 pixels of padding at the bottom.

Here is the main() file for your program:

int main( int argc, char *argv[] )
{
  int nrows, ncols ;
  int numAcross, numDown;

  FILE *image;

  /* check the command line parameters */
  if ( argc != 6 )
  {
    printf("checker fileName.pgm nrows ncols  squaresDown squaresAcross\n");
    return 0;
  }

  nrows = atoi( argv[2] );
  if ( nrows < 1 )
  {
    printf("number of rows must be positive\n");
    exit( EXIT_FAILURE );
  }

  ncols = atoi( argv[3] );
  if ( nrows < 1 )
  {
    printf("number of columns must be positive\n");
    exit( EXIT_FAILURE );
  }

  /* open the image file for writing */
  if ( (image = fopen( argv[1], "wb") ) == NULL )
  {
    printf("file %s could not be created\n", argv[1] );
    exit( EXIT_FAILURE );
  }

  /* write out the PGM Header information */
  fprintf( image, "P5\n");
  fprintf( image, "# Created by makeChecker\n");

  /* width, height, number of gray levels */
  fprintf( image, "%d %d %d\n", ncols, nrows, 255 );

  /* Get the number of squares down */
  numDown = atoi( argv[4] );
  if ( numDown<=0 || numDown>nrows )
  {
    printf("Can't create %d squares in %d rows\n",
      numDown, nrows );
    return 0;
  }

  /* Get the number of squares across */
  numAcross = atoi( argv[5] );
  if ( numAcross<=0 || numAcross>ncols )
  {
    printf("Can't create %d squares in %d columns\n",
      numAcross, ncols );
    return 0;
  }
  
  /* Create the Image */
  makeChecker(image, nrows, ncols, numDown, numAcross);

  /* close the file */
  fclose ( image );
  return 1;
}

vertical gray bands


Puzzle 06 — create an image where pixels get dimmer the further they are from the top left

Make an image where the gray level of each pixel depends on how far it is from the top left corner of the image. Make the top left of the image gray level 255. Make the rest of the pixels

255 * (1.0 - distFromTopLeft/diagonalLength)  

where the diagonalLength is the length of the diagonal from the top left corner of the image to the bottom right corner of the image, and distFromTopLeft is the distance the pixel is from the top left. Use floating point arithmetic, but cast the result to an int.

The diagonalLength is calculated with the Pythagorean formula:

( nrows2 + ncols2 )(1/2)

The distFromTopLeft for a pixel at row, col is calculated as:

( row2 + col2 )(1/2)  

For example,

C:\PuzzleFolder>distImage dist.pgm 300 500

creates the image:

Fade to black

A variation on this is to base the gray level of each pixel on the ratio of the square of its distance from the corner over the square of the diagonal length:

255 * ( 1.0 - distFromTopLeft2/diagonalLength2 )  

In doing this you avoid the need for the square root function. But you get a different image.

Go to Answer


Puzzle 07 — create an image where gray level depends on the distance from the center

Make an image where the gray level of each pixel depends on how far it is from the center of the image. Make the center gray level 255. Make the rest of the pixels

  255 * (1.0 - distFromCenter/halfDiagonalLength)  

where the halfDiagonalLength is half the length of the diagonal from the top left corner of the image to the bottom right corner of the image, and distFromCenter is the distance the pixel is from the top left. Use floating point arithmetic, but cast the result to an int.

For example,

C:\PuzzleFolder>distCenter distCenter.pgm 300 300

creates the image:

Bright center fades to black

This program can be created by modifying the previous one. Play with other ways to calculate pixel gray level if you want. For example, you might want to make the gray level proportional to the cosine of (pi/2)*(distFromCenter/halfDiagonalLength)

Go to Answer


Puzzle 08 — create an image of a filled circle

Write a program that creates an image with a filled circle placed in the center. Make pixels on the inside of the circle value 200 and those outside the cirlce value 50. Find the largest dimension of the image and make the radius of the circle one third that value.

For example,

C:\PuzzleFolder>circle circle.pgm 300 400

creates the image:

solid centered circle

This program can be created by modifying the previous program. To determine if a pixel is inside or outside of the circle, check if its distance from the center is less than the radius of the circle.

Go to Answer


Puzzle 09 — create an image of a circle filled with a checkerboard

Write a program that creates an image with a circle placed in the center. The circle is given a checkerboard texture. Find the largest dimension of the image and make the radius of the circle one third that value. Make the sides of the squares of the checkerboard 1/16 the largest dimension of the image.

Make the background a checkerboard with squares 1/12 the largest dimension of the image. Pick pleasing gray levels for the squares.

For example,

C:\PuzzleFolder>circleChecks circleCheck.pgm 300 400

creates the image:

checked circle centered on a checkerboard

This program can be created by combining the two previous programs.

Go to Answer


Puzzle 10 — Make a washerboard image

Create an image that has horizontal ripples that look like a washerboard. The number of ripples is a parameter of the command line.

Ripples are made by calculating the gray level of a pixel in row r to be:

level = 127 + 127*cos( 2N*π*r/nrows )

The cos function varies between -1 and +1. When it is -1, the above expression reduces to 127-127, or 0. When the cos is +1, the above expression reduces to 127+127, or 254. The cos goes through one complete cycle in radians. To get N complete cycles, we need 2Nπ radians. These are spread out over over nrows. A pixel at row n is at 2N*π(n/nrows) radians.

For example,

C:\PuzzleFolder>washerboard washerboard.pgm 400 400 12

creates the image:

washerboard

The definition of pi, M_PI, is found in math.h. Not all C environments support this. You may have to inspect your compiler options if it complains about this constant.

Go to Answer


Go to Contents — Return to the main contents page