Чтение бинарного файла PBM, заканчивающегося искаженным изображением

Я пытаюсь реализовать код C++ для чтения двоичного файла PBM. Прямо сейчас, после успешного чтения заголовка файла (магическое число, ширина и высота), я получил этот код для чтения каждого символа файла и извлечения отдельных битов из этого символа:

          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);
    }

При этом, когда я пытаюсь визуализировать изображение на экране, я получаю искаженное изображение, где некоторые пиксели как бы смещены от своего первоначального положения. У меня есть следующая программа, которая генерирует ascii-образ, который показывает, что я получил после чтения двоичного файла:

      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;
}

Изображение, которое я пытаюсь прочитать в своих тестах, — вот оно. Кто-нибудь может сказать мне, что здесь не так? Как я могу получить такое же изображение после чтения файла?

1 ответ

Я смог загрузить изображение:

Основная проблема, насколько я понимаю, заключается в том, что ваш код считывает каждый байт, начиная с младшего значащего бита, но пиксели сохраняются со старшего значащего бита вниз (что, по общему признанию, странно).

Вы можете, например, изменить(c & (1 << i)) != 0к(c & (1 << (i ^ 7))) != 0

Чтение дополнительных битов заполнения в конце строк, ширина которых не кратна 8, само по себе не должно быть проблемой, но когда вы «делинеаризируете» пиксели в 2D-сетке, вы должны позаботиться о том, чтобы ваш вектор содержал биты заполнения: это не ширина ваших строк, это ширина «полезной части» изображения.

Чтобы быть конкретным, хорошо иметь этот код:

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

Но тогда вы должны быть осторожны, чтобы использовать здесь ширину, округленную до числа, кратного 8:

      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)округляет до числа, кратного 8, есть и другие способы сделать это. Цикл, который выводит результат, должен использовать, а не округленныйw.

Подписанные символы или нет, не должны иметь никакого значения,cможет быть отрицательным, но биты, которые вы на самом деле читаете, не будут затронуты.

Другие вопросы по тегам