Reading PPM format images
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 5 of 5

Thread: Reading PPM format images

  1. #1
    Join Date
    Jan 2017
    Posts
    17

    Reading PPM format images

    I’m trying to read this PPM format image, which contains an ASCII header followed by binary pixel data. The header consists of something like this:

    P6
    650 652
    255

    P6 indicates that it is a PPM image. The next two fields are the width and height of the image. The last field gives the maximum pixel value. At the end of the header is a \n and then the binary pixel data. The image is in color so there are three bytes (red, green, blue). The goal of my readPPM function is to return the pixel data in a one-dimensional array of unsigned chars, plus the width and height of the image. The goal of my writePPM function (I haven't done anything for that function yet) is to write the PPM format image to an empty file from the given information returned from my readPPM function. I'm still not sure how to make this program work so I don't store the 650 in width and 652 in height. I'll worry about that once I can actually read and write the text files.


    main.cc
    Code:
      int main() {
    
    	//char fileName[50] = "binary_pixels.txt";
    	char fileName[50] = "test.ppm";
    	char pSix[10];		// indicates this is a PPM image
    	int width = 0;		// width of the image
    	int height = 0;		// heigt of the image
    	int maximum = 0;	// maximum pixel value
    	int size = 128;		// size of the array
    
    	// read the PPM file and store its contents inside an array and return the pointer to that array to pixelArray
    	unsigned char* pixelArray = readPPM(fileName, pSix, &width, &height, &maximum);

    readWritePPM.cc
    Code:
        unsigned char* readPPM(const char* fileName, char* pSix, int* width, int* height, int* maximum) {
    
    	// open the file to read just the header reading
    	FILE* fr = fopen(fileName, "r");
    
    	// formatted read of header
    	fscanf(fr, "%s", pSix);
    
    	// check to see if it's a PPM image file
    	if (strncmp(pSix, "P6" , 10) != 0) {
    		printf("They are not the same\n");
    	} else {
    		printf("They are the same\n");
    	}
    
    	// read the rest of header
    	fscanf(fr, "%d\n %d\n", width, height);
    
        fscanf(fr, "%d\n", maximum);
    
        // check to see if they were stored properly
        printf("PSix: %s\n", pSix);
        printf("Width: %d\n", *width);
        printf("Height: %d\n", *height);
        printf("maximum: %d\n", *maximum);
    
        //int size = width * height;
        int size = 423800;
    
        // allocate array for pixels
        unsigned char* pixels = new unsigned char[size];
    
    	// unformatted read of binary pixel data
    	while (fread(pixels, sizeof(int), 128, fr)) {
    		printf("%s", pixels);
    	} // end of for loop
    
    	// close file
    	fclose(fr);
    
    	// return the array
    	return pixels;
    	
    } // end of readPPM
    Last edited by 2kaud; February 18th, 2017 at 03:00 AM.

  2. #2
    2kaud's Avatar
    2kaud is online now Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    5,789

    Re: Reading PPM format images

    Code:
        while (fscanf(fr, "%c", &pixels) != EOF) {
    An issue is here. pixels is a pointer to type of unsigned char* - pointer to unsigned char. The fscanf() will read one char from the stream and put it into the memory location pointed to by the last parameter. In this case the address of a pointer to the pointer to memory of unsigned char - which is not what is wanted. Also the memory location to which the char is read isn't changed upon reading chars - so the same location is repeatedly overwritten. Consider (not tried)

    Code:
    unsigned char* readPPM(const char* fileName, int* width, int* height) {
        unsigned char *pixels = new unsigned char[128];
    
        FILE* fr = fopen(fileName, "r");
    
        for (unsigned char *ptr = pixels; fscanf(fr, "%c", ptr) != EOF; ++ptr)
            printf("%c", *ptr);
    
        fclose(fr);
        return pixels;
    }
    Also note that there is no checking to make sure that the file opened OK before being used.

    Code:
        unsigned char* pixelArray[size] = readPPM(fileName, &width, &height);
    See previous thread. This should be
    Code:
        unsigned char* pixelArray = readPPM(fileName, &width, &height);
    Code:
        printf("%s\n", pixelArray[i]);
    Shouldn't this be
    Code:
        printf("%c\n", pixelArray[i]);
    as the elements of pixelArray are of type unsigned char?
    Last edited by 2kaud; February 17th, 2017 at 04:07 AM.
    All advice is offered in good faith only. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/

    C++17 Compiler: Microsoft VS2017 (15.4.1)

  3. #3
    Join Date
    Jan 2017
    Posts
    17

    Re: Reading PPM format images

    I've successfully stored the header (P6, 650, 652, and 255)) in their right variables (I know because I tried printing their values and they came out right).
    My output is shown below.

    Output:
    PSix: P6

    Width: 650

    Height: 652

    maximum: 255

    I was wondering what those nonsense characters below the maximum were. Are those the binary pixel data? If so, then I just need to write my writePPM function. I also updated my codes above if you're curious.
    Last edited by 2kaud; February 18th, 2017 at 03:03 AM.

  4. #4
    2kaud's Avatar
    2kaud is online now Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    5,789

    Re: Reading PPM format images

    [Please don't retrospectively amend posts. It alters the flow of the threads. If code has been altered, then just post the revised code in a new post.

    Also, the 'nonsense' characters posted were interfering with the operation of the edit boxes so I've removed them.]

    Cheers!

    These characters are part of the non-printable ASCII characters that form the binary pixel data.
    Last edited by 2kaud; February 18th, 2017 at 03:08 AM.
    All advice is offered in good faith only. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/

    C++17 Compiler: Microsoft VS2017 (15.4.1)

  5. #5
    2kaud's Avatar
    2kaud is online now Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    5,789

    Re: Reading PPM format images

    A couple of points re the revised code.
    Code:
        //int size = width * height;
        int size = 423800;
    The size for the binary data for a P6 format file is width * height * 3 - as for each pixel there is 1 byte for each of RGB colours. So
    Code:
        int size = width * height * 3;
    To then read the binary data it's much easier to just use
    Code:
        if (fread(pixels, 1, size, fr) != size)
           puts("Error reading binary data");
    Code:
       fscanf(fr, "%s", pSix);
    This really is a big no-no! This will read an undermined number of characters from the file into memory starting at location pSix and continue until a white-space character is found. In a correct format file, this should read 2 chars, but in an incorrect format file could read any number. For the correct way is to specify the maximum number of chars to be read consider
    Code:
        fscanf(fr, "%5s", pSix);
    This will read no more than 5 chars into the memory pointed to by pSix.

    Why are you passing pSiz to readPPM()? As its only used within readPPM(), why not just define it within that function? You are also not freeing the allocated memory, neither are you checking for errors.

    Consider c code
    Code:
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    unsigned char* readPPM(const char* const fileName, unsigned int* const width, unsigned int* const height, unsigned int* const maximum)
    {
    	const char p6[] = "P6";
    	char header[10];
    	unsigned char *pixels = NULL;
    
    	*width = *height = *maximum = 0;
    
    	FILE* fr = fopen(fileName, "rb");
    	if (fr == NULL) {
    		printf("Unable to open file %s\n", fileName);
    		return NULL;
    	}
    
    	fscanf(fr, "%9s", header);
    	if (strcmp(header, p6))
    		printf("Incorrect header %s found\n", header);
    	else
    		if (fscanf(fr, "%d %d\n%d\n", width, height, maximum) != 3)
    			puts("Invalid header values");
    		else {
    			const unsigned int size = *width * *height * 3;
    
    			if ((pixels = malloc(size)) == NULL)
    				puts("Error allocating memory");
    			else {
    				unsigned int read = 0;
    
    				if ((read = fread(pixels, 1, size, fr)) != size) {
    					printf("Error reading binary data. Wanted %i, got %i\n", size, read);
    					free(pixels);
    					pixels = NULL;
    				}
    			}
    		}
    
    	fclose(fr);
    	return pixels;
    }
    
    int main()
    {
    	//char fileName[50] = "binary_pixels.txt";
    	char fileName[500] = "c:\\Program Files\\Git\\mingw64\\lib\\tk8.6\\demos\\images\\teapot.ppm";
    	unsigned int width = 0;
    	unsigned int height = 0;
    	unsigned int maximum = 0;
    
    	unsigned char* pixelArray = readPPM(fileName, &width, &height, &maximum);
    
    	if (pixelArray) {
    		printf("Width: %d\n", width);
    		printf("Height: %d\n", height);
    		printf("maximum: %d\n", maximum);
    		free(pixelArray);
    	}
    }
    Last edited by 2kaud; February 18th, 2017 at 12:58 PM.
    All advice is offered in good faith only. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/

    C++17 Compiler: Microsoft VS2017 (15.4.1)

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width

This a Codeguru.com survey!


On-Demand Webinars (sponsored)