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

如何在C++中正确读取二进制(raw).pbm文件?代码问题排查

问题根源

你的代码完全误解了P4格式的二进制存储规则:P4格式是将8个像素打包到一个字节中存储,而非每个像素占用一个字节。当前你把每个像素当作单独字节读写,导致数据严重错位,才会出现黑色区域异常的情况。

读取函数的错误与修正

原错误点

  1. 未处理width/height后的空白字符:使用>>读取宽高后,文件指针停在宽高后的换行或空格处,直接读取二进制数据会把空白字符当成像素数据。
  2. 逐字节读取单个像素:违背P4的位打包规则,每个字节实际对应8个像素(不足8的行补填充位)。

修正后的读取函数

#include <algorithm>
#include <limits>

void readPBMFile(const std::string& filePath, std::vector<std::vector<int>>& pixels) {
    std::ifstream file(filePath, std::ios::binary);
    if (!file.is_open()) {
        throw std::runtime_error("Failed to open file.");
    }

    std::string header;
    std::getline(file, header);
    if (header != "P4") {
        throw std::runtime_error("File is not in PBM format.");
    }

    int width, height;
    file >> width >> height;
    // 跳过宽高后的所有空白字符(包括换行),定位到二进制数据起始位置
    file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    pixels.resize(height, std::vector<int>(width));
    const int bytesPerRow = (width + 7) / 8; // 每行需要读取的字节数,向上取整

    for (int y = 0; y < height; ++y) {
        for (int byteIdx = 0; byteIdx < bytesPerRow; ++byteIdx) {
            unsigned char byte;
            file.read(reinterpret_cast<char*>(&byte), 1);
            // 逐个提取字节中的位,从最高位到最低位对应像素从左到右
            for (int bitIdx = 0; bitIdx < 8; ++bitIdx) {
                const int x = byteIdx * 8 + bitIdx;
                if (x >= width) break; // 跳过行末尾的填充位
                // P4规范:0表示黑色,1表示白色;位值为1时对应像素1(白),0对应0(黑)
                const int pixelValue = (byte >> (7 - bitIdx)) & 1;
                pixels[y][x] = pixelValue;
            }
        }
    }
}

保存函数的错误与修正

原错误点

逐字节写入单个像素:每个像素占一个字节,完全不符合P4的打包规则,导致文件体积膨胀且格式错误。

修正后的保存函数

void savePBMFile(const std::string& filePath, const std::vector<std::vector<int>>& pixels) {
    if (pixels.empty() || pixels[0].empty()) {
        throw std::runtime_error("Empty pixel data.");
    }

    std::ofstream file(filePath, std::ios::binary);
    if (!file.is_open()) {
        throw std::runtime_error("Failed to create file.");
    }

    const int width = pixels[0].size();
    const int height = pixels.size();
    // 写入文本头,注意换行符使用'\n',避免系统自动转换(因为开了binary模式)
    file << "P4\n" << width << " " << height << "\n";

    const int bytesPerRow = (width + 7) / 8;
    for (const auto& row : pixels) {
        unsigned char byte = 0;
        for (int x = 0; x < width; ++x) {
            const int bitPos = 7 - (x % 8); // 当前像素对应字节的位位置(从高位开始)
            if (row[x] == 1) {
                byte |= (1 << bitPos); // 置对应位为1(白色)
            }
            // 每8个像素或行末尾时写入字节
            if ((x + 1) % 8 == 0 || x == width - 1) {
                file.write(reinterpret_cast<const char*>(&byte), 1);
                byte = 0; // 重置字节
            }
        }
    }
}

额外注意事项

  • PBM规范中,0代表黑色像素,1代表白色像素,位提取和打包时要严格对应这个规则。
  • 读写时必须跳过文本头后的所有空白字符,否则会把换行/空格当成二进制数据读取,导致整个像素矩阵错位。
  • 对于宽度不是8的倍数的行,末尾会填充0位,读取时要跳过这些填充位,避免越界写入pixels。

内容的提问来源于stack exchange,提问作者Alexander Jordanov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 18:43:11