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

BMP灰度图像读写代码故障:生成图像损坏无法打开

BMP读写代码修复方案

你的代码存在几个关键错误,导致生成的BMP文件损坏,以下是问题排查和修复:

核心问题分析

1. 读取图像数据时的字节数计算错误

read_bmp函数中,读取每行像素时使用了*width作为读取字节数,但实际上应该是每行像素的总字节数,即*width * (*bits_per_pixel / 8)。比如24位色图像,每个像素占3字节,每行需要读取width*3字节,而非width字节。原代码仅能处理8位单通道图像,其他位深会导致数据读取不完整或错位。

2. 写入函数中错误访问参数

write_bmp函数中使用了abs(*height),但height是直接传入的int32_t参数,不是指针,此处会导致非法内存访问,编译或运行时出错。应改为abs(height)。

3. BMP头信息的header_size设置错误

BMP的DIB头(BITMAPINFOHEADER)标准大小为40字节,原代码用sizeof(BMPHeader) - 14动态计算,若结构体定义变化会导致错误。应直接设置为40。

4. 行大小计算的一致性问题

read_bmp中的行大小计算未考虑像素字节数,导致内存分配和数据存储错误;write_bmp中虽正确计算了行大小,但读取时的错误会导致写入的数据本身已错乱。

修复后的完整代码

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#pragma pack(push, 1)
typedef struct {
    uint16_t signature;
    uint32_t file_size;
    uint32_t reserved;
    uint32_t data_offset;
    uint32_t header_size;
    int32_t width;
    int32_t height;
    uint16_t planes;
    uint16_t bits_per_pixel;
    uint32_t compression;
    uint32_t image_size;
    int32_t x_pixels_per_meter;
    int32_t y_pixels_per_meter;
    uint32_t total_colors;
    uint32_t important_colors;
} BMPHeader;
#pragma pack(pop)

void read_bmp(const char* filename, uint8_t** image_data, int32_t* width, int32_t* height, uint16_t* bits_per_pixel)
{
    FILE* file = fopen(filename, "rb");
    if (!file) {
        fprintf(stderr, "Failed to open the file\n");
        exit(1);
    }

    BMPHeader header;
    fread(&header, sizeof(BMPHeader), 1, file);

    if (header.signature != 0x4D42) {
        fprintf(stderr, "Invalid BMP file\n");
        exit(1);
    }

    *width = header.width;
    *height = header.height;
    *bits_per_pixel = header.bits_per_pixel;

    // 计算每行像素字节数和填充字节
    int32_t pixel_bytes_per_row = *width * (*bits_per_pixel / 8);
    int32_t row_padding = (4 - (pixel_bytes_per_row % 4)) % 4;
    int32_t row_size = pixel_bytes_per_row + row_padding;
    int32_t data_size = row_size * abs(*height);

    *image_data = (uint8_t*)malloc(data_size);
    if (!*image_data) {
        fprintf(stderr, "Memory allocation failed\n");
        exit(1);
    }

    fseek(file, header.data_offset, SEEK_SET);

    int32_t row;
    for (row = 0; row < abs(*height); ++row) {
        // 读取每行的像素数据
        fread(*image_data + (row * row_size), 1, pixel_bytes_per_row, file);
        // 跳过填充字节
        fseek(file, row_padding, SEEK_CUR);
    }

    fclose(file);
}

void write_bmp(const char* filename, const uint8_t* image_data, int32_t width, int32_t height, uint16_t bits_per_pixel)
{
    FILE* file = fopen(filename, "wb");
    if (!file) {
        fprintf(stderr, "Failed to open the file for write\n");
        exit(1);
    }

    int32_t pixel_bytes_per_row = width * (bits_per_pixel / 8);
    int32_t row_padding = (4 - (pixel_bytes_per_row % 4)) % 4;
    int32_t row_size = pixel_bytes_per_row + row_padding;
    int32_t data_size = row_size * abs(height);

    BMPHeader header;
    header.signature = 0x4D42;
    // 文件大小:头大小 + 数据大小(若有调色板需额外添加)
    header.file_size = sizeof(BMPHeader) + data_size;
    header.reserved = 0;
    // 数据偏移:头大小(若有调色板需加上调色板大小)
    header.data_offset = sizeof(BMPHeader);
    // DIB头标准大小为40字节
    header.header_size = 40;
    header.width = width;
    header.height = height;
    header.planes = 1;
    header.bits_per_pixel = bits_per_pixel;
    header.compression = 0;
    header.image_size = data_size;
    header.x_pixels_per_meter = 0;
    header.y_pixels_per_meter = 0;
    header.total_colors = 0;
    header.important_colors = 0;

    fwrite(&header, sizeof(BMPHeader), 1, file);

    int32_t row;
    for (row = 0; row < abs(height); ++row) {
        // 写入每行像素数据
        fwrite(image_data + (row * row_size), 1, pixel_bytes_per_row, file);
        // 写入填充字节
        uint8_t padding[4] = {0}; // 最多3字节填充,定义4字节足够
        fwrite(padding, 1, row_padding, file);
    }

    fclose(file);
}

int main()
{
    const char* input_filename = "C:/Users/pp/Desktop/r/lena.BMP";
    const char* output_filename = "C:/Users/pp/Desktop/r/ccoutput.BMP";

    uint8_t* image_data;
    int32_t width, height;
    uint16_t bits_per_pixel;

    read_bmp(input_filename, &image_data, &width, &height, &bits_per_pixel);
    printf("bits_per_pixel = %d \n", bits_per_pixel);

    write_bmp(output_filename, image_data, width, height, bits_per_pixel);

    free(image_data);

    return 0;
}

额外说明

  • 上述代码仅支持无压缩、无调色板的BMP图像(如24位真彩色),若需支持8位带调色板的图像,需在读取时额外读取调色板数据,并在写入时添加到文件中,同时调整data_offset和file_size的计算。
  • 确保输入BMP文件为标准无压缩格式,避免处理RLE压缩等特殊类型。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 22:25:53