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

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;
}

修复要点总结

  1. 修正BMP头结构体定义,严格对齐字节长度
  2. 处理BMP行对齐填充,读取/写入完整行数据
  3. 改用二进制模式读写文件,避免数据损坏
  4. 调整像素存储顺序为BGR,匹配BMP格式规范

修复后复制出的图像可正常显示,后续可通过修改像素最低有效位(LSB)实现隐写功能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 08:15:29