Thread: Help parsing ppm in C

  1. #1
    Registered User
    Join Date
    Nov 2014
    Posts
    18

    Help parsing ppm in C

    Im new to the site so sorry if i posted this wrong. But can someone take a look at my parse file and tell me what I am doing wrong? The image will not open in vim and when i try to open the file I manipulated in vim , Im getting back that my height is invalid as well as my RGB component. I think it has something to do with my getc commands because i am not familiar with it but I am not too sure. Help ASAP would be much appreciated thank you!Help parsing ppm in C-screen-shot-2014-11-30-5-18-11-pm-jpg
    Help parsing ppm in C-screen-shot-2014-11-30-5-19-10-pm-jpg

  2. #2
    Registered User MartinR's Avatar
    Join Date
    Dec 2013
    Posts
    200
    Definitely post the code not the screenshot of the code, I can barely see something.

  3. #3
    Registered User
    Join Date
    Nov 2014
    Posts
    18
    Code:
    int  main (int argc, char *argv[])  {
    
    
            // declarations here
    
    
    
    
            // open input file 
            FILE *input;
            input= fopen(argv[2],"r");
    
    
    
    
    
    
            // parseHeader function call here
            parseHeader(input);
    
    
            // malloc space for the array (example given in assignment write-up)
            struct pixel *image =
             (struct pixel *) malloc(sizeof(struct pixel) * g_width * g_height);
    
    
            // parseImage function call here
            parseImage( input , image );
    
    
    
    
            // close input file 
            fclose(input);
    
    
    
    
            // manipulate the image according to command-line parameter
            //              1: mirror image
            //              2: upside down image
            //              3: rotate to the right 90 degrees
    
    
    switch (*argv[1])
    {
            case '1':
                    mirror(image);
                    break;
            case '2':
                    flip(image);
                    break;
            case '3':
                    rotate(image);
                    break;
            default:
                    printf("Invalid argument\n");
                    break;
    }
    
    
    
    
    
    
            return 0;
    }
    Code:
    void parseHeader(FILE *input)
    {
    char c;
    int rgbComp=255;
    
    
    //check for comments
    c= getc(input);
    while (c=='#')
    {
            while (getc(input) != '\n')
            c= getc(input);
    }
    
    
    ungetc(c,input);
    
    
    //get height and width
    if(fscanf(input,"P6 %d %d",&g_width,&g_height) != 2)
    {
    printf("Cannot read image size\n");
    }
    
    
    //get rgb component
    if(fscanf(input,"%d\n",&rgbComp) !=1)
    {
    printf("Cannot read RGB\n");
    
    
    }
    
    
    //read depth
    if(rgbComp != RGB_COMPONENT_COLOR)
    {
    printf("Invalid RGB\n");;
    }
    
    
    }
    
    
    
    
    void parseImage(FILE *input, struct pixel *theArray)
    {
    int i;
    int numPixels;
    
    
    numPixels=g_height*g_width;
    //read in pixel data
    for (i=0;i<numPixels;i++)
    {
       fscanf(input,"%c%c%c",&theArray[i].r,&theArray[i].g,&theArray[i].b);
    }
    }

  4. #4
    Registered User
    Join Date
    Nov 2014
    Posts
    18
    Anybody? I just need help figuring out why the image wont load. Am i using my getc functions right?

  5. #5
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,662
    > if(fscanf(input,"%d\n",&rgbComp) !=1)
    The trailing \n doesn't consume a single newline.
    It will eat any whitespace after newline as well, possibly part of your image data.

    FWIW, you're better off reading each header line with fgets() than fscanf()

    > fscanf(input,"%c%c%c",&theArray[i].r,&theArray[i].g,&theArray[i].b);
    Read each character using fgetc().
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  6. #6
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by Salem View Post
    FWIW, you're better off reading each header line with fgets() than fscanf()
    Actually, the PPM format is a bit funny; it allows comments in the header part, prior to the maxval component. For example, GIMP adds one (just prior to width). You really just need getc() and a helper function I call getnni():
    Code:
    #include <stdio.h>
    #include <errno.h>
    
    /** getnni() - Parse a nonnegative integer
     * @input - Input stream
     *
     * This function skips all leading whitespace,
     * including comments (to the end of the line) following a #,
     * then parses a nonnegative decimal integer.
     * The character following the number is left in the stream
     * (via ungetc()).
     *
     * The function returns the nonnegative number,
     * or -1 with errno set if an error occurs:
     *     ERANGE   The value is too large to fit in an integer
     *     ENOENT   Premature end of file
     *     EDOM     Not a nonnegative integer
     *
     * Use this code in any way you wish at your own risk,
     * just don't blame the author if it doesn't work.
    */
    static int getnni(FILE *const input)
    {
        int c;
    
        c = getc(input);
    
        while (c == '\t' || c == '\n' || c == '\v' ||
               c == '\f' || c == '\r' || c == ' ' ||
               c == '#')
            if (c == '#') {
                while (c != EOF && c != '\n' && c != '\r')
                    c = getc(input);
            } else
                c = getc(input);
    
        if (c >= '0' && c <= '9') {
            int v = 0;
    
            while (c >= '0' && c <= '9') {
                const int o = v;
                v = 10 * v + c - '0';
                if ((int)(v / 10) != o || v < 0)
                    return errno = ERANGE;
                c = getc(input);
            }
    
            if (c != EOF)
                ungetc(c, input);
    
            return v;
        }
    
        if (c == EOF)
            errno = ENOENT;
        else
            errno = EDOM;
    
        return -1;
    }
    Using the above, you can read and verify a PPM image header thus:
    Code:
        FILE *input;
        int c, width, height, maxval;
    
        /* Assuming input has been opened via
         *   input = fopen("filename", "rb");
         * or similar, and that you've checked that
         *   input != NULL
        */
    
        c = getc(input);
        if (c != 'P')
            NOT_A_PPM_IMAGE();
    
        c = getc(input);
        if (c != '6')
            NOT_A_BINARY_PPM_IMAGE();
    
        width = getnni(input);
        if (width <= 0)
            INVALID_PPM_WIDTH();
    
        height = getnni(input);
        if (height <= 0)
            INVALID_PPM_HEIGHT();
    
        maxval = getnni(input);
        if (maxval <= 0 || maxval >= 65536)
            INVALID_PPM_MAXVAL();
    If maxval >= 256, then each pixel is composed of a sextet of characters: RH RL GH GL BH BL, with red = RH×256 + RL, green = GH×256 + GL, and blue = BH×256 + BL, and 0 <= red, green, blue <= maxval.

    If maxval <= 255, then each pixel is composed of a triplet of characters: R G B, with 0 <= R, G, B <= maxval.

    In both cases, you can use fgetc() or getc() to read the characters. There is no padding, just width×height triplets/sextets of raw characters. (That is, the data is not even a string, just raw character data.)

    (PGM format is very similar; instead of red-green-blue triplets, you have just one character (maxval <= 255; a pair of characters if maxval >= 256) indicating gray levels: 0 is black, and maxval white. The magic value is P5, i.e. '5' instead of '6' above.)

    Although PPM (and PGM) is not compressed, it is one of the rare lossless formats that does make it easy to access/generate high dynamic range images (up to 65535:1 or 65536:1, depending on how you define the ratio). Although it's perfectly okay to only support maxval <= 255 PPM images for student work, if you get interested in image filters and so on, using the extra bits and chaining your filter programs makes for really powerful imaging tools. So, please don't just assume the six-character color format is never used.

  7. #7
    Registered User
    Join Date
    Sep 2014
    Posts
    364
    This is my attempt to read the header of an PBM/PGM/PPM.
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <string.h>
    
    #define MAX_LEN 1024
    #define PPM_DELIM "\r\n\t "
    
    typedef struct ppm_t {
        uint8_t magic;
        uint16_t width;
        uint16_t height;
        uint16_t brightness;
        void *data;
    } ppm;
    
    int parseHeader(ppm *picture, FILE *input) {
    
        char line[MAX_LEN] = {0}, *char_tmp;
        int ppm_magic = -1, ppm_width = -1, ppm_height = -1, ppm_brightness = -1, ppm_header = 1;
    
        while(ppm_header) {
    
            fgets(line, MAX_LEN, input);
    
    //check for comments
            if (line[0] == '#') continue;
    
            char_tmp = strtok(line, PPM_DELIM);
            while (char_tmp) {
    
    // get PPM format
                if(ppm_magic < 0) {
                    if(char_tmp[0] == 'P') {
                        if ((sscanf(++char_tmp, "%d", &ppm_magic)) != 1) {
                            fprintf(stderr, "could not read magic number. exit.\n");
                            return -1;
                        }
                        else if(ppm_magic < 1 || ppm_magic > 6) {
                            fprintf(stderr, "invalid magic number. exit.\n");
                            return -1;
                        }
                    }
                    else {
                        fprintf(stderr, "no magic number found. is it really a PPM? exit.\n");
                        return -1;
                    }
                }
    
                else {
    // get width
                    if (ppm_width < 0) {
                        if ((sscanf(char_tmp, "%d", &ppm_width)) != 1) {
                            fprintf(stderr, "could not read width. exit.\n");
                            return -1;
                        }
                    }
    
    // get hight
                    else if (ppm_height < 0) {
                        if ((sscanf(char_tmp, "%d", &ppm_height)) != 1) {
                            fprintf(stderr, "could not read height. exit.\n");
                            return -1;
                        }
                    }
    
    // get brightness
                    else if (ppm_brightness < 0) {
                        if ((sscanf(char_tmp, "%d", &ppm_brightness)) != 1) {
                            fprintf(stderr, "could not read brightness. exit.\n");
                            return -1;
                        }
                    }
    
                }
                char_tmp = strtok(NULL, PPM_DELIM);
            }
            if (ppm_magic > 0 && ppm_width > 0 && ppm_height > 0 && ppm_brightness > 0) ppm_header = 0;
        }
    
        picture->magic = ppm_magic;
        picture->width = ppm_width;
        picture->height = ppm_height;
        picture->brightness = ppm_brightness;
        picture->data = NULL;
    
        return 0;
    }
    
    int main (int argc, char *argv[]) {
    
        FILE *input = NULL;
        ppm my_picture;
    
        if (argc < 2) {
            fprintf(stderr, "too few arguments.\n");
            fprintf(stderr, "usage: %s <filename>\n", argv[0]);
            return EXIT_FAILURE;
        }
    
        if ((input = fopen(argv[1], "r")) == NULL) {
            fprintf(stderr, "could not open file '%s'.\n", argv[1]);
            return EXIT_FAILURE;
        }
    
        if (parseHeader(&my_picture, input) < 0) {
            fprintf(stderr, "something went wrong.\n");
            return EXIT_FAILURE;
        }
    
        printf("--- picture data ---\n");
        printf("Format    : %d\n", my_picture.magic);
        printf("Width     : %d\n", my_picture.width);
        printf("Height    : %d\n", my_picture.height);
        printf("Brightness: %d\n", my_picture.brightness);
    
    […]
    You can see i only use 'fgets()' and 'sscanf()'. Not playing around with 'getc()' and 'ungetc()'.
    Last edited by WoodSTokk; 12-02-2014 at 01:18 AM.

  8. #8
    Registered User
    Join Date
    Sep 2014
    Posts
    364
    Ooop, an Bitmap have no brightness.
    BugFix @ line 74:
    Code:
    // bitmap have no brightness
                               if (ppm_magic == 1 || ppm_magic == 4) ppm_brightness = 1;

  9. #9
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by WoodSTokk View Post
    Code:
    #define MAX_LEN 1024
    #define PPM_DELIM "\r\n\t "
    Well, actually, there is no length limit on the comments, nor do the comments need to start at the beginning of the line. That's why I said the format is slightly funny.

    Your code should be able to read typical ppm headers -- which typically have the comment immediately after the P6 format specifier --, but the PPM spec is quite a bit more relaxed.

    The component maximum is also not really brightness, just a maximum component value. That is, r=255,g=255,b=255 with maxval=255 is just as bright white as r=40,g=40,b=40 is with maxval=40.

    Aside from those nitpicks, there's not much to comment; your code should work fine in real life.

    Quote Originally Posted by WoodSTokk View Post
    You can see i only use 'fgets()' and 'sscanf()'. Not playing around with 'getc()' and 'ungetc()'.
    You might think using getc() and ungetc() is "playing around", but they're perfectly standard and reliable functions, and in this case, the getnni() function actually performs the correct nonnegative integer parsing and whitespace/comment handling used in PPM, PGM, and PGM formats, without adding any arbitrary restrictions like line length limits.

    The funny thing here is not the way getnni() is implemented, but the fact that correctly parsing PPM/PGM/PBM files, without arbitrary restrictions, kinda sorta requires a function much like it.

  10. #10
    Registered User
    Join Date
    Sep 2014
    Posts
    364
    Quote Originally Posted by Nominal Animal View Post
    Well, actually, there is no length limit on the comments, nor do the comments need to start at the beginning of the line. That's why I said the format is slightly funny.

    Your code should be able to read typical ppm headers -- which typically have the comment immediately after the P6 format specifier --, but the PPM spec is quite a bit more relaxed.
    Oh, i have interpret the spec that a comment can only be on a line without other thinks.
    But this is simple to fix @ line 31:
    Code:
    // comment fix
                if(char_tmp[0] == '#') break;
    Quote Originally Posted by Nominal Animal View Post
    The component maximum is also not really brightness, just a maximum component value. That is, r=255,g=255,b=255 with maxval=255 is just as bright white as r=40,g=40,b=40 is with maxval=40.
    I know, it should be named 'max_brightness' or 'max_value', but it works.
    Quote Originally Posted by Nominal Animal View Post
    Aside from those nitpicks, there's not much to comment; your code should work fine in real life.
    Thanks

    This code is not perfect, but present idears to bgibs24 to show there are other ways to read the header.
    You are right, the line length can be a problem.
    Other have classes, we are class

  11. #11
    Registered User
    Join Date
    Nov 2014
    Posts
    18
    thank you guys so much this all helped a lot

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. XML parsing with C++
    By jachstws7 in forum C++ Programming
    Replies: 9
    Last Post: 06-23-2012, 12:22 PM
  2. xml parsing in c
    By er.rohan88 in forum C Programming
    Replies: 5
    Last Post: 01-21-2010, 06:39 AM
  3. parsing in c++
    By svaidya in forum C++ Programming
    Replies: 1
    Last Post: 06-02-2009, 12:26 PM
  4. String parsing(parsing comments out of HTML file)
    By slcjoey in forum C# Programming
    Replies: 0
    Last Post: 07-29-2006, 08:28 PM
  5. Help parsing
    By caduardo21 in forum C Programming
    Replies: 7
    Last Post: 04-03-2005, 12:32 AM