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?
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: