Handling 16bit ppm in C

Usually pixel values in an image are expressed by 8bit integer. However, it is not insufficient for photometric analysis. Sometimes 16bit, 32bit, float(32bit), and double(64bit) values are used to express pixel intensities.

The following source code is an example to handle 16bit ppm image. I home this code will be helpful for your research.

Sample program to convert 16bit ppm to 8bit ppm
// Sample program of [ppm16to8]                         2009/11/20 Y.Mukaigaw
//
// This is a sample program to handle 16bit ppm image.
// 16bit ppm image is converted to 8bit ppm image.
//
// First, a 16bit ppm image is read from <STDIN>
// Then, pixel values are linearly normalized so that the maximum becomes 255.
// Finally, the input image is written to <STDOUT> as an 8bit ppm image.
//
// Usage: ppm16to8 < 16bit.ppm > 8bit.ppm

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

int main(){

  int width, height, max, x, y;
  int red, green, blue, pixelmax;
  char dummy[100];

  unsigned char      *Image8bit =NULL;
  unsigned short int *Image16bit=NULL;

  /*--< Read header information of 16bit ppm image from stdin >--*/
  fscanf(stdin, "%s", dummy);
  fscanf(stdin, "%d %d\n", &width, &height);
  fscanf(stdin, "%d", &max);
  fgetc(stdin);

  /*--< Really 16bit ppm? Check it >--*/
  if (strncmp(dummy, "P6", 2) != 0) {
    fprintf(stderr, "Error: The input data is not binary PPM.\n");
    exit(1);
  }
  if (max != 65535){
    fprintf(stderr, "Error: The input data is not 16bit PPM.\n");
    exit(1);
  }

  /*--< Malloc for the image >--*/
  Image8bit  = (unsigned char *)     malloc(width*height*3*sizeof(unsigned char));
  Image16bit = (unsigned short int *)malloc(width*height*3*sizeof(unsigned short int));

  /*--< Read 16bit ppm image from stdin >--*/
  fread(Image16bit, sizeof(unsigned short int), width*height*3, stdin);

  /*--< Find maximum value in the image >--*/
  pixelmax=0;
  for(y=0;y<height;y++){
    for(x=0;x<width;x++){
      red  = Image16bit[(x+y*width)*3  ];
      green= Image16bit[(x+y*width)*3+1];
      blue = Image16bit[(x+y*width)*3+2];

      // Exhange upper 8bit and lower 8bit for Intel x86
      red   = ((red   & 0x00ff)<<8)|((red   & 0xff00)>>8);
      green = ((green & 0x00ff)<<8)|((green & 0xff00)>>8);
      blue  = ((blue  & 0x00ff)<<8)|((blue  & 0xff00)>>8);

      if (pixelmax < red  ) pixelmax=red;
      if (pixelmax < green) pixelmax=green;
      if (pixelmax < blue ) pixelmax=blue;
    }
  }
  fprintf(stderr, "MAX = %d\n", pixelmax);

  /*--< Convert 8bit ppm image >--*/
  for(y=0;y<height;y++){
    for(x=0;x<width;x++){
      red  = Image16bit[(x+y*width)*3  ];
      green= Image16bit[(x+y*width)*3+1];
      blue = Image16bit[(x+y*width)*3+2];

      // Exhange upper 8bit and lower 8bit for Intel x86
      red   = ((red   & 0x00ff)<<8)|((red   & 0xff00)>>8);
      green = ((green & 0x00ff)<<8)|((green & 0xff00)>>8);
      blue  = ((blue  & 0x00ff)<<8)|((blue  & 0xff00)>>8);

      Image8bit[(x+y*width)*3  ] = red  *255/pixelmax;
      Image8bit[(x+y*width)*3+1] = green*255/pixelmax;
      Image8bit[(x+y*width)*3+2] = blue *255/pixelmax;
    }
  }
  
  /*--< Write 8bit ppm image to stdout >--*/
  printf("P6\n%d %d\n255\n", width, height);
  fwrite(Image8bit, sizeof(unsigned char), width*height*3, stdout);


  /*--< Clean >--*/
  free(Image16bit);
  free(Image8bit);

  return 0;
}

The following code is a simple example to handle 8bit and 16bit ppm/pgm images. While `ppm' is a color image including R/G/B channels, `pgm' is a grey image.

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

void write8ppm(FILE *fp, int width, int height, unsigned char *img){
  fprintf(fp,"P6\n%d %d\n255\n", width, height);
  fwrite(img, sizeof(unsigned char), width*height*3, fp);
}

void write8pgm(FILE *fp, int width, int height, unsigned char *img){
  fprintf(fp,"P5\n%d %d\n255\n", width, height);
  fwrite(img, sizeof(unsigned char), width*height, fp);
}

void write16ppm(FILE *fp, int width, int height, unsigned short int *img){
  unsigned char *p;
  int i, tmp;

  p = (unsigned char *) img;
  for (i=0; i<width*height; i++){
    tmp = *p; *p = *(p+1); *(p+1) = tmp; p+=2;
    tmp = *p; *p = *(p+1); *(p+1) = tmp; p+=2;
    tmp = *p; *p = *(p+1); *(p+1) = tmp; p+=2;
  }
  fprintf(fp,"P6\n%d %d\n65535\n", width, height);
  fwrite(img, sizeof(unsigned short int), width*height*3, fp);
}

void write16pgm(FILE *fp, int width, int height, unsigned short int *img){
  unsigned char *p;
  int i, tmp;

  p = (unsigned char *) img;
  for (i=0; i<width*height; i++){
    tmp = *p; *p = *(p+1); *(p+1) = tmp; p+=2;
  }
  fprintf(fp,"P5\n%d %d\n65535\n", width, height);
  fwrite(img, sizeof(unsigned short int), width*height, fp);
}


Keyword: 16bit 8bit ppm pgm pbm C convert programming