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

BMP文件像素读取异常求助:疑为行填充或fseek问题

BMP像素读取失败问题排查

问题概述

读取BMP文件像素时出现错误,怀疑行尾填充或fseek()函数导致故障。以下是核心代码、头文件定义及测试情况:

核心代码

bmp.c

struct pixel* read_data(FILE* stream, const struct bmp_header* header){
    if(stream == NULL || header == NULL) return NULL;
    
    fseek(stream, 0, SEEK_SET);

    size_t row_size = (header->width * sizeof(struct pixel)) + strlen(PADDING_CHAR);
    size_t total_size = row_size * header->height;

    struct pixel* data = (struct pixel*)malloc(total_size);
    if(data == NULL){
        printf("cant allocate");
        return NULL;
    }
    
    size_t pixels_read = fread(data, sizeof(struct pixel), 1, stream);
    
    if(pixels_read == 0){
        free(data);
        return NULL;
    }

    return data;
}

bmp.h

#define PADDING_CHAR "\0"

struct pixel {
    uint8_t blue;
    uint8_t green;
    uint8_t red;
    //uint8_t alpha;
} __attribute__((__packed__));

/**
 * Read the pixels
 *
 * Reads the data (pixels) from stream representing the image. If the stream
 * is not open or header is not provided, returns `NULL`.
 *
 * @param stream opened stream, where the image data are located
 * @param header the BMP header structure
 * @return the pixels of the image or `NULL` if stream or header are broken
 */
struct pixel* read_data(FILE* stream, const struct bmp_header* header);

BMP头定义(24位颜色)

struct bmp_header{
    uint16_t type;              // "BM" (0x42, 0x4D)
    uint32_t size;              // file size
    uint16_t reserved1;         // not used (0)
    uint16_t reserved2;         // not used (0)
    uint32_t offset;            // offset to image data (54B)
    uint32_t dib_size;          // DIB header size (40B)
    uint32_t width;             // width in pixels
    uint32_t height;            // height in pixels
    uint16_t planes;            // 1
    uint16_t bpp;               // bits per pixel (1/4/8/24)
    uint32_t compression;       // compression type (0/1/2) 0
    uint32_t image_size;        // size of picture in bytes, 0
    uint32_t x_ppm;             // X Pixels per meter (0)
    uint32_t y_ppm;             // X Pixels per meter (0)
    uint32_t num_colors;        // number of colors (0)
    uint32_t important_colors;  // important colors (0)
} __attribute__((__packed__));

测试情况

给定有效BMP流、正确文件位置及有效头,调用函数未成功读取数据:

  • 测试1:

    FILE* stream = "Qk1WAAAAAAAAADYAAAAoAAAAAgAAAAQAAAABABgAAAAAACAAAAAjLgAAIy4AAAAAAAAAAAAA////AAAAAAD/AAAA/wAAAAD/AAAA/wAAAAAA/wAAAAA="; // base64编码流
    struct bmp_header* header = read_bmp_header(stream);
    fseek(stream, offset, SEEK_SET);
    断言'read_data(stream, header) == "////AAAA/wAAAP8AAP8AAAD/AAAA/wAA"'失败。[实际结果: "Qk1WG21VAAAAAAAAAAAAAAAAAAAAAAAA"]
    
  • 测试2:

    FILE* stream = "Qk1OAAAAAAAAADYAAAAoAAAAAgAAAAMAAAABABgAAAAAABgAAAAjLgAAIy4AAAAAAAAAAAAAAAAA/wD/AAD/AAAA/wAAAP////8A/wAA"; // base64编码流
    struct bmp_header* header = read_bmp_header(stream);
    fseek(stream, offset, SEEK_SET);
    断言'read_data(stream, header) == "AAAA/wD//wAAAP8A/////wD/"'失败。[实际结果: "Qk1OG21VAAAAAAAAAAAAAAAA"]
    

注:断言"=="后为预期结果,括号内为代码实际输出结果,数据以Base64格式传输以便查看。

问题分析与修复方案

代码存在3个关键错误,直接导致读取失败:

  1. 错误重置文件指针
    调用方已通过fseek(stream, offset, SEEK_SET)定位到图像数据起始位置,但read_data里的fseek(stream, 0, SEEK_SET)将指针重置到文件开头,导致读取的是BMP文件头内容而非像素数据——这就是实际结果出现Qk1W/Qk1O(对应BMP标识BM的后续数据)的原因。

  2. 行尾填充计算错误
    BMP要求每行字节数对齐到4的倍数,正确计算方式应为row_size = (header->width * sizeof(struct pixel) + 3) & ~3。用strlen(PADDING_CHAR)计算填充长度完全错误:"\0"的strlen返回0,根本未处理填充,且不符合BMP的填充规则。

  3. fread参数错误
    fread(data, sizeof(struct pixel), 1, stream)仅读取1个像素结构体,而非全部需要的像素数据。正确调用应按总字节数读取所有图像数据。

修复后的代码示例

struct pixel* read_data(FILE* stream, const struct bmp_header* header){
    if(stream == NULL || header == NULL) return NULL;
    
    // 移除错误的指针重置,保留调用方设置的正确位置
    // fseek(stream, 0, SEEK_SET);

    // 正确计算对齐后的行字节数
    size_t pixel_bytes_per_row = header->width * sizeof(struct pixel);
    size_t row_size = (pixel_bytes_per_row + 3) & ~3; // 对齐到4字节
    size_t total_size = row_size * header->height;

    struct pixel* data = (struct pixel*)malloc(total_size);
    if(data == NULL){
        printf("cant allocate");
        return NULL;
    }
    
    // 按总字节数读取所有图像数据(含填充)
    size_t bytes_read = fread(data, 1, total_size, stream);
    
    if(bytes_read != total_size){
        free(data);
        return NULL;
    }

    // 可选:剥离行尾填充(根据后续处理需求)
    // struct pixel* stripped_data = malloc(header->width * header->height * sizeof(struct pixel));
    // if(stripped_data){
    //     for(int i=0; i<header->height; i++){
    //         memcpy(stripped_data + i*header->width, data + i*row_size, pixel_bytes_per_row);
    //     }
    //     free(data);
    //     return stripped_data;
    // }

    return data;
}

关键说明

  • 移除fseek(stream, 0, SEEK_SET),保留调用方设置的正确文件位置。
  • 用位运算实现行字节数的4字节对齐,符合BMP格式要求。
  • 调整fread参数,按总字节数读取全部图像数据;若后续不需要填充,可添加代码剥离填充部分。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 19:04:52