Miguel Gomes
Miguel Gomes

Reputation: 23

Reading an image (PPM) with C

So I have to read this image in a PPM format, and then write it to another file. It works, however if the image has comments it stops working. If the image is like this:

P3
3 2
255
255   0   0
  0 255   0
  0   0 255
255 255   0
255 255 255
  0   0   0

It works, but if the image is like this:

P3
3 2
255
# "3 2" is the width and height of the image in pixels
# "255" is the maximum value for each color
# The part below is image data: RGB triplets
255   0   0  # red
  0 255   0  # green
  0   0 255  # blue
255 255   0  # yellow
255 255 255  # white
  0   0   0  # black

It stops working. I really don't know why since I have programmed to ignore comments. I will leave the code below, maybe someone can help.

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

typedef struct pixel {
  unsigned int r,g,b;
} pixel;

typedef struct imagem{
  int cols,rows;
  char mnumber[2];
  int maxcolor;
  pixel *matrix;
}imagem;

static imagem read(const char *filename)
{
  imagem img;
  FILE *f;

  f = fopen(filename,"rb");

  if(f == NULL) {
    scanf("%c %c",&img.mnumber[0],&img.mnumber[1]);
    scanf("%d %d",&img.cols,&img.rows);
    scanf("%d",&img.maxcolor);

    img.matrix = (pixel*) malloc(img.cols*img.rows*sizeof(pixel));

    for(int i = 0; i < img.cols*img.rows;i++)
    {
      scanf("%u %u %u",&img.matrix[i].r,&img.matrix[i].g,&img.matrix[i].b);
    }
  } else {

      int i = 0;

      while(i != 2)
      {
        img.mnumber[i] = getc(f);
        //printf("%c\n",(char) img.mnumber[i]);
        i++;
      }
      
        int c = getc(f);
        while (c == '#') {
          while (getc(f) != '\n') {
            getc(f);
          }
          c = getc(f);
        }
        ungetc(c, f);

      
      fscanf(f,"%d %d",&img.cols,&img.rows);
     
      fscanf(f,"%d",&img.maxcolor);

      img.matrix = (pixel*)malloc(img.cols * img.rows* sizeof(pixel));


      for(int i = 0; i < img.cols*img.rows;i++)
      {
        fscanf(f,"%u %u %u",&img.matrix[i].r,&img.matrix[i].g,&img.matrix[i].b);
      }

      fclose(f);
      return img;
    }
    return img;
}

Upvotes: 1

Views: 1044

Answers (1)

tshiono
tshiono

Reputation: 22022

I would write it as something like:

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

typedef struct pixel {
    unsigned int r,g,b;
} pixel;

typedef struct imagem{
    int cols, rows;
    char mnumber[2];
    int maxcolor;
    pixel *matrix;
} imagem;

static imagem read(const char *filename)
{
    imagem img;
    FILE *f;
    int i;
    char buf[BUFSIZ];

    if (filename == NULL) {
        f = stdin;
    } else {
        f = fopen(filename, "r");
        if (f == NULL) {
            fprintf(stderr, "Can't open %s\n", filename);
            exit(1);
        }
    }

    if (fgets(buf, sizeof buf, f) == NULL) {
        fprintf(stderr, "Can't read data\n");
        exit(1);
    }

    for (i = 0; i < 2; i++) {
        img.mnumber[i] = buf[i];
    }

    if (fgets(buf, sizeof buf, f) == NULL) {
        fprintf(stderr, "Can't read data\n");
        exit(1);
    }
    sscanf(buf, "%d %d", &img.cols, &img.rows);
    if (fgets(buf, sizeof buf, f) == NULL) {
        fprintf(stderr, "Can't read data\n");
        exit(1);
    }
    sscanf(buf, "%d", &img.maxcolor);

    img.matrix = malloc(img.cols * img.rows * sizeof(pixel));
    if (img.matrix == NULL) {
        fprintf(stderr, "malloc failed\n");
        exit(1);
    }

    i = 0;
    while (fgets(buf, sizeof buf, f) != NULL) {
        if (buf[0] == '#') continue;
        else {
            sscanf(buf, "%u %u %u", &img.matrix[i].r, &img.matrix[i].g, &img.matrix[i].b);
            i++;
        }
    }    fclose(f);
    return img;
}
  • As suggested, it is better to use the combination of fgets() and sscanf() rather than fscanf() especially when we need to handle irregular lines.
  • You do not have to write a specific code to read from stdin when the filename is omitted. Just assign the file pointer f to stdin.
  • It is recommended to change the function name read() to something else as it conflicts with the existing system call function read(2).

Upvotes: 1

Related Questions