Home > Net >  Reading binary PBM file ending with distorted image
Reading binary PBM file ending with distorted image

Time:12-15

I am trying to implement a C code to read a binary PBM file. Right now, after read the header of the file successfully (magic number, width and height), I got this code to read each character of the file, and extract the individual bits from this character:

    std::vector<pixel> v;

    char c;
    while(input_file.get(c)) {
        for(int i=0; i<8; i  ) {
            pixel p;
            p.r = p.g = p.b = (c & (1 << i)) != 0;
            v.push_back(p);
        }
    }

After that, I transfer the data of this vector to a matrix:

    int h = stoi(height), w = stoi(width);

    int counter = 0;
    for(int i=0; i<h; i  ) {
        std::vector<pixel> row;
        for(int j=0; j<w; j  ) row.push_back(v[counter  ]);
        image.push_back(row);
    }

With that, when I try visualize the image on the screen, I got a distorted image, where some pixels seems to be dislocated from its original position. I got the following program that generate a ascii image that shows what I got after read the binary file:

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

int main(int argc, char *argv[])
{
    if(argc < 3) return 1;

    std::string input = argv[1];
    std::string output = argv[2];

    std::ifstream input_file(input);
    std::ofstream output_file(output);

    std::vector<std::vector<pixel>> image;
    std::vector<pixel> v;

    std::string line_one, line_two, line_pixels;

    char magicNumber;
    std::string width, height;

    while(getline(input_file, line_one)) {
        if(line_one.size() > 0 && line_one.at(0) != '#') {
            magicNumber = line_one.at(1);
            break;
        }
    }

    while(getline(input_file, line_two)) {
        if(line_two.size() > 0 && line_two.at(0) != '#') {
            std::stringstream ss(line_two);
            getline(ss, width, ' ');
            getline(ss, height, ' ');
            break;
        }
    }

    std::cout << magicNumber << std::endl;
    std::cout << width << " " << height << std::endl;

    if(magicNumber == '4') {
        std::vector<pixel> v;

        char c;
        while(input_file.get(c)) {
            for(int i=0; i<8; i  ) {
                pixel p;
                p.r = p.g = p.b = (c & (1 << i)) != 0;
                v.push_back(p);
            }
        }

        int h = stoi(height), w = stoi(width);

        int counter = 0;
        for(int i=0; i<h; i  ) {
            std::vector<pixel> row;
            for(int j=0; j<w; j  ) row.push_back(v[counter  ]);
            image.push_back(row);
        }
    }

    output_file << "P1" << std::endl << width << " " << height << std::endl;
    for(int i=0; i<stoi(height); i  ) {
        for(int j=0; j<stoi(width); j  ) {
            output_file << image[i][j].r << " ";
        }
        output_file << std::endl;
    }

    return 0;
}

The image I am trying to read in my tests is Decoded image

The main issue, as far as I see it, is that your code reads every byte from the least significant bit up, but the pixels are stored from the most significant bit down (which is admittedly strange).

You can for example change (c & (1 << i)) != 0 to (c & (1 << (i ^ 7))) != 0

Reading the extra padding bits at the end of rows that are not a multiple of 8 wide shouldn't be an issue by itself, but when you "de-linearize" the pixels into a 2D grid you must take care that your vector contains the padding bits: w is not the width of your rows, it's the width of the "useful part" of the image.

To be specific, it's fine to have this code:

for(int i=0; i<8; i  ) {
    pixel p;
    p.r = p.g = p.b = (c & (1 << (i ^ 7))) != 0;
    v.push_back(p);
}

But then you should be careful to use the width rounded up to a multiple of 8 here:

for(int i=0; i<h; i  ) {
    std::vector<pixel> row;
    for(int j=0; j < ((w   7) & -8); j  ) row.push_back(v[counter  ]);
    image.push_back(row);
}

((w 7) & -8) rounds up w to a multiple of 8, there are other ways to do that. The loop that outputs the result should use w though, and not the rounded-up w.

Chars being signed or not shouldn't make any difference, c may be negative but the bits that you're actually reading would not be affected.

  • Related