Search code examples
c++ppm

Issues reading ppm p6 image to completion. c++


int main(){
  std::fstream myfile; // file object is created
  myfile.open ("green.ppm");
  std::string line;   

  unsigned red,green,blue;  //to output values on 0 -255 scale.
  int width, height = 0;
  if (myfile.is_open())
   {
       std::getline (myfile,line);       //type of file, skip, it will always be for this code p6
       std::getline (myfile,line);       // width and height of the image
       std::stringstream  lineStream(line); //extract the width and height;
       lineStream >> width;
       lineStream >> height;
      //  std::cout<< width << " " << height <<" \n";
       getline (myfile,line);             //skip magic number
       getline (myfile,line);             // reach the matrix of numbers
          for (int  i = 0; i<(width*height*3) ; i= i+3){
         char num = line[i];  uint8_t number = num; red = number;
          num = line[i+1];   number = num; green = number;
          num = line[i+2];   number = num; blue = number;
         std::cout<<"pixel " << i/3 << " is " << red << " " << green << " " << blue << std::endl;
       }


//char to uint_8t to unsigned is a basic an inefficient way I found that takes the pixel rgb values in my ppm file and allows me to interpret them from a range of  0-255

       }

  // cout<<counter<<endl;
  myfile.close();
  return 0;
}

When I run this code over different ppm images it does actually extract the rgb values correctly however the issue is that it doesn't do it entirely. a basic 800 x 800 image has 640000 pixels and this code reads about 40800 and then ends as if it there weren't anymore.

I think this arises from a misunderstanding of the ppm format itself. I thought that beyond the header format which is the file with the type, width and size, and magic number there was only one more line and no more '\n' characters. Therefore the matrix could be read as a contiguous array of chars.

So why is this program stopping at such odd place?


Solution

  • I'm not good with C++ but I will try to explain what is wrong here.

    If the type of file is p6 then then the image data is stored in byte format, one byte per color component (r,g,b). From the comment in your code it seems that you always expect p6. This means that the matrix will storead as one continuous block of data like you assume. Other option is p3 which stores color components in ASCII format (example 0 0 0).

    The problem is that in p6 type file the data block is binary and you are treating it as string. Consider this, a pixel with r,g,b = (65, 13, 10) would be encoded in binary like this:

    0x41, 0xD, 0xA
    

    getline will read first byte which evaluates to ASCII character A but will stop reading after it because 0xD0xA is \r\n, a newline character (on Windows) which is a delimiter for getline.

    Instead of:

    getline (myfile,line);             // reach the matrix of numbers
    

    I would do something like:

    int bufsz = 3*width*height;
    char* pixelData = new char[bufsz];
    myfile.read(pixelData,bufsz);
    
    for ( int i = 0; i < bufsz; i+=3 ) {
      unsigned char red = pixelData[i];
      unsigned char green = pixelData[i+1];
      unsigned char blue = pixelData[i+2];
      // use unsigned char to express range [0,255],
      // this may make compiler to issue warnings
      // but it should be safe to do a cast 
    }
    
    // don't forget to release pixelData when done with it,
    // maybe use smart pointers
    delete[] pixelData;
    

    NOTE: The line before the matrix block contains max value for a color component, the PPM states that this can be larger than 255 so you need to be careful in these cases as the char array won't be good enough and you will need short array at least (2 bytes)

    Helpful links:

    1. Netpbm format
    2. basic_istream::read