You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++读取二进制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);
}

完整原测试代码:

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;
}
问题分析

导致图像失真错位的核心问题有三个:

  • 比特位读取顺序错误:P4格式中,每个字节的比特位是从**最高位(第7位)到最低位(第0位)**依次对应图像的像素,原代码从第0位开始读取,导致每个字节内的像素顺序完全颠倒。
  • 未处理行填充比特:P4要求每行的字节数向上取整为8的倍数,例如宽度为10的行,实际会用2个字节(16比特)存储,末尾6个比特是无效填充位,原代码把这些填充位也当作有效像素读取,导致后续行的像素位置整体偏移。
  • 二进制文件打开模式错误:原代码用默认文本模式打开文件,在Windows系统下会自动转换换行符(将\r\n转为\n),破坏二进制数据的完整性,必须用std::ios::binary模式打开。
修正后的完整代码
#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
#include <string>

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::ios::binary);
    std::ofstream output_file(output);

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

    std::string line_one, line_two;
    char magicNumber;
    std::string width_str, height_str;
    int width, height;

    // 读取魔数,跳过注释行
    while(getline(input_file, line_one)) {
        if(line_one.size() > 0 && line_one[0] != '#') {
            magicNumber = line_one[1];
            break;
        }
    }

    // 读取宽高,跳过注释行
    while(getline(input_file, line_two)) {
        if(line_two.size() > 0 && line_two[0] != '#') {
            std::stringstream ss(line_two);
            ss >> width_str >> height_str;
            width = std::stoi(width_str);
            height = std::stoi(height_str);
            break;
        }
    }

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

    if(magicNumber == '4') {
        // 计算每行实际需要的字节数(向上取整到8的倍数)
        int bytes_per_row = (width + 7) / 8;
        char c;

        for(int row = 0; row < height; ++row) {
            std::vector<pixel> current_row;
            for(int byte_idx = 0; byte_idx < bytes_per_row; ++byte_idx) {
                input_file.get(c);
                // 从最高位(第7位)到最低位(第0位)读取比特
                for(int bit_idx = 7; bit_idx >= 0; --bit_idx) {
                    if(current_row.size() >= width) {
                        break; // 跳过当前行的填充比特
                    }
                    pixel p;
                    p.r = p.g = p.b = (c & (1 << bit_idx)) != 0;
                    current_row.push_back(p);
                }
            }
            image.push_back(current_row);
        }
    }

    // 输出为P1格式的文本PBM
    output_file << "P1" << std::endl << width << " " << height << std::endl;
    for(const auto& row : image) {
        for(size_t j = 0; j < row.size(); ++j) {
            output_file << row[j].r;
            if(j != row.size() - 1) {
                output_file << " ";
            }
        }
        output_file << std::endl;
    }

    return 0;
}

内容的提问来源于stack exchange,提问作者Kleber Mota

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 01:31:03