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:
P5 # coins.pgm 300 246 255 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("");
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.

.NET Test Automation Recipes
Software Testing
SciPy Programming Succinctly
Keras Succinctly
R Programming
2026 Visual Studio Live
2025 Summer MLADS Conference
2026 DevIntersection Conference
2025 Machine Learning Week
2025 Ai4 Conference
2026 G2E Conference
2026 iSC West Conference
You must be logged in to post a comment.