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
相关产品推荐
相关产品推荐

