BMP图像复制损坏问题求助:后续将实现LSB隐写
问题:BMP图像复制后损坏,求修复方案(用于后续LSB隐写)
我尝试复制未压缩的BMP图像“esteganografia.bmp”为“encoded_img.bmp”,但复制后的图像损坏,急需解决该问题,解决后会基于此实现LSB隐写。
图像Header与DIB头信息
- Name: BM
- Size: 946746
- Offset: 138
- Header Size: 124
- Width: 710
- Height: 444
- Color Planes: 1
- Bits per pixel: 24
- Compression: 0
- Image Size: 946608
相关代码
#include <stdio.h> #include <string.h> #include <stdlib.h> typedef struct bmpheader{ char name[2]; unsigned int size; int garbage; unsigned int offset; } bmpheader; typedef struct dibheader{ unsigned int headersize; unsigned int width; unsigned int height; unsigned short int colorplanes; unsigned short int bitsperpixel; unsigned int compression; unsigned int imagesize; } dibheader; typedef struct RGB{ unsigned char red; unsigned char blue; unsigned char green; } RGB; typedef struct Image{ int height; int width; RGB **rgb; } Image; int main(){ FILE *img, *encodedimg; bmpheader header; dibheader Dheader; Image picture; int i; char mensagem[11]; //fgets(mensagem, 11, stdin); // recebe a mensagem a ser codificada via terminal. // inicio da leitura da imagem. img = fopen("esteganografia.bmp", "rb"); fread(header.name, 2, 1, img); fread(&header.size, 3*sizeof(int), 1, img); printf("Name: %c%c\nSize: %lu\nOffset: %lu\n", header.name[0], header.name[1], header.size, header.offset); fread(&Dheader.headersize, sizeof(dibheader), 1 , img); printf("Header Size: %lu\nWidth: %lu\nHeight: %lu\nColor Planes: %u\nBits per pixel: %u\nCompression: %lu\nImage Size: %lu\n", Dheader.headersize, Dheader.width, Dheader.height,Dheader.colorplanes, Dheader.bitsperpixel, Dheader.compression, Dheader.imagesize); fseek(img, header.offset, SEEK_SET); picture.height = Dheader.height; picture.width = Dheader.width; picture.rgb = (RGB **) malloc(picture.height*sizeof(RGB*)); for(i=(picture.height-1); i>=0; i--){ picture.rgb[i] = (RGB*) malloc(picture.width*sizeof(RGB)); fread(picture.rgb[i], picture.width, sizeof(RGB), img); } fclose(img); // fim da leitura da imagem. // inicio da criação de uma nova img. encodedimg = fopen("encoded_img.bmp", "w"); fwrite(header.name, 2, 1, encodedimg); fwrite(&header.size, 3*sizeof(int), 1, encodedimg); fwrite(&Dheader.headersize, sizeof(dibheader), 1 , encodedimg); for(i=(picture.height-1); i>=0; i--) fwrite(picture.rgb[i], picture.width, sizeof(RGB), encodedimg); fclose(encodedimg); // fim da criação da nova img. printf("%s\n", mensagem); for(i=(picture.height-1); i>=0; i--) free(picture.rgb[i]); free(picture.rgb); return 0; }
问题原因与修复方案
你的代码存在4个核心问题导致图像损坏:
1. 文件写入模式错误
写入新图像时使用了"w"文本模式,Windows系统下会自动转换换行符,破坏二进制数据。必须改用二进制写入模式"wb"。
2. BMP头结构与读写逻辑错误
- 原
bmpheader结构体中,garbage字段应为2个2字节的保留位(不是4字节int),内存偏移错误导致头信息读取混乱。 - 用
fread(&header.size, 3*sizeof(int), 1, img)一次性读取3个int完全错误,需按BMP头固定字段长度逐个读取:size(4字节)、两个保留位(各2字节)、offset(4字节)。
3. DIB头内存对齐问题
直接用sizeof(dibheader)读写DIB头,会因结构体内存对齐导致实际读写长度大于124字节,需严格按124字节定义结构体或逐个字段读取。
4. 忽略BMP行字节对齐要求
BMP每行字节数必须是4的倍数,不足部分会填充0。你的代码直接按width*3读取每行,忽略填充字节,导致后续行数据偏移错位。
修复后的完整代码
#include <stdio.h> #include <string.h> #include <stdlib.h> // 严格对齐14字节的BMP文件头 typedef struct bmpheader{ unsigned char name[2]; unsigned int size; unsigned short reserved1; unsigned short reserved2; unsigned int offset; } bmpheader; // 124字节的DIB头(BITMAPV5HEADER) typedef struct dibheader{ unsigned int headersize; int width; int height; unsigned short colorplanes; unsigned short bitsperpixel; unsigned int compression; unsigned int imagesize; int xpelspermeter; int ypelspermeter; unsigned int usedcolors; unsigned int importantcolors; unsigned char extra[124 - 4*4 - 2*2 - 4*4]; // 填充剩余字节 } dibheader; // BMP像素存储顺序为BGR,而非RGB typedef struct RGB{ unsigned char blue; unsigned char green; unsigned char red; } RGB; typedef struct Image{ int height; int width; int row_padding; // 每行填充字节数 unsigned char **row_data; // 存储整行数据(含填充字节) } Image; int main(){ FILE *img, *encodedimg; bmpheader header; dibheader Dheader; Image picture; int i; char mensagem[11] = ""; // 打开原图像 img = fopen("esteganografia.bmp", "rb"); if(!img) { perror("打开原图像失败"); return 1; } // 读取BMP文件头 fread(header.name, 2, 1, img); fread(&header.size, sizeof(unsigned int), 1, img); fread(&header.reserved1, sizeof(unsigned short), 1, img); fread(&header.reserved2, sizeof(unsigned short), 1, img); fread(&header.offset, sizeof(unsigned int), 1, img); printf("Name: %c%c\nSize: %u\nOffset: %u\n", header.name[0], header.name[1], header.size, header.offset); // 读取DIB头 fread(&Dheader, sizeof(dibheader), 1, img); printf("Header Size: %u\nWidth: %d\nHeight: %d\nColor Planes: %u\nBits per pixel: %u\nCompression: %u\nImage Size: %u\n", Dheader.headersize, Dheader.width, Dheader.height, Dheader.colorplanes, Dheader.bitsperpixel, Dheader.compression, Dheader.imagesize); // 计算每行填充字节数 int row_bytes = Dheader.width * (Dheader.bitsperpixel / 8); picture.row_padding = (4 - (row_bytes % 4)) % 4; int total_row_bytes = row_bytes + picture.row_padding; picture.height = abs(Dheader.height); // 兼容高度为负的BMP(顶行在前) picture.width = Dheader.width; // 分配行数据内存 picture.row_data = (unsigned char **)malloc(picture.height * sizeof(unsigned char*)); if(!picture.row_data) { perror("内存分配失败"); fclose(img); return 1; } // 定位到图像数据起始位置 fseek(img, header.offset, SEEK_SET); // 读取每行完整数据(含填充字节) for(i = 0; i < picture.height; i++){ picture.row_data[i] = (unsigned char *)malloc(total_row_bytes); if(!picture.row_data[i]) { perror("内存分配失败"); fclose(img); return 1; } fread(picture.row_data[i], total_row_bytes, 1, img); } fclose(img); // 创建新图像(二进制模式) encodedimg = fopen("encoded_img.bmp", "wb"); if(!encodedimg) { perror("创建新图像失败"); return 1; } // 写入BMP文件头 fwrite(header.name, 2, 1, encodedimg); fwrite(&header.size, sizeof(unsigned int), 1, encodedimg); fwrite(&header.reserved1, sizeof(unsigned short), 1, encodedimg); fwrite(&header.reserved2, sizeof(unsigned short), 1, encodedimg); fwrite(&header.offset, sizeof(unsigned int), 1, encodedimg); // 写入DIB头 fwrite(&Dheader, sizeof(dibheader), 1, encodedimg); // 写入完整图像数据 for(i = 0; i < picture.height; i++){ fwrite(picture.row_data[i], total_row_bytes, 1, encodedimg); free(picture.row_data[i]); } free(picture.row_data); fclose(encodedimg); printf("%s\n", mensagem); return 0; }
修复要点总结
- 修正BMP头结构体定义,严格对齐字节长度
- 处理BMP行对齐填充,读取/写入完整行数据
- 改用二进制模式读写文件,避免数据损坏
- 调整像素存储顺序为BGR,匹配BMP格式规范
修复后复制出的图像可正常显示,后续可通过修改像素最低有效位(LSB)实现隐写功能。
内容的提问来源于stack exchange,提问作者Vinicius
相关产品推荐
相关产品推荐

