Netpbm format: Difference between revisions
strtol and atoi were not compiling on my machine. Hopefully this change works for everyone. |
some cleaning up |
||
Line 101: | Line 101: | ||
//PGM Headers Variable Declaration |
//PGM Headers Variable Declaration |
||
char* type; |
char* type; |
||
char |
char* pEnd; |
||
int q,i,j; |
int q,i,j; |
||
char header[100]; |
char header[100]; |
||
//Open file for Reading in Binary Mode |
//Open file for Reading in Binary Mode |
||
//fp = fopen("/home/anthoniraj/image.pgm","rb"); |
|||
fp = fopen(imageName,"rb"); |
fp = fopen(imageName,"rb"); |
||
Line 141: | Line 140: | ||
q = strtol(header,&pEnd,10); |
q = strtol(header,&pEnd,10); |
||
//Allocating Array Size |
//Allocating Array Size |
||
charImage = (unsigned char*) malloc(width*height*sizeof(unsigned char)); |
charImage = (unsigned char*) malloc(width*height*sizeof(unsigned char)); |
||
pixels = (int **)malloc(height * sizeof(int *)); |
pixels = (int **)malloc(height * sizeof(int *)); |
Revision as of 06:42, 22 April 2009
Filename extension |
.ppm, .pgm, .pbm, .pnm |
---|---|
Internet media type |
image/x-portable-pixmap, -graymap, -bitmap, -anymap all unofficial |
Developed by | Jef Poskanzer |
Type of format | Image file formats |
The portable pixmap file format (PPM), the portable graymap file format (PGM) and the portable bitmap file format (PBM) specify rules for exchanging graphics files. They provide very basic functionality and serve as a least-common-denominator for converting pixmap, graymap, or bitmap files between different platforms. Several applications refer to them collectively as the PNM format (portable anymap).
Netpbm usage
An important use of the Netpbm formats is in conjunction with the Netpbm package to convert between different file formats and to do simple image manipulation. The Netpbm package can, for example, use two successive conversion programs to turn the code from the PBM example given below into a bmp file:
pgmtoppm "#FFFFFF" j.pbm > j.ppm ppmtobmp j.ppm > j.bmp
Depending on the identification of the file format, portable pixmap systems can distinguish three similar file formats, each with two versions:
- PBM - portable bitmap file format (P1/P4) - 1 bit per pixel
- PGM - portable graymap file format (P2/P5) - 8 bits per pixel
- PPM - portable pixmap file format (P3/P6) - 24 bits per pixel, 8 for red, 8 for green, 8 for blue
In each case, the lower-numbered version (P1, P2 or P3) refers to a human-readable, ASCII-based format similar to the one in the example above; and the higher-numbered version (P4, P5 or P6) refers to a binary format, not human-readable but more efficient at saving some space in the file, as well as easier to parse due to the lack of whitespace.
File format description
PBM example
Take the example of the letter "J" as a binary image:
....X. ....X. ....X. ....X. ....X. ....X. X...X. .XXX.. ...... ......
The most basic (monochrome) PBM format represents it as follows:
P1 # This is an example bit map file j.pbm 6 10 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
The string P1 identifies the file format. The hash sign introduces a comment. The next two numbers give the width and the height. Then follows the matrix with the pixel values (in the monochrome case here, only zeros and ones).
Here it is again magnified 20 times:
The PGM and PPM formats (both ASCII and binary versions) have an additional parameter for the maximum value in a line between the X and Y dimensions and the actual pixel data.
PGM example
P2 # feep.pgm from NetPBM man page on PGM 24 7 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 3 3 3 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 15 15 15 0 0 3 0 0 0 0 0 7 0 0 0 0 0 11 0 0 0 0 0 15 0 0 15 0 0 3 3 3 0 0 0 7 7 7 0 0 0 11 11 11 0 0 0 15 15 15 15 0 0 3 0 0 0 0 0 7 0 0 0 0 0 11 0 0 0 0 0 15 0 0 0 0 0 3 0 0 0 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Reading and Writing PGM Image Using C
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
/*
* Author : Anthoniraj.A
* Date : 24/03/2009
*/
int** imageRead(char imageName[]);
void imageWrite(int** pels);
int width , height ;
int **pixels;
unsigned char *charImage;
int** imageRead(char imageName[])
{
FILE* fp;
//PGM Headers Variable Declaration
char* type;
char* pEnd;
int q,i,j;
char header[100];
//Open file for Reading in Binary Mode
fp = fopen(imageName,"rb");
if(fp == NULL)
{
printf("Image does not exist \n");
}
else
{
//Check the PGM file Type P2 or P5
fgets(header,100,fp);
if((header[0] != 80) || (header[1] != 53 ))
{
printf("Image is not PGM\n");
}
//Check the Commnets
fgets(header,100,fp);
while(header[0] == '#')
{
//printf("%c\n", header[0]);
fgets(header,100,fp);
}
//Get Width and Height
width = strtol(header,&pEnd,10);
height = strtol(pEnd,&pEnd,10);
// Get Maximum Gray Value
fgets(header,100,fp);
q = strtol(header,&pEnd,10);
//Allocating Array Size
charImage = (unsigned char*) malloc(width*height*sizeof(unsigned char));
pixels = (int **)malloc(height * sizeof(int *));
for(i = 0; i < height; i++)
pixels[i] = (int *)malloc(width * sizeof(int));
// Pixel Extraction
fread(charImage,1,(width*height)*sizeof(unsigned char),fp);
for(i=0;i<height;i++)
{
for(j=0;j<width;j++)
{
//cout << (int)charImage[i*M+j] << " " ;
pixels[i][j]= (int)charImage[i*width+j];
printf("%d ",pixels[i][j]);
}
printf("\n");
//cout << "\n";
}
// Pixel Extraction
fclose(fp);
}
return pixels;
}
void imageWrite(int** pels)
{
int i,j;
FILE* fp1;
fp1 = fopen("out.pgm","w");
for(i=0;i<height;i++)
{
for(j=0;j<width;j++)
{
charImage[i*width+j] = pels[i][j];
//printf("%d ",charImage[i*width+j]);
}
//printf("\n");
}
fprintf(fp1, "P5\n%d\n%d\n255\n", width, height);
fwrite(charImage,1,width*height,fp1);
fclose(fp1);
}
int main()
{
int** p = imageRead("check.pgm");
imageWrite(p);
return 0;
}
PPM example
P3 #the P3 means colors are in ascii, then 3 columns and 2 rows, then 255 for max color, then RGB triplets 3 2 255 255 0 0 0 255 0 0 0 255 255 255 0 255 255 255 0 0 0
The above image was expanded without interpolation, using the imagemagick command
convert -sample %6400 tiny6pixel.ppm tiny6pixel.png
The P6 format of the same image will store a color with one byte, or three bytes per pixel. The file will be smaller but the color information will not be readable by humans:
P6 #any comment string 3 2 255 !@#$%^&*()_+|{}:"<
The PPM format is not compressed and so is extravagantly wasteful of space and bandwidth. For example, the above 192x128 PNG image has a file size of 187 bytes. When converted to a 192x128 PPM image, the file size is 73848 bytes. The PPM format is generally a vehicle to a more efficient format, notably the PNG (Portable Network Graphics) format. The PPM format can be converted to PNG, for example, without loss of information.
The PPM format is certainly simple to write from scratch. The following Python code makes the above example image. It can be adapted to making useful images by reading or constructing an array of numerical data, and programming a conversion of the data to color triplets.
#!/usr/bin/python
triplets=[ [255, 0, 0], [0, 255, 0], [0, 0, 255], [255, 255, 0], [255, 255, 255], [0, 0, 0] ]
width=3; height=2
comment='any comment string'
ftype='P6' #use 'P3' for ascii, 'P6' for binary
ppmfile=open('testimage.ppm','wb')
ppmfile.write("%s\n" % (ftype))
ppmfile.write("#%s\n" % comment )
ppmfile.write("%d %d\n" % (width, height))
ppmfile.write("255\n")
if ftype=='P3':
for red,green,blue in triplets:
ppmfile.write("%d %d %d\n" % (red,green,blue))
elif ftype=='P6': #print 1 byte per color
for red,green,blue in triplets:
ppmfile.write("%c%c%c" % (red,green,blue))
ppmfile.close()
Similar code in C :
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define xor(a, b) ((!(a && b)) && (a || b))
int main (int argc, char * argv[]){
/* Declare the variables */
FILE* ppmFile;
int columns, rows, numberOfPixelsInPicture;
/* Check the input arguments */
if(argc != 4){
printf("Usage: %s cols rows fname.ppm\n\n", *argv);
return 1;
}
/* Open the file and write the first 4 lines to it */
ppmFile = fopen(argv[3], "w");
columns = atoi(argv[1]);
rows = atoi(argv[2]);
numberOfPixelsInPicture = rows * columns;
fprintf(ppmFile, "P3\n%d\n%d\n255\n", columns, rows);
int m, n;
for(m=0;m<rows;m++) {
for(n=0;n<columns;n++) {
/* Set the pixels in the bottom left of the picture a green-yellow color */
if(m>n) {
fprintf(ppmFile, "%d 255 %d\n", (256*m)/rows, (256*n)/columns);
} else if(xor((m/10)%2,(n/10)%2)) {
/* Create a checkerboard pattern in the upper-right of the picture */
/* Create the blue-red pixels in the checkerboard pattern */
fprintf(ppmFile, "%d 0 %d\n", (255*m*n)/numberOfPixelsInPicture, 255 -(255*m*n)/numberOfPixelsInPicture);
} else {
/* Create the black pixels in the checkerboard pattern */
fprintf(ppmFile, "0 0 0\n");
}
}
/* Print a blank line between rows */
fprintf(ppmFile, "\n");
}
fclose(ppmFile);
return 0;
}
16-bit extensions
The original definition of the PGM and the PPM binary formats (the P5 and P6 formats) did not allow bit depths greater than 8 bits. One can of course use the ASCII format, but this format both slows down reading and makes the files much larger. Accordingly, many programmers have attempted to extend the format to allow higher bit depths. Using higher bit depths encounters the problem of having to decide on the endianness of the file. Unfortunately it appears that the various implementations could not agree on which byte order to use, and some connected the 16-bit endianness to the pixel packing order;[1] according to Netpbm, the de facto standard implementation of the PNM formats, the most significant byte is first.
References
- ^ "Pnmtotiff User Manual". netpbm doc at SourceForge. 27 March 2005.