A PGM Image Viewer using C#

I’ve been playing with image recognition lately, an area I am not very familiar with. Image recognition led me to Gaussian kernels and image distortion. And these topics led me to image viewing. For fun I wrote an image viewer program (see below) that displays PGM images.


The PGM format is one of the simplest image formats and is one I’d never heard of until doing my image research. PGM stands for Portable Gray Map. There are actually several variations of PGM files but the most common form is a binary file of pixel values, with some header lines.

For example, the screenshot above is my viewer program displaying file coins.pgm which contains:

# coins.pgm
300 246
49 50 48 . . . (a total of 300*246 values)

The P5 is called a “magic number” (even though it’s a string) that identifies the file type. Lines that start with ‘#’ are comments. The 300 and 246 are the width and height, in pixels. The 255 is the maximum pixel value in the file (0 is black, 255 is white, values in between are shades of gray). The values 49, 50, and so on are the pixel values, from left to right, top to bottom. The pixel values are stored in binary so if you opened file coins.pgm with notepad or Word, you would see strange characters.

The key calling code is:

string file = textBox1.Text;
PgmImage pgmImage = LoadImage(file);
int magnify = int.Parse(textBox2.Text.Trim());
Bitmap bitMap = MakeBitmap(pgmImage, magnify);
pictureBox1.Image = bitMap;

I use a program-defined PgmImage object:

public class PgmImage
  public int width;
  public int height;
  public int maxVal;
  public byte[][] pixels;

  public PgmImage(int width, int height, int maxVal,
    byte[][] pixels)
    this.width = width;
    this.height = height;
    this.maxVal = maxVal;
    this.pixels = pixels;

Method LoadImage reads the target PGM file using the .NET BinaryReader class:

public PgmImage LoadImage(string file)
  FileStream ifs = new FileStream(file, FileMode.Open);
  BinaryReader br = new BinaryReader(ifs);

  string magic = NextNonCommentLine(br);
  if (magic != "P5")
    throw new Exception("Unknown magic number: " + magic);
  listBox1.Items.Add("magic number = " + magic);

  string widthHeight = NextNonCommentLine(br);
  string[] tokens = widthHeight.Split(' ');
  int width = int.Parse(tokens[0]);
  int height = int.Parse(tokens[1]);
  listBox1.Items.Add("width height = " + width + " " + height);

  string sMaxVal = NextNonCommentLine(br);
  int maxVal = int.Parse(sMaxVal);
  listBox1.Items.Add("maxVal = " + maxVal);

  // read width * height pixel values . . .
  byte[][] pixels = new byte[height][];
  for (int i = 0; i < height; ++i)
    pixels[i] = new byte[width];

  for (int i = 0; i < height; ++i)
    for (int j = 0; j < width; ++j)
      pixels[i][j] = br.ReadByte();

  br.Close(); ifs.Close();

  PgmImage result = new PgmImage(width, height, maxVal, pixels);
  listBox1.Items.Add("image loaded");
  return result;

Reading the header lines is done by a helper NextNonCommentLine and its helper NextAnyLine:

static string NextAnyLine(BinaryReader br)
  string s = "";
  byte b = 0; // dummy
  while (b != 10) // newline
    b = br.ReadByte();
    char c = (char)b;
    s += c;
  return s.Trim();

static string NextNonCommentLine(BinaryReader br)
  string s = NextAnyLine(br);
  while (s.StartsWith("#") || s == "")
    s = NextAnyLine(br);
  return s;

Once the PGM image is loaded into the program-defined PgmImage object, a .NET Bitmap object (which is essentially an image) is created. The code is short but not obvious:

static Bitmap MakeBitmap(PgmImage pgmImage, int mag)
  int width = pgmImage.width * mag;
  int height = pgmImage.height * mag;
  Bitmap result = new Bitmap(width, height);
  Graphics gr = Graphics.FromImage(result);
  for (int i = 0; i < pgmImage.height; ++i) {
    for (int j = 0; j < pgmImage.width; ++j) {
      int pixelColor = pgmImage.pixels[i][j];
      Color c = Color.FromArgb(pixelColor, pixelColor, pixelColor);
      SolidBrush sb = new SolidBrush(c);
      gr.FillRectangle(sb, j * mag, i * mag, mag, mag);
  return result;

Once the Bitmap object is created, assigning the object to the Image property of a .NET PictureBox control automatically displays the image.

This entry was posted in Machine Learning. Bookmark the permalink.